diff --git a/DEPS b/DEPS
index 37d1ce2..f96ec26 100644
--- a/DEPS
+++ b/DEPS
@@ -213,7 +213,7 @@
   # luci-go CIPD package version.
   # Make sure the revision is uploaded by infra-packagers builder.
   # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console
-  'luci_go': 'git_revision:a1616e207f0d9c24beefe848ee899b7a73efcb70',
+  'luci_go': 'git_revision:cb424e70e75136736a86359ef070aa96425fe7a3',
 
   # This can be overridden, e.g. with custom_vars, to build clang from HEAD
   # instead of downloading the prebuilt pinned revision.
@@ -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': '6ff07e0b02968f8c27492f0a741aaa0d14b484b1',
+  'skia_revision': '6bb185bf5496ab1c5f967968b6b7b6773913ccd6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -265,11 +265,11 @@
   # 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': '80df3d84b2e40024917c02db011756566481e1f1',
+  'swiftshader_revision': '561264b73b3658773c037fc2d4a392a7cec8137c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'efe5fd20f952e08f82046f654ab4f7dcd8f42fea',
+  'pdfium_revision': '85803557a14fc84d07d1396ca7e6de7dba891ae2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # 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': '4756e385c6030e3da50cc5ea3724bdecea1fe7e6',
+  'catapult_revision': '10d8471fc78e1b76fac51b2cc07f5086fe92a2f2',
   # 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': '92c0e96770b1cf48c7d18b33d89b3774c5c5a6b8',
+  'devtools_frontend_revision': '2f82cbd3ab06bc795016865640eba43c5b9a986a',
   # 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,11 +368,11 @@
   # 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': 'd58b7ef97bde18e1ec2e6e1396698fca13b9f54e',
+  'dawn_revision': '6737e439ab071525e36d181eae4170fe61b2927b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '83d0700f7fd722e826df187b2776632e9a93e9f2',
+  'quiche_revision': '782d7feb570b73615a3c37abd200b6ab873bd5d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -1131,7 +1131,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e210b542901532a862e9c9d025f5f72121c97316',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'df1a3c0bb803ef47515f26a65d7dfba924f7853e',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1592,7 +1592,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/android/aemu/release/linux-amd64',
-              'version': 'duzfTEopbyRA5woJYenIk_Ix1_wGeq40HGwZWMddDS4C'
+              'version': 'zEbjq6NDQ_fslV3MPdxOb2NwqKkihXg99PdQyyA3dSMC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1735,7 +1735,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '08f9efb510710548a7bc7d6ccb6c8596a9770002',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b0044c1c539bfde6ee603e3a76e21121596f367b',
+    Var('webrtc_git') + '/src.git' + '@' + '251eca20a5ae35c01c8902ef0d42b04e2c5d2816',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1762,7 +1762,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'eZ3k373CYgRxlu4JKph6e-_7xkP02swy_jePFFMiyIQC',
+          'version': 'wjKDZ5vJELJ_j3O037nIWhBEMF0cY4Y1g4tLc47hPJoC',
         },
       ],
       'dep_type': 'cipd',
@@ -1772,7 +1772,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/windows-amd64',
-          'version': 'iEqqRADI7znrc6pG-MVnc5pBZwD25koILREPC6x2AFAC',
+          'version': 'Vg04A_bOadtB2ljbA9DGKe69_Uc6pmX5mk_ABoO2R3EC',
         },
       ],
       'dep_type': 'cipd',
@@ -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@e83bd8d98b7b6e52175b61a8f0aa5f461a298906',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@62f01da9c06a73f96cd3a420464b929899edf64d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 6aea1ac0..7954847 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -788,7 +788,6 @@
     "//components/metrics:metrics_java",
     "//components/network_session_configurator/android:network_session_configurator_java",
     "//components/power_scheduler:power_scheduler_java",
-    "//components/variations:variations_java",
     "//components/viz:viz_java",
     "//content/public/android:content_java",
     "//content/public/common:common_java",
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index a3fb8ed..528d771 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -12,7 +12,6 @@
 import org.chromium.components.metrics.MetricsSwitches;
 import org.chromium.components.network_session_configurator.NetworkSessionSwitches;
 import org.chromium.components.power_scheduler.PowerSchedulerFeatures;
-import org.chromium.components.variations.VariationsFeatures;
 import org.chromium.components.viz.common.VizFeatures;
 import org.chromium.content_public.common.ContentFeatures;
 import org.chromium.content_public.common.ContentSwitches;
@@ -274,7 +273,5 @@
             Flag.baseFeature(AwFeatures.WEBVIEW_X_REQUESTED_WITH_HEADER,
                     "Enables automatic insertion of XRequestedWith header "
                             + "on all outgoing requests."),
-            Flag.baseFeature(VariationsFeatures.VARIATIONS_FAKE_CRASH_AFTER_STARTUP,
-                    "Generates a crash dump without crashing after startup."),
     };
 }
diff --git a/apps/load_and_launch_browsertest.cc b/apps/load_and_launch_browsertest.cc
index 0088036..33b6620 100644
--- a/apps/load_and_launch_browsertest.cc
+++ b/apps/load_and_launch_browsertest.cc
@@ -6,8 +6,9 @@
 // The two cases are when chrome is running and another process uses the switch
 // and when chrome is started from scratch.
 
+#include <iterator>
+
 #include "apps/switches.h"
-#include "base/cxx17_backports.h"
 #include "base/process/launch.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_switches.h"
@@ -62,7 +63,7 @@
   const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
   base::CommandLine new_cmdline(cmdline.GetProgram());
   new_cmdline.CopySwitchesFrom(cmdline, kSwitchesToCopy,
-                               base::size(kSwitchesToCopy));
+                               std::size(kSwitchesToCopy));
 
   base::FilePath app_path = test_data_dir_
       .AppendASCII("platform_apps")
@@ -98,7 +99,7 @@
   const base::CommandLine& cmdline = *base::CommandLine::ForCurrentProcess();
   base::CommandLine new_cmdline(cmdline.GetProgram());
   new_cmdline.CopySwitchesFrom(cmdline, kSwitchesToCopy,
-                               base::size(kSwitchesToCopy));
+                               std::size(kSwitchesToCopy));
 
   base::FilePath app_path = test_data_dir_
       .AppendASCII("platform_apps")
diff --git a/ash/ambient/model/ambient_animation_photo_provider.cc b/ash/ambient/model/ambient_animation_photo_provider.cc
index 13018dd2..5c0d316 100644
--- a/ash/ambient/model/ambient_animation_photo_provider.cc
+++ b/ash/ambient/model/ambient_animation_photo_provider.cc
@@ -9,7 +9,6 @@
 #include <utility>
 #include <vector>
 
-#include "ash/ambient/model/ambient_backend_model.h"
 #include "ash/ambient/resources/ambient_animation_static_resources.h"
 #include "ash/utility/cropping_util.h"
 #include "ash/utility/lottie_util.h"
@@ -18,6 +17,7 @@
 #include "base/check.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/numerics/ranges.h"
 #include "base/rand_util.h"
 #include "cc/paint/paint_flags.h"
 #include "cc/paint/skottie_frame_data.h"
@@ -30,17 +30,17 @@
 
 namespace {
 
+// TODO(esum): Experiment with different filter qualities for different asset
+// types. Thus far, "high" quality has a large impact on performance;
+// the frame rate is cut in half due to the increased computational
+// complexity. "Medium" quality is the best compromise so far with little to
+// no visible difference from "high" quality while maintaining close to 60
+// fps.
+constexpr cc::PaintFlags::FilterQuality kFilterQuality =
+    cc::PaintFlags::FilterQuality::kMedium;
+
 cc::SkottieFrameData BuildSkottieFrameData(const gfx::ImageSkia& image,
                                            float scale_factor) {
-  // TODO(esum): Experiment with different filter qualities for different asset
-  // types. Thus far, "high" quality has a large impact on performance;
-  // the frame rate is cut in half due to the increased computational
-  // complexity. "Medium" quality is the best compromise so far with little to
-  // no visible difference from "high" quality while maintaining close to 60
-  // fps.
-  static constexpr cc::PaintFlags::FilterQuality kFilterQuality =
-      cc::PaintFlags::FilterQuality::kMedium;
-
   DCHECK(!image.isNull());
   const gfx::ImageSkiaRep& image_rep = image.GetRepresentation(scale_factor);
   DCHECK(!image_rep.is_null());
@@ -190,30 +190,16 @@
 class AmbientAnimationPhotoProvider::DynamicImageAssetImpl
     : public cc::SkottieFrameDataProvider::ImageAsset {
  public:
-  // |refresh_image_cb| is invoked whenever an asset detects a new animation
-  // cycle has started and it doesn't have a new image assigned to it yet. In
-  // practice, there may be multiple dynamic assets in an animation. So the
-  // first asset that detects a new animation cycle (which is arbitrary), will
-  // trigger a refresh and all of the dynamic assets will be assigned a new
-  // image when the callback is run. That is to say, for each animation cycle,
-  // the refresh callback will be run exactly once regardless of the number of
-  // frames in a cycle or dynamic assets in the animation.
-  DynamicImageAssetImpl(base::StringPiece asset_id,
-                        absl::optional<gfx::Size> size,
-                        base::RepeatingClosure refresh_image_cb)
-      : asset_id_(asset_id),
-        size_(std::move(size)),
-        refresh_image_cb_(std::move(refresh_image_cb)) {
-    DCHECK(refresh_image_cb_);
+  DynamicImageAssetImpl(
+      base::StringPiece asset_id,
+      absl::optional<gfx::Size> size,
+      const base::WeakPtr<AmbientAnimationPhotoProvider>& provider)
+      : asset_id_(asset_id), size_(std::move(size)), provider_(provider) {
+    DCHECK(provider_);
     if (!size_)
       DLOG(ERROR) << "Dimensions unavailable for dynamic asset " << asset_id_;
   }
 
-  void AssignNewImage(gfx::ImageSkia image) {
-    DCHECK(!image.isNull());
-    new_image_ = std::move(image);
-  }
-
   cc::SkottieFrameData GetFrameData(float t, float scale_factor) override {
     DVLOG(4) << "GetFrameData for asset " << asset_id_ << " time " << t;
     bool is_first_rendered_frame =
@@ -224,19 +210,22 @@
     // the start of a new cycle.
     bool is_starting_new_cycle = t < last_observed_animation_timestamp_;
     last_observed_animation_timestamp_ = t;
-    if (!is_first_rendered_frame && !is_starting_new_cycle) {
+    if (is_first_rendered_frame || is_starting_new_cycle) {
+      DVLOG(4) << "Returning new image for dynamic asset " << asset_id_;
+      if (provider_) {
+        current_topic_ = provider_->GenerateNextTopicForDynamicAsset(*this);
+        // Force |current_frame_data_| to be reset below.
+        current_frame_data_scale_factor_ = kImageScaleFactorInvalid;
+      } else {
+        // If this corner case does somehow happen, it will only be for a brief
+        // period when the animation is being torn down.
+        DVLOG(1) << "AmbientAnimationPhotoProvider has been destroyed. Cannot "
+                    "refresh images.";
+      }
+    } else {
       DVLOG(4) << "No update required to dynamic asset at this time";
-      return GetCurrentFrameData(scale_factor);
     }
-
-    if (new_image_.isNull())
-      refresh_image_cb_.Run();
-
-    DCHECK(!new_image_.isNull());
-    current_frame_data_ = BuildSkottieFrameData(new_image_, scale_factor);
-    current_frame_data_scale_factor_ = scale_factor;
-    new_image_ = gfx::ImageSkia();
-    DVLOG(4) << "Returning new image for dynamic asset " << asset_id_;
+    SetCurrentFrameDataForScale(scale_factor);
     return current_frame_data_;
   }
 
@@ -246,28 +235,60 @@
 
  private:
   static constexpr float kAnimationTimestampInvalid = -1.f;
+  static constexpr float kImageScaleFactorInvalid = 0.f;
 
   // Private destructor since cc::SkottieFrameDataProvider::ImageAsset is a
   // ref-counted API.
   ~DynamicImageAssetImpl() override = default;
 
-  const cc::SkottieFrameData& GetCurrentFrameData(float scale_factor) {
-    DCHECK(current_frame_data_.image);
-    if (current_frame_data_scale_factor_ != scale_factor) {
-      current_frame_data_ = BuildSkottieFrameData(new_image_, scale_factor);
-      current_frame_data_scale_factor_ = scale_factor;
+  void SetCurrentFrameDataForScale(float scale_factor) {
+    static constexpr float kScaleFactorEpsilon = 0.01f;
+    DCHECK(!current_topic_.photo.isNull());
+    if (current_frame_data_scale_factor_ != kImageScaleFactorInvalid &&
+        base::IsApproximatelyEqual(current_frame_data_scale_factor_,
+                                   scale_factor, kScaleFactorEpsilon)) {
+      DVLOG(4) << "Current frame data already matches target scale.";
+      return;
     }
-    return current_frame_data_;
+
+    // First load the closest image representation from the source at the target
+    // |scale_factor|, then crop the image representation to the asset's aspect
+    // ratio.
+    const gfx::ImageSkiaRep& image_rep =
+        current_topic_.photo.GetRepresentation(scale_factor);
+    DCHECK(!image_rep.is_null());
+    cc::PaintImage paint_image;
+    if (size_) {
+      // Crop the image such that it exactly matches this asset's aspect ratio.
+      // Skottie will handle rescaling the image to the exact desired
+      // dimensions farther down the pipeline.
+      SkBitmap cropped_bitmap = CenterCropImage(image_rep.GetBitmap(), *size_);
+      // Prevents a deep copy in PaintImage::CreateFromBitmap().
+      cropped_bitmap.setImmutable();
+      paint_image = cc::PaintImage::CreateFromBitmap(std::move(cropped_bitmap));
+    } else {
+      DLOG(ERROR) << "Dynamic asset " << asset_id_
+                  << " missing dimensions in lottie file";
+      DCHECK(image_rep.has_paint_image());
+      paint_image = image_rep.paint_image();
+    }
+    current_frame_data_.image = std::move(paint_image);
+    current_frame_data_.quality = kFilterQuality;
+    current_frame_data_scale_factor_ = scale_factor;
   }
 
   const std::string asset_id_;
   const absl::optional<gfx::Size> size_;
-  const base::RepeatingClosure refresh_image_cb_;
+  const base::WeakPtr<AmbientAnimationPhotoProvider> provider_;
   // Last animation frame timestamp that was observed.
   float last_observed_animation_timestamp_ = kAnimationTimestampInvalid;
   gfx::ImageSkia new_image_;
   cc::SkottieFrameData current_frame_data_;
-  float current_frame_data_scale_factor_ = 0;
+  float current_frame_data_scale_factor_ = kImageScaleFactorInvalid;
+  // The original topic off of which |current_frame_data_| was built. May have
+  // multiple scale representations in its image in the event that a different
+  // scale factor is required while rendering.
+  PhotoWithDetails current_topic_;
 };
 
 AmbientAnimationPhotoProvider::AmbientAnimationPhotoProvider(
@@ -292,18 +313,7 @@
   // change once the animation starts rendering.
   if (IsCustomizableLottieId(asset_id)) {
     dynamic_assets_.push_back(base::MakeRefCounted<DynamicImageAssetImpl>(
-        asset_id, size,
-        base::BindRepeating(
-            &AmbientAnimationPhotoProvider::RefreshDynamicImageAssets,
-            // In practice, this could be Unretained since the provider will
-            // outlive the assets in the lottie::Animation class. But use a
-            // WeakPtr here just to put the reader's mind at ease. If the
-            // provider theoretically was destroyed before its assets, the code
-            // wouldn't crash, and the assets just wouldn't receive further
-            // photo refresh updates. Alternatively,
-            // AmbientAnimationPhotoProvider could be made ref-counted, but that
-            // is overkill to account for something that isn't an actual issue.
-            weak_factory_.GetWeakPtr())));
+        asset_id, size, weak_factory_.GetWeakPtr()));
     return dynamic_assets_.back();
   } else {
     // For static assets, the |size| isn't needed. It should match the size of
@@ -322,28 +332,62 @@
   observers_.RemoveObserver(obs);
 }
 
-void AmbientAnimationPhotoProvider::RefreshDynamicImageAssets() {
+// Invoked whenever an asset detects a new animation cycle has started. In
+// practice, there may be multiple dynamic assets in an animation. So the
+// first asset that detects a new animation cycle (which is arbitrary), will
+// cause the provider internally to find a new topic for *all* dynamic assets in
+// the animation. The provider then returns the first asset's assigned topic and
+// saves the other N - 1 assets' topics, marking them as pending. When the other
+// N - 1 assets call GenerateNextTopicForDynamicAsset() shortly after, the
+// provider simply retrieves the corresponding pending topic until the set of
+// pending topics is empty. This process then repeats at the start of the next
+// animation cycle.
+PhotoWithDetails
+AmbientAnimationPhotoProvider::GenerateNextTopicForDynamicAsset(
+    const DynamicImageAssetImpl& target_asset) {
   DVLOG(4) << __func__;
+  PhotoWithDetails topic_for_target_asset =
+      ExtractPendingTopicForDynamicAsset(target_asset);
+  if (!topic_for_target_asset.photo.isNull()) {
+    return topic_for_target_asset;
+  }
+
+  DCHECK(pending_dynamic_asset_topics_.empty())
+      << "All pending topics should have been returned before the first frame "
+         "of each animation cycle.";
   DynamicImageProvider image_provider(backend_model_->all_decoded_topics());
-  base::flat_map<std::string, std::reference_wrapper<const PhotoWithDetails>>
-      new_topics;
   for (const auto& dynamic_asset : dynamic_assets_) {
-    const PhotoWithDetails& assigned_topic =
-        image_provider.GetTopicForAssetSize(dynamic_asset->size());
-    new_topics.emplace(dynamic_asset->asset_id(), std::cref(assigned_topic));
-    gfx::ImageSkia assigned_image = assigned_topic.photo;
-    if (dynamic_asset->size()) {
-      DCHECK(assigned_image.bitmap());
-      // Crop the image such that it exactly matches the aspect ratio of the
-      // asset that it's assigned to. Skottie will handle rescaling the image to
-      // the desired ultimate dimensions farther down the pipeline.
-      assigned_image = gfx::ImageSkia::CreateFrom1xBitmap(
-          CenterCropImage(*assigned_image.bitmap(), *dynamic_asset->size()));
-    } else {
-      DLOG(ERROR) << "Dynamic asset " << dynamic_asset->asset_id()
-                  << " missing dimensions in lottie file";
-    }
-    dynamic_asset->AssignNewImage(std::move(assigned_image));
+    pending_dynamic_asset_topics_.emplace(
+        dynamic_asset.get(),
+        image_provider.GetTopicForAssetSize(dynamic_asset->size()));
+  }
+  NotifyObserverOfNewTopics();
+  topic_for_target_asset = ExtractPendingTopicForDynamicAsset(target_asset);
+  DCHECK(!topic_for_target_asset.photo.isNull())
+      << "GenerateNextTopicForDynamicAsset() for unknown asset "
+      << target_asset.asset_id();
+  return topic_for_target_asset;
+}
+
+PhotoWithDetails
+AmbientAnimationPhotoProvider::ExtractPendingTopicForDynamicAsset(
+    const DynamicImageAssetImpl& asset) {
+  auto pending_topic_iter = pending_dynamic_asset_topics_.find(&asset);
+  if (pending_topic_iter == pending_dynamic_asset_topics_.end()) {
+    return PhotoWithDetails();
+  } else {
+    PhotoWithDetails pending_topic = std::move(pending_topic_iter->second);
+    pending_dynamic_asset_topics_.erase(pending_topic_iter);
+    return pending_topic;
+  }
+}
+
+void AmbientAnimationPhotoProvider::NotifyObserverOfNewTopics() {
+  base::flat_map</*asset_id*/ std::string,
+                 std::reference_wrapper<const PhotoWithDetails>>
+      new_topics;
+  for (const auto& [asset, topic] : pending_dynamic_asset_topics_) {
+    new_topics.emplace(asset->asset_id(), std::cref(topic));
   }
   for (Observer& obs : observers_) {
     obs.OnDynamicImageAssetsRefreshed(new_topics);
diff --git a/ash/ambient/model/ambient_animation_photo_provider.h b/ash/ambient/model/ambient_animation_photo_provider.h
index 8ebd2c0a..25873d62 100644
--- a/ash/ambient/model/ambient_animation_photo_provider.h
+++ b/ash/ambient/model/ambient_animation_photo_provider.h
@@ -8,6 +8,7 @@
 #include <functional>
 #include <vector>
 
+#include "ash/ambient/model/ambient_backend_model.h"
 #include "ash/ash_export.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
@@ -19,8 +20,6 @@
 namespace ash {
 
 class AmbientAnimationStaticResources;
-class AmbientBackendModel;
-struct PhotoWithDetails;
 
 // The |SkottieFrameDataProvider| implementation for ambient mode animations is
 // tied to a single animation instance for its lifetime. It has 2 purposes:
@@ -68,13 +67,19 @@
  private:
   class DynamicImageAssetImpl;
 
-  void RefreshDynamicImageAssets();
+  PhotoWithDetails GenerateNextTopicForDynamicAsset(
+      const DynamicImageAssetImpl& asset);
+  PhotoWithDetails ExtractPendingTopicForDynamicAsset(
+      const DynamicImageAssetImpl& asset);
+  void NotifyObserverOfNewTopics();
 
   // Unowned pointers. Must outlive the |AmbientAnimationPhotoProvider|.
   const AmbientAnimationStaticResources* const static_resources_;
   const AmbientBackendModel* const backend_model_;
 
   std::vector<scoped_refptr<DynamicImageAssetImpl>> dynamic_assets_;
+  base::flat_map<const DynamicImageAssetImpl*, PhotoWithDetails>
+      pending_dynamic_asset_topics_;
   base::ObserverList<Observer> observers_;
   base::WeakPtrFactory<AmbientAnimationPhotoProvider> weak_factory_;
 };
diff --git a/ash/ambient/model/ambient_animation_photo_provider_unittest.cc b/ash/ambient/model/ambient_animation_photo_provider_unittest.cc
index 311ec2a..8cc8ccb4 100644
--- a/ash/ambient/model/ambient_animation_photo_provider_unittest.cc
+++ b/ash/ambient/model/ambient_animation_photo_provider_unittest.cc
@@ -24,6 +24,8 @@
 #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_skia_rep.h"
 #include "ui/gfx/image/image_unittest_util.h"
 
 namespace ash {
@@ -139,7 +141,8 @@
 
   std::vector<cc::SkottieFrameData> GetFrameDataForAssets(
       const std::vector<scoped_refptr<ImageAsset>>& assets,
-      float timestamp) {
+      float timestamp,
+      float scale = kTestScaleFactor) {
     // The timestamp for a given frame is not guaranteed to be the same for each
     // asset per Skottie's API. Apply jitter to ensure the provider handles this
     // correctly.
@@ -148,8 +151,7 @@
     std::vector<cc::SkottieFrameData> all_frame_data;
     for (const scoped_refptr<ImageAsset>& asset : assets) {
       float jitter = add_jitter ? kTimestampJitter : 0.f;
-      all_frame_data.push_back(
-          asset->GetFrameData(timestamp + jitter, kTestScaleFactor));
+      all_frame_data.push_back(asset->GetFrameData(timestamp + jitter, scale));
       add_jitter = !add_jitter;
     }
     return all_frame_data;
@@ -477,4 +479,27 @@
   GetFrameDataForAssets(all_assets, /*timestamp=*/0);
 }
 
+TEST_F(AmbientAnimationPhotoProviderTest, LoadsDifferentImageScaleFactor) {
+  gfx::ImageSkia test_image =
+      gfx::test::CreateImageSkia(/*width=*/10, /*height=*/10);
+  test_image.AddRepresentation(
+      gfx::ImageSkiaRep(gfx::test::CreateBitmap(/*width=*/20, /*height=*/20),
+                        /*scale=*/kTestScaleFactor * 2));
+  AddImageToModel(test_image);
+
+  std::vector<scoped_refptr<ImageAsset>> all_assets = LoadAllDynamicAssets();
+
+  // Load at 1x.
+  std::vector<cc::SkottieFrameData> frame_data =
+      GetFrameDataForAssets(all_assets, /*timestamp=*/0);
+  ASSERT_THAT(frame_data, SizeIs(kNumDynamicAssets));
+  EXPECT_THAT(frame_data, Each(HasImageDimensions(10, 10)));
+
+  // Cycle 0 Frame 1
+  frame_data = GetFrameDataForAssets(all_assets, /*timestamp=*/0.5,
+                                     /*scale=*/kTestScaleFactor * 2);
+  ASSERT_THAT(frame_data, SizeIs(kNumDynamicAssets));
+  EXPECT_THAT(frame_data, Each(HasImageDimensions(20, 20)));
+}
+
 }  // namespace ash
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index fadd269..5864111a 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1592,12 +1592,21 @@
       <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_INLINE_REPLY_BUTTON" desc="Label for the inline reply button inside a PhoneHub notification.">
         Reply
       </message>
-      <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION" desc="Description for the notification opt in view.">
-        Get notifications from your phone on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
-      </message>
       <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON" desc="Label for the set up button to start the opt in flow to show notifications from the phone.">
         Set up
       </message>
+      <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_AND_APPS_OPT_IN_DESCRIPTION" desc="Description for the notification and apps opt in view.">
+        View your phone's recent photos, media, notifications, and messaging apps on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
+      </message>
+      <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION" desc="Description for the notification opt in view.">
+        View your phone's recent photos, media, and notifications on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
+      </message>
+      <message name="IDS_ASH_PHONE_HUB_APPS_OPT_IN_DESCRIPTION" desc="Description for the apps opt in view.">
+        Access your phone's messaging apps on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
+      </message>
+      <message name="IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION" desc="Description for the camera roll opt in view.">
+        View your phone's recent photos and media on your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
+      </message>
       <message name="IDS_ASH_PHONE_HUB_NOTIFICATION_HOTSPOT_FAILED_TITLE" desc="Title inside a PhoneHub notification when enable hotspot failed to find a connection.">
         Hotspot connection failed
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_APPS_OPT_IN_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_APPS_OPT_IN_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..1acaa60
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_APPS_OPT_IN_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+17b991295887ea1bae6a8ed93a5fed80c8a21bc8
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..65a8acd
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+d2cf56861308a9dec1530f8b157d9bd43c52047c
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_AND_APPS_OPT_IN_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_AND_APPS_OPT_IN_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..3b91d73
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_AND_APPS_OPT_IN_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+168f262c0f1fdcb5820308bbebcdfdd794ff9b2e
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION.png.sha1
index 1a2833a..9502eba 100644
--- a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION.png.sha1
@@ -1 +1 @@
-02e3ebceb70c32bbbc9a8ab8b577c7d59e7c4aa6
\ No newline at end of file
+e318207d30a102492b7ab8e45f9192608c75f67c
\ No newline at end of file
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index 2f937d40..e4b4627 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -1029,6 +1029,10 @@
   dict.SetStringKey(shill::kEapKeyMgmtProperty,
                     TranslateKeyManagement(cred->key_management));
 
+  if (cred->ca_certificate_pem.has_value()) {
+    dict.SetKey(shill::kEapCaCertPemProperty,
+                TranslateStringListToValue(cred->ca_certificate_pem.value()));
+  }
   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.
diff --git a/ash/components/phonehub/BUILD.gn b/ash/components/phonehub/BUILD.gn
index ae43c4fa..1518cfd 100644
--- a/ash/components/phonehub/BUILD.gn
+++ b/ash/components/phonehub/BUILD.gn
@@ -121,6 +121,7 @@
     "//ash/services/multidevice_setup/public/cpp:prefs",
     "//ash/services/secure_channel/public/cpp/client",
     "//ash/services/secure_channel/public/mojom",
+    "//ash/webui/eche_app_ui:eche_app_ui_pref",
     "//base",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
@@ -259,6 +260,7 @@
     "//ash/services/multidevice_setup/public/cpp:test_support",
     "//ash/services/secure_channel/public/cpp/client:test_support",
     "//ash/services/secure_channel/public/mojom",
+    "//ash/webui/eche_app_ui:eche_app_ui_pref",
     "//base",
     "//base/test:test_support",
     "//chromeos/components/multidevice",
diff --git a/ash/components/phonehub/browser_tabs_model_controller.cc b/ash/components/phonehub/browser_tabs_model_controller.cc
index 2ccf0c9..13ed6a9e 100644
--- a/ash/components/phonehub/browser_tabs_model_controller.cc
+++ b/ash/components/phonehub/browser_tabs_model_controller.cc
@@ -9,8 +9,8 @@
 
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 }  // namespace
 
diff --git a/ash/components/phonehub/browser_tabs_model_controller_unittest.cc b/ash/components/phonehub/browser_tabs_model_controller_unittest.cc
index b9a714c..a64cacb 100644
--- a/ash/components/phonehub/browser_tabs_model_controller_unittest.cc
+++ b/ash/components/phonehub/browser_tabs_model_controller_unittest.cc
@@ -13,8 +13,8 @@
 namespace ash {
 namespace phonehub {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 class BrowserTabsModelControllerTest : public testing::Test {
  protected:
diff --git a/ash/components/phonehub/camera_roll_manager_impl.cc b/ash/components/phonehub/camera_roll_manager_impl.cc
index f94a43d..e89af9e 100644
--- a/ash/components/phonehub/camera_roll_manager_impl.cc
+++ b/ash/components/phonehub/camera_roll_manager_impl.cc
@@ -28,8 +28,6 @@
 
 namespace {
 
-// TODO(https://crbug.com/1164001): remove after migrating to ash.
-namespace multidevice_setup = ::chromeos::multidevice_setup;
 namespace secure_channel = ::chromeos::secure_channel;
 
 constexpr int kMaxCameraRollItemCount = 4;
@@ -225,11 +223,11 @@
 }
 
 bool CameraRollManagerImpl::IsCameraRollSettingEnabled() {
-  chromeos::multidevice_setup::mojom::FeatureState camera_roll_feature_state =
+  multidevice_setup::mojom::FeatureState camera_roll_feature_state =
       multidevice_setup_client_->GetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
+          multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
   return camera_roll_feature_state ==
-         chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser;
+         multidevice_setup::mojom::FeatureState::kEnabledByUser;
 }
 
 void CameraRollManagerImpl::OnFeatureStatesChanged(
@@ -271,15 +269,15 @@
     return;
   }
 
-  chromeos::multidevice_setup::mojom::FeatureState feature_state =
+  multidevice_setup::mojom::FeatureState feature_state =
       multidevice_setup_client_->GetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
+          multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
   switch (feature_state) {
-    case chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser:
+    case multidevice_setup::mojom::FeatureState::kDisabledByUser:
       ui_state_ = CameraRollUiState::SHOULD_HIDE;
       ;
       break;
-    case chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser:
+    case multidevice_setup::mojom::FeatureState::kEnabledByUser:
       if (current_items().empty()) {
         ui_state_ = CameraRollUiState::SHOULD_HIDE;
       } else {
diff --git a/ash/components/phonehub/camera_roll_manager_impl_unittest.cc b/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
index 8cbdb17..d079e6592 100644
--- a/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
+++ b/ash/components/phonehub/camera_roll_manager_impl_unittest.cc
@@ -34,7 +34,7 @@
 using BatchDecodeCallback =
     base::OnceCallback<void(BatchDecodeResult,
                             const std::vector<CameraRollItem>&)>;
-using FeatureState = chromeos::multidevice_setup::mojom::FeatureState;
+using FeatureState = multidevice_setup::mojom::FeatureState;
 using FileTransferStatus = chromeos::secure_channel::mojom::FileTransferStatus;
 
 class FakeObserver : public CameraRollManager::Observer {
@@ -224,8 +224,7 @@
 
   void SetCameraRollFeatureState(FeatureState feature_state) {
     fake_multidevice_setup_client_->SetFeatureState(
-        chromeos::multidevice_setup::mojom::Feature::kPhoneHubCameraRoll,
-        feature_state);
+        multidevice_setup::mojom::Feature::kPhoneHubCameraRoll, feature_state);
   }
 
   void UngrantAndroidStoragePermission() {
diff --git a/ash/components/phonehub/cros_state_sender.cc b/ash/components/phonehub/cros_state_sender.cc
index d07620c..f71cf278 100644
--- a/ash/components/phonehub/cros_state_sender.cc
+++ b/ash/components/phonehub/cros_state_sender.cc
@@ -13,8 +13,8 @@
 namespace phonehub {
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 // The minimum time to wait before checking whether the phone has responded to
 // status messages sent by CrosStateSender, and re-sending the status messages
diff --git a/ash/components/phonehub/cros_state_sender_unittest.cc b/ash/components/phonehub/cros_state_sender_unittest.cc
index 5df9c154..1bb9552 100644
--- a/ash/components/phonehub/cros_state_sender_unittest.cc
+++ b/ash/components/phonehub/cros_state_sender_unittest.cc
@@ -17,8 +17,8 @@
 namespace ash {
 namespace phonehub {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 class CrosStateSenderTest : public testing::Test {
  protected:
diff --git a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
index 3f3c9464..b6b652f7 100644
--- a/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
+++ b/ash/components/phonehub/fake_multidevice_feature_access_manager.cc
@@ -44,6 +44,19 @@
   NotifyCameraRollAccessChanged();
 }
 
+void FakeMultideviceFeatureAccessManager::SetAppsAccessStatusInternal(
+    AccessStatus apps_access_status) {
+  if (apps_access_status_ == apps_access_status)
+    return;
+
+  apps_access_status_ = apps_access_status;
+}
+
+MultideviceFeatureAccessManager::AccessStatus
+FakeMultideviceFeatureAccessManager::GetAppsAccessStatus() const {
+  return apps_access_status_;
+}
+
 MultideviceFeatureAccessManager::AccessStatus
 FakeMultideviceFeatureAccessManager::GetNotificationAccessStatus() const {
   return notification_access_status_;
diff --git a/ash/components/phonehub/fake_multidevice_feature_access_manager.h b/ash/components/phonehub/fake_multidevice_feature_access_manager.h
index d33e68f..59ace9b 100644
--- a/ash/components/phonehub/fake_multidevice_feature_access_manager.h
+++ b/ash/components/phonehub/fake_multidevice_feature_access_manager.h
@@ -39,10 +39,15 @@
   void SetCameraRollAccessStatusInternal(
       AccessStatus camera_roll_access_status) override;
   AccessStatus GetCameraRollAccessStatus() const override;
+  AccessStatus GetAppsAccessStatus() const override;
+
+  // Test-only.
+  void SetAppsAccessStatusInternal(AccessStatus apps_access_status);
 
  private:
   AccessStatus notification_access_status_;
   AccessStatus camera_roll_access_status_;
+  AccessStatus apps_access_status_;
   AccessProhibitedReason access_prohibited_reason_;
   bool has_notification_setup_ui_been_dismissed_ = false;
 };
diff --git a/ash/components/phonehub/fake_recent_apps_interaction_handler.cc b/ash/components/phonehub/fake_recent_apps_interaction_handler.cc
index 1937cb3..6c3aa60 100644
--- a/ash/components/phonehub/fake_recent_apps_interaction_handler.cc
+++ b/ash/components/phonehub/fake_recent_apps_interaction_handler.cc
@@ -9,7 +9,7 @@
 namespace ash {
 namespace phonehub {
 
-using FeatureState = ::chromeos::multidevice_setup::mojom::FeatureState;
+using FeatureState = multidevice_setup::mojom::FeatureState;
 
 FakeRecentAppsInteractionHandler::FakeRecentAppsInteractionHandler() = default;
 
diff --git a/ash/components/phonehub/fake_recent_apps_interaction_handler.h b/ash/components/phonehub/fake_recent_apps_interaction_handler.h
index fd07185..558d81f 100644
--- a/ash/components/phonehub/fake_recent_apps_interaction_handler.h
+++ b/ash/components/phonehub/fake_recent_apps_interaction_handler.h
@@ -24,7 +24,7 @@
   ~FakeRecentAppsInteractionHandler() override;
 
   void OnFeatureStateChanged(
-      chromeos::multidevice_setup::mojom::FeatureState feature_state);
+      multidevice_setup::mojom::FeatureState feature_state);
 
   size_t HandledRecentAppsCount(const std::string& package_name) const {
     return package_name_to_click_count_.at(package_name);
@@ -47,8 +47,8 @@
   void ComputeAndUpdateUiState();
 
   size_t recent_app_click_observer_count_ = 0;
-  chromeos::multidevice_setup::mojom::FeatureState feature_state_ =
-      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser;
+  multidevice_setup::mojom::FeatureState feature_state_ =
+      multidevice_setup::mojom::FeatureState::kDisabledByUser;
 
   std::vector<std::pair<Notification::AppMetadata, base::Time>>
       recent_apps_metadata_;
diff --git a/ash/components/phonehub/feature_status_provider_impl.cc b/ash/components/phonehub/feature_status_provider_impl.cc
index 1b93068..a102ad70 100644
--- a/ash/components/phonehub/feature_status_provider_impl.cc
+++ b/ash/components/phonehub/feature_status_provider_impl.cc
@@ -24,9 +24,9 @@
 using ::chromeos::multidevice::RemoteDeviceRefList;
 using ::chromeos::multidevice::SoftwareFeature;
 using ::chromeos::multidevice::SoftwareFeatureState;
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 bool IsEligiblePhoneHubHost(const RemoteDeviceRef& device) {
   // Device must be capable of being a multi-device host.
diff --git a/ash/components/phonehub/feature_status_provider_impl_unittest.cc b/ash/components/phonehub/feature_status_provider_impl_unittest.cc
index a8024f07..0db2e06 100644
--- a/ash/components/phonehub/feature_status_provider_impl_unittest.cc
+++ b/ash/components/phonehub/feature_status_provider_impl_unittest.cc
@@ -24,9 +24,9 @@
 namespace phonehub {
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 const char kLocalDeviceBluetoothAddress[] = "01:23:45:67:89:AB";
 const char kPhoneBluetoothAddress[] = "23:45:67:89:AB:CD";
diff --git a/ash/components/phonehub/multidevice_feature_access_manager.h b/ash/components/phonehub/multidevice_feature_access_manager.h
index 4cff2c4a..dfaf4b37 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager.h
+++ b/ash/components/phonehub/multidevice_feature_access_manager.h
@@ -83,6 +83,8 @@
 
   virtual AccessStatus GetCameraRollAccessStatus() const = 0;
 
+  virtual AccessStatus GetAppsAccessStatus() const = 0;
+
   // Returns the reason notification access status is prohibited. The return
   // result is valid if the current access status (from GetAccessStatus())
   // is AccessStatus::kProhibited. Otherwise, the result is undefined and should
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.cc b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
index cf81a26..22ea4db4 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.cc
@@ -8,6 +8,7 @@
 #include "ash/components/phonehub/message_sender.h"
 #include "ash/components/phonehub/pref_names.h"
 #include "ash/components/phonehub/util/histogram_util.h"
+#include "ash/webui/eche_app_ui/pref_names.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -74,6 +75,15 @@
   return static_cast<AccessStatus>(status);
 }
 
+MultideviceFeatureAccessManager::AccessStatus
+MultideviceFeatureAccessManagerImpl::GetAppsAccessStatus() const {
+  // TODO(samchiu): The AppsAccessStatus will be updated by eche_app_ui
+  // component only. We should listen to pref change and update it to
+  // MultiDeviceFeatureOptInView.
+  int status = pref_service_->GetInteger(eche_app::prefs::kAppsAccessStatus);
+  return static_cast<AccessStatus>(status);
+}
+
 MultideviceFeatureAccessManagerImpl::AccessProhibitedReason
 MultideviceFeatureAccessManagerImpl::GetNotificationAccessProhibitedReason()
     const {
diff --git a/ash/components/phonehub/multidevice_feature_access_manager_impl.h b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
index fe27814..0670373 100644
--- a/ash/components/phonehub/multidevice_feature_access_manager_impl.h
+++ b/ash/components/phonehub/multidevice_feature_access_manager_impl.h
@@ -46,6 +46,7 @@
   void SetCameraRollAccessStatusInternal(
       AccessStatus camera_roll_access_status) override;
   AccessStatus GetCameraRollAccessStatus() const override;
+  AccessStatus GetAppsAccessStatus() const override;
   void OnSetupRequested() override;
 
   bool HasMultideviceFeatureSetupUiBeenDismissed() const override;
diff --git a/ash/components/phonehub/multidevice_setup_state_updater.cc b/ash/components/phonehub/multidevice_setup_state_updater.cc
index d999dc8..8db51c3 100644
--- a/ash/components/phonehub/multidevice_setup_state_updater.cc
+++ b/ash/components/phonehub/multidevice_setup_state_updater.cc
@@ -17,9 +17,9 @@
 
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 }  // namespace
 
diff --git a/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc b/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc
index 6b00325..5a28db2 100644
--- a/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc
+++ b/ash/components/phonehub/multidevice_setup_state_updater_unittest.cc
@@ -13,9 +13,9 @@
 namespace ash {
 namespace phonehub {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 // TODO(https://crbug.com/1164001): remove after migrating to namespace ash.
 namespace multidevice_setup = ::chromeos::multidevice_setup;
diff --git a/ash/components/phonehub/notification_manager_impl.cc b/ash/components/phonehub/notification_manager_impl.cc
index 4acdb35..d992be44 100644
--- a/ash/components/phonehub/notification_manager_impl.cc
+++ b/ash/components/phonehub/notification_manager_impl.cc
@@ -13,8 +13,8 @@
 namespace ash {
 namespace phonehub {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 NotificationManagerImpl::NotificationManagerImpl(
     MessageSender* message_sender,
diff --git a/ash/components/phonehub/notification_manager_impl.h b/ash/components/phonehub/notification_manager_impl.h
index 9e405d4..62fb7ea 100644
--- a/ash/components/phonehub/notification_manager_impl.h
+++ b/ash/components/phonehub/notification_manager_impl.h
@@ -40,8 +40,7 @@
   MessageSender* message_sender_;
   UserActionRecorder* user_action_recorder_;
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
-  chromeos::multidevice_setup::mojom::FeatureState
-      notifications_feature_status_;
+  multidevice_setup::mojom::FeatureState notifications_feature_status_;
 };
 
 }  // namespace phonehub
diff --git a/ash/components/phonehub/notification_manager_impl_unittest.cc b/ash/components/phonehub/notification_manager_impl_unittest.cc
index 42d1d2a..bcc5e2b3 100644
--- a/ash/components/phonehub/notification_manager_impl_unittest.cc
+++ b/ash/components/phonehub/notification_manager_impl_unittest.cc
@@ -18,8 +18,8 @@
 namespace phonehub {
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 const char16_t kAppName[] = u"Test App";
 const char kPackageName[] = "com.google.testapp";
diff --git a/ash/components/phonehub/onboarding_ui_tracker_impl.cc b/ash/components/phonehub/onboarding_ui_tracker_impl.cc
index 36f738e..4a42967 100644
--- a/ash/components/phonehub/onboarding_ui_tracker_impl.cc
+++ b/ash/components/phonehub/onboarding_ui_tracker_impl.cc
@@ -60,7 +60,7 @@
   // The user is already opted into Better Together, but not Phone Hub.
   if (status == FeatureStatus::kDisabled) {
     multidevice_setup_client_->SetFeatureEnabledState(
-        chromeos::multidevice_setup::mojom::Feature::kPhoneHub,
+        multidevice_setup::mojom::Feature::kPhoneHub,
         /*enabled=*/true, /*auth_token=*/absl::nullopt, base::DoNothing());
     util::LogFeatureOptInEntryPoint(util::OptInEntryPoint::kOnboardingFlow);
     return;
@@ -77,14 +77,13 @@
 void OnboardingUiTrackerImpl::OnFeatureStatesChanged(
     const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
         feature_states_map) {
-  const chromeos::multidevice_setup::mojom::FeatureState phonehub_state =
-      feature_states_map
-          .find(chromeos::multidevice_setup::mojom::Feature::kPhoneHub)
+  const multidevice_setup::mojom::FeatureState phonehub_state =
+      feature_states_map.find(multidevice_setup::mojom::Feature::kPhoneHub)
           ->second;
   // User has gone through the onboarding process, prevent the UI from
   // displaying again.
   if (phonehub_state ==
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser) {
+      multidevice_setup::mojom::FeatureState::kEnabledByUser) {
     pref_service_->SetBoolean(prefs::kHideOnboardingUi, true);
     UpdateShouldShowOnboardingUi();
   }
diff --git a/ash/components/phonehub/onboarding_ui_tracker_impl_unittest.cc b/ash/components/phonehub/onboarding_ui_tracker_impl_unittest.cc
index ff0162e..1f802bda 100644
--- a/ash/components/phonehub/onboarding_ui_tracker_impl_unittest.cc
+++ b/ash/components/phonehub/onboarding_ui_tracker_impl_unittest.cc
@@ -20,7 +20,8 @@
 namespace phonehub {
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 class FakeObserver : public OnboardingUiTracker::Observer {
  public:
@@ -72,8 +73,7 @@
     fake_feature_status_provider_->SetStatus(feature_status);
   }
 
-  void SetFeatureState(chromeos::multidevice_setup::mojom::Feature feature,
-                       chromeos::multidevice_setup::mojom::FeatureState state) {
+  void SetFeatureState(Feature feature, FeatureState state) {
     fake_multidevice_setup_client_.SetFeatureState(feature, state);
   }
 
@@ -177,25 +177,19 @@
 
   // Simulate feature disabled feature. Expect onboarding UI to still be
   // displayed.
-  SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kPhoneHub,
-      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser);
+  SetFeatureState(Feature::kPhoneHub, FeatureState::kDisabledByUser);
   EXPECT_EQ(GetOnShouldShowOnboardingUiChangedCallCount(), 1U);
   EXPECT_TRUE(ShouldShowOnboardingUi());
 
   // Toggle the feature to be enabled. Expect onboarding UI to no longer be
   // displayed.
-  SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kPhoneHub,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+  SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
   EXPECT_EQ(GetOnShouldShowOnboardingUiChangedCallCount(), 2U);
   EXPECT_FALSE(ShouldShowOnboardingUi());
 
   // Toggle the feature back to disabled. Expect onboarding UI to still be
   // hidden.
-  SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kPhoneHub,
-      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser);
+  SetFeatureState(Feature::kPhoneHub, FeatureState::kDisabledByUser);
   EXPECT_EQ(GetOnShouldShowOnboardingUiChangedCallCount(), 2U);
   EXPECT_FALSE(ShouldShowOnboardingUi());
 }
diff --git a/ash/components/phonehub/phone_status_processor.cc b/ash/components/phonehub/phone_status_processor.cc
index 49b3b6b..4651daf6 100644
--- a/ash/components/phonehub/phone_status_processor.cc
+++ b/ash/components/phonehub/phone_status_processor.cc
@@ -227,11 +227,10 @@
 
 void PhoneStatusProcessor::ProcessReceivedNotifications(
     const RepeatedPtrField<proto::Notification>& notification_protos) {
-  chromeos::multidevice_setup::mojom::FeatureState feature_state =
+  multidevice_setup::mojom::FeatureState feature_state =
       multidevice_setup_client_->GetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kPhoneHubNotifications);
-  if (feature_state !=
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser) {
+          multidevice_setup::mojom::Feature::kPhoneHubNotifications);
+  if (feature_state != multidevice_setup::mojom::FeatureState::kEnabledByUser) {
     // Do not process any notifications if notifications are not enabled in
     // settings.
     return;
diff --git a/ash/components/phonehub/phone_status_processor_unittest.cc b/ash/components/phonehub/phone_status_processor_unittest.cc
index aa56c0c..655eb0f3 100644
--- a/ash/components/phonehub/phone_status_processor_unittest.cc
+++ b/ash/components/phonehub/phone_status_processor_unittest.cc
@@ -35,9 +35,9 @@
 namespace ash {
 namespace phonehub {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 // A fake processor that immediately adds or removes notifications.
 class FakeNotificationProcessor : public NotificationProcessor {
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
index c776589..704ec91 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl.cc
@@ -13,9 +13,9 @@
 namespace ash {
 namespace phonehub {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 using HostStatusWithDevice =
     ::chromeos::multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice;
 using FeatureStatesMap =
diff --git a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
index 1fda1f20..a865752 100644
--- a/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
+++ b/ash/components/phonehub/recent_apps_interaction_handler_impl_unittest.cc
@@ -19,8 +19,8 @@
 namespace phonehub {
 namespace {
 
-using FeatureState = ::chromeos::multidevice_setup::mojom::FeatureState;
-using HostStatus = ::chromeos::multidevice_setup::mojom::HostStatus;
+using FeatureState = multidevice_setup::mojom::FeatureState;
+using HostStatus = multidevice_setup::mojom::HostStatus;
 
 // Garbage color for the purpose of verification in these tests.
 const SkColor kIconColor = SkColorSetRGB(0x12, 0x34, 0x56);
@@ -128,12 +128,12 @@
 
   void SetEcheFeatureState(FeatureState feature_state) {
     fake_multidevice_setup_client_->SetFeatureState(
-        chromeos::multidevice_setup::mojom::Feature::kEche, feature_state);
+        multidevice_setup::mojom::Feature::kEche, feature_state);
   }
 
   void SetPhoneHubNotificationsFeatureState(FeatureState feature_state) {
     fake_multidevice_setup_client_->SetFeatureState(
-        chromeos::multidevice_setup::mojom::Feature::kPhoneHubNotifications,
+        multidevice_setup::mojom::Feature::kPhoneHubNotifications,
         feature_state);
   }
 
diff --git a/ash/components/phonehub/tether_controller_impl.cc b/ash/components/phonehub/tether_controller_impl.cc
index ca6acd7..a35b76c 100644
--- a/ash/components/phonehub/tether_controller_impl.cc
+++ b/ash/components/phonehub/tether_controller_impl.cc
@@ -15,13 +15,13 @@
 namespace {
 
 using ::chromeos::multidevice_setup::MultiDeviceSetupClient;
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
 using ::chromeos::network_config::mojom::ConnectionStateType;
 using ::chromeos::network_config::mojom::DeviceStatePropertiesPtr;
 using ::chromeos::network_config::mojom::FilterType;
 using ::chromeos::network_config::mojom::NetworkType;
 using ::chromeos::network_config::mojom::StartConnectResult;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 // TODO(https://crbug.com/1164001): remove after migrating to namespace ash.
 namespace network_config = ::chromeos::network_config;
diff --git a/ash/components/phonehub/tether_controller_impl_unittest.cc b/ash/components/phonehub/tether_controller_impl_unittest.cc
index fcb08499..fcf919d 100644
--- a/ash/components/phonehub/tether_controller_impl_unittest.cc
+++ b/ash/components/phonehub/tether_controller_impl_unittest.cc
@@ -25,11 +25,11 @@
 namespace phonehub {
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
 using ::chromeos::network_config::mojom::ConnectionStateType;
 using ::chromeos::network_config::mojom::NetworkStatePropertiesPtr;
 using ::chromeos::network_config::mojom::StartConnectResult;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 constexpr char kWifiGuid[] = "WifiGuid";
 constexpr char kTetherGuid[] = "TetherGuid";
diff --git a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
index ae7ea79..3ca6e65f 100644
--- a/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
+++ b/ash/components/proximity_auth/proximity_auth_profile_pref_manager.cc
@@ -18,9 +18,8 @@
 
 namespace proximity_auth {
 namespace {
-
-using chromeos::multidevice_setup::mojom::Feature;
-using chromeos::multidevice_setup::mojom::FeatureState;
+using ::ash::multidevice_setup::mojom::Feature;
+using ::ash::multidevice_setup::mojom::FeatureState;
 }  // namespace
 
 ProximityAuthProfilePrefManager::ProximityAuthProfilePrefManager(
diff --git a/ash/components/proximity_auth/proximity_auth_profile_pref_manager_unittest.cc b/ash/components/proximity_auth/proximity_auth_profile_pref_manager_unittest.cc
index f107944f..14e9b41a 100644
--- a/ash/components/proximity_auth/proximity_auth_profile_pref_manager_unittest.cc
+++ b/ash/components/proximity_auth/proximity_auth_profile_pref_manager_unittest.cc
@@ -21,8 +21,8 @@
 namespace proximity_auth {
 namespace {
 
-using chromeos::multidevice_setup::mojom::Feature;
-using chromeos::multidevice_setup::mojom::FeatureState;
+using ::ash::multidevice_setup::mojom::Feature;
+using ::ash::multidevice_setup::mojom::FeatureState;
 
 const char kUserEmail[] = "testuser@example.com";
 
diff --git a/ash/components/tether/tether_host_fetcher_impl.cc b/ash/components/tether/tether_host_fetcher_impl.cc
index fffd7e57..70bc3eb 100644
--- a/ash/components/tether/tether_host_fetcher_impl.cc
+++ b/ash/components/tether/tether_host_fetcher_impl.cc
@@ -105,7 +105,7 @@
   multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice
       host_status_with_device = multidevice_setup_client_->GetHostStatus();
   if (host_status_with_device.first ==
-      chromeos::multidevice_setup::mojom::HostStatus::kHostVerified) {
+      multidevice_setup::mojom::HostStatus::kHostVerified) {
     host_list.push_back(*host_status_with_device.second);
   }
   return host_list;
diff --git a/ash/components/tether/tether_host_fetcher_impl_unittest.cc b/ash/components/tether/tether_host_fetcher_impl_unittest.cc
index 0f7dcb8..75f48e5 100644
--- a/ash/components/tether/tether_host_fetcher_impl_unittest.cc
+++ b/ash/components/tether/tether_host_fetcher_impl_unittest.cc
@@ -26,9 +26,6 @@
 
 namespace {
 
-// TODO(https://crbug.com/1164001): remove when multidevice_setup moved to ash
-namespace multidevice_setup = ::chromeos::multidevice_setup;
-
 const size_t kNumTestDevices = 5;
 
 class TestObserver : public TetherHostFetcher::Observer {
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 214fa86..457e9a1 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -470,10 +470,6 @@
 const base::Feature kEcheCustomWidget{"EcheCustomWidget",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables the naive resize for the Eche window.
-const base::Feature kEcheSWAResizing{"EcheSWAResizing",
-                                     base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables the Debug Mode of Eche.
 const base::Feature kEcheSWADebugMode{"EcheSWADebugMode",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
@@ -1613,10 +1609,6 @@
   return base::FeatureList::IsEnabled(kEcheCustomWidget);
 }
 
-bool IsEcheSWAResizingEnabled() {
-  return base::FeatureList::IsEnabled(kEcheSWAResizing);
-}
-
 bool IsEcheSWADebugModeEnabled() {
   return base::FeatureList::IsEnabled(kEcheSWADebugMode);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index ce61a70..8da554cf 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -195,7 +195,6 @@
 extern const base::Feature kEchePhoneHubPermissionsOnboarding;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheCustomWidget;
-COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheSWAResizing;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheSWADebugMode;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kEcheSWAInBackground;
 COMPONENT_EXPORT(ASH_CONSTANTS)
@@ -597,7 +596,6 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEchePhoneHubPermissionsOnboarding();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWAEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheCustomWidgetEnabled();
-COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWAResizingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWADebugModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsEcheSWAInBackgroundEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsESimPolicyEnabled();
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.cc b/ash/multi_device_setup/multi_device_notification_presenter.cc
index 4319bc2..c846a07b 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.cc
+++ b/ash/multi_device_setup/multi_device_notification_presenter.cc
@@ -199,7 +199,7 @@
         case 0:  // "Turn on" button
           PA_LOG(INFO) << "Enabling Wi-Fi Sync.";
           multidevice_setup_remote_->SetFeatureEnabledState(
-              chromeos::multidevice_setup::mojom::Feature::kWifiSync,
+              multidevice_setup::mojom::Feature::kWifiSync,
               /*enabled=*/true, /*auth_token=*/absl::nullopt,
               /*callback=*/base::DoNothing());
           break;
diff --git a/ash/multi_device_setup/multi_device_notification_presenter.h b/ash/multi_device_setup/multi_device_notification_presenter.h
index 81b4c2a..c48ba7c 100644
--- a/ash/multi_device_setup/multi_device_notification_presenter.h
+++ b/ash/multi_device_setup/multi_device_notification_presenter.h
@@ -40,7 +40,7 @@
 // Note that if one notification is showing and another one is triggered, the
 // old text is replaced (if it's different) and the notification pops up again.
 class ASH_EXPORT MultiDeviceNotificationPresenter
-    : public chromeos::multidevice_setup::mojom::AccountStatusChangeDelegate,
+    : public multidevice_setup::mojom::AccountStatusChangeDelegate,
       public SessionObserver,
       public message_center::MessageCenterObserver {
  public:
@@ -133,10 +133,9 @@
   // Status::kNoNotificationVisible if there isn't one.
   Status notification_status_ = Status::kNoNotificationVisible;
 
-  mojo::Remote<chromeos::multidevice_setup::mojom::MultiDeviceSetup>
+  mojo::Remote<multidevice_setup::mojom::MultiDeviceSetup>
       multidevice_setup_remote_;
-  mojo::Receiver<
-      chromeos::multidevice_setup::mojom::AccountStatusChangeDelegate>
+  mojo::Receiver<multidevice_setup::mojom::AccountStatusChangeDelegate>
       receiver_{this};
 
   base::WeakPtrFactory<MultiDeviceNotificationPresenter> weak_ptr_factory_{
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
index b03d577a..287c91e 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier.cc
@@ -11,6 +11,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 AccountStatusChangeDelegateNotifier::AccountStatusChangeDelegateNotifier() =
     default;
 
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier.h b/ash/services/multidevice_setup/account_status_change_delegate_notifier.h
index 86e2bc2..05203f9 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier.h
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier.h
@@ -28,7 +28,9 @@
   virtual ~AccountStatusChangeDelegateNotifier();
 
   void SetAccountStatusChangeDelegateRemote(
-      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate_remote);
+      mojo::PendingRemote<
+          ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+          delegate_remote);
 
  protected:
   AccountStatusChangeDelegateNotifier();
@@ -37,7 +39,7 @@
   // SetAccountStatusChangeDelegateRemote() is called.
   virtual void OnDelegateSet();
 
-  mojom::AccountStatusChangeDelegate* delegate() {
+  ash::multidevice_setup::mojom::AccountStatusChangeDelegate* delegate() {
     return delegate_remote_.is_bound() ? delegate_remote_.get() : nullptr;
   }
 
@@ -50,7 +52,8 @@
 
   void FlushForTesting();
 
-  mojo::Remote<mojom::AccountStatusChangeDelegate> delegate_remote_;
+  mojo::Remote<ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+      delegate_remote_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
index 0c53af4..d37f89f 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.cc
@@ -21,6 +21,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const int64_t kTimestampNotSet = 0;
 const char kNoHost[] = "";
 
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
index bc336e8..af1790f 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl.h
@@ -69,7 +69,9 @@
   ~AccountStatusChangeDelegateNotifierImpl() override;
 
   void SetAccountStatusChangeDelegateRemote(
-      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate_remote);
+      mojo::PendingRemote<
+          ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+          delegate_remote);
 
  private:
   friend class MultiDeviceSetupAccountStatusChangeDelegateNotifierTest;
@@ -106,7 +108,8 @@
       const HostStatusProvider::HostStatusWithDevice& host_status_with_device);
   void CheckForNoLongerNewUserEvent(
       const HostStatusProvider::HostStatusWithDevice& host_status_with_device,
-      const absl::optional<mojom::HostStatus> host_status_before_update);
+      const absl::optional<ash::multidevice_setup::mojom::HostStatus>
+          host_status_before_update);
   void CheckForExistingUserHostSwitchedEvent(
       const HostStatusProvider::HostStatusWithDevice& host_status_with_device,
       const absl::optional<std::string>& verified_host_device_id_before_update);
@@ -122,9 +125,11 @@
   absl::optional<std::string> verified_host_device_id_from_most_recent_update_;
 
   // Set to absl::nullopt until the first host status update.
-  absl::optional<mojom::HostStatus> host_status_from_most_recent_update_;
+  absl::optional<ash::multidevice_setup::mojom::HostStatus>
+      host_status_from_most_recent_update_;
 
-  mojo::Remote<mojom::AccountStatusChangeDelegate> delegate_remote_;
+  mojo::Remote<ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+      delegate_remote_;
   HostStatusProvider* host_status_provider_;
   PrefService* pref_service_;
   HostDeviceTimestampManager* host_device_timestamp_manager_;
diff --git a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
index cf761bb..e3c8e72 100644
--- a/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
+++ b/ash/services/multidevice_setup/account_status_change_delegate_notifier_impl_unittest.cc
@@ -27,6 +27,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const int64_t kTestTimeMillis = 1500000000000;
 const char kFakePhoneKey[] = "fake-phone-key";
 const char kFakePhoneName[] = "Phony Phone";
diff --git a/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc b/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
index 03fabb31..ec57d66 100644
--- a/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
+++ b/ash/services/multidevice_setup/android_sms_app_installing_status_observer.cc
@@ -18,6 +18,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const char kShouldAttemptReenable[] = "android_sms.should_attempt_reenable";
 
 }  // namespace
diff --git a/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc b/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
index 905bc2a..d1176d7 100644
--- a/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
+++ b/ash/services/multidevice_setup/android_sms_app_installing_status_observer_unittest.cc
@@ -20,6 +20,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const char kFakePhoneKey[] = "fake-phone-key";
 const char kFakePhoneName[] = "Phony Phone";
 const char kShouldAttemptReenable[] = "android_sms.should_attempt_reenable";
diff --git a/ash/services/multidevice_setup/fake_account_status_change_delegate.cc b/ash/services/multidevice_setup/fake_account_status_change_delegate.cc
index dd95deb..b3b21f0 100644
--- a/ash/services/multidevice_setup/fake_account_status_change_delegate.cc
+++ b/ash/services/multidevice_setup/fake_account_status_change_delegate.cc
@@ -8,6 +8,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FakeAccountStatusChangeDelegate::FakeAccountStatusChangeDelegate() = default;
 
 FakeAccountStatusChangeDelegate::~FakeAccountStatusChangeDelegate() = default;
diff --git a/ash/services/multidevice_setup/fake_account_status_change_delegate.h b/ash/services/multidevice_setup/fake_account_status_change_delegate.h
index 3bde458..56ae27e6 100644
--- a/ash/services/multidevice_setup/fake_account_status_change_delegate.h
+++ b/ash/services/multidevice_setup/fake_account_status_change_delegate.h
@@ -15,7 +15,7 @@
 
 // Fake mojom::AccountStatusChangeDelegate implementation for tests.
 class FakeAccountStatusChangeDelegate
-    : public mojom::AccountStatusChangeDelegate {
+    : public ash::multidevice_setup::mojom::AccountStatusChangeDelegate {
  public:
   FakeAccountStatusChangeDelegate();
 
@@ -26,7 +26,9 @@
 
   ~FakeAccountStatusChangeDelegate() override;
 
-  mojo::PendingRemote<mojom::AccountStatusChangeDelegate> GenerateRemote();
+  mojo::PendingRemote<
+      ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+  GenerateRemote();
 
   size_t num_new_user_potential_host_events_handled() const {
     return num_new_user_potential_host_events_handled_;
@@ -64,7 +66,8 @@
   size_t num_existing_user_chromebook_added_events_handled_ = 0u;
   size_t num_eligible_for_wifi_sync_events_handled_ = 0u;
 
-  mojo::ReceiverSet<mojom::AccountStatusChangeDelegate> receivers_;
+  mojo::ReceiverSet<ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+      receivers_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/fake_feature_state_manager.cc b/ash/services/multidevice_setup/fake_feature_state_manager.cc
index 7295644..daba4b9 100644
--- a/ash/services/multidevice_setup/fake_feature_state_manager.cc
+++ b/ash/services/multidevice_setup/fake_feature_state_manager.cc
@@ -10,6 +10,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // Each feature's default value is kUnavailableNoVerifiedHost until proven
 // otherwise.
 FeatureStateManager::FeatureStatesMap GenerateInitialDefaultCachedStateMap() {
diff --git a/ash/services/multidevice_setup/fake_feature_state_manager.h b/ash/services/multidevice_setup/fake_feature_state_manager.h
index 6053cc73..5de89763 100644
--- a/ash/services/multidevice_setup/fake_feature_state_manager.h
+++ b/ash/services/multidevice_setup/fake_feature_state_manager.h
@@ -23,8 +23,10 @@
 
   ~FakeFeatureStateManager() override;
 
-  mojom::FeatureState GetFeatureState(mojom::Feature feature);
-  void SetFeatureState(mojom::Feature feature, mojom::FeatureState state);
+  ash::multidevice_setup::mojom::FeatureState GetFeatureState(
+      ash::multidevice_setup::mojom::Feature feature);
+  void SetFeatureState(ash::multidevice_setup::mojom::Feature feature,
+                       ash::multidevice_setup::mojom::FeatureState state);
   void SetFeatureStates(const FeatureStatesMap& feature_states_map);
 
   using FeatureStateManager::NotifyFeatureStatesChange;
@@ -32,8 +34,9 @@
  private:
   // FeatureStateManager:
   FeatureStatesMap GetFeatureStates() override;
-  void PerformSetFeatureEnabledState(mojom::Feature feature,
-                                     bool enabled) override;
+  void PerformSetFeatureEnabledState(
+      ash::multidevice_setup::mojom::Feature feature,
+      bool enabled) override;
 
   FeatureStatesMap feature_states_map_;
 };
diff --git a/ash/services/multidevice_setup/fake_feature_state_observer.cc b/ash/services/multidevice_setup/fake_feature_state_observer.cc
index e9c5b5d..64aaf2d6 100644
--- a/ash/services/multidevice_setup/fake_feature_state_observer.cc
+++ b/ash/services/multidevice_setup/fake_feature_state_observer.cc
@@ -8,6 +8,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FakeFeatureStateObserver::FakeFeatureStateObserver() = default;
 
 FakeFeatureStateObserver::~FakeFeatureStateObserver() = default;
diff --git a/ash/services/multidevice_setup/fake_feature_state_observer.h b/ash/services/multidevice_setup/fake_feature_state_observer.h
index e78da962..cbfcdc1 100644
--- a/ash/services/multidevice_setup/fake_feature_state_observer.h
+++ b/ash/services/multidevice_setup/fake_feature_state_observer.h
@@ -15,7 +15,8 @@
 namespace multidevice_setup {
 
 // Fake mojom::FeatureStateObserver implementation for tests.
-class FakeFeatureStateObserver : public mojom::FeatureStateObserver {
+class FakeFeatureStateObserver
+    : public ash::multidevice_setup::mojom::FeatureStateObserver {
  public:
   FakeFeatureStateObserver();
 
@@ -24,9 +25,12 @@
 
   ~FakeFeatureStateObserver() override;
 
-  mojo::PendingRemote<mojom::FeatureStateObserver> GenerateRemote();
+  mojo::PendingRemote<ash::multidevice_setup::mojom::FeatureStateObserver>
+  GenerateRemote();
 
-  const std::vector<base::flat_map<mojom::Feature, mojom::FeatureState>>&
+  const std::vector<
+      base::flat_map<ash::multidevice_setup::mojom::Feature,
+                     ash::multidevice_setup::mojom::FeatureState>>&
   feature_state_updates() {
     return feature_state_updates_;
   }
@@ -34,13 +38,16 @@
  private:
   // mojom::FeatureStateObserver:
   void OnFeatureStatesChanged(
-      const base::flat_map<mojom::Feature, mojom::FeatureState>&
+      const base::flat_map<ash::multidevice_setup::mojom::Feature,
+                           ash::multidevice_setup::mojom::FeatureState>&
           feature_states_map) override;
 
-  std::vector<base::flat_map<mojom::Feature, mojom::FeatureState>>
+  std::vector<base::flat_map<ash::multidevice_setup::mojom::Feature,
+                             ash::multidevice_setup::mojom::FeatureState>>
       feature_state_updates_;
 
-  mojo::ReceiverSet<mojom::FeatureStateObserver> receivers_;
+  mojo::ReceiverSet<ash::multidevice_setup::mojom::FeatureStateObserver>
+      receivers_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/fake_host_status_observer.cc b/ash/services/multidevice_setup/fake_host_status_observer.cc
index b7dba1e..c8438b96 100644
--- a/ash/services/multidevice_setup/fake_host_status_observer.cc
+++ b/ash/services/multidevice_setup/fake_host_status_observer.cc
@@ -8,6 +8,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FakeHostStatusObserver::FakeHostStatusObserver() = default;
 
 FakeHostStatusObserver::~FakeHostStatusObserver() = default;
diff --git a/ash/services/multidevice_setup/fake_host_status_observer.h b/ash/services/multidevice_setup/fake_host_status_observer.h
index 94cfb8a..d4767df 100644
--- a/ash/services/multidevice_setup/fake_host_status_observer.h
+++ b/ash/services/multidevice_setup/fake_host_status_observer.h
@@ -15,7 +15,8 @@
 namespace multidevice_setup {
 
 // Fake mojom::HostStatusObserver implementation for tests.
-class FakeHostStatusObserver : public mojom::HostStatusObserver {
+class FakeHostStatusObserver
+    : public ash::multidevice_setup::mojom::HostStatusObserver {
  public:
   FakeHostStatusObserver();
 
@@ -24,10 +25,11 @@
 
   ~FakeHostStatusObserver() override;
 
-  mojo::PendingRemote<mojom::HostStatusObserver> GenerateRemote();
+  mojo::PendingRemote<ash::multidevice_setup::mojom::HostStatusObserver>
+  GenerateRemote();
 
-  const std::vector<
-      std::pair<mojom::HostStatus, absl::optional<multidevice::RemoteDevice>>>&
+  const std::vector<std::pair<ash::multidevice_setup::mojom::HostStatus,
+                              absl::optional<multidevice::RemoteDevice>>>&
   host_status_updates() const {
     return host_status_updates_;
   }
@@ -35,14 +37,15 @@
  private:
   // mojom::HostStatusObserver:
   void OnHostStatusChanged(
-      mojom::HostStatus host_status,
+      ash::multidevice_setup::mojom::HostStatus host_status,
       const absl::optional<multidevice::RemoteDevice>& host_device) override;
 
-  std::vector<
-      std::pair<mojom::HostStatus, absl::optional<multidevice::RemoteDevice>>>
+  std::vector<std::pair<ash::multidevice_setup::mojom::HostStatus,
+                        absl::optional<multidevice::RemoteDevice>>>
       host_status_updates_;
 
-  mojo::ReceiverSet<mojom::HostStatusObserver> receivers_;
+  mojo::ReceiverSet<ash::multidevice_setup::mojom::HostStatusObserver>
+      receivers_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/fake_host_status_provider.cc b/ash/services/multidevice_setup/fake_host_status_provider.cc
index 99371a7..96ddc2d 100644
--- a/ash/services/multidevice_setup/fake_host_status_provider.cc
+++ b/ash/services/multidevice_setup/fake_host_status_provider.cc
@@ -8,6 +8,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FakeHostStatusProvider::FakeHostStatusProvider() = default;
 
 FakeHostStatusProvider::~FakeHostStatusProvider() = default;
diff --git a/ash/services/multidevice_setup/fake_host_status_provider.h b/ash/services/multidevice_setup/fake_host_status_provider.h
index 17696b6..1c44467a 100644
--- a/ash/services/multidevice_setup/fake_host_status_provider.h
+++ b/ash/services/multidevice_setup/fake_host_status_provider.h
@@ -24,14 +24,15 @@
   ~FakeHostStatusProvider() override;
 
   void SetHostWithStatus(
-      mojom::HostStatus host_status,
+      ash::multidevice_setup::mojom::HostStatus host_status,
       const absl::optional<multidevice::RemoteDeviceRef>& host_device);
 
   // HostStatusProvider:
   HostStatusWithDevice GetHostWithStatus() const override;
 
  private:
-  mojom::HostStatus host_status_ = mojom::HostStatus::kNoEligibleHosts;
+  ash::multidevice_setup::mojom::HostStatus host_status_ =
+      ash::multidevice_setup::mojom::HostStatus::kNoEligibleHosts;
   absl::optional<multidevice::RemoteDeviceRef> host_device_;
 };
 
diff --git a/ash/services/multidevice_setup/feature_state_manager.cc b/ash/services/multidevice_setup/feature_state_manager.cc
index bdb8c2e8..72e83e5d 100644
--- a/ash/services/multidevice_setup/feature_state_manager.cc
+++ b/ash/services/multidevice_setup/feature_state_manager.cc
@@ -10,6 +10,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FeatureStateManager::FeatureStateManager() = default;
 
 FeatureStateManager::~FeatureStateManager() = default;
diff --git a/ash/services/multidevice_setup/feature_state_manager.h b/ash/services/multidevice_setup/feature_state_manager.h
index 54a5232..e73ccd5 100644
--- a/ash/services/multidevice_setup/feature_state_manager.h
+++ b/ash/services/multidevice_setup/feature_state_manager.h
@@ -19,7 +19,9 @@
 // interface as well as a way to toggle a feature on/off when appropriate.
 class FeatureStateManager {
  public:
-  using FeatureStatesMap = base::flat_map<mojom::Feature, mojom::FeatureState>;
+  using FeatureStatesMap =
+      base::flat_map<ash::multidevice_setup::mojom::Feature,
+                     ash::multidevice_setup::mojom::FeatureState>;
 
   class Observer {
    public:
@@ -42,7 +44,8 @@
   // state is mojom::FeatureState::kEnabledByUser,
   // mojom::FeatureState::kFurtherSetupRequired or
   // mojom::FeatureState::kDisabledByUser.
-  bool SetFeatureEnabledState(mojom::Feature feature, bool enabled);
+  bool SetFeatureEnabledState(ash::multidevice_setup::mojom::Feature feature,
+                              bool enabled);
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -52,8 +55,9 @@
 
   // Enables or disables the feature; by the time this function has been called,
   // it has already been confirmed that the state is indeed able to be changed.
-  virtual void PerformSetFeatureEnabledState(mojom::Feature feature,
-                                             bool enabled) = 0;
+  virtual void PerformSetFeatureEnabledState(
+      ash::multidevice_setup::mojom::Feature feature,
+      bool enabled) = 0;
 
   void NotifyFeatureStatesChange(const FeatureStatesMap& feature_states_map);
 
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl.cc b/ash/services/multidevice_setup/feature_state_manager_impl.cc
index 4eacbbd..9660c5be 100644
--- a/ash/services/multidevice_setup/feature_state_manager_impl.cc
+++ b/ash/services/multidevice_setup/feature_state_manager_impl.cc
@@ -28,6 +28,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 constexpr base::TimeDelta kFeatureStateLoggingPeriod = base::Minutes(30);
 
 constexpr std::array<mojom::Feature, 4> kPhoneHubSubFeatures{
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl.h b/ash/services/multidevice_setup/feature_state_manager_impl.h
index 3bfae7b..0caa7287 100644
--- a/ash/services/multidevice_setup/feature_state_manager_impl.h
+++ b/ash/services/multidevice_setup/feature_state_manager_impl.h
@@ -38,7 +38,8 @@
         HostStatusProvider* host_status_provider,
         device_sync::DeviceSyncClient* device_sync_client,
         AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
-        const base::flat_map<mojom::Feature, GlobalStateFeatureManager*>&
+        const base::flat_map<ash::multidevice_setup::mojom::Feature,
+                             GlobalStateFeatureManager*>&
             global_state_feature_managers,
         bool is_secondary_user);
     static void SetFactoryForTesting(Factory* test_factory);
@@ -50,7 +51,8 @@
         HostStatusProvider* host_status_provider,
         device_sync::DeviceSyncClient* device_sync_client,
         AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
-        const base::flat_map<mojom::Feature, GlobalStateFeatureManager*>&
+        const base::flat_map<ash::multidevice_setup::mojom::Feature,
+                             GlobalStateFeatureManager*>&
             global_state_feature_managers,
         bool is_secondary_user) = 0;
 
@@ -69,14 +71,16 @@
       HostStatusProvider* host_status_provider,
       device_sync::DeviceSyncClient* device_sync_client,
       AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker,
-      const base::flat_map<mojom::Feature, GlobalStateFeatureManager*>&
+      const base::flat_map<ash::multidevice_setup::mojom::Feature,
+                           GlobalStateFeatureManager*>&
           global_state_feature_managers,
       bool is_secondary_user);
 
   // FeatureStateManager:
   FeatureStatesMap GetFeatureStates() override;
-  void PerformSetFeatureEnabledState(mojom::Feature feature,
-                                     bool enabled) override;
+  void PerformSetFeatureEnabledState(
+      ash::multidevice_setup::mojom::Feature feature,
+      bool enabled) override;
 
   // HostStatusProvider::Observer,
   void OnHostStatusChange(const HostStatusProvider::HostStatusWithDevice&
@@ -90,15 +94,17 @@
 
   void OnPrefValueChanged();
   void UpdateFeatureStateCache(bool notify_observers_of_changes);
-  mojom::FeatureState ComputeFeatureState(mojom::Feature feature);
-  bool IsAllowedByPolicy(mojom::Feature feature);
-  bool IsSupportedByChromebook(mojom::Feature feature);
-  bool HasSufficientSecurity(mojom::Feature feature,
+  ash::multidevice_setup::mojom::FeatureState ComputeFeatureState(
+      ash::multidevice_setup::mojom::Feature feature);
+  bool IsAllowedByPolicy(ash::multidevice_setup::mojom::Feature feature);
+  bool IsSupportedByChromebook(ash::multidevice_setup::mojom::Feature feature);
+  bool HasSufficientSecurity(ash::multidevice_setup::mojom::Feature feature,
                              const multidevice::RemoteDeviceRef& host_device);
-  bool HasBeenActivatedByPhone(mojom::Feature feature,
+  bool HasBeenActivatedByPhone(ash::multidevice_setup::mojom::Feature feature,
                                const multidevice::RemoteDeviceRef& host_device);
-  bool RequiresFurtherSetup(mojom::Feature feature);
-  mojom::FeatureState GetEnabledOrDisabledState(mojom::Feature feature);
+  bool RequiresFurtherSetup(ash::multidevice_setup::mojom::Feature feature);
+  ash::multidevice_setup::mojom::FeatureState GetEnabledOrDisabledState(
+      ash::multidevice_setup::mojom::Feature feature);
 
   // Log the feature states in |cached_feature_state_map_|. Called 1) on
   // sign-in, 2) when at least one feature state changes, and 3) every 30
@@ -110,7 +116,8 @@
   HostStatusProvider* host_status_provider_;
   device_sync::DeviceSyncClient* device_sync_client_;
   AndroidSmsPairingStateTracker* android_sms_pairing_state_tracker_;
-  const base::flat_map<mojom::Feature, GlobalStateFeatureManager*>
+  const base::flat_map<ash::multidevice_setup::mojom::Feature,
+                       GlobalStateFeatureManager*>
       global_state_feature_managers_;
 
   // Certain features may be unavailable to secondary users logged into a
@@ -119,11 +126,13 @@
 
   // Map from feature to the pref name which indicates the enabled/disabled
   // boolean state for the feature.
-  base::flat_map<mojom::Feature, std::string> feature_to_enabled_pref_name_map_;
+  base::flat_map<ash::multidevice_setup::mojom::Feature, std::string>
+      feature_to_enabled_pref_name_map_;
 
   // Same as above, except that the pref names represent whether the feature is
   // allowed by policy or not.
-  base::flat_map<mojom::Feature, std::string> feature_to_allowed_pref_name_map_;
+  base::flat_map<ash::multidevice_setup::mojom::Feature, std::string>
+      feature_to_allowed_pref_name_map_;
 
   // Map from feature to state, which is updated each time a feature's state
   // changes. This cache is used to determine when a feature's state has changed
diff --git a/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc b/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
index c2e51363..0f65af1 100644
--- a/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
+++ b/ash/services/multidevice_setup/feature_state_manager_impl_unittest.cc
@@ -28,6 +28,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 multidevice::RemoteDeviceRef CreateTestLocalDevice() {
   multidevice::RemoteDeviceRef local_device =
       multidevice::CreateRemoteDeviceRefForTest();
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl.cc b/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
index 54a95f8f..a95ab08 100644
--- a/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
+++ b/ash/services/multidevice_setup/global_state_feature_manager_impl.cc
@@ -37,6 +37,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // This pref name is left in a legacy format to maintain compatibility.
 const char kWifiSyncPendingStatePrefName[] =
     "multidevice_setup.pending_set_wifi_sync_enabled_request";
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl.h b/ash/services/multidevice_setup/global_state_feature_manager_impl.h
index 8f67237..736252a 100644
--- a/ash/services/multidevice_setup/global_state_feature_manager_impl.h
+++ b/ash/services/multidevice_setup/global_state_feature_manager_impl.h
@@ -79,7 +79,7 @@
 
  private:
   GlobalStateFeatureManagerImpl(
-      mojom::Feature managed_feature,
+      ash::multidevice_setup::mojom::Feature managed_feature,
       multidevice::SoftwareFeature managed_host_feature,
       const std::string& pending_state_pref_name,
       HostStatusProvider* host_status_provider,
@@ -145,7 +145,7 @@
   bool ShouldAttemptToEnableAfterHostVerified();
 
   // The feature being managed.
-  mojom::Feature managed_feature_;
+  ash::multidevice_setup::mojom::Feature managed_feature_;
   // Corresponding CryptAuth host feature for |managed_feature_|.
   multidevice::SoftwareFeature managed_host_feature_;
   const std::string pending_state_pref_name_;
diff --git a/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc b/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
index e90d320..66faa55 100644
--- a/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
+++ b/ash/services/multidevice_setup/global_state_feature_manager_impl_unittest.cc
@@ -32,6 +32,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const GlobalStateFeatureManagerImpl::Factory::Option kTestOption =
     GlobalStateFeatureManagerImpl::Factory::Option::kWifiSync;
 const multidevice::SoftwareFeature kTestHostFeature =
diff --git a/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc b/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
index c63b607..e882858 100644
--- a/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
+++ b/ash/services/multidevice_setup/host_device_timestamp_manager_impl.cc
@@ -15,6 +15,10 @@
 namespace multidevice_setup {
 
 namespace {
+
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const int64_t kTimestampNotSet = 0;
 }  // namespace
 
diff --git a/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc b/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
index c5e4ed4..e84b7ea 100644
--- a/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_device_timestamp_manager_impl_unittest.cc
@@ -18,6 +18,9 @@
 namespace multidevice_setup {
 
 namespace {
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const base::Time kTestTime = base::Time::FromJavaTime(1500000000000);
 const base::Time kLaterTime = kTestTime + base::Milliseconds(123456789);
 }  // namespace
diff --git a/ash/services/multidevice_setup/host_status_provider.cc b/ash/services/multidevice_setup/host_status_provider.cc
index 3fc5acf..78dadff0 100644
--- a/ash/services/multidevice_setup/host_status_provider.cc
+++ b/ash/services/multidevice_setup/host_status_provider.cc
@@ -11,6 +11,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 HostStatusProvider::HostStatusWithDevice::HostStatusWithDevice(
     mojom::HostStatus host_status,
     const absl::optional<multidevice::RemoteDeviceRef>& host_device)
diff --git a/ash/services/multidevice_setup/host_status_provider.h b/ash/services/multidevice_setup/host_status_provider.h
index c5dda04..fcd717a 100644
--- a/ash/services/multidevice_setup/host_status_provider.h
+++ b/ash/services/multidevice_setup/host_status_provider.h
@@ -22,7 +22,7 @@
   class HostStatusWithDevice {
    public:
     HostStatusWithDevice(
-        mojom::HostStatus host_status,
+        ash::multidevice_setup::mojom::HostStatus host_status,
         const absl::optional<multidevice::RemoteDeviceRef>& host_device);
     HostStatusWithDevice(const HostStatusWithDevice& other);
     ~HostStatusWithDevice();
@@ -30,7 +30,9 @@
     bool operator==(const HostStatusWithDevice& other) const;
     bool operator!=(const HostStatusWithDevice& other) const;
 
-    mojom::HostStatus host_status() const { return host_status_; }
+    ash::multidevice_setup::mojom::HostStatus host_status() const {
+      return host_status_;
+    }
 
     // If host_status() is kNoEligibleHosts or
     // kEligibleHostExistsButNoHostSet, host_device() is null.
@@ -39,7 +41,7 @@
     }
 
    private:
-    mojom::HostStatus host_status_;
+    ash::multidevice_setup::mojom::HostStatus host_status_;
     absl::optional<multidevice::RemoteDeviceRef> host_device_;
   };
 
@@ -64,7 +66,7 @@
   HostStatusProvider();
 
   void NotifyHostStatusChange(
-      mojom::HostStatus host_status,
+      ash::multidevice_setup::mojom::HostStatus host_status,
       const absl::optional<multidevice::RemoteDeviceRef>& host_device);
 
  private:
diff --git a/ash/services/multidevice_setup/host_status_provider_impl.cc b/ash/services/multidevice_setup/host_status_provider_impl.cc
index c457b1b..4123791 100644
--- a/ash/services/multidevice_setup/host_status_provider_impl.cc
+++ b/ash/services/multidevice_setup/host_status_provider_impl.cc
@@ -19,6 +19,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 constexpr base::TimeDelta kHostStatusLoggingPeriod = base::Minutes(30);
 
 }  // namespace
diff --git a/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc b/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
index 8975c5c..4c84bc39 100644
--- a/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
+++ b/ash/services/multidevice_setup/host_status_provider_impl_unittest.cc
@@ -22,6 +22,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const size_t kNumTestDevices = 5;
 
 }  // namespace
diff --git a/ash/services/multidevice_setup/multidevice_setup_base.cc b/ash/services/multidevice_setup/multidevice_setup_base.cc
index 4770d72..acf1660 100644
--- a/ash/services/multidevice_setup/multidevice_setup_base.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_base.cc
@@ -8,6 +8,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 MultiDeviceSetupBase::MultiDeviceSetupBase() = default;
 
 MultiDeviceSetupBase::~MultiDeviceSetupBase() = default;
diff --git a/ash/services/multidevice_setup/multidevice_setup_base.h b/ash/services/multidevice_setup/multidevice_setup_base.h
index e6fd6823..226169a 100644
--- a/ash/services/multidevice_setup/multidevice_setup_base.h
+++ b/ash/services/multidevice_setup/multidevice_setup_base.h
@@ -14,14 +14,17 @@
 namespace multidevice_setup {
 
 // MultiDeviceSetup implementation which accepts receivers to bind to it.
-class MultiDeviceSetupBase : public mojom::MultiDeviceSetup {
+class MultiDeviceSetupBase
+    : public ash::multidevice_setup::mojom::MultiDeviceSetup {
  public:
   MultiDeviceSetupBase(const MultiDeviceSetupBase&) = delete;
   MultiDeviceSetupBase& operator=(const MultiDeviceSetupBase&) = delete;
 
   ~MultiDeviceSetupBase() override;
 
-  void BindReceiver(mojo::PendingReceiver<mojom::MultiDeviceSetup> receiver);
+  void BindReceiver(
+      mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
+          receiver);
   void CloseAllReceivers();
 
   // Sets the device with the given ID as the multi-device host for this
@@ -30,13 +33,14 @@
   // use Instance ID since all devices are guaranteed to have one.
   virtual void SetHostDeviceWithoutAuthToken(
       const std::string& host_instance_id_or_legacy_device_id,
-      mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback callback) = 0;
+      ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter::
+          SetHostDeviceCallback callback) = 0;
 
  protected:
   MultiDeviceSetupBase();
 
  private:
-  mojo::ReceiverSet<mojom::MultiDeviceSetup> receivers_;
+  mojo::ReceiverSet<ash::multidevice_setup::mojom::MultiDeviceSetup> receivers_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl.cc b/ash/services/multidevice_setup/multidevice_setup_impl.cc
index f86a4d0..0d5b349 100644
--- a/ash/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_impl.cc
@@ -38,6 +38,10 @@
 namespace multidevice_setup {
 
 namespace {
+
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const char kTestDeviceNameForDebugNotification[] = "Test Device";
 
 // This enum is tied directly to a UMA enum defined in
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl.h b/ash/services/multidevice_setup/multidevice_setup_impl.h
index f7f5543..7063fb4 100644
--- a/ash/services/multidevice_setup/multidevice_setup_impl.h
+++ b/ash/services/multidevice_setup/multidevice_setup_impl.h
@@ -96,12 +96,15 @@
 
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
-      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate)
+      mojo::PendingRemote<
+          ash::multidevice_setup::mojom::AccountStatusChangeDelegate> delegate)
       override;
   void AddHostStatusObserver(
-      mojo::PendingRemote<mojom::HostStatusObserver> observer) override;
+      mojo::PendingRemote<ash::multidevice_setup::mojom::HostStatusObserver>
+          observer) override;
   void AddFeatureStateObserver(
-      mojo::PendingRemote<mojom::FeatureStateObserver> observer) override;
+      mojo::PendingRemote<ash::multidevice_setup::mojom::FeatureStateObserver>
+          observer) override;
   void GetEligibleHostDevices(GetEligibleHostDevicesCallback callback) override;
   void GetEligibleActiveHostDevices(
       GetEligibleActiveHostDevicesCallback callback) override;
@@ -110,21 +113,21 @@
                      SetHostDeviceCallback callback) override;
   void RemoveHostDevice() override;
   void GetHostStatus(GetHostStatusCallback callback) override;
-  void SetFeatureEnabledState(mojom::Feature feature,
+  void SetFeatureEnabledState(ash::multidevice_setup::mojom::Feature feature,
                               bool enabled,
                               const absl::optional<std::string>& auth_token,
                               SetFeatureEnabledStateCallback callback) override;
   void GetFeatureStates(GetFeatureStatesCallback callback) override;
   void RetrySetHostNow(RetrySetHostNowCallback callback) override;
   void TriggerEventForDebugging(
-      mojom::EventTypeForDebugging type,
+      ash::multidevice_setup::mojom::EventTypeForDebugging type,
       TriggerEventForDebuggingCallback callback) override;
 
   // MultiDeviceSetupBase:
   void SetHostDeviceWithoutAuthToken(
       const std::string& host_instance_id_or_legacy_device_id,
-      mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback callback)
-      override;
+      ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter::
+          SetHostDeviceCallback callback) override;
 
   // HostStatusProvider::Observer:
   void OnHostStatusChange(const HostStatusProvider::HostStatusWithDevice&
@@ -137,8 +140,9 @@
   // Attempts to set the host device, returning a boolean of whether the attempt
   // was successful.
   bool AttemptSetHost(const std::string& host_instance_id_or_legacy_device_id);
-  bool IsAuthTokenRequiredForFeatureStateChange(mojom::Feature feature,
-                                                bool enabled);
+  bool IsAuthTokenRequiredForFeatureStateChange(
+      ash::multidevice_setup::mojom::Feature feature,
+      bool enabled);
 
   void FlushForTesting();
 
@@ -158,8 +162,10 @@
       android_sms_app_installing_host_observer_;
   AuthTokenValidator* auth_token_validator_;
 
-  mojo::RemoteSet<mojom::HostStatusObserver> host_status_observers_;
-  mojo::RemoteSet<mojom::FeatureStateObserver> feature_state_observers_;
+  mojo::RemoteSet<ash::multidevice_setup::mojom::HostStatusObserver>
+      host_status_observers_;
+  mojo::RemoteSet<ash::multidevice_setup::mojom::FeatureStateObserver>
+      feature_state_observers_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
index 6a0fc49..2863996 100644
--- a/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -53,6 +53,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const size_t kNumTestDevices = 3;
 
 const char kValidAuthToken[] = "validAuthToken";
diff --git a/ash/services/multidevice_setup/multidevice_setup_initializer.cc b/ash/services/multidevice_setup/multidevice_setup_initializer.cc
index 803fdad9..e30d110 100644
--- a/ash/services/multidevice_setup/multidevice_setup_initializer.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_initializer.cc
@@ -17,6 +17,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // static
 MultiDeviceSetupInitializer::Factory*
     MultiDeviceSetupInitializer::Factory::test_factory_ = nullptr;
diff --git a/ash/services/multidevice_setup/multidevice_setup_initializer.h b/ash/services/multidevice_setup/multidevice_setup_initializer.h
index e5255ca..2cb9e6a 100644
--- a/ash/services/multidevice_setup/multidevice_setup_initializer.h
+++ b/ash/services/multidevice_setup/multidevice_setup_initializer.h
@@ -82,7 +82,8 @@
     // For SetHostDeviceWithoutAuthToken().
     SetHostDeviceArgs(
         const std::string& host_instance_id_or_legacy_device_id,
-        mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback callback);
+        ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter::
+            SetHostDeviceCallback callback);
 
     ~SetHostDeviceArgs();
 
@@ -104,12 +105,15 @@
 
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
-      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate)
+      mojo::PendingRemote<
+          ash::multidevice_setup::mojom::AccountStatusChangeDelegate> delegate)
       override;
   void AddHostStatusObserver(
-      mojo::PendingRemote<mojom::HostStatusObserver> observer) override;
+      mojo::PendingRemote<ash::multidevice_setup::mojom::HostStatusObserver>
+          observer) override;
   void AddFeatureStateObserver(
-      mojo::PendingRemote<mojom::FeatureStateObserver> observer) override;
+      mojo::PendingRemote<ash::multidevice_setup::mojom::FeatureStateObserver>
+          observer) override;
   void GetEligibleHostDevices(GetEligibleHostDevicesCallback callback) override;
   void GetEligibleActiveHostDevices(
       GetEligibleActiveHostDevicesCallback callback) override;
@@ -118,21 +122,21 @@
                      SetHostDeviceCallback callback) override;
   void RemoveHostDevice() override;
   void GetHostStatus(GetHostStatusCallback callback) override;
-  void SetFeatureEnabledState(mojom::Feature feature,
+  void SetFeatureEnabledState(ash::multidevice_setup::mojom::Feature feature,
                               bool enabled,
                               const absl::optional<std::string>& auth_token,
                               SetFeatureEnabledStateCallback callback) override;
   void GetFeatureStates(GetFeatureStatesCallback callback) override;
   void RetrySetHostNow(RetrySetHostNowCallback callback) override;
   void TriggerEventForDebugging(
-      mojom::EventTypeForDebugging type,
+      ash::multidevice_setup::mojom::EventTypeForDebugging type,
       TriggerEventForDebuggingCallback callback) override;
 
   // MultiDeviceSetupBase:
   void SetHostDeviceWithoutAuthToken(
       const std::string& host_instance_id_or_legacy_device_id,
-      mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback callback)
-      override;
+      ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter::
+          SetHostDeviceCallback callback) override;
 
   // device_sync::DeviceSyncClient::Observer:
   void OnReady() override;
@@ -153,16 +157,20 @@
   // If API functions are called before initialization is complete, their
   // parameters are cached here. Once asynchronous initialization is complete,
   // the parameters are passed to |multidevice_setup_impl_|.
-  mojo::PendingRemote<mojom::AccountStatusChangeDelegate> pending_delegate_;
-  std::vector<mojo::PendingRemote<mojom::HostStatusObserver>>
+  mojo::PendingRemote<
+      ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+      pending_delegate_;
+  std::vector<
+      mojo::PendingRemote<ash::multidevice_setup::mojom::HostStatusObserver>>
       pending_host_status_observers_;
-  std::vector<mojo::PendingRemote<mojom::FeatureStateObserver>>
+  std::vector<
+      mojo::PendingRemote<ash::multidevice_setup::mojom::FeatureStateObserver>>
       pending_feature_state_observers_;
   std::vector<GetEligibleHostDevicesCallback> pending_get_eligible_hosts_args_;
   std::vector<GetEligibleActiveHostDevicesCallback>
       pending_get_eligible_active_hosts_args_;
   std::vector<GetHostStatusCallback> pending_get_host_args_;
-  std::vector<std::tuple<mojom::Feature,
+  std::vector<std::tuple<ash::multidevice_setup::mojom::Feature,
                          bool,
                          absl::optional<std::string>,
                          SetFeatureEnabledStateCallback>>
diff --git a/ash/services/multidevice_setup/multidevice_setup_service.cc b/ash/services/multidevice_setup/multidevice_setup_service.cc
index eda21dd0..bac6973 100644
--- a/ash/services/multidevice_setup/multidevice_setup_service.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_service.cc
@@ -25,6 +25,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // static
 void MultiDeviceSetupService::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
diff --git a/ash/services/multidevice_setup/multidevice_setup_service.h b/ash/services/multidevice_setup/multidevice_setup_service.h
index e3c76fa..52524c3 100644
--- a/ash/services/multidevice_setup/multidevice_setup_service.h
+++ b/ash/services/multidevice_setup/multidevice_setup_service.h
@@ -51,9 +51,11 @@
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
   void BindMultiDeviceSetup(
-      mojo::PendingReceiver<mojom::MultiDeviceSetup> receiver);
+      mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
+          receiver);
   void BindPrivilegedHostDeviceSetter(
-      mojo::PendingReceiver<mojom::PrivilegedHostDeviceSetter> receiver);
+      mojo::PendingReceiver<
+          ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter> receiver);
 
  private:
   std::unique_ptr<MultiDeviceSetupBase> multidevice_setup_;
diff --git a/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc b/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
index bf2df97..b42b2d6 100644
--- a/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
+++ b/ash/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -32,6 +32,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const size_t kNumTestDevices = 3;
 
 class FakeMultiDeviceSetupFactory : public MultiDeviceSetupImpl::Factory {
diff --git a/ash/services/multidevice_setup/privileged_host_device_setter_base.cc b/ash/services/multidevice_setup/privileged_host_device_setter_base.cc
index 101a20f9..0954969 100644
--- a/ash/services/multidevice_setup/privileged_host_device_setter_base.cc
+++ b/ash/services/multidevice_setup/privileged_host_device_setter_base.cc
@@ -8,6 +8,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 PrivilegedHostDeviceSetterBase::PrivilegedHostDeviceSetterBase() = default;
 
 PrivilegedHostDeviceSetterBase::~PrivilegedHostDeviceSetterBase() = default;
diff --git a/ash/services/multidevice_setup/privileged_host_device_setter_base.h b/ash/services/multidevice_setup/privileged_host_device_setter_base.h
index 1eb42b1..504766b 100644
--- a/ash/services/multidevice_setup/privileged_host_device_setter_base.h
+++ b/ash/services/multidevice_setup/privileged_host_device_setter_base.h
@@ -16,7 +16,7 @@
 // PrivilegedHostDeviceSetter implementation which accepts receivers to bind to
 // it.
 class PrivilegedHostDeviceSetterBase
-    : public mojom::PrivilegedHostDeviceSetter {
+    : public ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter {
  public:
   PrivilegedHostDeviceSetterBase(const PrivilegedHostDeviceSetterBase&) =
       delete;
@@ -26,13 +26,15 @@
   ~PrivilegedHostDeviceSetterBase() override;
 
   void BindReceiver(
-      mojo::PendingReceiver<mojom::PrivilegedHostDeviceSetter> receiver);
+      mojo::PendingReceiver<
+          ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter> receiver);
 
  protected:
   PrivilegedHostDeviceSetterBase();
 
  private:
-  mojo::ReceiverSet<mojom::PrivilegedHostDeviceSetter> receivers_;
+  mojo::ReceiverSet<ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter>
+      receivers_;
 };
 
 }  // namespace multidevice_setup
diff --git a/ash/services/multidevice_setup/proto/multidevice_setup.proto b/ash/services/multidevice_setup/proto/multidevice_setup.proto
index 6b1efda..4e1145a5 100644
--- a/ash/services/multidevice_setup/proto/multidevice_setup.proto
+++ b/ash/services/multidevice_setup/proto/multidevice_setup.proto
@@ -4,7 +4,7 @@
 
 syntax = "proto2";
 
-package chromeos.multidevice_setup;
+package ash.multidevice_setup;
 
 option optimize_for = LITE_RUNTIME;
 
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
index 6ab8ea24..9bd695c 100644
--- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
+++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.cc
@@ -14,6 +14,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FakeMultiDeviceSetup::FakeMultiDeviceSetup() = default;
 
 FakeMultiDeviceSetup::~FakeMultiDeviceSetup() {
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h
index 227990f..8ff8751 100644
--- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h
+++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup.h
@@ -35,13 +35,15 @@
   bool HasAtLeastOneFeatureStateObserver();
 
   void NotifyHostStatusChanged(
-      mojom::HostStatus host_status,
+      ash::multidevice_setup::mojom::HostStatus host_status,
       const absl::optional<multidevice::RemoteDevice>& host_device);
   void NotifyFeatureStateChanged(
-      const base::flat_map<mojom::Feature, mojom::FeatureState>&
+      const base::flat_map<ash::multidevice_setup::mojom::Feature,
+                           ash::multidevice_setup::mojom::FeatureState>&
           feature_states);
 
-  mojo::Remote<mojom::AccountStatusChangeDelegate>& delegate() {
+  mojo::Remote<ash::multidevice_setup::mojom::AccountStatusChangeDelegate>&
+  delegate() {
     return delegate_;
   }
 
@@ -58,7 +60,7 @@
 
   std::vector<GetHostStatusCallback>& get_host_args() { return get_host_args_; }
 
-  std::vector<std::tuple<mojom::Feature,
+  std::vector<std::tuple<ash::multidevice_setup::mojom::Feature,
                          bool,
                          absl::optional<std::string>,
                          SetFeatureEnabledStateCallback>>&
@@ -74,15 +76,15 @@
     return retry_set_host_now_args_;
   }
 
-  std::vector<std::pair<mojom::EventTypeForDebugging,
+  std::vector<std::pair<ash::multidevice_setup::mojom::EventTypeForDebugging,
                         TriggerEventForDebuggingCallback>>&
   triggered_debug_events() {
     return triggered_debug_events_;
   }
 
-  std::vector<
-      std::pair<std::string,
-                mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback>>&
+  std::vector<std::pair<std::string,
+                        ash::multidevice_setup::mojom::
+                            PrivilegedHostDeviceSetter::SetHostDeviceCallback>>&
   set_host_without_auth_args() {
     return set_host_without_auth_args_;
   }
@@ -90,12 +92,15 @@
  private:
   // mojom::MultiDeviceSetup:
   void SetAccountStatusChangeDelegate(
-      mojo::PendingRemote<mojom::AccountStatusChangeDelegate> delegate)
+      mojo::PendingRemote<
+          ash::multidevice_setup::mojom::AccountStatusChangeDelegate> delegate)
       override;
   void AddHostStatusObserver(
-      mojo::PendingRemote<mojom::HostStatusObserver> observer) override;
+      mojo::PendingRemote<ash::multidevice_setup::mojom::HostStatusObserver>
+          observer) override;
   void AddFeatureStateObserver(
-      mojo::PendingRemote<mojom::FeatureStateObserver> observer) override;
+      mojo::PendingRemote<ash::multidevice_setup::mojom::FeatureStateObserver>
+          observer) override;
   void GetEligibleHostDevices(GetEligibleHostDevicesCallback callback) override;
   void GetEligibleActiveHostDevices(
       GetEligibleActiveHostDevicesCallback callback) override;
@@ -104,25 +109,28 @@
                      SetHostDeviceCallback callback) override;
   void RemoveHostDevice() override;
   void GetHostStatus(GetHostStatusCallback callback) override;
-  void SetFeatureEnabledState(mojom::Feature feature,
+  void SetFeatureEnabledState(ash::multidevice_setup::mojom::Feature feature,
                               bool enabled,
                               const absl::optional<std::string>& auth_token,
                               SetFeatureEnabledStateCallback callback) override;
   void GetFeatureStates(GetFeatureStatesCallback callback) override;
   void RetrySetHostNow(RetrySetHostNowCallback callback) override;
   void TriggerEventForDebugging(
-      mojom::EventTypeForDebugging type,
+      ash::multidevice_setup::mojom::EventTypeForDebugging type,
       TriggerEventForDebuggingCallback callback) override;
 
   // MultiDeviceSetupBase:
   void SetHostDeviceWithoutAuthToken(
       const std::string& host_instance_id_or_legacy_device_id,
-      mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback callback)
-      override;
+      ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter::
+          SetHostDeviceCallback callback) override;
 
-  mojo::Remote<mojom::AccountStatusChangeDelegate> delegate_;
-  mojo::RemoteSet<mojom::HostStatusObserver> host_status_observers_;
-  mojo::RemoteSet<mojom::FeatureStateObserver> feature_state_observers_;
+  mojo::Remote<ash::multidevice_setup::mojom::AccountStatusChangeDelegate>
+      delegate_;
+  mojo::RemoteSet<ash::multidevice_setup::mojom::HostStatusObserver>
+      host_status_observers_;
+  mojo::RemoteSet<ash::multidevice_setup::mojom::FeatureStateObserver>
+      feature_state_observers_;
 
   std::vector<GetEligibleHostDevicesCallback> get_eligible_hosts_args_;
   std::vector<GetEligibleActiveHostDevicesCallback>
@@ -131,19 +139,19 @@
       set_host_args_;
   size_t num_remove_host_calls_ = 0u;
   std::vector<GetHostStatusCallback> get_host_args_;
-  std::vector<std::tuple<mojom::Feature,
+  std::vector<std::tuple<ash::multidevice_setup::mojom::Feature,
                          bool,
                          absl::optional<std::string>,
                          SetFeatureEnabledStateCallback>>
       set_feature_enabled_args_;
   std::vector<GetFeatureStatesCallback> get_feature_states_args_;
   std::vector<RetrySetHostNowCallback> retry_set_host_now_args_;
-  std::vector<
-      std::pair<mojom::EventTypeForDebugging, TriggerEventForDebuggingCallback>>
+  std::vector<std::pair<ash::multidevice_setup::mojom::EventTypeForDebugging,
+                        TriggerEventForDebuggingCallback>>
       triggered_debug_events_;
-  std::vector<
-      std::pair<std::string,
-                mojom::PrivilegedHostDeviceSetter::SetHostDeviceCallback>>
+  std::vector<std::pair<std::string,
+                        ash::multidevice_setup::mojom::
+                            PrivilegedHostDeviceSetter::SetHostDeviceCallback>>
       set_host_without_auth_args_;
 };
 
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.cc b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.cc
index d08532f..31f778c1 100644
--- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.cc
+++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.cc
@@ -8,10 +8,13 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 FakeMultiDeviceSetupClient::FakeMultiDeviceSetupClient()
     : host_status_with_device_(GenerateDefaultHostStatusWithDevice()),
       feature_states_map_(GenerateDefaultFeatureStatesMap(
-          multidevice_setup::mojom::FeatureState::kProhibitedByPolicy)) {}
+          mojom::FeatureState::kProhibitedByPolicy)) {}
 
 FakeMultiDeviceSetupClient::~FakeMultiDeviceSetupClient() {
   DCHECK(get_eligible_host_devices_callback_queue_.empty());
diff --git a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
index 5675cd1..fc832df 100644
--- a/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
+++ b/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h
@@ -34,8 +34,9 @@
   void SetHostStatusWithDevice(
       const HostStatusWithDevice& host_status_with_device);
   void SetFeatureStates(const FeatureStatesMap& feature_states_map);
-  void SetFeatureState(mojom::Feature feature,
-                       mojom::FeatureState feature_state);
+  void SetFeatureState(
+      ash::multidevice_setup::mojom::Feature feature,
+      ash::multidevice_setup::mojom::FeatureState feature_state);
 
   void InvokePendingGetEligibleHostDevicesCallback(
       const multidevice::RemoteDeviceRefList& eligible_devices);
@@ -44,13 +45,13 @@
       const std::string& expected_auth_token,
       bool success);
   void InvokePendingSetFeatureEnabledStateCallback(
-      mojom::Feature expected_feature,
+      ash::multidevice_setup::mojom::Feature expected_feature,
       bool expected_enabled,
       const absl::optional<std::string>& expected_auth_token,
       bool success);
   void InvokePendingRetrySetHostNowCallback(bool success);
   void InvokePendingTriggerEventForDebuggingCallback(
-      mojom::EventTypeForDebugging expected_type,
+      ash::multidevice_setup::mojom::EventTypeForDebugging expected_type,
       bool success);
 
   size_t NumPendingSetFeatureEnabledStateCalls() const;
@@ -69,40 +70,44 @@
   void SetHostDevice(
       const std::string& host_instance_id_or_legacy_device_id,
       const std::string& auth_token,
-      mojom::MultiDeviceSetup::SetHostDeviceCallback callback) override;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::SetHostDeviceCallback
+          callback) override;
   void RemoveHostDevice() override;
   void SetFeatureEnabledState(
-      mojom::Feature feature,
+      ash::multidevice_setup::mojom::Feature feature,
       bool enabled,
       const absl::optional<std::string>& auth_token,
-      mojom::MultiDeviceSetup::SetFeatureEnabledStateCallback callback)
-      override;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::
+          SetFeatureEnabledStateCallback callback) override;
   void RetrySetHostNow(
-      mojom::MultiDeviceSetup::RetrySetHostNowCallback callback) override;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::RetrySetHostNowCallback
+          callback) override;
   void TriggerEventForDebugging(
-      mojom::EventTypeForDebugging type,
-      mojom::MultiDeviceSetup::TriggerEventForDebuggingCallback callback)
-      override;
+      ash::multidevice_setup::mojom::EventTypeForDebugging type,
+      ash::multidevice_setup::mojom::MultiDeviceSetup::
+          TriggerEventForDebuggingCallback callback) override;
 
   size_t num_remove_host_device_called_ = 0u;
 
   std::queue<GetEligibleHostDevicesCallback>
       get_eligible_host_devices_callback_queue_;
-  std::queue<std::tuple<std::string,
-                        std::string,
-                        mojom::MultiDeviceSetup::SetHostDeviceCallback>>
+  std::queue<std::tuple<
+      std::string,
+      std::string,
+      ash::multidevice_setup::mojom::MultiDeviceSetup::SetHostDeviceCallback>>
       set_host_args_queue_;
-  std::queue<
-      std::tuple<mojom::Feature,
-                 bool,
-                 absl::optional<std::string>,
-                 mojom::MultiDeviceSetup::SetFeatureEnabledStateCallback>>
+  std::queue<std::tuple<ash::multidevice_setup::mojom::Feature,
+                        bool,
+                        absl::optional<std::string>,
+                        ash::multidevice_setup::mojom::MultiDeviceSetup::
+                            SetFeatureEnabledStateCallback>>
       set_feature_enabled_state_args_queue_;
-  std::queue<mojom::MultiDeviceSetup::RetrySetHostNowCallback>
-      retry_set_host_now_callback_queue_;
   std::queue<
-      std::pair<mojom::EventTypeForDebugging,
-                mojom::MultiDeviceSetup::TriggerEventForDebuggingCallback>>
+      ash::multidevice_setup::mojom::MultiDeviceSetup::RetrySetHostNowCallback>
+      retry_set_host_now_callback_queue_;
+  std::queue<std::pair<ash::multidevice_setup::mojom::EventTypeForDebugging,
+                       ash::multidevice_setup::mojom::MultiDeviceSetup::
+                           TriggerEventForDebuggingCallback>>
       trigger_event_for_debugging_type_and_callback_queue_;
 
   HostStatusWithDevice host_status_with_device_;
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
index b694c0ff..70b62e8 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.cc
@@ -11,6 +11,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // static
 MultiDeviceSetupClient::HostStatusWithDevice
 MultiDeviceSetupClient::GenerateDefaultHostStatusWithDevice() {
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
index 31bd24d9..237d64b 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h
@@ -23,9 +23,11 @@
 class MultiDeviceSetupClient {
  public:
   using HostStatusWithDevice =
-      std::pair<mojom::HostStatus,
+      std::pair<ash::multidevice_setup::mojom::HostStatus,
                 absl::optional<multidevice::RemoteDeviceRef>>;
-  using FeatureStatesMap = base::flat_map<mojom::Feature, mojom::FeatureState>;
+  using FeatureStatesMap =
+      base::flat_map<ash::multidevice_setup::mojom::Feature,
+                     ash::multidevice_setup::mojom::FeatureState>;
 
   class Observer {
    public:
@@ -49,7 +51,7 @@
 
   static HostStatusWithDevice GenerateDefaultHostStatusWithDevice();
   static FeatureStatesMap GenerateDefaultFeatureStatesMap(
-      mojom::FeatureState default_value);
+      ash::multidevice_setup::mojom::FeatureState default_value);
 
   MultiDeviceSetupClient();
 
@@ -66,21 +68,26 @@
   virtual void SetHostDevice(
       const std::string& host_instance_id_or_legacy_device_id,
       const std::string& auth_token,
-      mojom::MultiDeviceSetup::SetHostDeviceCallback callback) = 0;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::SetHostDeviceCallback
+          callback) = 0;
   virtual void RemoveHostDevice() = 0;
   virtual const HostStatusWithDevice& GetHostStatus() const = 0;
   virtual void SetFeatureEnabledState(
-      mojom::Feature feature,
+      ash::multidevice_setup::mojom::Feature feature,
       bool enabled,
       const absl::optional<std::string>& auth_token,
-      mojom::MultiDeviceSetup::SetFeatureEnabledStateCallback callback) = 0;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::
+          SetFeatureEnabledStateCallback callback) = 0;
   virtual const FeatureStatesMap& GetFeatureStates() const = 0;
-  mojom::FeatureState GetFeatureState(mojom::Feature feature) const;
+  ash::multidevice_setup::mojom::FeatureState GetFeatureState(
+      ash::multidevice_setup::mojom::Feature feature) const;
   virtual void RetrySetHostNow(
-      mojom::MultiDeviceSetup::RetrySetHostNowCallback callback) = 0;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::RetrySetHostNowCallback
+          callback) = 0;
   virtual void TriggerEventForDebugging(
-      mojom::EventTypeForDebugging type,
-      mojom::MultiDeviceSetup::TriggerEventForDebuggingCallback callback) = 0;
+      ash::multidevice_setup::mojom::EventTypeForDebugging type,
+      ash::multidevice_setup::mojom::MultiDeviceSetup::
+          TriggerEventForDebuggingCallback callback) = 0;
 
  protected:
   void NotifyHostStatusChanged(
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
index 15caae5..d7cb6ca 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.cc
@@ -35,8 +35,7 @@
   base::UmaHistogramEnumeration(
       "MultiDevice.BetterTogetherSuite.MultiDeviceFeatureState.MojoClient",
       feature_states_map
-          .find(
-              chromeos::multidevice_setup::mojom::Feature::kBetterTogetherSuite)
+          .find(ash::multidevice_setup::mojom::Feature::kBetterTogetherSuite)
           ->second);
 }
 
@@ -46,6 +45,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // static
 MultiDeviceSetupClientImpl::Factory*
     MultiDeviceSetupClientImpl::Factory::test_factory_ = nullptr;
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
index c240618..e39b24c 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl.h
@@ -25,20 +25,23 @@
 namespace multidevice_setup {
 
 // Concrete implementation of MultiDeviceSetupClient.
-class MultiDeviceSetupClientImpl : public MultiDeviceSetupClient,
-                                   public mojom::HostStatusObserver,
-                                   public mojom::FeatureStateObserver {
+class MultiDeviceSetupClientImpl
+    : public MultiDeviceSetupClient,
+      public ash::multidevice_setup::mojom::HostStatusObserver,
+      public ash::multidevice_setup::mojom::FeatureStateObserver {
  public:
   class Factory {
    public:
     static std::unique_ptr<MultiDeviceSetupClient> Create(
-        mojo::PendingRemote<mojom::MultiDeviceSetup> remote_setup);
+        mojo::PendingRemote<ash::multidevice_setup::mojom::MultiDeviceSetup>
+            remote_setup);
     static void SetFactoryForTesting(Factory* test_factory);
 
    protected:
     virtual ~Factory();
     virtual std::unique_ptr<MultiDeviceSetupClient> CreateInstance(
-        mojo::PendingRemote<mojom::MultiDeviceSetup> remote_setup) = 0;
+        mojo::PendingRemote<ash::multidevice_setup::mojom::MultiDeviceSetup>
+            remote_setup) = 0;
 
    private:
     static Factory* test_factory_;
@@ -55,26 +58,28 @@
   void SetHostDevice(
       const std::string& host_instance_id_or_legacy_device_id,
       const std::string& auth_token,
-      mojom::MultiDeviceSetup::SetHostDeviceCallback callback) override;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::SetHostDeviceCallback
+          callback) override;
   void RemoveHostDevice() override;
   const HostStatusWithDevice& GetHostStatus() const override;
   void SetFeatureEnabledState(
-      mojom::Feature feature,
+      ash::multidevice_setup::mojom::Feature feature,
       bool enabled,
       const absl::optional<std::string>& auth_token,
-      mojom::MultiDeviceSetup::SetFeatureEnabledStateCallback callback)
-      override;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::
+          SetFeatureEnabledStateCallback callback) override;
   const FeatureStatesMap& GetFeatureStates() const override;
   void RetrySetHostNow(
-      mojom::MultiDeviceSetup::RetrySetHostNowCallback callback) override;
+      ash::multidevice_setup::mojom::MultiDeviceSetup::RetrySetHostNowCallback
+          callback) override;
   void TriggerEventForDebugging(
-      mojom::EventTypeForDebugging type,
-      mojom::MultiDeviceSetup::TriggerEventForDebuggingCallback callback)
-      override;
+      ash::multidevice_setup::mojom::EventTypeForDebugging type,
+      ash::multidevice_setup::mojom::MultiDeviceSetup::
+          TriggerEventForDebuggingCallback callback) override;
 
   // mojom::HostStatusObserver:
   void OnHostStatusChanged(
-      mojom::HostStatus host_status,
+      ash::multidevice_setup::mojom::HostStatus host_status,
       const absl::optional<multidevice::RemoteDevice>& host_device) override;
 
   // mojom::FeatureStateObserver:
@@ -85,7 +90,8 @@
   friend class MultiDeviceSetupClientImplTest;
 
   explicit MultiDeviceSetupClientImpl(
-      mojo::PendingRemote<mojom::MultiDeviceSetup> remote_setup);
+      mojo::PendingRemote<ash::multidevice_setup::mojom::MultiDeviceSetup>
+          remote_setup);
 
   void OnGetEligibleHostDevicesCompleted(
       GetEligibleHostDevicesCallback callback,
@@ -93,18 +99,19 @@
 
   void OnFeatureStateMetricTimerFired();
 
-  mojo::PendingRemote<mojom::HostStatusObserver>
+  mojo::PendingRemote<ash::multidevice_setup::mojom::HostStatusObserver>
   GenerateHostStatusObserverRemote();
-  mojo::PendingRemote<mojom::FeatureStateObserver>
+  mojo::PendingRemote<ash::multidevice_setup::mojom::FeatureStateObserver>
   GenerateFeatureStatesObserverRemote();
 
   void FlushForTesting();
 
-  mojo::Remote<mojom::MultiDeviceSetup> multidevice_setup_remote_;
-  mojo::Receiver<mojom::HostStatusObserver> host_status_observer_receiver_{
-      this};
-  mojo::Receiver<mojom::FeatureStateObserver> feature_state_observer_receiver_{
-      this};
+  mojo::Remote<ash::multidevice_setup::mojom::MultiDeviceSetup>
+      multidevice_setup_remote_;
+  mojo::Receiver<ash::multidevice_setup::mojom::HostStatusObserver>
+      host_status_observer_receiver_{this};
+  mojo::Receiver<ash::multidevice_setup::mojom::FeatureStateObserver>
+      feature_state_observer_receiver_{this};
   std::unique_ptr<multidevice::RemoteDeviceCache> remote_device_cache_;
 
   // We want to delay the initial logging of the feature states map to better
diff --git a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
index 4b61703e..e38e7ae1 100644
--- a/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
+++ b/ash/services/multidevice_setup/public/cpp/multidevice_setup_client_impl_unittest.cc
@@ -30,6 +30,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const size_t kNumTestDevices = 5u;
 constexpr char kMultideviceBetterTogetherMetric[] =
     "MultiDevice.BetterTogetherSuite.MultiDeviceFeatureState.MojoClient";
diff --git a/ash/services/multidevice_setup/public/cpp/prefs.cc b/ash/services/multidevice_setup/public/cpp/prefs.cc
index cc8ba6b5..8305577e 100644
--- a/ash/services/multidevice_setup/public/cpp/prefs.cc
+++ b/ash/services/multidevice_setup/public/cpp/prefs.cc
@@ -12,6 +12,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 // Note: Pref name strings follow an inconsistent naming convention because some
 // of them were created before the MultiDeviceSetup project.
 
diff --git a/ash/services/multidevice_setup/public/cpp/prefs.h b/ash/services/multidevice_setup/public/cpp/prefs.h
index 834824c1..c01eecd6 100644
--- a/ash/services/multidevice_setup/public/cpp/prefs.h
+++ b/ash/services/multidevice_setup/public/cpp/prefs.h
@@ -48,12 +48,14 @@
 
 void RegisterFeaturePrefs(PrefRegistrySimple* registry);
 bool AreAnyMultiDeviceFeaturesAllowed(const PrefService* pref_service);
-bool IsFeatureAllowed(mojom::Feature feature, const PrefService* pref_service);
+bool IsFeatureAllowed(ash::multidevice_setup::mojom::Feature feature,
+                      const PrefService* pref_service);
 
 // Returns true if the pref tracking |feature|'s enabled state is using the
 // default value it was registered with.
-bool IsDefaultFeatureEnabledValue(mojom::Feature feature,
-                                  const PrefService* pref_service);
+bool IsDefaultFeatureEnabledValue(
+    ash::multidevice_setup::mojom::Feature feature,
+    const PrefService* pref_service);
 
 }  // namespace multidevice_setup
 
@@ -65,6 +67,7 @@
 namespace multidevice_setup {
 using ::chromeos::multidevice_setup::AreAnyMultiDeviceFeaturesAllowed;
 using ::chromeos::multidevice_setup::IsFeatureAllowed;
+using ::chromeos::multidevice_setup::RegisterFeaturePrefs;
 }  // namespace multidevice_setup
 }  // namespace ash
 
diff --git a/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
index 79f7eb98a..affb0b2 100644
--- a/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
+++ b/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module chromeos.multidevice_setup.mojom;
+module ash.multidevice_setup.mojom;
 
 import "chromeos/components/multidevice/mojom/multidevice_types.mojom";
 import "chromeos/services/device_sync/public/mojom/device_sync.mojom";
diff --git a/ash/services/multidevice_setup/wifi_sync_notification_controller.cc b/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
index fa89a65..f4e94b7 100644
--- a/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
+++ b/ash/services/multidevice_setup/wifi_sync_notification_controller.cc
@@ -27,6 +27,9 @@
 
 namespace multidevice_setup {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const char kCanShowWifiSyncAnnouncementPrefName[] =
     "multidevice_setup.can_show_wifi_sync_announcement";
 
diff --git a/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc b/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
index 50cf3f7..2566609 100644
--- a/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
+++ b/ash/services/multidevice_setup/wifi_sync_notification_controller_unittest.cc
@@ -30,6 +30,9 @@
 
 namespace {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace mojom = ::ash::multidevice_setup::mojom;
+
 const size_t kNumTestDevices = 4;
 
 }  // namespace
diff --git a/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc b/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
index 0adb332..78975eb 100644
--- a/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
+++ b/ash/services/secure_channel/public/cpp/client/connection_manager_impl_unittest.cc
@@ -31,7 +31,7 @@
 const char kConnectionDurationMetricName[] = "PhoneHub.Connection.Duration";
 const char kConnectionLatencyMetricName[] = "PhoneHub.Connectivity.Latency";
 
-using multidevice_setup::mojom::HostStatus;
+using ::ash::multidevice_setup::mojom::HostStatus;
 
 constexpr base::TimeDelta kFakeConnectionLatencyTime(base::Seconds(3u));
 constexpr base::TimeDelta kFakeConnectionDurationTime(base::Seconds(10u));
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 03a45e1..d4d5280f 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -98,8 +98,8 @@
 
   // Binds a MultiDeviceSetup receiver for the primary profile.
   virtual void BindMultiDeviceSetup(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup> receiver) = 0;
+      mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>
+          receiver) = 0;
 
   // Returns an interface to the Media Session service, or null if not
   // available.
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.cc b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
index 813d174..339b7e8 100644
--- a/ash/system/phonehub/multidevice_feature_opt_in_view.cc
+++ b/ash/system/phonehub/multidevice_feature_opt_in_view.cc
@@ -14,6 +14,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/phonehub/phone_hub_metrics.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
+#include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 
 namespace ash {
@@ -22,25 +23,108 @@
 using phone_hub_metrics::LogInterstitialScreenEvent;
 
 namespace {
+
+using PermissionSetupMode = MultideviceFeatureOptInView::PermissionSetupMode;
+
 // URL of the multidevice settings page with the URL parameter that will
 // start up the opt-in-flow.
 // TODO: Update this URL once the new access setup dialog has been updated
 constexpr char kMultideviceSettingsUrl[] =
     "chrome://os-settings/multidevice/"
-    "features?showPhonePermissionSetupDialog";
+    "features?showPhonePermissionSetupDialog&mode=%d";
+
+PermissionSetupMode GetPermissionSetupMode(
+    phonehub::MultideviceFeatureAccessManager*
+        multidevice_feature_access_manager) {
+  bool can_request_notification_access =
+      multidevice_feature_access_manager->GetNotificationAccessStatus() ==
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted;
+  bool can_request_apps_acess =
+      features::IsEcheSWAEnabled() &&
+      features::IsEchePhoneHubPermissionsOnboarding() &&
+      multidevice_feature_access_manager->GetAppsAccessStatus() ==
+          phonehub::MultideviceFeatureAccessManager::AccessStatus::
+              kAvailableButNotGranted;
+  bool can_request_camera_roll_access =
+      features::IsPhoneHubCameraRollEnabled() &&
+      multidevice_feature_access_manager->GetCameraRollAccessStatus() ==
+          phonehub::MultideviceFeatureAccessManager::AccessStatus::
+              kAvailableButNotGranted;
+
+  PA_LOG(INFO) << "MultideviceFeatureOptInView can_request_notification_access:"
+               << can_request_notification_access
+               << ", can_request_apps_acess:" << can_request_apps_acess
+               << ", can_request_camera_roll_access:"
+               << can_request_camera_roll_access;
+  if (can_request_notification_access && can_request_camera_roll_access &&
+      can_request_apps_acess) {
+    return PermissionSetupMode::kAllPermissionsSetupMode;
+  } else if (can_request_notification_access && can_request_apps_acess &&
+             !can_request_camera_roll_access) {
+    return PermissionSetupMode::kNotificationAndApps;
+  } else if (can_request_apps_acess && can_request_camera_roll_access &&
+             !can_request_notification_access) {
+    return PermissionSetupMode::kAppsAndCameraRoll;
+  } else if (can_request_notification_access &&
+             can_request_camera_roll_access && !can_request_apps_acess) {
+    return PermissionSetupMode::kNotificationAndCameraRoll;
+  } else if (!can_request_notification_access &&
+             !can_request_camera_roll_access && can_request_apps_acess) {
+    return PermissionSetupMode::kAppsSetupMode;
+  } else if (!can_request_notification_access && !can_request_apps_acess &&
+             can_request_camera_roll_access) {
+    return PermissionSetupMode::kCameraRollSetupMode;
+  } else if (!can_request_camera_roll_access && !can_request_apps_acess &&
+             can_request_notification_access) {
+    return PermissionSetupMode::kNotificationSetupMode;
+  }
+
+  return PermissionSetupMode::kNone;
+}
+
+int GetDescriptionStringId(phonehub::MultideviceFeatureAccessManager*
+                               multidevice_feature_access_manager) {
+  MultideviceFeatureOptInView::PermissionSetupMode permission_setup_mode =
+      GetPermissionSetupMode(multidevice_feature_access_manager);
+  switch (permission_setup_mode) {
+    case PermissionSetupMode::kCameraRollSetupMode:
+      return IDS_ASH_PHONE_HUB_CAMERA_ROLL_OPT_IN_DESCRIPTION;
+    case PermissionSetupMode::kAppsSetupMode:
+      return IDS_ASH_PHONE_HUB_APPS_OPT_IN_DESCRIPTION;
+    case PermissionSetupMode::kNotificationAndCameraRoll:
+    case PermissionSetupMode::kNotificationSetupMode:
+      return IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION;
+    case PermissionSetupMode::kNotificationAndApps:
+    case PermissionSetupMode::kAppsAndCameraRoll:
+    case PermissionSetupMode::kAllPermissionsSetupMode:
+      return IDS_ASH_PHONE_HUB_NOTIFICATION_AND_APPS_OPT_IN_DESCRIPTION;
+    case PermissionSetupMode::kNone:
+      // Just return the default strings since the MultideviceFeatureOptInView
+      // will be invisible.
+      return IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION;
+  }
+}
+
+std::string GetMultiDeviceSettingUrl(
+    PermissionSetupMode permission_setup_mode) {
+  return base::StringPrintf(kMultideviceSettingsUrl,
+                            static_cast<int>(permission_setup_mode));
+}
 
 }  // namespace
 
 MultideviceFeatureOptInView::MultideviceFeatureOptInView(
     phonehub::MultideviceFeatureAccessManager*
         multidevice_feature_access_manager)
-    : SubFeatureOptInView(PhoneHubViewID::kMultideviceFeatureOptInView,
-                          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_DESCRIPTION,
-                          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON),
+    : SubFeatureOptInView(
+          PhoneHubViewID::kMultideviceFeatureOptInView,
+          GetDescriptionStringId(multidevice_feature_access_manager),
+          IDS_ASH_PHONE_HUB_NOTIFICATION_OPT_IN_SET_UP_BUTTON),
       multidevice_feature_access_manager_(multidevice_feature_access_manager) {
   DCHECK(multidevice_feature_access_manager_);
+  setup_mode_ = GetPermissionSetupMode(multidevice_feature_access_manager_);
   access_manager_observation_.Observe(multidevice_feature_access_manager_);
-
   // Checks and updates its visibility upon creation.
   UpdateVisibility();
 
@@ -54,9 +138,11 @@
   // Opens the notification set up dialog in settings to start the opt in flow.
   LogNotificationOptInEvent(InterstitialScreenEvent::kConfirm);
   // This intentionally uses GetInstance() to open an OS Settings page in ash.
+  std::string url = GetMultiDeviceSettingUrl(setup_mode_);
+  PA_LOG(INFO) << "MultideviceFeatureOptInView SetUpButtonPressed target url:"
+               << url;
   NewWindowDelegate::GetInstance()->OpenUrl(
-      GURL(kMultideviceSettingsUrl),
-      NewWindowDelegate::OpenUrlFrom::kUserInteraction);
+      GURL(url), NewWindowDelegate::OpenUrlFrom::kUserInteraction);
 }
 
 void MultideviceFeatureOptInView::DismissButtonPressed() {
@@ -76,22 +162,11 @@
 
 void MultideviceFeatureOptInView::UpdateVisibility() {
   DCHECK(multidevice_feature_access_manager_);
-
-  // Can only request access if it is available but has not yet been granted.
-  bool can_request_notification_access =
-      multidevice_feature_access_manager_->GetNotificationAccessStatus() ==
-      phonehub::MultideviceFeatureAccessManager::AccessStatus::
-          kAvailableButNotGranted;
-  bool can_request_camera_roll_access =
-      features::IsPhoneHubCameraRollEnabled() &&
-      multidevice_feature_access_manager_->GetCameraRollAccessStatus() ==
-          phonehub::MultideviceFeatureAccessManager::AccessStatus::
-              kAvailableButNotGranted;
-  const bool should_show =
-      (can_request_notification_access || can_request_camera_roll_access) &&
-      !multidevice_feature_access_manager_
-           ->HasMultideviceFeatureSetupUiBeenDismissed();
-  SetVisible(should_show);
+  // Refresh the permission status.
+  setup_mode_ = GetPermissionSetupMode(multidevice_feature_access_manager_);
+  SetVisible(setup_mode_ != PermissionSetupMode::kNone &&
+             !multidevice_feature_access_manager_
+                  ->HasMultideviceFeatureSetupUiBeenDismissed());
 }
 
 BEGIN_METADATA(MultideviceFeatureOptInView, views::View)
diff --git a/ash/system/phonehub/multidevice_feature_opt_in_view.h b/ash/system/phonehub/multidevice_feature_opt_in_view.h
index ebba6135..833b34b 100644
--- a/ash/system/phonehub/multidevice_feature_opt_in_view.h
+++ b/ash/system/phonehub/multidevice_feature_opt_in_view.h
@@ -22,9 +22,21 @@
  public:
   METADATA_HEADER(MultideviceFeatureOptInView);
 
+  enum class PermissionSetupMode {
+    kNone = 0,
+    kNotificationSetupMode = 1,
+    kAppsSetupMode = 2,
+    kCameraRollSetupMode = 3,
+    kNotificationAndApps = 4,
+    kNotificationAndCameraRoll = 5,
+    kAppsAndCameraRoll = 6,
+    kAllPermissionsSetupMode = 7,
+  };
+
   explicit MultideviceFeatureOptInView(
       phonehub::MultideviceFeatureAccessManager*
           multidevice_feature_access_manager);
+
   MultideviceFeatureOptInView(const MultideviceFeatureOptInView&) = delete;
   MultideviceFeatureOptInView& operator=(const MultideviceFeatureOptInView&) =
       delete;
@@ -48,6 +60,9 @@
   base::ScopedObservation<phonehub::MultideviceFeatureAccessManager,
                           phonehub::MultideviceFeatureAccessManager::Observer>
       access_manager_observation_{this};
+
+  // The current setup mode.
+  MultideviceFeatureOptInView::PermissionSetupMode setup_mode_;
 };
 
 }  // namespace ash
diff --git a/ash/system/phonehub/phone_hub_recent_apps_view_unittest.cc b/ash/system/phonehub/phone_hub_recent_apps_view_unittest.cc
index 97b9a28..430c468 100644
--- a/ash/system/phonehub/phone_hub_recent_apps_view_unittest.cc
+++ b/ash/system/phonehub/phone_hub_recent_apps_view_unittest.cc
@@ -20,7 +20,7 @@
 
 namespace {
 
-using FeatureState = ::chromeos::multidevice_setup::mojom::FeatureState;
+using FeatureState = multidevice_setup::mojom::FeatureState;
 
 class FakeEvent : public ui::Event {
  public:
diff --git a/ash/system/phonehub/phone_hub_tray_unittest.cc b/ash/system/phonehub/phone_hub_tray_unittest.cc
index 07fce45..5d3bec0 100644
--- a/ash/system/phonehub/phone_hub_tray_unittest.cc
+++ b/ash/system/phonehub/phone_hub_tray_unittest.cc
@@ -308,7 +308,7 @@
   // for the notification set up flow.
   EXPECT_CALL(new_window_delegate(),
               OpenUrl(GURL("chrome://os-settings/multidevice/"
-                           "features?showPhonePermissionSetupDialog"),
+                           "features?showPhonePermissionSetupDialog&mode=5"),
                       NewWindowDelegate::OpenUrlFrom::kUserInteraction));
 
   LeftClickOn(notification_opt_in_set_up_button());
@@ -332,6 +332,160 @@
   EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
 }
 
+TEST_F(PhoneHubTrayTest, StartAllPermissionSetUpFlow) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{chromeos::features::kPhoneHub,
+                            chromeos::features::kPhoneHubCameraRoll,
+                            chromeos::features::kEcheSWA,
+                            chromeos::features::
+                                kEchePhoneHubPermissionsOnboarding},
+      /*disabled_features=*/{});
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetAppsAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+
+  ClickTrayButton();
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
+
+  // Clicking on the set up button should open the corresponding settings page
+  // for the notification set up flow.
+  EXPECT_CALL(new_window_delegate(),
+              OpenUrl(GURL("chrome://os-settings/multidevice/"
+                           "features?showPhonePermissionSetupDialog&mode=7"),
+                      NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+
+  LeftClickOn(notification_opt_in_set_up_button());
+}
+
+TEST_F(PhoneHubTrayTest, StartNotificationAndAppSetUpFlow) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{chromeos::features::kPhoneHub,
+                            chromeos::features::kEcheSWA,
+                            chromeos::features::
+                                kEchePhoneHubPermissionsOnboarding},
+      /*disabled_features=*/{});
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetAppsAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  ClickTrayButton();
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
+
+  // Clicking on the set up button should open the corresponding settings page
+  // for the notification set up flow.
+  EXPECT_CALL(new_window_delegate(),
+              OpenUrl(GURL("chrome://os-settings/multidevice/"
+                           "features?showPhonePermissionSetupDialog&mode=4"),
+                      NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+
+  LeftClickOn(notification_opt_in_set_up_button());
+}
+
+TEST_F(PhoneHubTrayTest, StartNotificationAccessOnlySetUpFlow) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{chromeos::features::kPhoneHub,
+                            chromeos::features::kEcheSWA,
+                            chromeos::features::
+                                kEchePhoneHubPermissionsOnboarding},
+      /*disabled_features=*/{});
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetAppsAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  ClickTrayButton();
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
+
+  // Clicking on the set up button should open the corresponding settings page
+  // for the notification set up flow.
+  EXPECT_CALL(new_window_delegate(),
+              OpenUrl(GURL("chrome://os-settings/multidevice/"
+                           "features?showPhonePermissionSetupDialog&mode=1"),
+                      NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+
+  LeftClickOn(notification_opt_in_set_up_button());
+}
+
+TEST_F(PhoneHubTrayTest, StartAppsAccessOnlySetUpFlow) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{chromeos::features::kPhoneHub,
+                            chromeos::features::kEcheSWA,
+                            chromeos::features::
+                                kEchePhoneHubPermissionsOnboarding},
+      /*disabled_features=*/{});
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetAppsAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+
+  ClickTrayButton();
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
+
+  // Clicking on the set up button should open the corresponding settings page
+  // for the notification set up flow.
+  EXPECT_CALL(new_window_delegate(),
+              OpenUrl(GURL("chrome://os-settings/multidevice/"
+                           "features?showPhonePermissionSetupDialog&mode=2"),
+                      NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+
+  LeftClickOn(notification_opt_in_set_up_button());
+}
+
+TEST_F(PhoneHubTrayTest, StartCameraRollOnlySetUpFlow) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatures(
+      /*enabled_features=*/{chromeos::features::kPhoneHub,
+                            chromeos::features::kEcheSWA,
+                            chromeos::features::
+                                kEchePhoneHubPermissionsOnboarding},
+      /*disabled_features=*/{});
+  GetMultideviceFeatureAccessManager()->SetNotificationAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+  GetMultideviceFeatureAccessManager()->SetCameraRollAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted);
+  GetMultideviceFeatureAccessManager()->SetAppsAccessStatusInternal(
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted);
+
+  ClickTrayButton();
+  EXPECT_TRUE(multidevice_feature_opt_in_view());
+  EXPECT_TRUE(multidevice_feature_opt_in_view()->GetVisible());
+
+  // Clicking on the set up button should open the corresponding settings page
+  // for the notification set up flow.
+  EXPECT_CALL(new_window_delegate(),
+              OpenUrl(GURL("chrome://os-settings/multidevice/"
+                           "features?showPhonePermissionSetupDialog&mode=3"),
+                      NewWindowDelegate::OpenUrlFrom::kUserInteraction));
+
+  LeftClickOn(notification_opt_in_set_up_button());
+}
+
 TEST_F(PhoneHubTrayTest, HideTrayItemOnUiStateChange) {
   ClickTrayButton();
   EXPECT_TRUE(phone_hub_tray_->is_active());
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index 6f9b5c54..f4a2ed6 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -68,7 +68,7 @@
 }
 
 void TestShellDelegate::BindMultiDeviceSetup(
-    mojo::PendingReceiver<chromeos::multidevice_setup::mojom::MultiDeviceSetup>
+    mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>
         receiver) {
   if (multidevice_setup_binder_)
     multidevice_setup_binder_.Run(std::move(receiver));
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index f83d2bcd..7f8ef21 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -27,8 +27,7 @@
   // Allows tests to override the MultiDeviceSetup binding behavior for this
   // TestShellDelegate.
   using MultiDeviceSetupBinder = base::RepeatingCallback<void(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup>)>;
+      mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>)>;
   void SetMultiDeviceSetupBinder(MultiDeviceSetupBinder binder) {
     multidevice_setup_binder_ = std::move(binder);
   }
@@ -50,9 +49,8 @@
   bool ShouldWaitForTouchPressAck(gfx::NativeWindow window) override;
   int GetBrowserWebUITabStripHeight() override;
   void BindMultiDeviceSetup(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup> receiver)
-      override;
+      mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>
+          receiver) override;
   bool IsSessionRestoreInProgress() const override;
   void SetUpEnvironmentForLockedFullscreen(bool locked) override {}
   const GURL& GetLastCommittedURLForWindowIfAny(aura::Window* window) override;
diff --git a/ash/webui/camera_app_ui/resources/.eslintrc.js b/ash/webui/camera_app_ui/resources/.eslintrc.js
index 2c77ea3fa1..1e3facd 100644
--- a/ash/webui/camera_app_ui/resources/.eslintrc.js
+++ b/ash/webui/camera_app_ui/resources/.eslintrc.js
@@ -452,6 +452,10 @@
     'valid-jsdoc': 'off',
     'require-jsdoc': 'off',
 
+    // This is not useful since ES6 and contradicts to
+    // go/tsstyle#function-declarations.
+    'no-inner-declarations': 'off',
+
     // go/tsstyle#omit-comments-that-are-redundant-with-typescript
     'jsdoc/no-types': 'error',
     'jsdoc/require-jsdoc': [
@@ -546,6 +550,21 @@
         message: 'Use named function or arrow function instead. ' +
             '(go/tsstyle#function-declarations)',
       },
+      // Disallow local function declaration with arrow function without
+      // accessing this. This might have some false negative if the "this" is
+      // accessed deep inside the function in another scope, but should be
+      // rare. (go/tsstyle#function-declarations)
+      {
+        selector: 'VariableDeclarator:not(:has(.id[typeAnnotation]))' +
+            ' > ArrowFunctionExpression.init:not(:has(ThisExpression))',
+        message: 'Use named function to declare local function. ' +
+            '(go/tsstyle#function-declarations)',
+      },
+      // Disallow private fields. (go/tsstyle#private-fields)
+      {
+        selector: 'TSPrivateIdentifier',
+        message: 'Private fields are not allowed. (go/tsstyle#private-fields)',
+      },
     ],
 
     '@typescript-eslint/naming-convention': [
@@ -658,6 +677,9 @@
     'cca/parameter-comment-format': 'error',
 
     'cca/generic-parameter-on-declaration-type': 'error',
+
+    // go/tsstyle#constructors
+    'new-parens': 'error',
   }),
   overrides: [{
     files: ['**/*.ts'],
diff --git a/ash/webui/camera_app_ui/resources/js/animation.ts b/ash/webui/camera_app_ui/resources/js/animation.ts
index 2f32d14..016d744c 100644
--- a/ash/webui/camera_app_ui/resources/js/animation.ts
+++ b/ash/webui/camera_app_ui/resources/js/animation.ts
@@ -77,7 +77,7 @@
     Promise<void> {
   doCancel({el, onChild});
   const queue = getQueueFor(el);
-  const job = async () => {
+  async function job() {
     /**
      * Force repaint before applying the animation.
      */
@@ -86,7 +86,7 @@
     await Promise.allSettled(
         getAnimations({el, onChild}).map((a) => a.finished));
     el.classList.remove('animate');
-  };
+  }
   await queue.push(job);
 }
 
diff --git a/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts b/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts
index 4898e79..bbebaed 100644
--- a/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/camera_manager.ts
@@ -236,8 +236,9 @@
   async initialize(cameraViewUI: CameraViewUI): Promise<void> {
     const helper = ChromeHelper.getInstance();
 
-    const setTablet = (isTablet: boolean) =>
-        state.set(state.State.TABLET, isTablet);
+    function setTablet(isTablet: boolean) {
+      state.set(state.State.TABLET, isTablet);
+    }
     const isTablet = await helper.initTabletModeMonitor(setTablet);
     setTablet(isTablet);
 
diff --git a/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts b/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts
index 175f398..d88daaf 100644
--- a/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/constraints_preferrer.ts
@@ -151,10 +151,12 @@
     const aspectRatio = captureResolution.width / captureResolution.height;
     const rs = Math.min(screenWidth, Math.floor(screenHeight * aspectRatio));
     const rc = captureResolution.width;
-    const cmpDescending = (r1: Resolution, r2: Resolution) =>
-        r2.width - r1.width;
-    const cmpAscending = (r1: Resolution, r2: Resolution) =>
-        r1.width - r2.width;
+    function cmpDescending(r1: Resolution, r2: Resolution) {
+      return r2.width - r1.width;
+    }
+    function cmpAscending(r1: Resolution, r2: Resolution) {
+      return r1.width - r2.width;
+    }
 
     if (rc <= rs) {
       const notLargerThanR =
@@ -218,13 +220,13 @@
    */
   protected groupResolutionRatio(resolutions: ResolutionList):
       Map<number, ResolutionList> {
-    const toSupportedPreviewRatio = (r: Resolution): number => {
+    function toSupportedPreviewRatio(r: Resolution): number {
       // Special aspect ratio mapping rule, see http://b/147986763.
       if (r.width === 848 && r.height === 480) {
         return (new Resolution(16, 9)).aspectRatio;
       }
       return r.aspectRatio;
-    };
+    }
 
     const result = new Map<number, ResolutionList>();
     for (const r of resolutions) {
@@ -368,9 +370,10 @@
       }
       this.deviceVideoPreviewResolutionMap.set(deviceId, pairedResolutions);
 
-      const findResolution = (width: number, height: number): Resolution|
-          undefined => videoResolutions.find(
-              (r) => r.width === width && r.height === height);
+      function findResolution(width: number, height: number) {
+        return videoResolutions.find(
+            (r) => r.width === width && r.height === height);
+      }
 
       let prefResolution = this.getPrefResolution(deviceId) ??
           findResolution(1920, 1080) ?? findResolution(1280, 720) ??
@@ -505,16 +508,18 @@
           return constFpses.map((fps) => ({r, fps}));
         };
 
-    const toPreivewConstraints =
-        ({width, height}: Resolution, fps: number|null): StreamConstraints => ({
-          deviceId,
-          audio: true,
-          video: {
-            frameRate: fps !== null ? {exact: fps} : {min: 20, ideal: 30},
-            width,
-            height,
-          },
-        });
+    function toPreivewConstraints(
+        {width, height}: Resolution, fps: number|null): StreamConstraints {
+      return {
+        deviceId,
+        audio: true,
+        video: {
+          frameRate: fps !== null ? {exact: fps} : {min: 20, ideal: 30},
+          width,
+          height,
+        },
+      };
+    }
 
     const prefResolution =
         this.getPrefResolution(deviceId) ?? new Resolution(0, -1);
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
index 9ee8caa..e97f4ac9 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/index.ts
@@ -172,14 +172,11 @@
 
     // Workaround for b/184089334 on PTZ camera to use preview frame as photo
     // result.
-    const checkSupportPTZForPhotoMode =
-        (captureResolution: Resolution, previewResolution: Resolution) =>
-            captureResolution.equals(previewResolution);
+    function checkSupportPTZForPhotoMode(
+        captureResolution: Resolution, previewResolution: Resolution) {
+      return captureResolution.equals(previewResolution);
+    }
 
-    // clang-format format this wrong if we use async (...) => {...} (missing a
-    // space after async). Using async function instead to get around this.
-    // TODO(pihsun): style guide recommends using function xxx() instead of
-    // lambda anyway, change other location too.
     /**
      * Prepare the device for the specific resolution and capture intent.
      */
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts b/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts
index 7f2b657..ad682f4 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/portrait.ts
@@ -73,13 +73,13 @@
       throw e;
     }
 
-    const toPhotoResult = async (pendingResult: TakePhotoResult) => {
+    async function toPhotoResult(pendingResult: TakePhotoResult) {
       const blob = await pendingResult.pendingBlob;
       const image = await util.blobToImage(blob);
       const resolution = new Resolution(image.width, image.height);
       const metadata = await pendingResult.pendingMetadata;
       return {blob, timestamp, resolution, metadata};
-    };
+    }
     return () => this.portraitHandler.onPortraitCaptureDone(
                toPhotoResult(reference), toPhotoResult(portrait));
   }
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/record_time.ts b/ash/webui/camera_app_ui/resources/js/device/mode/record_time.ts
index 1acb5ee..863617fd 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/record_time.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/record_time.ts
@@ -139,9 +139,9 @@
 
   getTimeMessage(ticks: number): string {
     // Format time into HH:MM:SS or MM:SS.
-    const pad = (n: number) => {
+    function pad(n: number) {
       return (n < 10 ? '0' : '') + n;
-    };
+    }
     let hh = '';
     if (ticks >= 3600) {
       hh = pad(Math.floor(ticks / 3600)) + ':';
diff --git a/ash/webui/camera_app_ui/resources/js/device/mode/video.ts b/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
index fef84b32..18367a0 100644
--- a/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/mode/video.ts
@@ -328,12 +328,12 @@
       this.togglePausedInternal = null;
       waitable.signal();
     };
-    const playEffect = async () => {
+    async function playEffect() {
       state.set(state.State.RECORDING_UI_PAUSED, toBePaused);
       await sound.play(dom.get(
           toBePaused ? '#sound-rec-pause' : '#sound-rec-start',
           HTMLAudioElement));
-    };
+    }
 
     this.mediaRecorder.addEventListener(toggledEvent, onToggled);
     if (toBePaused) {
@@ -532,7 +532,7 @@
     await new Promise<void>((resolve) => {
       let encodedFrames = 0;
       let start = 0.0;
-      const updateCanvas = (now: number) => {
+      function updateCanvas(now: number) {
         if (start === 0.0) {
           start = now;
         }
@@ -546,7 +546,7 @@
           gifSaver.write(context.getImageData(0, 0, width, height).data);
         }
         video.requestVideoFrameCallback(updateCanvas);
-      };
+      }
       video.requestVideoFrameCallback(updateCanvas);
     });
     return gifSaver;
@@ -565,14 +565,14 @@
       await new Promise((resolve, reject) => {
         let noChunk = true;
 
-        const ondataavailable = (event: BlobEvent) => {
+        function onDataAvailable(event: BlobEvent) {
           if (event.data && event.data.size > 0) {
             noChunk = false;
             saver.write(event.data);
           }
-        };
+        }
 
-        const onstop = async () => {
+        const onStop = async () => {
           assert(this.mediaRecorder !== null);
 
           state.set(state.State.RECORDING, false);
@@ -580,8 +580,8 @@
           state.set(state.State.RECORDING_UI_PAUSED, false);
 
           this.mediaRecorder.removeEventListener(
-              'dataavailable', ondataavailable);
-          this.mediaRecorder.removeEventListener('stop', onstop);
+              'dataavailable', onDataAvailable);
+          this.mediaRecorder.removeEventListener('stop', onStop);
 
           if (noChunk) {
             reject(new NoChunkError());
@@ -591,17 +591,17 @@
           }
         };
 
-        const onstart = () => {
+        const onStart = () => {
           assert(this.mediaRecorder !== null);
 
           state.set(state.State.RECORDING, true);
-          this.mediaRecorder.removeEventListener('start', onstart);
+          this.mediaRecorder.removeEventListener('start', onStart);
         };
 
         assert(this.mediaRecorder !== null);
-        this.mediaRecorder.addEventListener('dataavailable', ondataavailable);
-        this.mediaRecorder.addEventListener('stop', onstop);
-        this.mediaRecorder.addEventListener('start', onstart);
+        this.mediaRecorder.addEventListener('dataavailable', onDataAvailable);
+        this.mediaRecorder.addEventListener('stop', onStop);
+        this.mediaRecorder.addEventListener('start', onStart);
 
         window.addEventListener('beforeunload', beforeUnloadListener);
 
diff --git a/ash/webui/camera_app_ui/resources/js/device/preview.ts b/ash/webui/camera_app_ui/resources/js/device/preview.ts
index 37e2107..c70c749 100644
--- a/ash/webui/camera_app_ui/resources/js/device/preview.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/preview.ts
@@ -266,10 +266,10 @@
     const tpl = util.instantiateTemplate('#preview-video-template');
     const video = dom.getFrom(tpl, 'video', HTMLVideoElement);
     await new Promise<void>((resolve) => {
-      const handler = () => {
+      function handler() {
         video.removeEventListener('canplay', handler);
         resolve();
-      };
+      }
       video.addEventListener('canplay', handler);
       video.srcObject = stream;
     });
@@ -413,38 +413,37 @@
       element.style.display = 'none';
     }
 
-    const displayCategory = (selector: string, enabled: boolean) => {
+    function displayCategory(selector: string, enabled: boolean) {
       dom.get(selector, HTMLElement).classList.toggle('mode-on', enabled);
-    };
+    }
 
-    const showValue = (selector: string, val: string) => {
+    function showValue(selector: string, val: string) {
       const element = dom.get(selector, HTMLElement);
       element.style.display = '';
       element.textContent = val;
-    };
+    }
 
-    const buildInverseLookupFunction =
-        (obj: Record<string, number>, prefix: string): (key: number) =>
-            string => {
-              const map = new Map<number, string>();
-              for (const [key, val] of Object.entries(obj)) {
-                if (!key.startsWith(prefix)) {
-                  continue;
-                }
-                if (map.has(val)) {
-                  reportError(
-                      ErrorType.METADATA_MAPPING_FAILURE, ErrorLevel.ERROR,
-                      new Error(`Duplicated value: ${val}`));
-                  continue;
-                }
-                map.set(val, key.slice(prefix.length));
-              }
-              return (key: number) => {
-                const val = map.get(key);
-                assert(val !== undefined);
-                return val;
-              };
-            };
+    function buildInverseLookupFunction(
+        obj: Record<string, number>, prefix: string): (key: number) => string {
+      const map = new Map<number, string>();
+      for (const [key, val] of Object.entries(obj)) {
+        if (!key.startsWith(prefix)) {
+          continue;
+        }
+        if (map.has(val)) {
+          reportError(
+              ErrorType.METADATA_MAPPING_FAILURE, ErrorLevel.ERROR,
+              new Error(`Duplicated value: ${val}`));
+          continue;
+        }
+        map.set(val, key.slice(prefix.length));
+      }
+      return (key: number) => {
+        const val = map.get(key);
+        assert(val !== undefined);
+        return val;
+      };
+    }
 
     const afStateNameLookup = buildInverseLookupFunction(
         AndroidControlAfState, 'ANDROID_CONTROL_AF_STATE_');
@@ -458,12 +457,12 @@
 
     let sensorSensitivity: number|null = null;
     let sensorSensitivityBoost = 100;
-    const getSensitivity = () => {
+    function getSensitivity() {
       if (sensorSensitivity === null) {
         return 'N/A';
       }
       return sensorSensitivity * sensorSensitivityBoost / 100;
-    };
+    }
 
     const tag = CameraMetadataTag;
     const metadataEntryHandlers: Record<string, (values: number[]) => void> = {
@@ -596,7 +595,7 @@
                          .ANDROID_STATISTICS_FACE_DETECT_MODE_OFF;
       let faceRects: number[] = [];
 
-      const tryParseFaceEntry = (entry: CameraMetadataEntry) => {
+      function tryParseFaceEntry(entry: CameraMetadataEntry) {
         switch (entry.tag) {
           case tag.ANDROID_STATISTICS_FACE_DETECT_MODE: {
             const data = parseMetadata(entry);
@@ -610,7 +609,7 @@
           }
         }
         return false;
-      };
+      }
 
       assert(metadata.entries !== undefined);
       for (const entry of metadata.entries) {
diff --git a/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts b/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts
index 5fad2b2..9464abea 100644
--- a/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts
+++ b/ash/webui/camera_app_ui/resources/js/device/stream_manager.ts
@@ -191,10 +191,12 @@
    * virtual device.
    */
   private async doDeviceNotify(devices: DeviceInfo[]) {
-    const isVirtual = (d: DeviceInfo) => d.v3Info !== null &&
-        (d.v3Info.facing === Facing.VIRTUAL_USER ||
-         d.v3Info.facing === Facing.VIRTUAL_ENV ||
-         d.v3Info.facing === Facing.VIRTUAL_EXT);
+    function isVirtual(d: DeviceInfo) {
+      return d.v3Info !== null &&
+          (d.v3Info.facing === Facing.VIRTUAL_USER ||
+           d.v3Info.facing === Facing.VIRTUAL_ENV ||
+           d.v3Info.facing === Facing.VIRTUAL_EXT);
+    }
     const realDevices = devices.filter((d) => !isVirtual(d));
     const virtualDevices = devices.filter(isVirtual);
     // We currently only support one virtual device.
diff --git a/ash/webui/camera_app_ui/resources/js/error.ts b/ash/webui/camera_app_ui/resources/js/error.ts
index beee31c..4efa8f65 100644
--- a/ash/webui/camera_app_ui/resources/js/error.ts
+++ b/ash/webui/camera_app_ui/resources/js/error.ts
@@ -30,7 +30,9 @@
   if (fileName.startsWith(window.location.origin)) {
     fileName = fileName.substring(window.location.origin.length + 1);
   }
-  const ensureNumber = (n: number|undefined) => (n === undefined ? -1 : n);
+  function ensureNumber(n: number|undefined) {
+    return n === undefined ? -1 : n;
+  }
   return {
     fileName,
     funcName: callsite.getFunctionName() ?? '[Anonymous]',
diff --git a/ash/webui/camera_app_ui/resources/js/focus_ring.ts b/ash/webui/camera_app_ui/resources/js/focus_ring.ts
index 3605410..f4e7566 100644
--- a/ash/webui/camera_app_ui/resources/js/focus_ring.ts
+++ b/ash/webui/camera_app_ui/resources/js/focus_ring.ts
@@ -67,12 +67,12 @@
   ring = dom.get('#focus-ring', HTMLElement);
   ringCSSStyle = cssStyle('#focus-ring');
 
-  const setup = (el: HTMLElement) => {
+  function setup(el: HTMLElement) {
     el.addEventListener('focus', () => showFocus(el));
     if (el === document.activeElement) {
       showFocus(el);
     }
-  };
+  }
 
   for (const el of dom.getAll('[tabindex]', HTMLElement)) {
     setup(el);
diff --git a/ash/webui/camera_app_ui/resources/js/geometry.ts b/ash/webui/camera_app_ui/resources/js/geometry.ts
index 3aeb018..d129934d 100644
--- a/ash/webui/camera_app_ui/resources/js/geometry.ts
+++ b/ash/webui/camera_app_ui/resources/js/geometry.ts
@@ -235,25 +235,25 @@
      * @return Intersection of segment pt1, pt2 and segment pt3, pt4.
      *     Returns null for no intersection between two segment.
      */
-    const intersect =
-        (pt1: Point, pt2: Point, pt3: Point, pt4: Point): Point|null => {
-          const uDenom = (pt4.y - pt3.y) * (pt2.x - pt1.x) -
-              (pt4.x - pt3.x) * (pt2.y - pt1.y);
-          if (uDenom === 0) {
-            return null;
-          }
-          const ua = ((pt4.x - pt3.x) * (pt1.y - pt3.y) -
-                      (pt4.y - pt3.y) * (pt1.x - pt3.x)) /
-              uDenom;
-          const ub = ((pt2.x - pt1.x) * (pt1.y - pt3.y) -
-                      (pt2.y - pt1.y) * (pt1.x - pt3.x)) /
-              uDenom;
-          if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
-            return new Point(
-                pt1.x + ua * (pt2.x - pt1.x), pt1.y + ua * (pt2.y - pt1.y));
-          }
-          return null;
-        };
+    function intersect(pt1: Point, pt2: Point, pt3: Point, pt4: Point): Point|
+        null {
+      const uDenom =
+          (pt4.y - pt3.y) * (pt2.x - pt1.x) - (pt4.x - pt3.x) * (pt2.y - pt1.y);
+      if (uDenom === 0) {
+        return null;
+      }
+      const ua = ((pt4.x - pt3.x) * (pt1.y - pt3.y) -
+                  (pt4.y - pt3.y) * (pt1.x - pt3.x)) /
+          uDenom;
+      const ub = ((pt2.x - pt1.x) * (pt1.y - pt3.y) -
+                  (pt2.y - pt1.y) * (pt1.x - pt3.x)) /
+          uDenom;
+      if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
+        return new Point(
+            pt1.x + ua * (pt2.x - pt1.x), pt1.y + ua * (pt2.y - pt1.y));
+      }
+      return null;
+    }
 
     const cornRD = new Point(this.size.width, this.size.height);
     const cornLD = new Point(0, this.size.height);
diff --git a/ash/webui/camera_app_ui/resources/js/h264.ts b/ash/webui/camera_app_ui/resources/js/h264.ts
index 3d91245..1a4b2e9f 100644
--- a/ash/webui/camera_app_ui/resources/js/h264.ts
+++ b/ash/webui/camera_app_ui/resources/js/h264.ts
@@ -68,8 +68,9 @@
 }
 
 const levelLimits = (() => {
-  const limit = (processRate: number, frameSize: number, mainBitrate: number) =>
-      ({processRate, frameSize, mainBitrate});
+  function limit(processRate: number, frameSize: number, mainBitrate: number) {
+    return {processRate, frameSize, mainBitrate};
+  }
   return {
     [Level.LV30]: limit(40500, 1620, 10000),
     [Level.LV31]: limit(108000, 3600, 14000),
diff --git a/ash/webui/camera_app_ui/resources/js/intent.ts b/ash/webui/camera_app_ui/resources/js/intent.ts
index 1241b379..3101692 100644
--- a/ash/webui/camera_app_ui/resources/js/intent.ts
+++ b/ash/webui/camera_app_ui/resources/js/intent.ts
@@ -107,7 +107,9 @@
    */
   static create(url: URL, mode: Mode): Intent {
     const params = url.searchParams;
-    const getBool = (key: string) => params.get(key) === '1';
+    function getBool(key: string) {
+      return params.get(key) === '1';
+    }
     const param = params.get('intentId');
     if (param === null) {
       throw new ParseError(url);
diff --git a/ash/webui/camera_app_ui/resources/js/main.ts b/ash/webui/camera_app_ui/resources/js/main.ts
index b69c41d..98497839 100644
--- a/ash/webui/camera_app_ui/resources/js/main.ts
+++ b/ash/webui/camera_app_ui/resources/js/main.ts
@@ -133,11 +133,11 @@
         }
       });
 
-      const save = (element: HTMLInputElement) => {
+      function save(element: HTMLInputElement) {
         if (element.dataset['key'] !== undefined) {
           localStorage.set(element.dataset['key'], element.checked);
         }
-      };
+      }
       element.addEventListener('change', (event) => {
         if (element.dataset['state'] !== undefined) {
           state.set(
@@ -280,17 +280,18 @@
     })();
 
     const preloadImages = (async () => {
-      const loadImage = (url: string) =>
-          new Promise<void>((resolve, reject) => {
-            const link = document.createElement('link');
-            link.rel = 'preload';
-            link.as = 'image';
-            link.href = url;
-            link.onload = () => resolve();
-            link.onerror = () =>
-                reject(new Error(`Failed to preload image ${url}`));
-            document.head.appendChild(link);
-          });
+      function loadImage(url: string) {
+        return new Promise<void>((resolve, reject) => {
+          const link = document.createElement('link');
+          link.rel = 'preload';
+          link.as = 'image';
+          link.href = url;
+          link.onload = () => resolve();
+          link.onerror = () =>
+              reject(new Error(`Failed to preload image ${url}`));
+          document.head.appendChild(link);
+        });
+      }
       const results = await Promise.allSettled(
           preloadImagesList.map((name) => loadImage(`/images/${name}`)));
       for (const result of results) {
diff --git a/ash/webui/camera_app_ui/resources/js/metrics.ts b/ash/webui/camera_app_ui/resources/js/metrics.ts
index ad447c9..80d2ff3 100644
--- a/ash/webui/camera_app_ui/resources/js/metrics.ts
+++ b/ash/webui/camera_app_ui/resources/js/metrics.ts
@@ -41,15 +41,15 @@
  */
 async function sendEvent(
     event: UniversalAnalytics.FieldsObject, dimen?: Map<number, unknown>) {
-  const assignDimension =
-      (e: UniversalAnalytics.FieldsObject, d: Map<number, unknown>) => {
-        for (const [key, value] of d.entries()) {
-          // The TypeScript definition for UniversalAnalytics.FieldsObject
-          // manually listed out dimension1 ~ dimension200, and TypeScript don't
-          // recognize accessing it using []. Force the type here.
-          (e as Record<string, unknown>)[`dimension${key}`] = value;
-        }
-      };
+  function assignDimension(
+      e: UniversalAnalytics.FieldsObject, d: Map<number, unknown>) {
+    for (const [key, value] of d.entries()) {
+      // The TypeScript definition for UniversalAnalytics.FieldsObject
+      // manually listed out dimension1 ~ dimension200, and TypeScript don't
+      // recognize accessing it using []. Force the type here.
+      (e as Record<string, unknown>)[`dimension${key}`] = value;
+    }
+  }
 
   assert(baseDimen !== null);
   assignDimension(event, baseDimen);
@@ -152,9 +152,9 @@
   const GA_LOCAL_STORAGE_KEY = 'google-analytics.analytics.user-id';
   const clientId = localStorage.getString(GA_LOCAL_STORAGE_KEY);
 
-  const setClientId = (id: string) => {
+  function setClientId(id: string) {
     localStorage.set(GA_LOCAL_STORAGE_KEY, id);
-  };
+  }
 
   await (await gaHelper).initGA(GA_ID, clientId, Comlink.proxy(setClientId));
   ready.signal();
@@ -307,17 +307,19 @@
   recordType = RecordType.NOT_RECORDING,
   gifResult = GifResultType.NOT_GIF_RESULT,
 }: CaptureEventParam): void {
-  const condState =
-      (states: state.StateUnion[], cond?: state.StateUnion, strict?: boolean):
-          string => {
-            // Return the first existing state among the given states only if
-            // there is no gate condition or the condition is met.
-            const prerequisite = !cond || state.get(cond);
-            if (strict && !prerequisite) {
-              return '';
-            }
-            return prerequisite && states.find((s) => state.get(s)) || 'n/a';
-          };
+  function condState(
+      states: state.StateUnion[],
+      cond?: state.StateUnion,
+      strict?: boolean,
+      ): string {
+    // Return the first existing state among the given states only if
+    // there is no gate condition or the condition is met.
+    const prerequisite = !cond || state.get(cond);
+    if (strict && !prerequisite) {
+      return '';
+    }
+    return prerequisite && states.find((s) => state.get(s)) || 'n/a';
+  }
 
   sendEvent(
       {
@@ -414,7 +416,9 @@
  */
 export function sendIntentEvent({intent, result}: IntentEventParam): void {
   const {mode, shouldHandleResult, shouldDownScale, isSecure} = intent;
-  const getBoolValue = (b: boolean) => b ? '1' : '0';
+  function getBoolValue(b: boolean) {
+    return b ? '1' : '0';
+  }
   sendEvent(
       {
         eventCategory: 'intent',
diff --git a/ash/webui/camera_app_ui/resources/js/models/async_writer.ts b/ash/webui/camera_app_ui/resources/js/models/async_writer.ts
index aa9a218..e395eff6 100644
--- a/ash/webui/camera_app_ui/resources/js/models/async_writer.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/async_writer.ts
@@ -80,19 +80,19 @@
    * @return The combined writer.
    */
   static combine(...writers: AsyncWriter[]): AsyncWriter {
-    const write = async (blob: Blob) => {
+    async function write(blob: Blob) {
       await Promise.all(writers.map((writer) => writer.write(blob)));
-    };
+    }
 
     const allSeekable = writers.every((writer) => writer.seekable());
-    const seekAll = async (offset: number) => {
+    async function seekAll(offset: number) {
       await Promise.all(writers.map((writer) => writer.seek(offset)));
-    };
+    }
     const seek = allSeekable ? seekAll : null;
 
-    const close = async () => {
+    async function close() {
       await Promise.all(writers.map((writer) => writer.close()));
-    };
+    }
 
     return new AsyncWriter({write, seek, close});
   }
diff --git a/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts b/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts
index 1a887be..3e30e53 100644
--- a/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/barcode_worker.ts
@@ -15,12 +15,12 @@
 
     const cx = bitmap.width / 2;
     const cy = bitmap.height / 2;
-    const distanceToCenter = (code: DetectedBarcode): number => {
+    function distanceToCenter(code: DetectedBarcode): number {
       const {left, right, top, bottom} = code.boundingBox;
       const x = (left + right) / 2;
       const y = (top + bottom) / 2;
       return Math.hypot(x - cx, y - cy);
-    };
+    }
 
     let minDistance = Infinity;
     let bestCode: DetectedBarcode|null = null;
diff --git a/ash/webui/camera_app_ui/resources/js/models/ffmpeg/video_processor.ts b/ash/webui/camera_app_ui/resources/js/models/ffmpeg/video_processor.ts
index ea0c121..f8d68bc5 100644
--- a/ash/webui/camera_app_ui/resources/js/models/ffmpeg/video_processor.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/ffmpeg/video_processor.ts
@@ -351,7 +351,7 @@
       },
     };
 
-    const initFFmpeg = () => {
+    function initFFmpeg() {
       return new Promise<void>((resolve) => {
         // runFFmpeg() is a special function exposed by Emscripten that will
         // return an object with then(). The function passed into then() would
@@ -360,7 +360,7 @@
         // would cause an infinite loop.
         runFFmpeg(config).then(() => resolve());
       });
-    };
+    }
     this.jobQueue.push(initFFmpeg);
 
     // This is a function to be called by ffmpeg before running read() in C.
diff --git a/ash/webui/camera_app_ui/resources/js/models/file_namer.ts b/ash/webui/camera_app_ui/resources/js/models/file_namer.ts
index e3ba49a..4484db2b 100644
--- a/ash/webui/camera_app_ui/resources/js/models/file_namer.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/file_namer.ts
@@ -40,7 +40,9 @@
  * @return Transformed datetime name.
  */
 function timestampToDatetimeName(timestamp: number): string {
-  const pad = (n: number) => (n < 10 ? '0' : '') + n;
+  function pad(n: number) {
+    return (n < 10 ? '0' : '') + n;
+  }
   const date = new Date(timestamp);
   return date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) +
       '_' + pad(date.getHours()) + pad(date.getMinutes()) +
@@ -75,9 +77,9 @@
    * @return New filename.
    */
   newBurstName(isCover: boolean): string {
-    const prependZeros = (n: number, width: number) => {
+    function prependZeros(n: number, width: number) {
       return String(n).padStart(width, '0');
-    };
+    }
     return IMAGE_PREFIX + timestampToDatetimeName(this.timestamp) +
         BURST_SUFFIX + prependZeros(++this.burstCount, 5) +
         (isCover ? BURST_COVER_SUFFIX : '') + '.jpg';
diff --git a/ash/webui/camera_app_ui/resources/js/models/video_saver.ts b/ash/webui/camera_app_ui/resources/js/models/video_saver.ts
index 8115e9d6..b7a428d 100644
--- a/ash/webui/camera_app_ui/resources/js/models/video_saver.ts
+++ b/ash/webui/camera_app_ui/resources/js/models/video_saver.ts
@@ -61,9 +61,9 @@
  * Creates an AsyncWriter that writes to the given intent.
  */
 function createWriterForIntent(intent: Intent): AsyncWriter {
-  const write = async (blob: Blob) => {
+  async function write(blob: Blob) {
     await intent.appendData(new Uint8Array(await blob.arrayBuffer()));
-  };
+  }
   // TODO(crbug.com/1140852): Supports seek.
   return new AsyncWriter({write, seek: null, close: null});
 }
diff --git a/ash/webui/camera_app_ui/resources/js/sound.ts b/ash/webui/camera_app_ui/resources/js/sound.ts
index 1c34b44..abad3b93 100644
--- a/ash/webui/camera_app_ui/resources/js/sound.ts
+++ b/ash/webui/camera_app_ui/resources/js/sound.ts
@@ -36,13 +36,13 @@
 
   const audioStopped = new WaitableEvent<boolean>();
   const events = ['ended', 'pause'];
-  const onAudioStopped = () => {
+  function onAudioStopped() {
     elementsStatus.set(el, Status.PAUSED);
     audioStopped.signal(el.ended);
     for (const event of events) {
       el.removeEventListener(event, onAudioStopped);
     }
-  };
+  }
   for (const event of events) {
     el.addEventListener(event, onAudioStopped);
   }
@@ -63,10 +63,10 @@
     el.pause();
   } else if (status === Status.LOADING) {
     const canceled = new WaitableEvent();
-    const onPlaying = () => {
+    function onPlaying() {
       el.pause();
       canceled.signal();
-    };
+    }
     el.addEventListener('playing', onPlaying, {once: true});
     await canceled.wait();
   }
diff --git a/ash/webui/camera_app_ui/resources/js/thumbnailer.ts b/ash/webui/camera_app_ui/resources/js/thumbnailer.ts
index cd6bfe5..35c6e392 100644
--- a/ash/webui/camera_app_ui/resources/js/thumbnailer.ts
+++ b/ash/webui/camera_app_ui/resources/js/thumbnailer.ts
@@ -164,7 +164,7 @@
    * @return Returns begin of found pattern index or -1 for no further pattern
    *     is found.
    */
-  const findPattern = (...patterns: number[]): number => {
+  function findPattern(...patterns: number[]): number {
     for (; i + patterns.length < view.length; i++) {
       if (patterns.every((b, index) => b === view[i + index])) {
         const ret = i;
@@ -173,7 +173,7 @@
       }
     }
     return -1;
-  };
+  }
   // Parse object contains /Subtype /Image name and field from pdf format:
   // <</Name1 /Field1... \n/Name2... >>...<<...>>
   // The jpeg stream will follow the target object with length in field of
diff --git a/ash/webui/camera_app_ui/resources/js/tooltip.ts b/ash/webui/camera_app_ui/resources/js/tooltip.ts
index 7a5bc01..deea5af 100644
--- a/ash/webui/camera_app_ui/resources/js/tooltip.ts
+++ b/ash/webui/camera_app_ui/resources/js/tooltip.ts
@@ -95,12 +95,12 @@
     NodeListOf<HTMLElement> {
   wrapper = dom.get('#tooltip', HTMLElement);
   for (const el of elements) {
-    const handler = () => {
+    function handler() {
       // Handler hides tooltip only when it's for the element.
       if (el === hovered) {
         hide();
       }
-    };
+    }
     el.addEventListener('mouseout', handler);
     el.addEventListener('click', handler);
     el.addEventListener('mouseover', () => show(el));
diff --git a/ash/webui/camera_app_ui/resources/js/unload.ts b/ash/webui/camera_app_ui/resources/js/unload.ts
index 3251cf82..a7cf531 100644
--- a/ash/webui/camera_app_ui/resources/js/unload.ts
+++ b/ash/webui/camera_app_ui/resources/js/unload.ts
@@ -4,12 +4,12 @@
 
 const callbacks: Array<(() => void)> = [];
 
-const onWindowUnload = () => {
+function onWindowUnload() {
   for (const callback of callbacks) {
     callback();
   }
   window.removeEventListener('unload', onWindowUnload);
-};
+}
 
 window.addEventListener('unload', onWindowUnload);
 
diff --git a/ash/webui/camera_app_ui/resources/js/util.ts b/ash/webui/camera_app_ui/resources/js/util.ts
index e9e9ee9..c17edd26 100644
--- a/ash/webui/camera_app_ui/resources/js/util.ts
+++ b/ash/webui/camera_app_ui/resources/js/util.ts
@@ -101,13 +101,16 @@
  * @param rootElement Root of DOM subtree to be set up with.
  */
 export function setupI18nElements(rootElement: DocumentFragment|Element): void {
-  const getElements = (attr: string) =>
-      dom.getAllFrom(rootElement, `[${attr}]`, HTMLElement);
-  const getMessage = (element: HTMLElement, attr: string) =>
-      loadTimeData.getI18nMessage(
-          assertEnumVariant(I18nString, element.getAttribute(attr)));
-  const setAriaLabel = (element: HTMLElement, attr: string) =>
-      element.setAttribute('aria-label', getMessage(element, attr));
+  function getElements(attr: string) {
+    return dom.getAllFrom(rootElement, `[${attr}]`, HTMLElement);
+  }
+  function getMessage(element: HTMLElement, attr: string) {
+    return loadTimeData.getI18nMessage(
+        assertEnumVariant(I18nString, element.getAttribute(attr)));
+  }
+  function setAriaLabel(element: HTMLElement, attr: string) {
+    element.setAttribute('aria-label', getMessage(element, attr));
+  }
 
   for (const element of getElements('i18n-text')) {
     element.textContent = getMessage(element, 'i18n-text');
@@ -166,11 +169,11 @@
       onLabel: I18nString,
       offLabel: I18nString,
     }): void {
-  const update = (value: boolean) => {
+  function update(value: boolean) {
     const label = value ? onLabel : offLabel;
     element.setAttribute('i18n-label', label);
     element.setAttribute('aria-label', loadTimeData.getI18nMessage(label));
-  };
+  }
   update(state.get(s));
   state.addObserver(s, update);
 }
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera.ts b/ash/webui/camera_app_ui/resources/js/views/camera.ts
index 8546027..6fe8d8e 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera.ts
@@ -148,14 +148,14 @@
     /**
      * Gets type of ways to trigger shutter from click event.
      */
-    const getShutterType = (e: MouseEvent) => {
+    function getShutterType(e: MouseEvent) {
       if (e.clientX === 0 && e.clientY === 0) {
         return metrics.ShutterType.KEYBOARD;
       }
       return e.sourceCapabilities?.firesTouchEvents ?
           metrics.ShutterType.TOUCH :
           metrics.ShutterType.MOUSE;
-    };
+    }
 
     dom.get('#start-takephoto', HTMLButtonElement)
         .addEventListener('click', (e) => {
@@ -691,8 +691,10 @@
     let result = null;
     try {
       await this.prepareReview(async () => {
-        const doCrop = (blob: Blob, corners: Point[], rotation: number) =>
-            helper.convertToDocument(blob, corners, rotation, MimeType.JPEG);
+        function doCrop(blob: Blob, corners: Point[], rotation: number) {
+          return helper.convertToDocument(
+              blob, corners, rotation, MimeType.JPEG);
+        }
         let corners =
             refCorners ?? getDefaultScanCorners(originImage.resolution);
         // This is definitely assigned in either the async doRecrop or the else
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
index 3d872dc..e141703 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/document_corner_overlay.ts
@@ -238,8 +238,9 @@
           }
           this.maybeUpdatePointOfInterest(corners);
           const rect = this.cornerContainer.getBoundingClientRect();
-          const toOverlaySpace = (pt: Point) =>
-              new Point(rect.width * pt.x, rect.height * pt.y);
+          function toOverlaySpace(pt: Point) {
+            return new Point(rect.width * pt.x, rect.height * pt.y);
+          }
           this.onCornerDetected(corners.map(toOverlaySpace));
         });
     this.hide();
@@ -337,17 +338,17 @@
     /**
      * Start point(corner coordinates + outer shift) of settle animation.
      */
-    const calculateSettleStart =
-        (corner: Point, corner2: Point, corner3: Point, d: number): Point => {
-          const side = vectorFromPoints(corner2, corner);
-          const norm = side.normal().multiply(d);
+    function calculateSettleStart(
+        corner: Point, corner2: Point, corner3: Point, d: number): Point {
+      const side = vectorFromPoints(corner2, corner);
+      const norm = side.normal().multiply(d);
 
-          const side2 = vectorFromPoints(corner2, corner3);
-          const angle = side.rotation(side2);
-          const dir = side.direction().multiply(d / Math.tan(angle / 2));
+      const side2 = vectorFromPoints(corner2, corner3);
+      const angle = side.rotation(side2);
+      const dir = side.direction().multiply(d / Math.tan(angle / 2));
 
-          return vectorFromPoints(corner2).add(norm).add(dir).point();
-        };
+      return vectorFromPoints(corner2).add(norm).add(dir).point();
+    }
     const starts = corners.map((_, idx) => {
       const prevIdx = (idx + 3) % 4;
       const nextIdx = (idx + 1) % 4;
diff --git a/ash/webui/camera_app_ui/resources/js/views/camera/timertick.ts b/ash/webui/camera_app_ui/resources/js/views/camera/timertick.ts
index 12e1c657..6d7509d6 100644
--- a/ash/webui/camera_app_ui/resources/js/views/camera/timertick.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/camera/timertick.ts
@@ -42,7 +42,7 @@
       3: '#sound-tick-inc',
       [tickCounter]: '#sound-tick-start',
     };
-    const onTimerTick = () => {
+    function onTimerTick() {
       if (tickCounter === 0) {
         resolve();
       } else {
@@ -54,7 +54,7 @@
         tickTimeout = setTimeout(onTimerTick, 1000);
         tickCounter--;
       }
-    };
+    }
     // First tick immediately in the next message loop cycle.
     tickTimeout = setTimeout(onTimerTick, 0);
   });
diff --git a/ash/webui/camera_app_ui/resources/js/views/crop_document.ts b/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
index 1cec982..664da62 100644
--- a/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
@@ -214,7 +214,9 @@
 
       // Use arrow key to move corner.
       const KEYS = ['ArrowUp', 'ArrowLeft', 'ArrowDown', 'ArrowRight'];
-      const getKeyIndex = (e: KeyboardEvent) => KEYS.indexOf(e.key);
+      function getKeyIndex(e: KeyboardEvent) {
+        return KEYS.indexOf(e.key);
+      }
       const KEY_MOVEMENTS = [
         new Vector(0, -1),
         new Vector(-1, 0),
@@ -223,13 +225,13 @@
       ];
       const pressedKeyIndices = new Set<number>();
       let keyInterval: util.DelayInterval|null = null;
-      const clearKeydown = () => {
+      function clearKeydown() {
         if (keyInterval !== null) {
           keyInterval.stop();
           keyInterval = null;
         }
         pressedKeyIndices.clear();
-      };
+      }
       const announcer = new MovementAnnouncer();
 
       corner.el.addEventListener('blur', () => {
@@ -449,25 +451,25 @@
      * @return Square distance of |pt3| to segment formed by |pt1| and |pt2|
      *     and the corresponding nearest point on the segment.
      */
-    const distToSegment = (pt1: Point, pt2: Point, pt3: Point):
-        {dist2: number, nearest: Point} => {
-          // Minimum Distance between a Point and a Line:
-          // http://paulbourke.net/geometry/pointlineplane/
-          const v12 = vectorFromPoints(pt2, pt1);
-          const v13 = vectorFromPoints(pt3, pt1);
-          const u = (v12.x * v13.x + v12.y * v13.y) / v12.length2();
-          if (u <= 0) {
-            return {dist2: v13.length2(), nearest: pt1};
-          }
-          if (u >= 1) {
-            return {dist2: vectorFromPoints(pt3, pt2).length2(), nearest: pt2};
-          }
-          const projection = vectorFromPoints(pt1).add(v12.multiply(u)).point();
-          return {
-            dist2: vectorFromPoints(projection, pt3).length2(),
-            nearest: projection,
-          };
-        };
+    function distToSegment(
+        pt1: Point, pt2: Point, pt3: Point): {dist2: number, nearest: Point} {
+      // Minimum Distance between a Point and a Line:
+      // http://paulbourke.net/geometry/pointlineplane/
+      const v12 = vectorFromPoints(pt2, pt1);
+      const v13 = vectorFromPoints(pt3, pt1);
+      const u = (v12.x * v13.x + v12.y * v13.y) / v12.length2();
+      if (u <= 0) {
+        return {dist2: v13.length2(), nearest: pt1};
+      }
+      if (u >= 1) {
+        return {dist2: vectorFromPoints(pt3, pt2).length2(), nearest: pt2};
+      }
+      const projection = vectorFromPoints(pt1).add(v12.multiply(u)).point();
+      return {
+        dist2: vectorFromPoints(projection, pt3).length2(),
+        nearest: projection,
+      };
+    }
 
     // Project |pt| to nearest point on boundary.
     let mn = Infinity;
diff --git a/ash/webui/camera_app_ui/resources/js/views/ptz_panel.ts b/ash/webui/camera_app_ui/resources/js/views/ptz_panel.ts
index 3864ff8..139e032 100644
--- a/ash/webui/camera_app_ui/resources/js/views/ptz_panel.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/ptz_panel.ts
@@ -59,21 +59,21 @@
 }) {
   let interval: DelayInterval|null = null;
 
-  const press = () => {
+  function press() {
     if (interval !== null) {
       interval.stop();
     }
     handlePress();
     interval = new DelayInterval(handleHold, pressTimeout, holdInterval);
-  };
+  }
 
-  const release = () => {
+  function release() {
     if (interval !== null) {
       interval.stop();
       interval = null;
     }
     handleRelease();
-  };
+  }
 
   button.onpointerdown = press;
   button.onpointerleave = release;
@@ -157,10 +157,10 @@
           return;
         }
         const style = getComputedStyle(el, '::before');
-        const getStyleValue = (attr: string) => {
+        function getStyleValue(attr: string) {
           const px = style.getPropertyValue(attr);
           return Number(px.replace(/^([\d.]+)px$/, '$1'));
-        };
+        }
         const pRect = el.getBoundingClientRect();
         focusRing.setUIRect(new DOMRectReadOnly(
             /* x */ pRect.left + getStyleValue('left'),
@@ -183,10 +183,10 @@
         assert(target.offsetParent !== null);
         const pRect = target.offsetParent.getBoundingClientRect();
         const style = getComputedStyle(target, '::before');
-        const getStyleValue = (attr: string) => {
+        function getStyleValue(attr: string) {
           const px = style.getPropertyValue(attr);
           return Number(px.replace(/^([\d.]+)px$/, '$1'));
-        };
+        }
         const offsetX = getStyleValue('left');
         const offsetY = getStyleValue('top');
         const width = getStyleValue('width');
@@ -228,7 +228,10 @@
     const track = this.track;
     assert(track !== null);
     const {min, max, step} = track.getCapabilities()[attr];
-    const getCurrent = () => assertExists(track.getSettings()[attr]);
+    function getCurrent() {
+      assert(track !== null);
+      return assertExists(track.getSettings()[attr]);
+    }
     this.checkDisabled();
 
     const queue = new AsyncJobQueue();
@@ -311,15 +314,15 @@
     }
     const capabilities = this.track.getCapabilities();
     const settings = this.track.getSettings();
-    const updateDisable =
-        (incBtn: HTMLButtonElement, decBtn: HTMLButtonElement,
-         attr: 'pan'|'tilt'|'zoom') => {
-          const current = settings[attr];
-          const {min, max, step} = capabilities[attr];
-          assert(current !== undefined);
-          decBtn.disabled = current - step < min;
-          incBtn.disabled = current + step > max;
-        };
+    function updateDisable(
+        incBtn: HTMLButtonElement, decBtn: HTMLButtonElement,
+        attr: 'pan'|'tilt'|'zoom') {
+      const current = settings[attr];
+      const {min, max, step} = capabilities[attr];
+      assert(current !== undefined);
+      decBtn.disabled = current - step < min;
+      incBtn.disabled = current + step > max;
+    }
     if (capabilities.zoom !== undefined) {
       updateDisable(this.zoomIn, this.zoomOut, 'zoom');
     }
diff --git a/ash/webui/camera_app_ui/resources/js/views/settings.ts b/ash/webui/camera_app_ui/resources/js/views/settings.ts
index b02cf59..9e6b970 100644
--- a/ash/webui/camera_app_ui/resources/js/views/settings.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/settings.ts
@@ -401,9 +401,12 @@
    */
   private photoOptionTextTemplate(
       resolution: Resolution, resolutions: ResolutionList): string {
-    const gcd = (a: number, b: number): number => (a === 0 ? b : gcd(b % a, a));
-    const toMegapixel = ({area}: Resolution): number =>
-        area >= 1e6 ? Math.round(area / 1e6) : Math.round(area / 1e5) / 10;
+    function gcd(a: number, b: number): number {
+      return a === 0 ? b : gcd(b % a, a);
+    }
+    function toMegapixel({area}: Resolution): number {
+      return area >= 1e6 ? Math.round(area / 1e6) : Math.round(area / 1e5) / 10;
+    }
     const d = gcd(resolution.width, resolution.height);
 
     if (resolutions.some(
@@ -448,17 +451,17 @@
    * Updates resolution information of front, back camera and external cameras.
    */
   private updateResolutions() {
-    const prepareItem =
-        (item: HTMLElement, id: string,
-         {prefResolution, resolutions}: ResolutionConfig,
-         optionTextTemplate:
-             (prefResolutions: Resolution, resolutions: ResolutionList) =>
-                 string) => {
-          item.dataset['deviceId'] = id;
-          item.classList.toggle('multi-option', resolutions.length > 1);
-          dom.getFrom(item, '.description>span', HTMLSpanElement).textContent =
-              optionTextTemplate(prefResolution, resolutions);
-        };
+    function prepareItem(
+        item: HTMLElement, id: string,
+        {prefResolution, resolutions}: ResolutionConfig,
+        optionTextTemplate:
+            (prefResolutions: Resolution, resolutions: ResolutionList) =>
+                string) {
+      item.dataset['deviceId'] = id;
+      item.classList.toggle('multi-option', resolutions.length > 1);
+      dom.getFrom(item, '.description>span', HTMLSpanElement).textContent =
+          optionTextTemplate(prefResolution, resolutions);
+    }
 
     // Update front camera setting
     state.set(state.State.HAS_FRONT_CAMERA, this.frontSetting !== null);
diff --git a/ash/webui/eche_app_ui/BUILD.gn b/ash/webui/eche_app_ui/BUILD.gn
index 9241bef..e9186600 100644
--- a/ash/webui/eche_app_ui/BUILD.gn
+++ b/ash/webui/eche_app_ui/BUILD.gn
@@ -13,6 +13,15 @@
 preprocess_mojo_manifest = "preprocessed_mojo_manifest.json"
 preprocess_folder = "preprocessed"
 
+static_library("eche_app_ui_pref") {
+  sources = [
+    "pref_names.cc",
+    "pref_names.h",
+  ]
+
+  deps = [ "//base:base" ]
+}
+
 static_library("eche_app_ui") {
   sources = [
     "apps_access_manager.cc",
@@ -107,7 +116,10 @@
 
   public_deps = [ ":eche_app_ui" ]
 
-  deps = [ "//base" ]
+  deps = [
+    "//ash/components/phonehub:phonehub",
+    "//base",
+  ]
 }
 
 source_set("unit_tests") {
diff --git a/ash/webui/eche_app_ui/apps_access_manager.cc b/ash/webui/eche_app_ui/apps_access_manager.cc
index 97af238..b434d88 100644
--- a/ash/webui/eche_app_ui/apps_access_manager.cc
+++ b/ash/webui/eche_app_ui/apps_access_manager.cc
@@ -4,6 +4,7 @@
 
 #include "ash/webui/eche_app_ui/apps_access_manager.h"
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/webui/eche_app_ui/proto/exo_messages.pb.h"
 #include "base/memory/ptr_util.h"
 #include "chromeos/components/multidevice/logging/logging.h"
@@ -11,6 +12,9 @@
 namespace ash {
 namespace eche_app {
 
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
+
 AppsAccessManager::AppsAccessManager() = default;
 
 AppsAccessManager::~AppsAccessManager() = default;
@@ -74,18 +78,5 @@
     PA_LOG(INFO) << "Apps access setup operation has ended.";
 }
 
-std::ostream& operator<<(std::ostream& stream,
-                         AppsAccessManager::AccessStatus status) {
-  switch (status) {
-    case AppsAccessManager::AccessStatus::kAvailableButNotGranted:
-      stream << "[Access available but not granted]";
-      break;
-    case AppsAccessManager::AccessStatus::kAccessGranted:
-      stream << "[Access granted]";
-      break;
-  }
-  return stream;
-}
-
 }  // namespace eche_app
 }  // namespace ash
diff --git a/ash/webui/eche_app_ui/apps_access_manager.h b/ash/webui/eche_app_ui/apps_access_manager.h
index a250969..72f5edc 100644
--- a/ash/webui/eche_app_ui/apps_access_manager.h
+++ b/ash/webui/eche_app_ui/apps_access_manager.h
@@ -7,6 +7,7 @@
 
 #include <ostream>
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/webui/eche_app_ui/apps_access_setup_operation.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
@@ -16,20 +17,12 @@
 namespace ash {
 namespace eche_app {
 
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
 // Tracks the status of whether the user has enabled apps access on
 // their phones.
 class AppsAccessManager {
  public:
-  // Status of apps access. Numerical values are stored in prefs and
-  // should not be changed or reused.
-  enum class AccessStatus {
-    // Access has not been granted, but the user is free to grant access.
-    kAvailableButNotGranted = 0,
-
-    // Access has been granted by the user.
-    kAccessGranted = 1
-  };
-
   class Observer : public base::CheckedObserver {
    public:
     ~Observer() override = default;
@@ -71,9 +64,6 @@
   base::WeakPtrFactory<AppsAccessManager> weak_ptr_factory_{this};
 };
 
-std::ostream& operator<<(std::ostream& stream,
-                         AppsAccessManager::AccessStatus status);
-
 }  // namespace eche_app
 }  // namespace ash
 
diff --git a/ash/webui/eche_app_ui/apps_access_manager_impl.cc b/ash/webui/eche_app_ui/apps_access_manager_impl.cc
index c981132..f9cc48ef 100644
--- a/ash/webui/eche_app_ui/apps_access_manager_impl.cc
+++ b/ash/webui/eche_app_ui/apps_access_manager_impl.cc
@@ -4,6 +4,7 @@
 
 #include "ash/webui/eche_app_ui/apps_access_manager_impl.h"
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "ash/webui/eche_app_ui/pref_names.h"
@@ -17,11 +18,14 @@
 
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 }  // namespace
 
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
+
 // static
 void AppsAccessManagerImpl::RegisterPrefs(PrefRegistrySimple* registry) {
   registry->RegisterIntegerPref(
@@ -52,7 +56,7 @@
   message_receiver_->RemoveObserver(this);
 }
 
-AppsAccessManager::AccessStatus AppsAccessManagerImpl::GetAccessStatus() const {
+AccessStatus AppsAccessManagerImpl::GetAccessStatus() const {
   int status = pref_service_->GetInteger(prefs::kAppsAccessStatus);
   return static_cast<AccessStatus>(status);
 }
@@ -198,13 +202,14 @@
       SetAppsSetupOperationStatus(
           AppsAccessSetupOperation::Status::kCompletedSuccessfully);
       break;
+    case AccessStatus::kProhibited:
     case AccessStatus::kAvailableButNotGranted:
       // Intentionally blank; the operation status should not change.
       break;
   }
 }
 
-AppsAccessManager::AccessStatus AppsAccessManagerImpl::ComputeAppsAccessState(
+AccessStatus AppsAccessManagerImpl::ComputeAppsAccessState(
     proto::AppsAccessState apps_access_state) {
   if (apps_access_state == proto::AppsAccessState::ACCESS_GRANTED) {
     return AccessStatus::kAccessGranted;
@@ -226,6 +231,7 @@
             base::DoNothing());
       }
       break;
+    case AccessStatus::kProhibited:
     case AccessStatus::kAvailableButNotGranted:
       // Disable Apps if apps access has been revoked
       // by the phone.
diff --git a/ash/webui/eche_app_ui/apps_access_manager_impl.h b/ash/webui/eche_app_ui/apps_access_manager_impl.h
index 94302a1..3be419e8 100644
--- a/ash/webui/eche_app_ui/apps_access_manager_impl.h
+++ b/ash/webui/eche_app_ui/apps_access_manager_impl.h
@@ -7,6 +7,7 @@
 
 #include <ostream>
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "ash/webui/eche_app_ui/apps_access_manager.h"
 #include "ash/webui/eche_app_ui/eche_connector.h"
@@ -20,6 +21,9 @@
 namespace ash {
 namespace eche_app {
 
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
+
 // Implements AppsAccessManager by persisting the last-known
 // apps access value to user prefs.
 class AppsAccessManagerImpl : public AppsAccessManager,
@@ -60,8 +64,7 @@
   void UpdateFeatureEnabledState(AccessStatus access_status);
   bool IsWaitingForAccessToInitiallyEnableApps() const;
 
-  AppsAccessManager::AccessStatus ComputeAppsAccessState(
-      proto::AppsAccessState apps_access_state);
+  AccessStatus ComputeAppsAccessState(proto::AppsAccessState apps_access_state);
 
   FeatureStatus current_feature_status_;
   EcheConnector* eche_connector_;
diff --git a/ash/webui/eche_app_ui/apps_access_manager_impl_unittest.cc b/ash/webui/eche_app_ui/apps_access_manager_impl_unittest.cc
index 6f8c23e..9412be2f 100644
--- a/ash/webui/eche_app_ui/apps_access_manager_impl_unittest.cc
+++ b/ash/webui/eche_app_ui/apps_access_manager_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/webui/eche_app_ui/apps_access_manager_impl.h"
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
@@ -19,6 +20,10 @@
 
 namespace ash {
 namespace eche_app {
+
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
+
 namespace {
 class FakeObserver : public AppsAccessManager::Observer {
  public:
@@ -53,11 +58,8 @@
 };
 }  // namespace
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-
-// TODO(https://crbug.com/1164001): remove after migrating to namespace ash.
-namespace multidevice_setup = ::chromeos::multidevice_setup;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 class AppsAccessManagerImplTest : public testing::Test {
  protected:
@@ -91,7 +93,7 @@
     fake_feature_status_provider_.reset();
   }
 
-  void Initialize(AppsAccessManager::AccessStatus expected_status) {
+  void Initialize(AccessStatus expected_status) {
     pref_service_.SetInteger(prefs::kAppsAccessStatus,
                              static_cast<int>(expected_status));
     apps_access_manager_ = std::make_unique<AppsAccessManagerImpl>(
@@ -119,8 +121,7 @@
     return fake_feature_status_provider_->GetStatus();
   }
 
-  void VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus expected_status) {
+  void VerifyAppsAccessGrantedState(AccessStatus expected_status) {
     EXPECT_EQ(static_cast<int>(expected_status),
               pref_service_.GetInteger(prefs::kAppsAccessStatus));
     EXPECT_EQ(expected_status, apps_access_manager_->GetAccessStatus());
@@ -174,8 +175,8 @@
 };
 
 TEST_F(AppsAccessManagerImplTest, InitiallyGranted) {
-  Initialize(AppsAccessManager::AccessStatus::kAccessGranted);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  Initialize(AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
 
   // Cannot start the apps access setup flow if access has already been
   // granted.
@@ -184,9 +185,8 @@
 }
 
 TEST_F(AppsAccessManagerImplTest, OnFeatureStatusChanged) {
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   // Set initial state to disconnected.
   SetFeatureStatus(FeatureStatus::kDisconnected);
@@ -235,9 +235,8 @@
   // Set initial state to disconnected.
   SetFeatureStatus(FeatureStatus::kDisconnected);
 
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   // Start a setup operation with enabled but disconnected status and access
   // not granted.
@@ -259,7 +258,7 @@
   // Simulate getting a response back from the phone.
   FakeSendAppsSetupResponse(eche_app::proto::Result::RESULT_NO_ERROR,
                             eche_app::proto::AppsAccessState::ACCESS_GRANTED);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
   EXPECT_EQ(AppsAccessSetupOperation::Status::kCompletedSuccessfully,
             GetAppsAccessSetupOperationStatus());
 }
@@ -268,9 +267,8 @@
   // Set initial state to connecting.
   SetFeatureStatus(FeatureStatus::kConnecting);
 
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   // Start a setup operation with enabled and connecting status and access
   // not granted.
@@ -290,7 +288,7 @@
   // Simulate getting a response back from the phone.
   FakeSendAppsSetupResponse(eche_app::proto::Result::RESULT_NO_ERROR,
                             eche_app::proto::AppsAccessState::ACCESS_GRANTED);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
   EXPECT_EQ(AppsAccessSetupOperation::Status::kCompletedSuccessfully,
             GetAppsAccessSetupOperationStatus());
 }
@@ -299,9 +297,8 @@
   // Set initial state to connected.
   SetFeatureStatus(FeatureStatus::kConnected);
 
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   // Start a setup operation with enabled and connected status and access
   // not granted.
@@ -318,15 +315,14 @@
   // Simulate getting a response back from the phone.
   FakeSendAppsSetupResponse(eche_app::proto::Result::RESULT_NO_ERROR,
                             eche_app::proto::AppsAccessState::ACCESS_GRANTED);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
   EXPECT_EQ(AppsAccessSetupOperation::Status::kCompletedSuccessfully,
             GetAppsAccessSetupOperationStatus());
 }
 
 TEST_F(AppsAccessManagerImplTest, SimulateConnectingToDisconnected) {
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   auto operation = StartSetupOperation();
   EXPECT_TRUE(operation);
@@ -339,9 +335,8 @@
 }
 
 TEST_F(AppsAccessManagerImplTest, SimulateConnectedToDisconnected) {
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   auto operation = StartSetupOperation();
   EXPECT_TRUE(operation);
@@ -356,9 +351,8 @@
 }
 
 TEST_F(AppsAccessManagerImplTest, SimulateConnectedToDisabled) {
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   auto operation = StartSetupOperation();
   EXPECT_TRUE(operation);
@@ -374,9 +368,8 @@
 }
 
 TEST_F(AppsAccessManagerImplTest, SimulateConnectedToDependentFeature) {
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   auto operation = StartSetupOperation();
   EXPECT_TRUE(operation);
@@ -392,9 +385,8 @@
 }
 
 TEST_F(AppsAccessManagerImplTest, SimulateConnectedToDependentFeaturePending) {
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   auto operation = StartSetupOperation();
   EXPECT_TRUE(operation);
@@ -412,16 +404,15 @@
 TEST_F(AppsAccessManagerImplTest, FlipAccessNotGrantedToGranted) {
   SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
   SetFeatureState(Feature::kEche, FeatureState::kEnabledByUser);
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   // Simulate flipping the access state to no granted.
   FakeGetAppsAccessStateResponse(
       eche_app::proto::Result::RESULT_NO_ERROR,
       eche_app::proto::AppsAccessState::ACCESS_GRANTED);
 
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
   EXPECT_EQ(1u, GetNumObserverCalls());
 
   fake_multidevice_setup_client()->InvokePendingSetFeatureEnabledStateCallback(
@@ -433,31 +424,30 @@
 TEST_F(AppsAccessManagerImplTest, FlipAccessGrantedToNotGranted) {
   SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
   SetFeatureState(Feature::kEche, FeatureState::kDisabledByUser);
-  Initialize(AppsAccessManager::AccessStatus::kAccessGranted);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  Initialize(AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
 
   // Simulate flipping the access state to no granted.
   FakeGetAppsAccessStateResponse(
       eche_app::proto::Result::RESULT_NO_ERROR,
       eche_app::proto::AppsAccessState::ACCESS_NOT_GRANTED);
 
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
   EXPECT_EQ(1u, GetNumObserverCalls());
 }
 
 TEST_F(AppsAccessManagerImplTest, AccessNotChanged) {
   SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
   SetFeatureState(Feature::kEche, FeatureState::kEnabledByUser);
-  Initialize(AppsAccessManager::AccessStatus::kAccessGranted);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  Initialize(AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
 
   // Simulate flipping the access state to granted.
   FakeGetAppsAccessStateResponse(
       eche_app::proto::Result::RESULT_NO_ERROR,
       eche_app::proto::AppsAccessState::ACCESS_GRANTED);
 
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
   EXPECT_EQ(0u, GetNumObserverCalls());
 
   fake_multidevice_setup_client()->InvokePendingSetFeatureEnabledStateCallback(
@@ -468,7 +458,7 @@
 
 TEST_F(AppsAccessManagerImplTest, InitiallyEnableApps) {
   SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
 
   // Simulate flipping the access state to granted.
   FakeGetAppsAccessStateResponse(
@@ -488,9 +478,8 @@
   // Explicitly disable Phone Hub, all sub feature should be disabled
   SetFeatureState(Feature::kPhoneHub, FeatureState::kDisabledByUser);
   SetFeatureState(Feature::kEche, FeatureState::kDisabledByUser);
-  Initialize(AppsAccessManager::AccessStatus::kAvailableButNotGranted);
-  VerifyAppsAccessGrantedState(
-      AppsAccessManager::AccessStatus::kAvailableButNotGranted);
+  Initialize(AccessStatus::kAvailableButNotGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAvailableButNotGranted);
 
   // No action after access is granted
   // Simulate flipping the access state to granted.
@@ -507,8 +496,8 @@
        SimulateAccessNotGrantedShouleDisableEcheFeature) {
   SetFeatureState(Feature::kPhoneHub, FeatureState::kEnabledByUser);
   SetFeatureState(Feature::kEche, FeatureState::kEnabledByUser);
-  Initialize(AppsAccessManager::AccessStatus::kAccessGranted);
-  VerifyAppsAccessGrantedState(AppsAccessManager::AccessStatus::kAccessGranted);
+  Initialize(AccessStatus::kAccessGranted);
+  VerifyAppsAccessGrantedState(AccessStatus::kAccessGranted);
 
   // Test that there is a call to disable kEche when apps access has been
   // revoked. Simulate flipping the access state to not granted.
diff --git a/ash/webui/eche_app_ui/eche_feature_status_provider.cc b/ash/webui/eche_app_ui/eche_feature_status_provider.cc
index 616b807..209ae20 100644
--- a/ash/webui/eche_app_ui/eche_feature_status_provider.cc
+++ b/ash/webui/eche_app_ui/eche_feature_status_provider.cc
@@ -21,9 +21,9 @@
 using ::chromeos::multidevice::SoftwareFeature;
 using ::chromeos::multidevice::SoftwareFeatureState;
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 bool IsHostDisabled(const RemoteDeviceRef& device) {
   return device.GetSoftwareFeatureState(SoftwareFeature::kBetterTogetherHost) !=
diff --git a/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc b/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
index 69f5c668..9a4fb7f 100644
--- a/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_feature_status_provider_unittest.cc
@@ -21,9 +21,9 @@
 namespace eche_app {
 namespace {
 
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-using ::chromeos::multidevice_setup::mojom::HostStatus;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::HostStatus;
 
 multidevice::RemoteDeviceRef CreateLocalDevice(bool supports_eche_client) {
   multidevice::RemoteDeviceRefBuilder builder;
diff --git a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
index 0f80bd0..7e78ea34 100644
--- a/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
+++ b/ash/webui/eche_app_ui/eche_presence_manager_unittest.cc
@@ -62,9 +62,9 @@
     scoped_feature_list_.InitWithFeatures(
         /*enabled_features=*/{chromeos::features::kEcheSWA},
         /*disabled_features=*/{});
-    fake_multidevice_setup_client_.SetHostStatusWithDevice(std::make_pair(
-        chromeos::multidevice_setup::mojom::HostStatus::kHostVerified,
-        test_remote_device_));
+    fake_multidevice_setup_client_.SetHostStatusWithDevice(
+        std::make_pair(multidevice_setup::mojom::HostStatus::kHostVerified,
+                       test_remote_device_));
     fake_device_sync_client_.set_local_device_metadata(test_devices_[0]);
     fake_device_sync_client_.NotifyReady();
     fake_eche_connector_ = std::make_unique<FakeEcheConnector>();
diff --git a/ash/webui/eche_app_ui/fake_apps_access_manager.cc b/ash/webui/eche_app_ui/fake_apps_access_manager.cc
index 94924ff..efba2fc9 100644
--- a/ash/webui/eche_app_ui/fake_apps_access_manager.cc
+++ b/ash/webui/eche_app_ui/fake_apps_access_manager.cc
@@ -3,16 +3,20 @@
 // found in the LICENSE file.
 
 #include "ash/webui/eche_app_ui/fake_apps_access_manager.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 
 namespace ash {
 namespace eche_app {
 
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
+
 FakeAppsAccessManager::FakeAppsAccessManager(AccessStatus access_status)
     : access_status_(access_status) {}
 
 FakeAppsAccessManager::~FakeAppsAccessManager() = default;
 
-AppsAccessManager::AccessStatus FakeAppsAccessManager::GetAccessStatus() const {
+AccessStatus FakeAppsAccessManager::GetAccessStatus() const {
   return access_status_;
 }
 
diff --git a/ash/webui/eche_app_ui/fake_apps_access_manager.h b/ash/webui/eche_app_ui/fake_apps_access_manager.h
index 107e26b7..9fc2ae27 100644
--- a/ash/webui/eche_app_ui/fake_apps_access_manager.h
+++ b/ash/webui/eche_app_ui/fake_apps_access_manager.h
@@ -5,6 +5,7 @@
 #ifndef ASH_WEBUI_ECHE_APP_UI_FAKE_APPS_ACCESS_MANAGER_H_
 #define ASH_WEBUI_ECHE_APP_UI_FAKE_APPS_ACCESS_MANAGER_H_
 
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/webui/eche_app_ui/apps_access_manager.h"
 
 namespace chromeos {
@@ -16,6 +17,9 @@
 namespace ash {
 namespace eche_app {
 
+using AccessStatus =
+    ash::phonehub::MultideviceFeatureAccessManager::AccessStatus;
+
 class FakeAppsAccessManager : public AppsAccessManager {
  public:
   explicit FakeAppsAccessManager(
diff --git a/ash/webui/eche_app_ui/pref_names.cc b/ash/webui/eche_app_ui/pref_names.cc
index 9030198..20ca2af 100644
--- a/ash/webui/eche_app_ui/pref_names.cc
+++ b/ash/webui/eche_app_ui/pref_names.cc
@@ -9,7 +9,7 @@
 namespace prefs {
 // The last provided apps access status provided by the phone. This pref
 // stores the numerical value associated with the
-// AppsAccessManager::AccessStatus enum.
+// phonehub::MultideviceFeatureAccessManager::AccessStatus enum.
 const char kAppsAccessStatus[] = "cros.echeapps.apps_access_status";
 }  // namespace prefs
 }  // namespace eche_app
diff --git a/ash/webui/multidevice_debug/proximity_auth_ui.cc b/ash/webui/multidevice_debug/proximity_auth_ui.cc
index 5ac591dc..63b2101 100644
--- a/ash/webui/multidevice_debug/proximity_auth_ui.cc
+++ b/ash/webui/multidevice_debug/proximity_auth_ui.cc
@@ -47,7 +47,7 @@
 ProximityAuthUI::~ProximityAuthUI() = default;
 
 void ProximityAuthUI::BindInterface(
-    mojo::PendingReceiver<chromeos::multidevice_setup::mojom::MultiDeviceSetup>
+    mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>
         receiver) {
   multidevice_setup_binder_.Run(std::move(receiver));
 }
diff --git a/ash/webui/multidevice_debug/proximity_auth_ui.h b/ash/webui/multidevice_debug/proximity_auth_ui.h
index a0d3215c..66106fb 100644
--- a/ash/webui/multidevice_debug/proximity_auth_ui.h
+++ b/ash/webui/multidevice_debug/proximity_auth_ui.h
@@ -20,8 +20,7 @@
 class ProximityAuthUI : public ui::MojoWebUIController {
  public:
   using MultiDeviceSetupBinder = base::RepeatingCallback<void(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup>)>;
+      mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>)>;
 
   // Note: |web_ui| is not owned by this instance and must outlive this
   // instance.
@@ -37,8 +36,8 @@
   // Instantiates implementor of the mojom::MultiDeviceSetup mojo interface
   // passing the pending receiver that will be internally bound.
   void BindInterface(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup> receiver);
+      mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>
+          receiver);
 
  private:
   const MultiDeviceSetupBinder multidevice_setup_binder_;
diff --git a/ash/webui/multidevice_debug/resources/proximity_auth.js b/ash/webui/multidevice_debug/resources/proximity_auth.js
index c7a6354c..63cdfdc 100644
--- a/ash/webui/multidevice_debug/resources/proximity_auth.js
+++ b/ash/webui/multidevice_debug/resources/proximity_auth.js
@@ -92,7 +92,7 @@
         this.showExistingUserNewChromebookNotification_.bind(this);
 
     this.multiDeviceSetup =
-        chromeos.multideviceSetup.mojom.MultiDeviceSetup.getRemote();
+        ash.multideviceSetup.mojom.MultiDeviceSetup.getRemote();
   }
 
   /**
@@ -207,7 +207,7 @@
    */
   showNewUserNotification_() {
     this.showMultiDeviceSetupPromoNotification_(
-        chromeos.multideviceSetup.mojom.EventTypeForDebugging
+        ash.multideviceSetup.mojom.EventTypeForDebugging
             .kNewUserPotentialHostExists);
   }
 
@@ -216,7 +216,7 @@
    */
   showExistingUserNewHostNotification_() {
     this.showMultiDeviceSetupPromoNotification_(
-        chromeos.multideviceSetup.mojom.EventTypeForDebugging
+        ash.multideviceSetup.mojom.EventTypeForDebugging
             .kExistingUserConnectedHostSwitched);
   }
 
@@ -225,13 +225,13 @@
    */
   showExistingUserNewChromebookNotification_() {
     this.showMultiDeviceSetupPromoNotification_(
-        chromeos.multideviceSetup.mojom.EventTypeForDebugging
+        ash.multideviceSetup.mojom.EventTypeForDebugging
             .kExistingUserNewChromebookAdded);
   }
 
   /**
    * Shows a "MultiDevice Setup" notification of the given type.
-   * @param {!chromeos.multideviceSetup.mojom.EventTypeForDebugging} type
+   * @param {!ash.multideviceSetup.mojom.EventTypeForDebugging} type
    */
   showMultiDeviceSetupPromoNotification_(type) {
     this.multiDeviceSetup.triggerEventForDebugging(type)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feature_guide/notifications/FeatureNotificationUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/feature_guide/notifications/FeatureNotificationUtils.java
index 3e13d26..dcdf9ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/feature_guide/notifications/FeatureNotificationUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/feature_guide/notifications/FeatureNotificationUtils.java
@@ -13,6 +13,7 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
+import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManager;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoDeps;
@@ -21,6 +22,8 @@
 import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.user_prefs.UserPrefs;
 
 /**
  * Various utility methods needed by the feature notification guide and external clients to show
@@ -168,7 +171,12 @@
      * @return True if the feature should be skipped, false otherwise.
      */
     public static boolean shouldSkipFeature(@FeatureType int featureType) {
-        if (featureType == FeatureType.DEFAULT_BROWSER) return !shouldShowDefaultBrowserPromo();
+        if (featureType == FeatureType.DEFAULT_BROWSER) {
+            return !shouldShowDefaultBrowserPromo();
+        } else if (featureType == FeatureType.NTP_SUGGESTION_CARD) {
+            PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile());
+            return !prefService.getBoolean(Pref.ARTICLES_LIST_VISIBLE);
+        }
         return false;
     }
 
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 60ee582..c3bee19d 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -4547,12 +4547,6 @@
   <message name="IDS_CROSTINI_INSTALLER_SETUP_CONTAINER_MESSAGE" desc="Text shown in the Crostini installer dialog when the container inside the VM is being set up">
     This process may take a few minutes. Setting up the Linux container.
   </message>
-  <message name="IDS_CROSTINI_INSTALLER_FETCH_SSH_KEYS_MESSAGE" desc="Text shown in the Crostini installer dialog when fetching ssh keys">
-    This process may take a few minutes. Setting up the Linux container.
-  </message>
-  <message name="IDS_CROSTINI_INSTALLER_MOUNT_CONTAINER_MESSAGE" desc="Text shown in the Crostini installer dialog when doing sshfs mount of container">
-    This process may take a few minutes. Setting up the Linux container.
-  </message>
   <message name="IDS_CROSTINI_INSTALLER_RETRY_BUTTON" desc="Retry button of the Crostini installer dialog that starts the install steps again.">
     Retry
   </message>
@@ -5793,6 +5787,10 @@
   <message name="IDS_EDU_LOGIN_PARENT_SIGNIN_PASSWORD_HIDE" desc="A tool tip on a button that hides the saved password that is being shown.">
     Hide password
   </message>
+  <!--
+    IDS_EDU_LOGIN_INFO_* strings END
+    ==================================================================================
+  -->
 
   <!-- Borealis strings -->
   <!-- TODO(danielng): Add string descriptions and remove translateable tags
@@ -5872,10 +5870,15 @@
   <message name="IDS_BOREALIS_APPLICATION_UNINSTALL_CONFIRM_BODY" desc="Confirmation dialog for uninstalling a game.">
       Data associated with this app may be removed from this device
   </message>
-  <!--
-    IDS_EDU_LOGIN_INFO_* strings END
-    ==================================================================================
-  -->
+  <message name="IDS_BOREALIS_FEEDBACK_NOTIFICATION_TITLE" desc="Title shown in notification that prompts users for feedback">
+    How was your gameplay experience?
+  </message>
+  <message name="IDS_BOREALIS_FEEDBACK_NOTIFICATION_MESSAGE" desc="Message shown in notification that prompts users for feedback">
+    Help us improve gaming on Chromebooks
+  </message>
+  <message name="IDS_BOREALIS_FEEDBACK_NOTIFICATION_SOURCE" desc="Source of notification that prompts users for feedback">
+    Chromebook Gaming
+  </message>
 
   <!-- TPM firmware auto-update notifications -->
   <message name="IDS_TPM_AUTO_UPDATE_PLANNED_NOTIFICATION_TITLE" desc="Title for the notification informing the user that local data will be deleted.">
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_MESSAGE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_MESSAGE.png.sha1
new file mode 100644
index 0000000..488f224
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_MESSAGE.png.sha1
@@ -0,0 +1 @@
+8f02be629cb6c7c8199d08ff717ad5f8bcc3d8ec
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_SOURCE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_SOURCE.png.sha1
new file mode 100644
index 0000000..488f224
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_SOURCE.png.sha1
@@ -0,0 +1 @@
+8f02be629cb6c7c8199d08ff717ad5f8bcc3d8ec
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_TITLE.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 0000000..488f224
--- /dev/null
+++ b/chrome/app/chromeos_strings_grdp/IDS_BOREALIS_FEEDBACK_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+8f02be629cb6c7c8199d08ff717ad5f8bcc3d8ec
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 48034ae..87253f6 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -592,9 +592,12 @@
   <message name="IDS_SETTINGS_PASSWORD_COPY" desc="Label for a context menu item that allows to copy the selected password into clipboard.">
     Copy password
   </message>
-    <message name="IDS_SETTINGS_PASSWORD_SEND" desc="Label for a context menu item that allows to send the selected password.">
+  <message name="IDS_SETTINGS_PASSWORD_SEND" desc="Label for a context menu item that allows to send the selected password.">
     Send password
   </message>
+  <message name="IDS_SETTINGS_USERNAME_COPY" desc="Label for the button when copying a username in the password edit dialog.">
+    Copy username
+  </message>
   <message name="IDS_SETTINGS_PASSWORDS_WEBSITE" desc="Label for the website a password is for when editing a password.">
     Site
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_USERNAME_COPY.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_USERNAME_COPY.png.sha1
new file mode 100644
index 0000000..6a7e3870
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_USERNAME_COPY.png.sha1
@@ -0,0 +1 @@
+cd4c005250acf8fe9cb652fd157cc405a94969e4
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f1ad996..bf8ce7d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4562,6 +4562,8 @@
       "apps/app_discovery_service/result.h",
       "apps/app_discovery_service/test_fetcher.cc",
       "apps/app_discovery_service/test_fetcher.h",
+      "apps/app_provisioning_service/app_provisioning_data_manager.cc",
+      "apps/app_provisioning_service/app_provisioning_data_manager.h",
       "apps/app_service/app_icon/arc_activity_adaptive_icon_impl.cc",
       "apps/app_service/app_icon/arc_activity_adaptive_icon_impl.h",
       "apps/app_service/app_icon/arc_icon_once_loader.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2f94d1a62..3a55299 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4732,9 +4732,6 @@
     {"eche-swa", flag_descriptions::kEcheSWAName,
      flag_descriptions::kEcheSWADescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kEcheSWA)},
-    {"eche-swa-resizing", flag_descriptions::kEcheSWAResizingName,
-     flag_descriptions::kEcheSWAResizingDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::features::kEcheSWAResizing)},
     {"eche-swa-debug-mode", flag_descriptions::kEcheSWADebugModeName,
      flag_descriptions::kEcheSWADebugModeDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kEcheSWADebugMode)},
@@ -8044,12 +8041,6 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(features::kLargeFaviconFromGoogle,
                                     kLargeFaviconFromGoogleVariations,
                                     "LargeFaviconFromGoogle")},
-#if BUILDFLAG(IS_ANDROID)
-    {"web-bluetooth-request-larger-mtu",
-     flag_descriptions::kWebBluetoothRequestLargerMtuName,
-     flag_descriptions::kWebBluetoothRequestLargerMtuDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(features::kWebBluetoothRequestLargerMtu)},
-#endif  // BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
     {"request-desktop-site-exceptions",
diff --git a/chrome/browser/apps/app_provisioning_service/OWNERS b/chrome/browser/apps/app_provisioning_service/OWNERS
new file mode 100644
index 0000000..6b8b8d1
--- /dev/null
+++ b/chrome/browser/apps/app_provisioning_service/OWNERS
@@ -0,0 +1,6 @@
+jshikaram@chromium.org
+melzhang@chromium.org
+mxcai@chromium.org
+
+# For backup
+dominickn@chromium.org
diff --git a/chrome/browser/apps/app_provisioning_service/app_provisioning_data_manager.cc b/chrome/browser/apps/app_provisioning_service/app_provisioning_data_manager.cc
new file mode 100644
index 0000000..2cb7486
--- /dev/null
+++ b/chrome/browser/apps/app_provisioning_service/app_provisioning_data_manager.cc
@@ -0,0 +1,26 @@
+// 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/apps/app_provisioning_service/app_provisioning_data_manager.h"
+
+#include "base/logging.h"
+
+namespace apps {
+
+// static
+AppProvisioningDataManager* AppProvisioningDataManager::Get() {
+  static base::NoDestructor<AppProvisioningDataManager> instance;
+  return instance.get();
+}
+
+AppProvisioningDataManager::AppProvisioningDataManager() = default;
+
+AppProvisioningDataManager::~AppProvisioningDataManager() = default;
+
+void AppProvisioningDataManager::PopulateFromDynamicUpdate(
+    const std::string& binary_pb) {
+  DVLOG(1) << "Binary received " << binary_pb;
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_provisioning_service/app_provisioning_data_manager.h b/chrome/browser/apps/app_provisioning_service/app_provisioning_data_manager.h
new file mode 100644
index 0000000..ea689d3
--- /dev/null
+++ b/chrome/browser/apps/app_provisioning_service/app_provisioning_data_manager.h
@@ -0,0 +1,47 @@
+// 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 CHROME_BROWSER_APPS_APP_PROVISIONING_SERVICE_APP_PROVISIONING_DATA_MANAGER_H_
+#define CHROME_BROWSER_APPS_APP_PROVISIONING_SERVICE_APP_PROVISIONING_DATA_MANAGER_H_
+
+#include <string>
+
+#include "base/no_destructor.h"
+
+namespace apps {
+
+// The AppProvisioningDataManager parses the updates received from the Component
+// Updater and forwards the data in the desired format to the relevant service.
+// E.g. Component Updater sends through new discovery app data, after parsing
+// and formatting the proto, this class would then send the update to the App
+// Discovery Service.
+class AppProvisioningDataManager {
+ public:
+  static AppProvisioningDataManager* Get();
+
+  AppProvisioningDataManager(const AppProvisioningDataManager&) = delete;
+  AppProvisioningDataManager& operator=(const AppProvisioningDataManager&) =
+      delete;
+  // Note that AppProvisioningDataManager is a NoDestructor and thus never
+  // destroyed.
+  virtual ~AppProvisioningDataManager();
+
+  static AppProvisioningDataManager* GetInstance();  // Singleton
+
+  // Update the internal list from a binary proto fetched from the network.
+  // Same integrity checks apply. This can be called multiple times with new
+  // protos.
+  void PopulateFromDynamicUpdate(const std::string& binary_pb);
+
+ protected:
+  // Creator must call one of Populate* before calling other methods.
+  AppProvisioningDataManager();
+
+ private:
+  friend class base::NoDestructor<AppProvisioningDataManager>;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_PROVISIONING_SERVICE_APP_PROVISIONING_DATA_MANAGER_H_
diff --git a/chrome/browser/apps/app_service/app_service_proxy_base.cc b/chrome/browser/apps/app_service/app_service_proxy_base.cc
index 64dc174..1ca3812 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_base.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_base.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/app_service/metrics/app_service_metrics.h"
-#include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "components/services/app_service/app_service_mojom_impl.h"
@@ -33,6 +32,7 @@
 #include "url/url_constants.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/common/chrome_features.h"
 #endif
 
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index cfb6f5df..72b24c5bc 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -11,7 +11,6 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/file_utils.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
-#include "chrome/browser/ash/file_manager/fileapi_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -37,6 +36,10 @@
 #include "chromeos/crosapi/mojom/app_service_types.mojom.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/file_manager/fileapi_util.h"
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/browser/lacros/lacros_extensions_util.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
index bcb3c24..537f61c 100644
--- a/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.cc
@@ -4,11 +4,9 @@
 
 #include "chrome/browser/apps/app_service/metrics/app_service_metrics.h"
 
-#include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "components/app_constants/constants.h"
@@ -17,6 +15,7 @@
 #include "extensions/common/constants.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chrome/browser/ash/file_manager/app_id.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
@@ -150,6 +149,7 @@
   }
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 void RecordBuiltInAppLaunch(apps::BuiltInAppName built_in_app_name,
                             apps::mojom::LaunchSource launch_source) {
   switch (launch_source) {
@@ -195,6 +195,7 @@
       break;
   }
 }
+#endif
 
 }  // namespace
 
@@ -232,11 +233,9 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   } else if (app_id == arc::kGoogleDuoAppId) {
     RecordDefaultAppLaunch(DefaultAppName::kDuo, launch_source);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   } else if (app_id == extension_misc::kFilesManagerAppId ||
              app_id == file_manager::kFileManagerSwaAppId) {
     RecordDefaultAppLaunch(DefaultAppName::kFiles, launch_source);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   } else if (app_id == extension_misc::kGmailAppId ||
              app_id == arc::kGmailAppId) {
     RecordDefaultAppLaunch(DefaultAppName::kGmail, launch_source);
@@ -298,6 +297,7 @@
 
   // Above are default apps; below are built-in apps.
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
   if (app_id == ash::kInternalAppIdKeyboardShortcutViewer) {
     RecordBuiltInAppLaunch(BuiltInAppName::kKeyboardShortcutViewer,
                            launch_source);
@@ -305,17 +305,18 @@
     RecordBuiltInAppLaunch(BuiltInAppName::kSettings, launch_source);
   } else if (app_id == ash::kInternalAppIdContinueReading) {
     RecordBuiltInAppLaunch(BuiltInAppName::kContinueReading, launch_source);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   } else if (app_id == plugin_vm::kPluginVmShelfAppId) {
     RecordBuiltInAppLaunch(BuiltInAppName::kPluginVm, launch_source);
+  }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-  } else if (app_id == web_app::kMockSystemAppId) {
+  if (app_id == web_app::kMockSystemAppId) {
     RecordDefaultAppLaunch(DefaultAppName::kMockSystemApp, launch_source);
   } else if (app_id == web_app::kOsFeedbackAppId) {
     RecordDefaultAppLaunch(DefaultAppName::kOsFeedbackApp, launch_source);
   }
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 void RecordBuiltInAppSearchResult(const std::string& app_id) {
   if (app_id == ash::kInternalAppIdKeyboardShortcutViewer) {
     base::UmaHistogramEnumeration("Apps.AppListSearchResultInternalApp.Show",
@@ -326,13 +327,12 @@
   } else if (app_id == ash::kInternalAppIdContinueReading) {
     base::UmaHistogramEnumeration("Apps.AppListSearchResultInternalApp.Show",
                                   BuiltInAppName::kContinueReading);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
   } else if (app_id == plugin_vm::kPluginVmShelfAppId) {
     base::UmaHistogramEnumeration("Apps.AppListSearchResultInternalApp.Show",
                                   BuiltInAppName::kPluginVm);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   }
 }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 void RecordAppBounce(const apps::AppUpdate& app) {
   base::Time install_time = app.InstallTime();
diff --git a/chrome/browser/apps/app_service/metrics/app_service_metrics.h b/chrome/browser/apps/app_service/metrics/app_service_metrics.h
index cad4a1b9..f385e7bc 100644
--- a/chrome/browser/apps/app_service/metrics/app_service_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_service_metrics.h
@@ -8,6 +8,7 @@
 #include <map>
 #include <string>
 
+#include "build/chromeos_buildflags.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 
 namespace apps {
@@ -88,7 +89,9 @@
 void RecordAppLaunch(const std::string& app_id,
                      apps::mojom::LaunchSource launch_source);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
 void RecordBuiltInAppSearchResult(const std::string& app_id);
+#endif
 
 void RecordAppBounce(const apps::AppUpdate& app);
 
diff --git a/chrome/browser/apps/app_service/publisher_host.cc b/chrome/browser/apps/app_service/publisher_host.cc
index d4d88b31..597aa486 100644
--- a/chrome/browser/apps/app_service/publisher_host.cc
+++ b/chrome/browser/apps/app_service/publisher_host.cc
@@ -8,8 +8,6 @@
 
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/publishers/extension_apps.h"
-#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/web_applications/app_service/web_apps.h"
 #include "chrome/common/chrome_features.h"
 
@@ -22,6 +20,8 @@
 #include "chrome/browser/apps/app_service/publishers/plugin_vm_apps.h"
 #include "chrome/browser/apps/app_service/publishers/standalone_browser_apps.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "components/services/app_service/public/cpp/instance_registry.h"
 #endif
 
diff --git a/chrome/browser/apps/app_service/publishers/borealis_apps.cc b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
index 29db6ac..e9cb0ed8 100644
--- a/chrome/browser/apps/app_service/publishers/borealis_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
@@ -51,33 +51,40 @@
   return nullptr;
 }
 
-void SetAppAllowed(apps::mojom::App* app, bool allowed) {
+void SetAppAllowed(apps::mojom::App* app,
+                   bool allowed,
+                   bool partially_hidden = false) {
   app->readiness = allowed ? apps::mojom::Readiness::kReady
                            : apps::mojom::Readiness::kDisabledByPolicy;
 
   const apps::mojom::OptionalBool opt_allowed =
       allowed ? apps::mojom::OptionalBool::kTrue
               : apps::mojom::OptionalBool::kFalse;
+  const apps::mojom::OptionalBool opt_partial_allowed =
+      allowed && !partially_hidden ? apps::mojom::OptionalBool::kTrue
+                                   : apps::mojom::OptionalBool::kFalse;
 
   app->recommendable = opt_allowed;
   app->searchable = opt_allowed;
-  app->show_in_launcher = opt_allowed;
+  app->show_in_launcher = opt_partial_allowed;
   app->show_in_shelf = opt_allowed;
-  app->show_in_search = opt_allowed;
-  app->show_in_management = opt_allowed;
+  app->show_in_search = opt_partial_allowed;
+  app->show_in_management = opt_partial_allowed;
   app->handles_intents = opt_allowed;
 }
 
-void SetAppAllowed(bool allowed, apps::App& app) {
+void SetAppAllowed(bool allowed,
+                   apps::App& app,
+                   bool partially_hidden = false) {
   app.readiness =
       allowed ? apps::Readiness::kReady : apps::Readiness::kDisabledByPolicy;
 
   app.recommendable = allowed;
   app.searchable = allowed;
-  app.show_in_launcher = allowed;
+  app.show_in_launcher = allowed && !partially_hidden;
   app.show_in_shelf = allowed;
-  app.show_in_search = allowed;
-  app.show_in_management = allowed;
+  app.show_in_search = allowed && !partially_hidden;
+  app.show_in_management = allowed && !partially_hidden;
   app.handles_intents = allowed;
 }
 
@@ -92,7 +99,7 @@
       apps::IconKey(apps::IconKey::kDoesNotChangeOverTime,
                     IDR_LOGO_BOREALIS_DEFAULT_192, apps::IconEffects::kNone);
 
-  SetAppAllowed(allowed, *app);
+  SetAppAllowed(allowed, *app, /*partially_hidden=*/true);
 
   app->allow_uninstall =
       borealis::BorealisService::GetForProfile(profile)->Features().IsEnabled();
@@ -113,7 +120,7 @@
       apps::mojom::IconKey::kDoesNotChangeOverTime,
       IDR_LOGO_BOREALIS_DEFAULT_192, apps::IconEffects::kNone);
 
-  SetAppAllowed(app.get(), allowed);
+  SetAppAllowed(app.get(), allowed, /*partially_hidden=*/true);
 
   app->allow_uninstall = (borealis::BorealisService::GetForProfile(profile)
                               ->Features()
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index bae394b..c32e4a1e 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -30,6 +30,7 @@
 #include "components/services/app_service/public/cpp/permission.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
@@ -42,6 +43,7 @@
 #include "chrome/browser/apps/app_service/publishers/standalone_browser_extension_apps_factory.h"
 #include "chrome/browser/apps/app_service/publishers/web_apps_crosapi.h"
 #include "chrome/browser/apps/app_service/publishers/web_apps_crosapi_factory.h"
+#include "chrome/browser/ash/borealis/borealis_util.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
@@ -162,6 +164,18 @@
   return filters;
 }
 
+MATCHER(Ready, "App has readiness=\"kReady\"") {
+  return arg.readiness == apps::Readiness::kReady;
+}
+
+MATCHER_P(ShownInShelf, shown, "App shown on the shelf") {
+  return arg.show_in_shelf.has_value() && arg.show_in_shelf == shown;
+}
+
+MATCHER_P(ShownInLauncher, shown, "App shown in the launcher") {
+  return arg.show_in_launcher.has_value() && arg.show_in_launcher == shown;
+}
+
 }  // namespace
 
 namespace apps {
@@ -229,6 +243,12 @@
     }
   }
 
+  const AppPtr& GetApp(const std::string& app_id) {
+    AppRegistryCache& cache =
+        AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
+    return cache.states_[app_id];
+  }
+
   void VerifyApp(AppType app_type,
                  const std::string& app_id,
                  const std::string& name,
@@ -554,6 +574,30 @@
             /*handles_intents=*/true, /*allow_uninstall=*/true,
             /*has_badge=*/true, /*paused=*/true, WindowMode::kBrowser);
 }
+
+// This framework conveniently sets up everything but borealis.
+using NonBorealisPublisherTest = StandaloneBrowserPublisherTest;
+
+TEST_F(NonBorealisPublisherTest, BorealisAppsNotAllowed) {
+  EXPECT_THAT(*GetApp(borealis::kInstallerAppId), testing::Not(Ready()));
+}
+
+class BorealisPublisherTest : public StandaloneBrowserPublisherTest {
+ public:
+  BorealisPublisherTest() {
+    scoped_feature_list_.Reset();
+    scoped_feature_list_.InitWithFeatures(
+        {features::kBorealis, chromeos::features::kBorealisPermitted,
+         kAppServiceOnAppTypeInitializedWithoutMojom},
+        {});
+  }
+};
+
+TEST_F(BorealisPublisherTest, BorealisAppsAllowed) {
+  EXPECT_THAT(
+      *GetApp(borealis::kInstallerAppId),
+      testing::AllOf(Ready(), ShownInShelf(true), ShownInLauncher(false)));
+}
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
index fce70875252..c44fd4b 100644
--- a/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
+++ b/chrome/browser/ash/android_sms/android_sms_app_setup_controller_impl.cc
@@ -22,6 +22,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "net/base/url_util.h"
@@ -78,7 +79,12 @@
 
   provider->install_finalizer().UninstallExternalWebApp(
       app_id, webapps::WebappUninstallSource::kInternalPreinstalled,
-      std::move(callback));
+      base::BindOnce(
+          [](SuccessCallback callback, webapps::UninstallResultCode code) {
+            std::move(callback).Run(code ==
+                                    webapps::UninstallResultCode::kSuccess);
+          },
+          std::move(callback)));
 }
 
 AndroidSmsAppSetupControllerImpl::AndroidSmsAppSetupControllerImpl(
diff --git a/chrome/browser/ash/android_sms/android_sms_service_factory.cc b/chrome/browser/ash/android_sms/android_sms_service_factory.cc
index 52cc304f..377ccbb 100644
--- a/chrome/browser/ash/android_sms/android_sms_service_factory.cc
+++ b/chrome/browser/ash/android_sms/android_sms_service_factory.cc
@@ -26,8 +26,7 @@
 
 bool ShouldStartAndroidSmsService(Profile* profile) {
   const bool multidevice_feature_allowed = multidevice_setup::IsFeatureAllowed(
-      chromeos::multidevice_setup::mojom::Feature::kMessages,
-      profile->GetPrefs());
+      multidevice_setup::mojom::Feature::kMessages, profile->GetPrefs());
 
   const bool has_user_for_profile =
       !!ProfileHelper::Get()->GetUserByProfile(profile);
diff --git a/chrome/browser/ash/android_sms/connection_manager.cc b/chrome/browser/ash/android_sms/connection_manager.cc
index 8d4ead2..ccb57306 100644
--- a/chrome/browser/ash/android_sms/connection_manager.cc
+++ b/chrome/browser/ash/android_sms/connection_manager.cc
@@ -16,13 +16,6 @@
 #include "content/public/browser/storage_partition.h"
 
 namespace ash {
-
-// TODO(https://crbug.com/1164001): remove when chromeos/multidevice_setup is
-// migrated.
-namespace multidevice_setup {
-namespace mojom = ::chromeos::multidevice_setup::mojom;
-}
-
 namespace android_sms {
 
 ConnectionManager::ServiceWorkerProvider::ServiceWorkerProvider() = default;
diff --git a/chrome/browser/ash/android_sms/connection_manager_unittest.cc b/chrome/browser/ash/android_sms/connection_manager_unittest.cc
index a89ba41e..668f73c 100644
--- a/chrome/browser/ash/android_sms/connection_manager_unittest.cc
+++ b/chrome/browser/ash/android_sms/connection_manager_unittest.cc
@@ -17,13 +17,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
-
-// TODO(https://crbug.com/1164001): remove when chromeos/multidevice_setup is
-// migrated.
-namespace multidevice_setup {
-namespace mojom = ::chromeos::multidevice_setup::mojom;
-}
-
 namespace android_sms {
 
 namespace {
diff --git a/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc b/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc
index 8f16980f..064ee68 100644
--- a/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc
+++ b/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -435,8 +436,8 @@
       base::RunLoop run_loop;
       provider_->install_finalizer().UninstallExternalWebApp(
           id, webapps::WebappUninstallSource::kArc,
-          base::BindLambdaForTesting([&](bool uninstalled) {
-            EXPECT_TRUE(uninstalled);
+          base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+            EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
             run_loop.Quit();
           }));
       run_loop.Run();
diff --git a/chrome/browser/ash/borealis/borealis_context.cc b/chrome/browser/ash/borealis/borealis_context.cc
index f032f821..59e27a1 100644
--- a/chrome/browser/ash/borealis/borealis_context.cc
+++ b/chrome/browser/ash/borealis/borealis_context.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/public/cpp/new_window_delegate.h"
+#include "ash/public/cpp/notification_utils.h"
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/containers/flat_map.h"
@@ -25,13 +26,20 @@
 #include "chrome/browser/ash/borealis/borealis_util.h"
 #include "chrome/browser/ash/borealis/borealis_window_manager.h"
 #include "chrome/browser/ash/guest_os/guest_os_stability_monitor.h"
+#include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/grit/generated_resources.h"
 #include "components/exo/shell_surface_util.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
 namespace borealis {
 
 namespace {
 
+constexpr char kFeedbackNotificationId[] =
+    "borealis-post-game-feedback-notification";
+constexpr char kNotifierBorealis[] = "ash.borealis";
+
 // Similar to a delayed callback, but can be cancelled by deleting.
 class ScopedDelayedCallback {
  public:
@@ -107,8 +115,54 @@
 
   void OnDelayComplete(GURL gurl, std::string app_id) {
     app_delayers_.erase(app_id);
-    ash::NewWindowDelegate::GetPrimary()->OpenUrl(
-        gurl, ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction);
+    CreateFeedbackNotification(gurl);
+  }
+
+  // Creates a notification, that when clicked, will close itself and redirect
+  // the user to a feedback form. If there is an existing notification, this
+  // will replace it.
+  void CreateFeedbackNotification(GURL gurl) {
+    // Delegate for handling click events on the notification.
+    auto on_click_handler =
+        base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
+            base::BindRepeating(
+                [](GURL gurl, Profile* profile_) {
+                  ash::NewWindowDelegate::GetPrimary()->OpenUrl(
+                      gurl,
+                      ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction);
+
+                  NotificationDisplayService::GetForProfile(profile_)->Close(
+                      NotificationHandler::Type::TRANSIENT,
+                      kFeedbackNotificationId);
+                },
+                gurl, profile_));
+
+    // Close the current notification (if any).
+    NotificationDisplayService::GetForProfile(profile_)->Close(
+        NotificationHandler::Type::TRANSIENT, kFeedbackNotificationId);
+
+    // Create the new notification.
+    message_center::Notification notification(
+        /*type=*/message_center::NOTIFICATION_TYPE_SIMPLE,
+        /*id=*/kFeedbackNotificationId,
+        /*title=*/
+        l10n_util::GetStringUTF16(IDS_BOREALIS_FEEDBACK_NOTIFICATION_TITLE),
+        /*message=*/
+        l10n_util::GetStringUTF16(IDS_BOREALIS_FEEDBACK_NOTIFICATION_MESSAGE),
+        /*icon=*/gfx::Image(),
+        /*display_source=*/
+        l10n_util::GetStringUTF16(IDS_BOREALIS_FEEDBACK_NOTIFICATION_SOURCE),
+        /*origin_url=*/GURL(),
+        /*notifier_id=*/
+        message_center::NotifierId(
+            message_center::NotifierType::SYSTEM_COMPONENT, kNotifierBorealis),
+        /*optional_fields=*/message_center::RichNotificationData(),
+        /*delegate*/ on_click_handler);
+
+    // Display the new notification.
+    NotificationDisplayService::GetForProfile(profile_)->Display(
+        NotificationHandler::Type::TRANSIENT, notification,
+        /*metadata=*/nullptr);
   }
 
   Profile* const profile_;
diff --git a/chrome/browser/ash/borealis/borealis_features.cc b/chrome/browser/ash/borealis/borealis_features.cc
index 8b095641..bca190a1 100644
--- a/chrome/browser/ash/borealis/borealis_features.cc
+++ b/chrome/browser/ash/borealis/borealis_features.cc
@@ -8,13 +8,17 @@
 
 #include "ash/components/settings/cros_settings_names.h"
 #include "ash/constants/ash_features.h"
+#include "base/base64.h"
 #include "base/callback.h"
 #include "base/cpu.h"
-#include "base/feature_list.h"
+#include "base/location.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/threading/scoped_blocking_call.h"
 #include "chrome/browser/ash/borealis/borealis_prefs.h"
 #include "chrome/browser/ash/guest_os/infra/cached_callback.h"
 #include "chrome/browser/ash/guest_os/virtual_machines/virtual_machines_util.h"
@@ -28,6 +32,7 @@
 #include "chromeos/tpm/install_attributes.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/channel.h"
+#include "crypto/sha2.h"
 #include "third_party/re2/src/re2/re2.h"
 
 using AllowStatus = borealis::BorealisFeatures::AllowStatus;
@@ -49,11 +54,65 @@
 // Matches i5 and i7 of the 11th generation and up.
 constexpr char kMinimumCpuRegex[] = "[1-9][1-9].. Gen.*i[57]-";
 
-AllowStatus GetAsyncAllowStatus() {
-  // First, avoid excluding -borealis variants, which are dev-only boards that
+// Used to make it difficult to tell what someone's token is based on their
+// prefs.
+constexpr char kSaltForPrefStorage[] = "/!RoFN8,nDxiVgTI6CvU";
+
+// A prime number chosen to give ~0.1s of wait time on my DUT.
+constexpr unsigned kHashIterations = 100129;
+
+// The below mechanism is not secure, and is not intended to be. It is a
+// temporary measure that does not warrant any more effort. You might say
+// it can be gamed 😎.
+//
+// Reminder: Don't Roll Your Own Crypto! Security should be left to the
+// experts.
+//
+// TODO(b/218403711): This mechanism is temporary. It exists to allow borealis
+// developers to verify that borealis functions correctly on the target
+// platforms before releasing borealis broadly. We only need it because the
+// boards we are targeting are publicly available, and going forward we will
+// verify borealis is functioning on hardware before its public release.
+std::string H(std::string input, const std::string& salt) {
+  // Hashing is not strictly "blocking" since the cpu is probably busy, but best
+  // not to call this method if you're on a thread that disallows blocking.
+  base::ScopedBlockingCall sbc(FROM_HERE, base::BlockingType::WILL_BLOCK);
+  std::string ret = std::move(input);
+  for (int i = 0; i < kHashIterations; ++i) {
+    std::string raw_sha = crypto::SHA256HashString(ret + salt);
+    base::Base64Encode(raw_sha, &ret);
+  }
+  return ret;
+}
+
+bool HasCorrectToken(const std::string& board,
+                     const std::string& hash_of_current_token) {
+  // If you are supposed to know the correct token, then you will be able to
+  // find it ~if you go to the place we all know and love~.
+  //
+  // For the maintainer:
+  //
+  // H(H("token", kSaltForPrefStorage), "salt") =
+  //   "aT79k1Uv7v7D5s2/rpYUJYRXTUq4EkPN2FK4JBQJWgw=";
+  if (base::EndsWith(board, kOverrideHardwareChecksBoardSuffix)) {
+    return H(hash_of_current_token, "MXlY+SFZ!2,P_k^02]hK") ==
+           "FbxB2mxNa/uqskX4X+NqHhAE6ebHeWC0u+Y+UlGEB/4=";
+  } else if (board == "volteer") {
+    return H(hash_of_current_token, "F9sOMmgrk9%C$poxLT.Eg") ==
+           "Gn5gDfMLbMrBI10zrVba6q/1QEGJilyEyUeNiOID0X8=";
+  }
+  return false;
+}
+
+AllowStatus GetAsyncAllowStatus(const std::string& hash_of_current_token) {
+  // First, check the token.
+  std::string board = base::SysInfo::GetLsbReleaseBoard();
+  if (!HasCorrectToken(board, hash_of_current_token))
+    return AllowStatus::kIncorrectToken;
+
+  // Then, avoid excluding -borealis variants, which are dev-only boards that
   // get a free pass.
-  if (base::EndsWith(base::SysInfo::GetLsbReleaseBoard(),
-                     kOverrideHardwareChecksBoardSuffix)) {
+  if (base::EndsWith(board, kOverrideHardwareChecksBoardSuffix)) {
     // Note: the comment on GetLsbReleaseBoard() (rightly) points out that we're
     // not supposed to use LsbReleaseBoard directly, but rather set a flag in
     // the overlay. I am not doing that as the following check is only a
@@ -63,7 +122,7 @@
     return AllowStatus::kAllowed;
   }
 
-  // Second, exclude variants of the boards that we don't expect to work on.
+  // Next, exclude variants of the boards that we don't expect to work on.
   std::string model_name;
   if (!chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
           chromeos::system::kCustomizationIdKey, &model_name)) {
@@ -79,7 +138,7 @@
   if (!found)
     return AllowStatus::kUnsupportedModel;
 
-  // Third, check system requirements.
+  // Finally, check system requirements.
   if (base::SysInfo::AmountOfPhysicalMemory() < kMinimumMemoryBytes) {
     return AllowStatus::kHardwareChecksFailed;
   } else if (!RE2::PartialMatch(
@@ -94,6 +153,9 @@
 }  // namespace
 
 class AsyncAllowChecker : public guest_os::CachedCallback<AllowStatus, bool> {
+ public:
+  explicit AsyncAllowChecker(Profile* profile) : profile_(profile) {}
+
  private:
   void Build(RealCallback callback) override {
     // Testing hardware capabilities in unit tests is kindof pointless. The
@@ -107,12 +169,19 @@
       return;
     }
 
+    // Bail out if the prefs service is not up and running, this just means we
+    // retry later.
+    if (!profile_ || !profile_->GetPrefs()) {
+      std::move(callback).Run(Failure(Reject()));
+      return;
+    }
+
     chromeos::system::StatisticsProvider::GetInstance()
         ->ScheduleOnMachineStatisticsLoaded(base::BindOnce(
-            [](RealCallback callback) {
+            [](RealCallback callback, const std::string& token_hash) {
               base::ThreadPool::PostTaskAndReplyWithResult(
                   FROM_HERE, base::MayBlock(),
-                  base::BindOnce(&GetAsyncAllowStatus),
+                  base::BindOnce(&GetAsyncAllowStatus, token_hash),
                   base::BindOnce(
                       [](RealCallback callback, AllowStatus status) {
                         // "Success" here means we successfully determined the
@@ -123,15 +192,19 @@
                       },
                       std::move(callback)));
             },
-            std::move(callback)));
+            std::move(callback),
+            profile_->GetPrefs()->GetString(prefs::kBorealisVmTokenHash)));
   }
+
+  Profile* const profile_;
 };
 
 BorealisFeatures::BorealisFeatures(Profile* profile)
-    : profile_(profile), async_checker_(std::make_unique<AsyncAllowChecker>()) {
-  // Issue a request for the async status immediately upon creation, in case
-  // it's needed.
-  async_checker_->Get(base::DoNothing());
+    : profile_(profile),
+      async_checker_(std::make_unique<AsyncAllowChecker>(profile_)) {
+  // Issue a request for the status immediately upon creation, in case
+  // it's needed later.
+  IsAllowed(base::DoNothing());
 }
 
 BorealisFeatures::~BorealisFeatures() = default;
@@ -199,6 +272,28 @@
   return profile_->GetPrefs()->GetBoolean(prefs::kBorealisInstalledOnDevice);
 }
 
+void BorealisFeatures::SetVmToken(
+    std::string token,
+    base::OnceCallback<void(AllowStatus)> callback) {
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, base::MayBlock(),
+      base::BindOnce(&H, std::move(token), kSaltForPrefStorage),
+      base::BindOnce(&BorealisFeatures::OnVmTokenDetermined,
+                     weak_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void BorealisFeatures::OnVmTokenDetermined(
+    base::OnceCallback<void(AllowStatus)> callback,
+    std::string hashed_token) {
+  // This has the effect that you could overwrite the correct token and disable
+  // borealis. Adding extra code to avoid that is not worth while because end
+  // users aren't supposed to have the correct token anyway.
+  profile_->GetPrefs()->SetString(prefs::kBorealisVmTokenHash, hashed_token);
+  // The user has given a new password, so we invalidate the old status.
+  async_checker_->Invalidate();
+  IsAllowed(std::move(callback));
+}
+
 }  // namespace borealis
 
 std::ostream& operator<<(std::ostream& os, const AllowStatus& reason) {
@@ -231,5 +326,7 @@
       return os << "Borealis is not supported on this model hardware";
     case AllowStatus::kHardwareChecksFailed:
       return os << "Insufficient CPU/Memory to run Borealis";
+    case AllowStatus::kIncorrectToken:
+      return os << "Borealis needs a valid permission token";
   }
 }
diff --git a/chrome/browser/ash/borealis/borealis_features.h b/chrome/browser/ash/borealis/borealis_features.h
index 959c32a..f8bfab2 100644
--- a/chrome/browser/ash/borealis/borealis_features.h
+++ b/chrome/browser/ash/borealis/borealis_features.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
 
 class Profile;
 
@@ -32,6 +33,7 @@
     kBlockedByFlag,
     kUnsupportedModel,
     kHardwareChecksFailed,
+    kIncorrectToken,
   };
 
   // Creates a per-profile instance of the feature-checker for borealis.
@@ -54,9 +56,20 @@
   // Returns true if borealis has been installed and can be run in the profile.
   bool IsEnabled();
 
+  // Sets the token used to authorize borealis. Since doing this will usually
+  // cause IsAllowed() to change we also invoke |callback| with the new
+  // allowedness status.
+  void SetVmToken(std::string token,
+                  base::OnceCallback<void(AllowStatus)> callback);
+
  private:
+  void OnVmTokenDetermined(base::OnceCallback<void(AllowStatus)> callback,
+                           std::string hashed_token);
+
   Profile* const profile_;
   std::unique_ptr<AsyncAllowChecker> async_checker_;
+  // TODO(b/218403711): remove this.
+  base::WeakPtrFactory<BorealisFeatures> weak_factory_{this};
 };
 
 }  // namespace borealis
diff --git a/chrome/browser/ash/borealis/borealis_prefs.cc b/chrome/browser/ash/borealis/borealis_prefs.cc
index a69d0799..fb2f08e 100644
--- a/chrome/browser/ash/borealis/borealis_prefs.cc
+++ b/chrome/browser/ash/borealis/borealis_prefs.cc
@@ -12,6 +12,8 @@
 
 const char kBorealisInstalledOnDevice[] = "borealis.installed_on_device";
 
+const char kBorealisVmTokenHash[] = "borealis.vm_token_hash";
+
 const char kBorealisAllowedForUser[] = "borealis.allowed_for_user";
 
 const char kEngagementPrefsPrefix[] = "borealis.metrics";
@@ -22,6 +24,7 @@
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(kBorealisInstalledOnDevice, false);
+  registry->RegisterStringPref(kBorealisVmTokenHash, "");
   registry->RegisterBooleanPref(kBorealisAllowedForUser, true);
   registry->RegisterBooleanPref(kBorealisMicAllowed, false);
   registry->RegisterStringPref(kExtraLaunchOptions, std::string());
diff --git a/chrome/browser/ash/borealis/borealis_prefs.h b/chrome/browser/ash/borealis/borealis_prefs.h
index 8c7b1d5b..29db5fa 100644
--- a/chrome/browser/ash/borealis/borealis_prefs.h
+++ b/chrome/browser/ash/borealis/borealis_prefs.h
@@ -14,6 +14,11 @@
 // on the device.
 extern const char kBorealisInstalledOnDevice[];
 
+// A string pref which records the user's current "insert_coin" guess. We record
+// the hash of that guess here, and the hash of this will be used to check the
+// token (i.e. we double-hash whatever the user typed in).
+extern const char kBorealisVmTokenHash[];
+
 // A boolean preference for managing whether borealis is allowed for the user
 // (mainly used by enterprises).
 extern const char kBorealisAllowedForUser[];
diff --git a/chrome/browser/ash/borealis/borealis_util.cc b/chrome/browser/ash/borealis/borealis_util.cc
index d62add2..5b4a0569 100644
--- a/chrome/browser/ash/borealis/borealis_util.cc
+++ b/chrome/browser/ash/borealis/borealis_util.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/borealis/borealis_util.h"
 
 #include "base/base64.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
 #include "base/process/launch.h"
 #include "base/strings/string_number_conversions.h"
@@ -13,6 +14,7 @@
 #include "base/system/sys_info.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/values.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
@@ -33,23 +35,27 @@
 // are updated.
 const char kBorealisAppIdRegex[] = "([^/]+\\d+)";
 const char kProtonVersionGameMismatch[] = "UNKNOWN (GameID mismatch)";
+const char kDeviceInformationKey[] = "entry.1613887985";
+
+const char kInsertCoinSuccessMessage[] = "Success";
+const char kInsertCoinRejectMessage[] = "Coin Invalid";
 
 namespace {
 
 // Base feedback form URL, without query parameters for prefilling.
 static constexpr char kFeedbackUrl[] =
     "https://docs.google.com/forms/d/e/"
-    "1FAIpQLSfyI7K7xV3pKiQeJ-yZlep4XI8ZY9bbr7D33LY2jm4Zoda1cg/"
+    "1FAIpQLScGvT2BIwYJe9g15OINX2pvw6TgK8e2ihvSq3hHZudAneRmuA/"
     "viewform?usp=pp_url";
-
 // Query parameter keys for prefilling form data.
-static constexpr char kAppNameKey[] = "entry.1661950665";
-static constexpr char kBoardKey[] = "entry.2066138756";
-static constexpr char kSpecsKey[] = "entry.1341753442";
-static constexpr char kPlatformVersionKey[] = "entry.1193918294";
-static constexpr char kAppIdKey[] = "entry.2112096055";
-static constexpr char kProtonVersionKey[] = "entry.810819520";
-static constexpr char kSlrVersionKey[] = "entry.300735843";
+static constexpr char kAppNameKey[] = "entry.504707995";
+// JSON keys for prefilling JSON section.
+static constexpr char kJSONAppIdKey[] = "steam_appid";
+static constexpr char kJSONBoardKey[] = "board";
+static constexpr char kJSONPlatformKey[] = "platform_version";
+static constexpr char kJSONProtonKey[] = "proton_version";
+static constexpr char kJSONSpecsKey[] = "specs";
+static constexpr char kJSONSteamKey[] = "steam_runtime_version";
 
 // App IDs prefixed with this are identified with a numeric "Borealis ID".
 const base::StringPiece kBorealisWindowWithIdPrefix(
@@ -64,16 +70,22 @@
 GURL GetSysInfoForUrlAsync(GURL url,
                            absl::optional<int> game_id,
                            std::string owner_id) {
-  url = net::AppendQueryParameter(url, kBoardKey,
-                                  base::SysInfo::HardwareModelName());
-  url = net::AppendQueryParameter(
-      url, kSpecsKey,
+  base::DictionaryValue json_root;
+
+  if (game_id.has_value()) {
+    json_root.SetString(kJSONAppIdKey,
+                        base::StringPrintf("%d", game_id.value()));
+  }
+
+  json_root.SetString(kJSONBoardKey, base::SysInfo::HardwareModelName());
+  json_root.SetString(
+      kJSONSpecsKey,
       base::StringPrintf("%ldGB; %s",
                          (long)(base::SysInfo::AmountOfPhysicalMemory() /
                                 (1000 * 1000 * 1000)),
                          base::SysInfo::CPUModelName().c_str()));
-  url = net::AppendQueryParameter(url, kPlatformVersionKey,
-                                  base::SysInfo::OperatingSystemVersion());
+  json_root.SetString(kJSONPlatformKey,
+                      base::SysInfo::OperatingSystemVersion());
 
   borealis::ProtonVersionInfo version_info;
   std::string output;
@@ -83,8 +95,12 @@
     LOG(WARNING) << "Failed to run get_proton_version.py:";
     LOG(WARNING) << output;
   }
-  url = net::AppendQueryParameter(url, kProtonVersionKey, version_info.proton);
-  url = net::AppendQueryParameter(url, kSlrVersionKey, version_info.slr);
+  json_root.SetString(kJSONProtonKey, version_info.proton);
+  json_root.SetString(kJSONSteamKey, version_info.slr);
+
+  std::string json_string;
+  base::JSONWriter::Write(json_root, &json_string);
+  url = net::AppendQueryParameter(url, kDeviceInformationKey, json_string);
 
   return url;
 }
@@ -143,10 +159,6 @@
       registry_service->GetRegistration(app_id);
   if (registration.has_value()) {
     borealis_app_id = GetBorealisAppId(registration->Exec());
-    if (borealis_app_id.has_value()) {
-      url = net::AppendQueryParameter(
-          url, kAppIdKey, base::StringPrintf("%d", borealis_app_id.value()));
-    }
   }
 
   base::ThreadPool::PostTaskAndReplyWithResult(
diff --git a/chrome/browser/ash/borealis/borealis_util.h b/chrome/browser/ash/borealis/borealis_util.h
index 4d02300..e4c59ec 100644
--- a/chrome/browser/ash/borealis/borealis_util.h
+++ b/chrome/browser/ash/borealis/borealis_util.h
@@ -39,6 +39,14 @@
 // parsed with /usr/bin/get_proton_version.py in the Borealis VM does not
 // match the GameID expected based on extraction with kBorealisAppIdRegex.
 extern const char kProtonVersionGameMismatch[];
+// Query parameter key for device information in the borealis feedback
+// form.
+extern const char kDeviceInformationKey[];
+
+// TODO(b/218403711): remove these when insert_coin is deprecated. We only have
+// insert_coin in the short-term until installer UX is finalized.
+extern const char kInsertCoinSuccessMessage[];
+extern const char kInsertCoinRejectMessage[];
 
 struct ProtonVersionInfo {
   std::string proton = "Not applicable";
diff --git a/chrome/browser/ash/borealis/borealis_util_unittest.cc b/chrome/browser/ash/borealis/borealis_util_unittest.cc
index 8125f58..7a5a3d5 100644
--- a/chrome/browser/ash/borealis/borealis_util_unittest.cc
+++ b/chrome/browser/ash/borealis/borealis_util_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash/borealis/borealis_util.h"
 
+#include "base/json/json_reader.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
@@ -93,8 +94,14 @@
     }
     it.Advance();
   }
+  EXPECT_EQ(entries, 2);  // we currently prefill this many form fields.
 
-  EXPECT_EQ(entries, 6);  // we currently prefill this many form fields
+  std::string json_string;
+  EXPECT_TRUE(
+      net::GetValueForKeyInQuery(url, kDeviceInformationKey, &json_string));
+  auto json_root = base::JSONReader::Read(json_string);
+  EXPECT_EQ(json_root.value().GetDict().size(), 5);  // we expect the JSON field
+  // to have 5 key/value pairs.
 }
 
 TEST_F(BorealisUtilTest, ProtonVersionProtonTitle) {
diff --git a/chrome/browser/ash/borealis/borealis_window_manager.cc b/chrome/browser/ash/borealis/borealis_window_manager.cc
index a4840540..63c70d92 100644
--- a/chrome/browser/ash/borealis/borealis_window_manager.cc
+++ b/chrome/browser/ash/borealis/borealis_window_manager.cc
@@ -95,11 +95,6 @@
   return kBorealisAnonymousPrefix + *GetWindowId(window);
 }
 
-bool IsAnonymousAppId(const std::string& app_id) {
-  return base::StartsWith(app_id, kBorealisAnonymousPrefix,
-                          base::CompareCase::SENSITIVE);
-}
-
 }  // namespace
 
 // static
@@ -153,6 +148,12 @@
   return true;
 }
 
+// static
+bool BorealisWindowManager::IsAnonymousAppId(const std::string& app_id) {
+  return base::StartsWith(app_id, kBorealisAnonymousPrefix,
+                          base::CompareCase::SENSITIVE);
+}
+
 BorealisWindowManager::BorealisWindowManager(Profile* profile)
     : profile_(profile), instance_registry_observation_(this) {}
 
diff --git a/chrome/browser/ash/borealis/borealis_window_manager.h b/chrome/browser/ash/borealis/borealis_window_manager.h
index cab90f6..2ed9d566 100644
--- a/chrome/browser/ash/borealis/borealis_window_manager.h
+++ b/chrome/browser/ash/borealis/borealis_window_manager.h
@@ -49,6 +49,9 @@
   // TODO(b/210569001): this is intended to be a temporary solution.
   static bool ShouldNewWindowBeMinimized(const std::string& window_id);
 
+  // Returns true when the given |app_id| is for an anonymous borealis app.
+  static bool IsAnonymousAppId(const std::string& app_id);
+
   // An observer for tracking the creation and deletion of anonymous windows.
   class AnonymousAppObserver : public base::CheckedObserver {
    public:
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
index 4c431ef..1ec2bf9e 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
@@ -41,6 +41,7 @@
 #include "components/services/app_service/public/cpp/app_update.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -188,10 +189,11 @@
           .UninstallExternalWebApp(
               app_id.app_id(),
               webapps::WebappUninstallSource::kExternalPreinstalled,
-              base::BindLambdaForTesting([&](bool uninstalled) {
-                EXPECT_TRUE(uninstalled);
-                run_loop.Quit();
-              }));
+              base::BindLambdaForTesting(
+                  [&](webapps::UninstallResultCode code) {
+                    EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
+                    run_loop.Quit();
+                  }));
       run_loop.Run();
       task_environment_.RunUntilIdle();
       return;
diff --git a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
index 182d6cb..6c40980 100644
--- a/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
+++ b/chrome/browser/ash/crosapi/browser_data_migrator_browsertest.cc
@@ -60,7 +60,7 @@
     // 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);
+                                MoveMigrator::ResumeStep::kMoveLacrosItems);
   }
 
   bool LoginAsRegularUser() {
@@ -124,7 +124,7 @@
     // Setting this pref triggers a restart to resume move migration. Check
     // `BrowserDataMigratorImpl::MaybeForceResumeMoveMigration()`.
     MoveMigrator::SetResumeStep(g_browser_process->local_state(), kUserIdHash,
-                                MoveMigrator::ResumeStep::kRemoveHardLinks);
+                                MoveMigrator::ResumeStep::kMoveLacrosItems);
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/ash/crosapi/environment_provider.cc b/chrome/browser/ash/crosapi/environment_provider.cc
index 712c691..2ce529f 100644
--- a/chrome/browser/ash/crosapi/environment_provider.cc
+++ b/chrome/browser/ash/crosapi/environment_provider.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/crosapi/environment_provider.h"
 
 #include "base/files/file_util.h"
+#include "base/path_service.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -99,6 +100,9 @@
         base::FilePath(file_manager::util::kAndroidFilesPath);
     default_paths->linux_files =
         file_manager::util::GetCrostiniMountDirectory(profile);
+    base::FilePath ash_resources;
+    if (base::PathService::Get(base::DIR_ASSETS, &ash_resources))
+      default_paths->ash_resources = ash_resources;
   } else {
     // On developer linux workstations the above functions do path mangling to
     // support multi-signin which gets undone later in ash-specific code. This
@@ -109,6 +113,7 @@
     default_paths->drivefs = home.Append("Drive");
     default_paths->android_files = home.Append("Android");
     default_paths->linux_files = home.Append("Crostini");
+    default_paths->ash_resources = home.Append("Ash");
   }
 
   // CrosDisksClient already has a convention for its removable media directory
diff --git a/chrome/browser/ash/crosapi/move_migrator.cc b/chrome/browser/ash/crosapi/move_migrator.cc
index b9015a0b..b4993597 100644
--- a/chrome/browser/ash/crosapi/move_migrator.cc
+++ b/chrome/browser/ash/crosapi/move_migrator.cc
@@ -72,16 +72,16 @@
           base::BindOnce(&MoveMigrator::OnPreMigrationCleanUp,
                          weak_factory_.GetWeakPtr()));
       return;
-    case ResumeStep::kRemoveHardLinks:
+    case ResumeStep::kMoveLacrosItems:
       LOG(ERROR) << "Migration did not complete in the previous attempt. "
-                    "Resuming migration from kRemoveHardLinks step.";
+                    "Resuming migration from kMoveLacrosItems step.";
       base::ThreadPool::PostTaskAndReplyWithResult(
           FROM_HERE,
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
-          base::BindOnce(&MoveMigrator::RemoveHardLinksFromOriginalDir,
+          base::BindOnce(&MoveMigrator::MoveLacrosItemsToNewDir,
                          original_profile_dir_),
-          base::BindOnce(&MoveMigrator::OnRemoveHardLinksFromOriginalDir,
+          base::BindOnce(&MoveMigrator::OnMoveLacrosItemsToNewDir,
                          weak_factory_.GetWeakPtr()));
       return;
     case ResumeStep::kMoveTmpDir:
@@ -119,7 +119,7 @@
   switch (resume_step) {
     case ResumeStep::kStart:
       return false;
-    case ResumeStep::kRemoveHardLinks:
+    case ResumeStep::kMoveLacrosItems:
       return true;
     case ResumeStep::kMoveTmpDir:
       return true;
@@ -206,7 +206,7 @@
   if (base::PathExists(tmp_user_dir)) {
     // Delete tmp_user_dir if any were left from a previous failed move
     // migration attempt. Note that if resuming move migration from later steps
-    // such as `RemoveHardLinksFromOriginalDir()`, this tmp_user_dir will not be
+    // such as `MoveLacrosItemsToNewDir()`, this tmp_user_dir will not be
     // deleted. This is an intended behaviour because we do not want to delete
     // tmp_user_dir once we start deleting items from the Ash PDD.
     if (!base::DeletePathRecursively(tmp_user_dir)) {
@@ -315,34 +315,6 @@
     return false;
   }
 
-  browser_data_migrator_util::TargetItems lacros_items =
-      browser_data_migrator_util::GetTargetItems(
-          original_profile_dir, browser_data_migrator_util::ItemType::kLacros);
-
-  // This check ensures that the migrator can at least rename the directory to
-  // `<kRemoveDir>/<item.path.BaseName()>` to make it inaccessible from ash in
-  // `RemoveHardLinksFromOriginalDir()`. Note that not having write permission
-  // to a directory does not automatically mean that creating a hard link fails.
-  // As long as the process has rx permission to the parent directory, a hard
-  // link can be created for a file. Also note that for a file, write permission
-  // is not required for renaming. Only the w permission for the parent
-  // directory is checked.
-  for (const auto& item : lacros_items.items) {
-    if (item.is_directory && !base::PathIsWritable(item.path)) {
-      // TODO(ythjkt): Add a UMA.
-      PLOG(ERROR) << "The current process does not have write permission to "
-                     "the directory "
-                  << item.path.value();
-      return false;
-    }
-  }
-
-  if (!browser_data_migrator_util::CopyTargetItemsByHardLinks(
-          tmp_profile_dir, lacros_items, cancel_flag.get())) {
-    LOG(ERROR) << "CopyTargetItemsByHardLinks() failed for lacros_items.";
-    return false;
-  }
-
   if (!base::WriteFile(tmp_user_dir.Append(chrome::kFirstRunSentinel), "")) {
     LOG(ERROR) << "WriteFile() failed for " << chrome::kFirstRunSentinel;
     return false;
@@ -360,82 +332,59 @@
     return;
   }
 
-  // `RemoveHardLinksFromOriginalDir()` is the point of no return. Once it is
-  // started, it has to be completed. Otherwise the profile in ash directory
-  // becomes fragmented. The profile in lacros will be complete but with hard
-  // links left in ash directory causing the files with hard links left in ash
-  // to be updated by both ash and lacros. This is obviously a dangerous
-  // situation to be in. We store the resume step as `kRemoveHardLinks` in Local
-  // State so that if the migration is interrupted during
-  // `RemoveHardLinksFromOriginalDir()` then the migrator can resume the
-  // migration from that point.
-  SetResumeStep(local_state_, user_id_hash_, ResumeStep::kRemoveHardLinks);
+  // Once `MoveLacrosItemsToNewDir()` is started, it should be completed.
+  // Otherwise the profile in ash directory becomes fragmented. We store the
+  // resume step as `kMoveLacrosItems` in Local State so that if the migration
+  // is interrupted during `MoveLacrosItemsToNewDir()` then the migrator can
+  // resume the migration from that point.
+  SetResumeStep(local_state_, user_id_hash_, ResumeStep::kMoveLacrosItems);
 
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE,
       {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
        base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
-      base::BindOnce(&MoveMigrator::RemoveHardLinksFromOriginalDir,
+      base::BindOnce(&MoveMigrator::MoveLacrosItemsToNewDir,
                      original_profile_dir_),
-      base::BindOnce(&MoveMigrator::OnRemoveHardLinksFromOriginalDir,
+      base::BindOnce(&MoveMigrator::OnMoveLacrosItemsToNewDir,
                      weak_factory_.GetWeakPtr()));
 }
 
 // static
-bool MoveMigrator::RemoveHardLinksFromOriginalDir(
+bool MoveMigrator::MoveLacrosItemsToNewDir(
     const base::FilePath& original_profile_dir) {
-  LOG(WARNING) << "Running RemoveHardLinksFromOriginalDir()";
+  LOG(WARNING) << "Running MoveLacrosItemsToNewDir()";
 
   browser_data_migrator_util::TargetItems lacros_items =
       browser_data_migrator_util::GetTargetItems(
           original_profile_dir, browser_data_migrator_util::ItemType::kLacros);
 
-  const base::FilePath remove_dir =
-      original_profile_dir.Append(browser_data_migrator_util::kRemoveDir);
-  if (!base::DirectoryExists(remove_dir) &&
-      !base::CreateDirectory(remove_dir)) {
-    LOG(ERROR) << remove_dir.value() << " could not be created.";
-    return false;
-  }
-
-  // Delete hard links for lacros file/dirs in ash directory. If deletion fails,
-  // try moving them to `kRemoveDir` so that they become inaccessible from ash.
   for (const auto& item : lacros_items.items) {
-    if (!base::DeletePathRecursively(item.path)) {
-      // One cause of this failure is that there is a subdirectory in
-      // `item.path` that chronos does not have w permission of. Even in such a
-      // case, moving the parent directory `item.path` should succeed as long as
-      // that is owned by chronos.
-      PLOG(ERROR) << "Failed deleting item " << item.path.value()
-                  << ". Trying renaming to make the item inaccessible instead.";
-      if (!base::Move(item.path, remove_dir.Append(item.path.BaseName()))) {
-        PLOG(ERROR) << "Failed moving " << item.path.value() << " to "
-                    << remove_dir.value();
-        return false;
-      }
+    if (item.is_directory && !base::PathIsWritable(item.path)) {
+      // TODO(ythjkt): Add a UMA.
+      PLOG(ERROR) << "The current process does not have write permission to "
+                     "the directory "
+                  << item.path.value();
+      return false;
     }
   }
 
-  if (!base::DeletePathRecursively(remove_dir)) {
-    // This indicates that there is a subdirectory in `remove_dir` which chronos
-    // does not have a write permission to. Failing to remove this directory is
-    // not critical since it is not accessible by ash or lacros so only log the
-    // error but continue with the migration.
-    // TODO(ythjkt): Add a logic to make session_manager delete this directory
-    // with root privilege.
-    // TODO(ythjkt): Add UMA to collect cases of this happening.
-    PLOG(ERROR) << "Failed removing "
-                << original_profile_dir
-                       .Append(browser_data_migrator_util::kRemoveDir)
-                       .value();
-  }
+  const base::FilePath tmp_profile_dir =
+      original_profile_dir.Append(browser_data_migrator_util::kMoveTmpDir)
+          .Append(browser_data_migrator_util::kLacrosProfilePath);
 
+  for (const auto& item : lacros_items.items) {
+    if (!base::Move(item.path, tmp_profile_dir.Append(item.path.BaseName()))) {
+      PLOG(ERROR) << "Failed to move item " << item.path.value() << " to "
+                  << tmp_profile_dir.Append(item.path.BaseName()) << ": ";
+      return false;
+    }
+  }
   return true;
 }
 
-void MoveMigrator::OnRemoveHardLinksFromOriginalDir(bool success) {
+void MoveMigrator::OnMoveLacrosItemsToNewDir(bool success) {
   if (!success) {
-    LOG(ERROR) << "Removing hard links have failed.";
+    LOG(ERROR) << "Moving Lacros items to temporary directory failed.";
     std::move(finished_callback_)
         .Run({BrowserDataMigratorImpl::DataWipeResult::kSucceeded,
               {BrowserDataMigrator::ResultKind::kFailed}});
diff --git a/chrome/browser/ash/crosapi/move_migrator.h b/chrome/browser/ash/crosapi/move_migrator.h
index ba8b3753..b5a3d66 100644
--- a/chrome/browser/ash/crosapi/move_migrator.h
+++ b/chrome/browser/ash/crosapi/move_migrator.h
@@ -45,15 +45,11 @@
 // forward, to the new profile data directory
 // (/<Ash PDD>/lacros/Default/) with the steps described below. The renaming of
 // <kMoveTmpDir> is the last step of the migration so that the existence of
-// <Ash PDD>/lacros/ is equivalent to having completed the migration. Also it
-// ensures that there is no state in which Ash and Lacros have access to the
-// same inode via two hardlinks.
+// <Ash PDD>/lacros/ is equivalent to having completed the migration.
 // 1) Delete any `ItemType::kDeletable` items in <Ash PDD>.
 // 2) Setup <Ash PDD>/<kMoveTmpDir> by copying `ItemType::kNeedCopy`
-// items into it and creating hard links for `ItemType::kLacros` in it.
-// 3) Delete the original hard links for `ItemType::kLacros` in <Ash PDD>. If
-// deletion fails, move them to `<Ash PDD>/<kRemoveDir>` to make them
-// inaccessible by Ash.
+// items into it.
+// 3) Move `ItemType::kLacros` in <Ash PDD> to <lacros PDD>.
 // 4) Rename <Ash PDD>/<kMoveTmpDir>/ as <Ash PDD>/lacros/.
 class MoveMigrator : public BrowserDataMigratorImpl::MigratorDelegate {
  public:
@@ -61,7 +57,7 @@
   // in the previous attempt.
   enum class ResumeStep {
     kStart = 0,
-    kRemoveHardLinks = 1,
+    kMoveLacrosItems = 1,
     kMoveTmpDir = 2,
     kCompleted = 3,
   };
@@ -111,11 +107,12 @@
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, ResumeRequired);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, PreMigrationCleanUp);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, SetupLacrosDir);
-  FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest,
-                           SetupLacrosDirFailIfNoWritePermForLacrosItem);
-  FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, RemoveHardLinksFromOriginalDir);
+  FRIEND_TEST_ALL_PREFIXES(
+      MoveMigratorTest,
+      MoveLacrosItemsToNewDirFailIfNoWritePermForLacrosItem);
+  FRIEND_TEST_ALL_PREFIXES(MoveMigratorTest, MoveLacrosItemsToNewDir);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorMigrateTest,
-                           MigrateResumeFromRemoveHardLinks);
+                           MigrateResumeFromMoveLacrosItems);
   FRIEND_TEST_ALL_PREFIXES(MoveMigratorMigrateTest, MigrateResumeFromMove);
   friend class BrowserDataMigratorResumeOnSignInTest;
   friend class BrowserDataMigratorResumeRestartInSession;
@@ -157,8 +154,8 @@
   // `SetupLacrosRemoveHardLinksFromAshDir()` as the next step.
   void OnPreMigrationCleanUp(PreMigrationCleanUpResult);
 
-  // Set up lacros user directory by copying `ItemType::kNeedCopy` items and
-  // creating hard links for `ItemType::kLacros` into it.
+  // Set up lacros user directory by copying `ItemType::kNeedCopy` items
+  // and also creating `First Run` file in Lacros user data dir.
   static bool SetupLacrosDir(
       const base::FilePath& original_profile_dir,
       std::unique_ptr<MigrationProgressTracker> progress_tracker,
@@ -168,14 +165,13 @@
   // `SetupLacrosRemoveHardLinksFromAshDir()` as the next step.
   void OnSetupLacrosDir(bool success);
 
-  // Removes hard links for `ItemType::kLacros` in the original profile
-  // directory. Hard links pointing to the same inode should have been created
-  // in `OnSetupLacrosDir()` inside lacros profile directory.
-  static bool RemoveHardLinksFromOriginalDir(
+  // Move `ItemType::kLacros` in the original profile
+  // directory to the temp dir.
+  static bool MoveLacrosItemsToNewDir(
       const base::FilePath& original_profile_dir);
 
-  // Called as a reply to `RemoveHardLinksFromOriginalDir()`.
-  void OnRemoveHardLinksFromOriginalDir(bool success);
+  // Called as a reply to `MoveLacrosItemsToNewDir()`.
+  void OnMoveLacrosItemsToNewDir(bool success);
 
   // Moves newly created `kMoveTmpDir` to `kLacrosDir` to complete the
   // migration.
diff --git a/chrome/browser/ash/crosapi/move_migrator_unittest.cc b/chrome/browser/ash/crosapi/move_migrator_unittest.cc
index 31a4c811..779d07e 100644
--- a/chrome/browser/ash/crosapi/move_migrator_unittest.cc
+++ b/chrome/browser/ash/crosapi/move_migrator_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ash/crosapi/move_migrator.h"
 
-#include <sys/stat.h>
-
 #include <memory>
 #include <string>
 
@@ -29,6 +27,7 @@
 namespace {
 
 constexpr char kBookmarksFilePath[] = "Bookmarks";   // lacros
+constexpr char kCookiesFilePath[] = "Cookies";       // lacros
 constexpr char kDownloadsFilePath[] = "Downloads";   // remain in ash
 constexpr char kLoginDataFilePath[] = "Login Data";  // need copy
 constexpr char kCacheFilePath[] = "Cache";           // deletable
@@ -43,6 +42,7 @@
 void SetUpProfileDirectory(const base::FilePath& path) {
   // Setup `path` as below.
   // |- Bookmarks/
+  // |- Cookies
   // |- Downloads/
   // |- Login Data/
   // |- Cache/
@@ -63,6 +63,9 @@
       base::WriteFile(path.Append(kBookmarksFilePath).Append(kDataFilePath),
                       kDataContent, kDataSize),
       kDataSize);
+  ASSERT_EQ(
+      base::WriteFile(path.Append(kCookiesFilePath), kDataContent, kDataSize),
+      kDataSize);
 
   ASSERT_TRUE(base::CreateDirectory(path.Append(kLoginDataFilePath)));
   ASSERT_EQ(
@@ -146,55 +149,39 @@
   // lacros dir.
   EXPECT_TRUE(base::PathExists(tmp_user_dir.Append(chrome::kFirstRunSentinel)));
   EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kLoginDataFilePath)));
-  EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kBookmarksFilePath)));
-
-  // Check that the lacros files exists as a hard link to the original file i.e.
-  // they point to the same inode. Note that directories are created.
-  const base::FilePath original_bookmarks_data_path =
-      original_profile_dir.Append(kBookmarksFilePath).Append(kDataFilePath);
-  const base::FilePath new_bookmarks_data_path =
-      tmp_profile_dir.Append(kBookmarksFilePath).Append(kDataFilePath);
-
-  struct stat st_1;
-  ASSERT_EQ(stat(original_bookmarks_data_path.value().c_str(), &st_1), 0);
-
-  struct stat st_2;
-  ASSERT_EQ(stat(new_bookmarks_data_path.value().c_str(), &st_2), 0);
-
-  EXPECT_EQ(st_1.st_ino, st_2.st_ino);
 }
 
-TEST(MoveMigratorTest, SetupLacrosDirFailIfNoWritePermForLacrosItem) {
+TEST(MoveMigratorTest, MoveLacrosItemsToNewDir) {
   base::ScopedTempDir scoped_temp_dir;
   ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
   const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
   SetUpProfileDirectory(original_profile_dir);
 
-  std::unique_ptr<MigrationProgressTracker> progress_tracker =
-      std::make_unique<FakeMigrationProgressTracker>();
-  scoped_refptr<browser_data_migrator_util::CancelFlag> cancel_flag =
-      base::MakeRefCounted<browser_data_migrator_util::CancelFlag>();
+  const base::FilePath tmp_profile_dir =
+      original_profile_dir.Append(browser_data_migrator_util::kMoveTmpDir)
+          .Append(browser_data_migrator_util::kLacrosProfilePath);
+
+  ASSERT_TRUE(base::CreateDirectory(tmp_profile_dir));
+  EXPECT_TRUE(MoveMigrator::MoveLacrosItemsToNewDir(original_profile_dir));
+
+  EXPECT_FALSE(
+      base::PathExists(original_profile_dir.Append(kBookmarksFilePath)));
+  EXPECT_FALSE(base::PathExists(original_profile_dir.Append(kCookiesFilePath)));
+  EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kBookmarksFilePath)));
+  EXPECT_TRUE(base::PathExists(tmp_profile_dir.Append(kCookiesFilePath)));
+}
+
+TEST(MoveMigratorTest, MoveLacrosItemsToNewDirFailIfNoWritePermForLacrosItem) {
+  base::ScopedTempDir scoped_temp_dir;
+  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
+  const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
+  SetUpProfileDirectory(original_profile_dir);
 
   // Remove write permission from a lacros item.
   base::SetPosixFilePermissions(original_profile_dir.Append(kBookmarksFilePath),
                                 0500);
 
-  EXPECT_FALSE(MoveMigrator::SetupLacrosDir(
-      original_profile_dir, std::move(progress_tracker), cancel_flag));
-}
-
-TEST(MoveMigratorTest, RemoveHardLinksFromOriginalDir) {
-  base::ScopedTempDir scoped_temp_dir;
-  ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
-  const base::FilePath original_profile_dir = scoped_temp_dir.GetPath();
-  SetUpProfileDirectory(original_profile_dir);
-
-  EXPECT_TRUE(
-      MoveMigrator::RemoveHardLinksFromOriginalDir(original_profile_dir));
-
-  // Check that lacros items are deleted.
-  EXPECT_FALSE(
-      base::PathExists(original_profile_dir.Append(kBookmarksFilePath)));
+  EXPECT_FALSE(MoveMigrator::MoveLacrosItemsToNewDir(original_profile_dir));
 }
 
 TEST(MoveMigratorTest, ResumeRequired) {
@@ -205,7 +192,7 @@
   EXPECT_FALSE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
 
   MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
-                              MoveMigrator::ResumeStep::kRemoveHardLinks);
+                              MoveMigrator::ResumeStep::kMoveLacrosItems);
   EXPECT_TRUE(MoveMigrator::ResumeRequired(&pref_service, user_id_hash));
 
   MoveMigrator::SetResumeStep(&pref_service, user_id_hash,
@@ -264,6 +251,7 @@
     // |- lacros/Default/
     //     |- Login Data
     //     |- Bookmarks
+    //     |- Cookies
 
     const base::FilePath new_user_dir =
         original_profile_dir_.Append(browser_data_migrator_util::kLacrosDir);
@@ -277,6 +265,10 @@
         base::PathExists(original_profile_dir_.Append(kBookmarksFilePath)));
     EXPECT_TRUE(base::PathExists(new_profile_dir.Append(kBookmarksFilePath)));
 
+    EXPECT_FALSE(
+        base::PathExists(original_profile_dir_.Append(kCookiesFilePath)));
+    EXPECT_TRUE(base::PathExists(new_profile_dir.Append(kCookiesFilePath)));
+
     EXPECT_TRUE(
         base::PathExists(original_profile_dir_.Append(kLoginDataFilePath)));
     EXPECT_TRUE(base::PathExists(new_profile_dir.Append(kLoginDataFilePath)));
@@ -318,12 +310,12 @@
   CheckProfileDirFinalState();
 }
 
-TEST_F(MoveMigratorMigrateTest, MigrateResumeFromRemoveHardLinks) {
+TEST_F(MoveMigratorMigrateTest, MigrateResumeFromMoveLacrosItems) {
   MoveMigrator::SetResumeStep(&pref_service_, user_id_hash_,
-                              MoveMigrator::ResumeStep::kRemoveHardLinks);
+                              MoveMigrator::ResumeStep::kMoveLacrosItems);
 
   // Setup `original_profile_dir_` as below.
-  // |- Bookmarks
+  // |- Cookies
   // |- Downloads
   // |- Login Data
   // |- move_migrator/First Run
@@ -346,9 +338,8 @@
   ASSERT_TRUE(base::CopyDirectory(
       original_profile_dir_.Append(kLoginDataFilePath),
       tmp_profile_dir.Append(kLoginDataFilePath), true /* recursive */));
-  ASSERT_TRUE(browser_data_migrator_util::CopyDirectoryByHardLinks(
-      original_profile_dir_.Append(kBookmarksFilePath),
-      tmp_profile_dir.Append(kBookmarksFilePath)));
+  ASSERT_TRUE(base::Move(original_profile_dir_.Append(kBookmarksFilePath),
+                         tmp_profile_dir.Append(kBookmarksFilePath)));
 
   migrator_->Migrate();
   run_loop_->Run();
@@ -372,6 +363,7 @@
   // |- move_migrator/Default/
   //     |- Login Data
   //     |- Bookmarks
+  //     |- Cookies
 
   const base::FilePath tmp_user_dir =
       original_profile_dir_.Append(browser_data_migrator_util::kMoveTmpDir);
@@ -388,11 +380,10 @@
   ASSERT_TRUE(base::CopyDirectory(
       original_profile_dir_.Append(kLoginDataFilePath),
       tmp_profile_dir.Append(kLoginDataFilePath), true /* recursive */));
-  ASSERT_TRUE(browser_data_migrator_util::CopyDirectoryByHardLinks(
-      original_profile_dir_.Append(kBookmarksFilePath),
-      tmp_profile_dir.Append(kBookmarksFilePath)));
-  ASSERT_TRUE(base::DeletePathRecursively(
-      original_profile_dir_.Append(kBookmarksFilePath)));
+  ASSERT_TRUE(base::Move(original_profile_dir_.Append(kBookmarksFilePath),
+                         tmp_profile_dir.Append(kBookmarksFilePath)));
+  ASSERT_TRUE(base::Move(original_profile_dir_.Append(kCookiesFilePath),
+                         tmp_profile_dir.Append(kCookiesFilePath)));
 
   migrator_->Migrate();
   run_loop_->Run();
diff --git a/chrome/browser/ash/dbus/vm/org.chromium.VmLaunchService.conf b/chrome/browser/ash/dbus/vm/org.chromium.VmLaunchService.conf
index 65dbaa0..0ad40a9 100644
--- a/chrome/browser/ash/dbus/vm/org.chromium.VmLaunchService.conf
+++ b/chrome/browser/ash/dbus/vm/org.chromium.VmLaunchService.conf
@@ -19,4 +19,10 @@
            send_interface="org.chromium.VmLaunchService"
            send_member="StopWaylandServer"/>
   </policy>
+
+  <policy user="root">
+    <allow send_destination="org.chromium.VmLaunchService"
+           send_interface="org.chromium.VmLaunchService"
+	   send_member="ProvideVmToken"/>
+  </policy>
 </busconfig>
diff --git a/chrome/browser/ash/dbus/vm/vm_launch_service_provider.cc b/chrome/browser/ash/dbus/vm/vm_launch_service_provider.cc
index 443d034..15b4ebc 100644
--- a/chrome/browser/ash/dbus/vm/vm_launch_service_provider.cc
+++ b/chrome/browser/ash/dbus/vm/vm_launch_service_provider.cc
@@ -6,10 +6,14 @@
 
 #include <dbus/dbus-protocol.h>
 #include <memory>
+#include <sstream>
 
 #include "base/bind.h"
 #include "base/logging.h"
+#include "chrome/browser/ash/borealis/borealis_app_launcher.h"
+#include "chrome/browser/ash/borealis/borealis_features.h"
 #include "chrome/browser/ash/borealis/borealis_service.h"
+#include "chrome/browser/ash/borealis/borealis_util.h"
 #include "chrome/browser/ash/borealis/borealis_wayland_interface.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -21,12 +25,49 @@
 namespace ash {
 
 namespace {
+
 void OnExported(const std::string& interface_name,
                 const std::string& method_name,
                 bool success) {
   LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "."
                           << method_name;
 }
+
+void OnTokenChecked(Profile* profile,
+                    dbus::MethodCall* method_call,
+                    dbus::ExportedObject::ResponseSender response_sender,
+                    bool launch,
+                    borealis::BorealisFeatures::AllowStatus new_allowed) {
+  // TODO(b/218403711): Remove these messages. These messages are shown to users
+  // of the Borealis Alpha based on the status of their device, however they are
+  // not translated, because this API is only a temporary measure put in place
+  // until borealis' installer UX is finalized.
+  if (new_allowed == borealis::BorealisFeatures::AllowStatus::kAllowed) {
+    if (launch) {
+      // When requested, setting the correct token should have the effect of
+      // running the client app, which will bring up the installer or launch the
+      // client as needed.
+      borealis::BorealisService::GetForProfile(profile)->AppLauncher().Launch(
+          borealis::kClientAppId, base::DoNothing());
+    }
+    std::unique_ptr<dbus::Response> response =
+        dbus::Response::FromMethodCall(method_call);
+    dbus::MessageWriter writer(response.get());
+    writer.AppendString(borealis::kInsertCoinSuccessMessage);
+    std::move(response_sender).Run(std::move(response));
+    return;
+  }
+  std::stringstream ss;
+  if (new_allowed == borealis::BorealisFeatures::AllowStatus::kIncorrectToken) {
+    ss << borealis::kInsertCoinRejectMessage;
+  } else {
+    ss << new_allowed;
+  }
+  std::move(response_sender)
+      .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
+                                               ss.str()));
+}
+
 }  // namespace
 
 VmLaunchServiceProvider::VmLaunchServiceProvider() = default;
@@ -48,6 +89,13 @@
       base::BindRepeating(&VmLaunchServiceProvider::StopWaylandServer,
                           weak_ptr_factory_.GetWeakPtr()),
       base::BindOnce(&OnExported));
+
+  exported_object->ExportMethod(
+      vm_tools::launch::kVmLaunchServiceInterface,
+      vm_tools::launch::kVmLaunchServiceProvideVmTokenMethod,
+      base::BindRepeating(&VmLaunchServiceProvider::ProvideVmToken,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&OnExported));
 }
 
 void VmLaunchServiceProvider::StartWaylandServer(
@@ -130,4 +178,34 @@
           method_call, DBUS_ERROR_NOT_SUPPORTED, "Not implemented."));
 }
 
+void VmLaunchServiceProvider::ProvideVmToken(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  std::string token;
+  dbus::MessageReader reader(method_call);
+  if (!reader.PopString(&token)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_INVALID_ARGS, "Token not provided"));
+    return;
+  }
+
+  bool launch;
+  if (!reader.PopBool(&launch))
+    launch = true;
+
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+  if (!profile) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, DBUS_ERROR_FAILED,
+            "Unable to determine primary profile"));
+    return;
+  }
+
+  borealis::BorealisService::GetForProfile(profile)->Features().SetVmToken(
+      token, base::BindOnce(&OnTokenChecked, profile, method_call,
+                            std::move(response_sender), launch));
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/dbus/vm/vm_launch_service_provider.h b/chrome/browser/ash/dbus/vm/vm_launch_service_provider.h
index eab18fac..f672ab9 100644
--- a/chrome/browser/ash/dbus/vm/vm_launch_service_provider.h
+++ b/chrome/browser/ash/dbus/vm/vm_launch_service_provider.h
@@ -42,6 +42,21 @@
   void StopWaylandServer(dbus::MethodCall* method_call,
                          dbus::ExportedObject::ResponseSender response_sender);
 
+  // Sets the token used by certain VMs to authorize their usage. This method
+  // expects a string for the token, and optionally a boolean to control whether
+  // we should install/launch on successfully setting the token, or just return.
+  //
+  // Example usage:
+  // dbus-send --print-reply --system --type=method_call                 \
+  //   --dest=org.chromium.VmLaunchService /org/chromium/VmLaunchService \
+  //   org.chromium.VmLaunchService.ProvideVmToken                       \
+  //   string:<TOKEN> boolean:<bool>
+  //
+  // TODO(b/218403711): This API is temporary, and will be removed. Criteria for
+  // its removal is documented in the linked bug.
+  void ProvideVmToken(dbus::MethodCall* method_call,
+                      dbus::ExportedObject::ResponseSender response_sender);
+
   base::WeakPtrFactory<VmLaunchServiceProvider> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/drive/drive_integration_service.cc b/chrome/browser/ash/drive/drive_integration_service.cc
index 8468a60..a71ae52 100644
--- a/chrome/browser/ash/drive/drive_integration_service.cc
+++ b/chrome/browser/ash/drive/drive_integration_service.cc
@@ -1304,8 +1304,13 @@
 void DriveIntegrationService::ToggleMirroring(
     bool enabled,
     drivefs::mojom::DriveFs::ToggleMirroringCallback callback) {
-  if (chromeos::features::IsDriveFsMirroringEnabled() &&
-      GetDriveFsInterface()) {
+  if (!chromeos::features::IsDriveFsMirroringEnabled()) {
+    std::move(callback).Run(
+        drivefs::mojom::MirrorSyncStatus::kFeatureNotEnabled);
+    return;
+  }
+
+  if (GetDriveFsInterface()) {
     GetDriveFsInterface()->ToggleMirroring(enabled, std::move(callback));
   }
 }
diff --git a/chrome/browser/ash/drive/drive_integration_service_browsertest.cc b/chrome/browser/ash/drive/drive_integration_service_browsertest.cc
index d0340e2f..516ef2a 100644
--- a/chrome/browser/ash/drive/drive_integration_service_browsertest.cc
+++ b/chrome/browser/ash/drive/drive_integration_service_browsertest.cc
@@ -298,6 +298,40 @@
   EXPECT_FALSE(drive_service->is_enabled());
 }
 
+IN_PROC_BROWSER_TEST_F(DriveIntegrationServiceBrowserTest,
+                       EnableMirrorSync_FeatureDisabled) {
+  auto* drive_service =
+      DriveIntegrationServiceFactory::FindForProfile(browser()->profile());
+
+  {
+    base::RunLoop run_loop;
+    auto quit_closure = run_loop.QuitClosure();
+    drive_service->ToggleMirroring(
+        /*enabled=*/true,
+        base::BindLambdaForTesting(
+            [quit_closure](drivefs::mojom::MirrorSyncStatus status) {
+              EXPECT_EQ(drivefs::mojom::MirrorSyncStatus::kFeatureNotEnabled,
+                        status);
+              quit_closure.Run();
+            }));
+    run_loop.Run();
+  }
+
+  {
+    base::RunLoop run_loop;
+    auto quit_closure = run_loop.QuitClosure();
+    drive_service->ToggleMirroring(
+        /*enabled=*/false,
+        base::BindLambdaForTesting(
+            [quit_closure](drivefs::mojom::MirrorSyncStatus status) {
+              EXPECT_EQ(drivefs::mojom::MirrorSyncStatus::kFeatureNotEnabled,
+                        status);
+              quit_closure.Run();
+            }));
+    run_loop.Run();
+  }
+}
+
 class DriveMirrorSyncStatusObserver : public DriveIntegrationServiceObserver {
  public:
   explicit DriveMirrorSyncStatusObserver(bool expected_status)
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index 0a6064c..67a5ab4 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -1627,8 +1627,10 @@
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     GuestOs, /* guest_os.js */
     FilesAppBrowserTest,
-    ::testing::Values(TestCase("fakesListed").EnableGuestOsFiles(),
-                      TestCase("mountGuestError").EnableGuestOsFiles(),
-                      TestCase("notListedWithoutFlag")));
+    ::testing::Values(
+        TestCase("fakesListed").EnableGuestOsFiles(),
+        TestCase("mountGuestError").EnableGuestOsFiles(),
+        TestCase("listUpdatedWhenGuestsChanged").EnableGuestOsFiles(),
+        TestCase("notListedWithoutFlag")));
 
 }  // namespace file_manager
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.cc
index d6803b1..f72a853 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.cc
@@ -31,7 +31,7 @@
 
 bool IsFeatureAllowed(content::BrowserContext* context) {
   return multidevice_setup::IsFeatureAllowed(
-      chromeos::multidevice_setup::mojom::Feature::kSmartLock,
+      multidevice_setup::mojom::Feature::kSmartLock,
       Profile::FromBrowserContext(context)->GetPrefs());
 }
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
index 418fe70..b5bf1926 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular.cc
@@ -55,13 +55,6 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 
 namespace ash {
-// TODO(https://crbug.com/1164001): remove when migrated to ash::
-namespace multidevice_setup {
-namespace mojom {
-using ::chromeos::multidevice_setup::mojom::Feature;
-using ::chromeos::multidevice_setup::mojom::FeatureState;
-}  // namespace mojom
-}  // namespace multidevice_setup
 
 namespace {
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
index b9049529..6d9d2da 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_service_regular_unittest.cc
@@ -205,11 +205,9 @@
 
   void SetIsEnabled(bool is_enabled) {
     fake_multidevice_setup_client_->SetFeatureState(
-        chromeos::multidevice_setup::mojom::Feature::kSmartLock,
-        is_enabled
-            ? chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser
-            : chromeos::multidevice_setup::mojom::FeatureState::
-                  kDisabledByUser);
+        multidevice_setup::mojom::Feature::kSmartLock,
+        is_enabled ? multidevice_setup::mojom::FeatureState::kEnabledByUser
+                   : multidevice_setup::mojom::FeatureState::kDisabledByUser);
   }
 
   void SetEasyUnlockAllowedPolicy(bool allowed) {
@@ -296,8 +294,8 @@
 TEST_F(EasyUnlockServiceRegularTest, NotAllowedWhenProhibited) {
   InitializeService(true /* should_initialize_all_dependencies */);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kSmartLock,
-      chromeos::multidevice_setup::mojom::FeatureState::kProhibitedByPolicy);
+      multidevice_setup::mojom::Feature::kSmartLock,
+      multidevice_setup::mojom::FeatureState::kProhibitedByPolicy);
 
   EXPECT_FALSE(easy_unlock_service_regular_->IsAllowed());
 }
diff --git a/chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics_unittest.cc b/chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics_unittest.cc
index 51a9caa6..c054564 100644
--- a/chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/smartlock_feature_usage_metrics_unittest.cc
@@ -15,8 +15,8 @@
 namespace ash {
 namespace {
 
-using chromeos::multidevice_setup::mojom::Feature;
-using chromeos::multidevice_setup::mojom::FeatureState;
+using multidevice_setup::mojom::Feature;
+using multidevice_setup::mojom::FeatureState;
 
 bool IsEligible1() {
   return true;
diff --git a/chrome/browser/ash/login/screens/multidevice_setup_screen.cc b/chrome/browser/ash/login/screens/multidevice_setup_screen.cc
index d6e5f81..1eee428 100644
--- a/chrome/browser/ash/login/screens/multidevice_setup_screen.cc
+++ b/chrome/browser/ash/login/screens/multidevice_setup_screen.cc
@@ -71,8 +71,7 @@
     return true;
   }
   if (setup_client_->GetHostStatus().first !=
-      chromeos::multidevice_setup::mojom::HostStatus::
-          kEligibleHostExistsButNoHostSet) {
+      multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet) {
     VLOG(1) << "Skipping MultiDevice setup screen; host status: "
             << setup_client_->GetHostStatus().first;
     exit_callback_.Run(Result::NOT_APPLICABLE);
diff --git a/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc b/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc
index eba79b74..c5d1bea 100644
--- a/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/multidevice_setup_screen_browsertest.cc
@@ -22,11 +22,6 @@
 
 namespace ash {
 
-// TODO(https://crbug.com/1164001): remove when migrated to ash::
-namespace multidevice_setup {
-namespace mojom = ::chromeos::multidevice_setup::mojom;
-}
-
 constexpr test::UIPath kMultideviceSetupPath = {"multidevice-setup-screen",
                                                 "multideviceSetup"};
 
diff --git a/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc b/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
index 2772fbc7..e93de16a 100644
--- a/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
+++ b/chrome/browser/ash/multidevice_setup/multidevice_setup_client_factory.cc
@@ -23,10 +23,6 @@
 namespace ash {
 namespace multidevice_setup {
 
-// TODO(https://crbug.com/1164001): remove when
-// chromeos/services/multidevice_setup is migrated.
-namespace mojom = ::chromeos::multidevice_setup::mojom;
-
 namespace {
 
 bool IsAllowedByPolicy(content::BrowserContext* context) {
diff --git a/chrome/browser/ash/network_change_manager_client_browsertest.cc b/chrome/browser/ash/network_change_manager_client_browsertest.cc
index a3b4f1b9..be5dfb2 100644
--- a/chrome/browser/ash/network_change_manager_client_browsertest.cc
+++ b/chrome/browser/ash/network_change_manager_client_browsertest.cc
@@ -158,7 +158,6 @@
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
-  IgnoreNetworkServiceCrashes();
   network_service_test->SimulateCrash();
 
   service_client()->AddService("wifi", "wifi", "wifi", shill::kTypeWifi,
diff --git a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
index 4c2af09..5d07ade 100644
--- a/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
+++ b/chrome/browser/ash/phonehub/browser_tabs_model_provider_impl_unittest.cc
@@ -129,10 +129,9 @@
   }
 
   void SetPiiFreeName(const std::string& pii_free_name) {
-    fake_multidevice_setup_client_.SetHostStatusWithDevice(
-        std::make_pair(chromeos::multidevice_setup::mojom::HostStatus::
-                           kEligibleHostExistsButNoHostSet,
-                       CreatePhoneDevice(/*pii_name=*/pii_free_name)));
+    fake_multidevice_setup_client_.SetHostStatusWithDevice(std::make_pair(
+        multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet,
+        CreatePhoneDevice(/*pii_name=*/pii_free_name)));
   }
 
   base::CallbackListSubscription MockSubscribeToForeignSessionsChanged(
diff --git a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
index 5b732a9..74c702c0 100644
--- a/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
+++ b/chrome/browser/ash/phonehub/phone_hub_manager_factory.cc
@@ -41,8 +41,7 @@
 
 bool IsProhibitedByPolicy(Profile* profile) {
   return !multidevice_setup::IsFeatureAllowed(
-      chromeos::multidevice_setup::mojom::Feature::kPhoneHub,
-      profile->GetPrefs());
+      multidevice_setup::mojom::Feature::kPhoneHub, profile->GetPrefs());
 }
 
 bool IsLoggedInAsPrimaryUser(Profile* profile) {
diff --git a/chrome/browser/ash/tether/tether_service.cc b/chrome/browser/ash/tether/tether_service.cc
index d4c90500..a6bdaff39 100644
--- a/chrome/browser/ash/tether/tether_service.cc
+++ b/chrome/browser/ash/tether/tether_service.cc
@@ -310,8 +310,8 @@
 
   if (is_enabled != was_pref_enabled) {
     multidevice_setup_client_->SetFeatureEnabledState(
-        chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-        is_enabled, absl::nullopt /* auth_token */, base::DoNothing());
+        multidevice_setup::mojom::Feature::kInstantTethering, is_enabled,
+        absl::nullopt /* auth_token */, base::DoNothing());
   } else {
     UpdateTetherTechnologyState();
   }
@@ -340,19 +340,18 @@
 void TetherService::OnFeatureStatesChanged(
     const chromeos::multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
         feature_states_map) {
-  const chromeos::multidevice_setup::mojom::FeatureState new_state =
+  const multidevice_setup::mojom::FeatureState new_state =
       feature_states_map
-          .find(chromeos::multidevice_setup::mojom::Feature::kInstantTethering)
+          .find(multidevice_setup::mojom::Feature::kInstantTethering)
           ->second;
 
   // If the feature changed from enabled to disabled or vice-versa, log the
   // associated metric.
-  if (new_state ==
-          chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser &&
+  if (new_state == multidevice_setup::mojom::FeatureState::kEnabledByUser &&
       previous_feature_state_ == TetherFeatureState::USER_PREFERENCE_DISABLED) {
     LogUserPreferenceChanged(true /* is_now_enabled */);
-  } else if (new_state == chromeos::multidevice_setup::mojom::FeatureState::
-                              kDisabledByUser &&
+  } else if (new_state ==
+                 multidevice_setup::mojom::FeatureState::kDisabledByUser &&
              previous_feature_state_ == TetherFeatureState::ENABLED) {
     LogUserPreferenceChanged(false /* is_now_enabled */);
   }
@@ -527,37 +526,34 @@
   if (!IsBluetoothPowered())
     return BLUETOOTH_DISABLED;
 
-  chromeos::multidevice_setup::mojom::FeatureState tether_multidevice_state =
+  multidevice_setup::mojom::FeatureState tether_multidevice_state =
       multidevice_setup_client_->GetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kInstantTethering);
+          multidevice_setup::mojom::Feature::kInstantTethering);
   switch (tether_multidevice_state) {
-    case chromeos::multidevice_setup::mojom::FeatureState::kProhibitedByPolicy:
+    case multidevice_setup::mojom::FeatureState::kProhibitedByPolicy:
       return PROHIBITED;
-    case chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser:
+    case multidevice_setup::mojom::FeatureState::kDisabledByUser:
       return USER_PREFERENCE_DISABLED;
-    case chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser:
+    case multidevice_setup::mojom::FeatureState::kEnabledByUser:
       return ENABLED;
-    case chromeos::multidevice_setup::mojom::FeatureState::
-        kUnavailableSuiteDisabled:
+    case multidevice_setup::mojom::FeatureState::kUnavailableSuiteDisabled:
       return BETTER_TOGETHER_SUITE_DISABLED;
-    case chromeos::multidevice_setup::mojom::FeatureState::
-        kUnavailableNoVerifiedHost:
+    case multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost:
       // Note that because of the early return above after
       // !HasSyncedTetherHosts, if this point is hit, there are synced tether
       // hosts available, but the multidevice state is unverified.
       [[fallthrough]];
-    case chromeos::multidevice_setup::mojom::FeatureState::
+    case multidevice_setup::mojom::FeatureState::
         kUnavailableNoVerifiedHost_ClientNotReady:
       [[fallthrough]];
-    case chromeos::multidevice_setup::mojom::FeatureState::
-        kNotSupportedByChromebook:
+    case multidevice_setup::mojom::FeatureState::kNotSupportedByChromebook:
       // CryptAuth may not yet know that this device supports
       // MAGIC_TETHER_CLIENT (and the local device metadata is reflecting
       // that). This should be resolved shortly once DeviceReenroller realizes
       // reconciles the discrepancy. For now, fall through to mark as
       // unavailable.
       [[fallthrough]];
-    case chromeos::multidevice_setup::mojom::FeatureState::kNotSupportedByPhone:
+    case multidevice_setup::mojom::FeatureState::kNotSupportedByPhone:
       return NO_AVAILABLE_HOSTS;
     default:
       // Other FeatureStates:
diff --git a/chrome/browser/ash/tether/tether_service.h b/chrome/browser/ash/tether/tether_service.h
index ab5dcc2..3b12c792 100644
--- a/chrome/browser/ash/tether/tether_service.h
+++ b/chrome/browser/ash/tether/tether_service.h
@@ -248,8 +248,8 @@
 
   bool is_adapter_being_fetched_ = false;
 
-  chromeos::multidevice_setup::mojom::HostStatus host_status_ =
-      chromeos::multidevice_setup::mojom::HostStatus::kNoEligibleHosts;
+  multidevice_setup::mojom::HostStatus host_status_ =
+      multidevice_setup::mojom::HostStatus::kNoEligibleHosts;
 
   // The first report of TetherFeatureState::BLE_NOT_PRESENT is usually
   // incorrect and hence is a false positive. This property tracks if the first
diff --git a/chrome/browser/ash/tether/tether_service_factory.cc b/chrome/browser/ash/tether/tether_service_factory.cc
index dbf2272c..faf1634 100644
--- a/chrome/browser/ash/tether/tether_service_factory.cc
+++ b/chrome/browser/ash/tether/tether_service_factory.cc
@@ -30,7 +30,7 @@
 
 bool IsFeatureAllowed(content::BrowserContext* context) {
   return chromeos::multidevice_setup::IsFeatureAllowed(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::Feature::kInstantTethering,
       Profile::FromBrowserContext(context)->GetPrefs());
 }
 
diff --git a/chrome/browser/ash/tether/tether_service_unittest.cc b/chrome/browser/ash/tether/tether_service_unittest.cc
index 9fdf207..f93225d0 100644
--- a/chrome/browser/ash/tether/tether_service_unittest.cc
+++ b/chrome/browser/ash/tether/tether_service_unittest.cc
@@ -272,7 +272,7 @@
   // chromeos::multidevice_setup::MultiDeviceSetupClientImpl::Factory:
   std::unique_ptr<chromeos::multidevice_setup::MultiDeviceSetupClient>
   CreateInstance(
-      mojo::PendingRemote<chromeos::multidevice_setup::mojom::MultiDeviceSetup>)
+      mojo::PendingRemote<multidevice_setup::mojom::MultiDeviceSetup>)
       override {
     auto fake_multidevice_setup_client = std::make_unique<
         chromeos::multidevice_setup::FakeMultiDeviceSetupClient>();
@@ -339,7 +339,7 @@
     chromeos::multidevice_setup::MultiDeviceSetupClientImpl::Factory::
         SetFactoryForTesting(fake_multidevice_setup_client_impl_factory_.get());
     initial_feature_state_ =
-        chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser;
+        multidevice_setup::mojom::FeatureState::kEnabledByUser;
 
     fake_enrollment_manager_ = std::make_unique<
         chromeos::device_sync::FakeCryptAuthEnrollmentManager>();
@@ -415,7 +415,7 @@
 
   void CreateTetherService() {
     fake_multidevice_setup_client_->SetFeatureState(
-        chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
+        multidevice_setup::mojom::Feature::kInstantTethering,
         initial_feature_state_);
 
     tether_service_ = base::WrapUnique(new TestTetherService(
@@ -554,7 +554,7 @@
   std::unique_ptr<chromeos::device_sync::FakeCryptAuthEnrollmentManager>
       fake_enrollment_manager_;
 
-  chromeos::multidevice_setup::mojom::FeatureState initial_feature_state_;
+  multidevice_setup::mojom::FeatureState initial_feature_state_;
 
   scoped_refptr<device::MockBluetoothAdapter> mock_adapter_;
   bool is_adapter_present_;
@@ -672,8 +672,8 @@
 TEST_F(TetherServiceTest,
        TestMultiDeviceSetupClientInitiallyHasNoVerifiedHost) {
   fake_tether_host_fetcher_factory_->SetNoInitialDevices();
-  initial_feature_state_ = chromeos::multidevice_setup::mojom::FeatureState::
-      kUnavailableNoVerifiedHost;
+  initial_feature_state_ =
+      multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost;
 
   CreateTetherService();
 
@@ -686,8 +686,8 @@
   fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts(
       test_devices_);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kEnabledByUser);
 
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
@@ -708,9 +708,8 @@
 
   fake_tether_host_fetcher_factory_->last_created()->set_tether_hosts({});
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::
-          kUnavailableNoVerifiedHost);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kUnavailableNoVerifiedHost);
 
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
@@ -728,8 +727,8 @@
 }
 
 TEST_F(TetherServiceTest, TestBetterTogetherSuiteInitiallyDisabled) {
-  initial_feature_state_ = chromeos::multidevice_setup::mojom::FeatureState::
-      kUnavailableSuiteDisabled;
+  initial_feature_state_ =
+      multidevice_setup::mojom::FeatureState::kUnavailableSuiteDisabled;
 
   CreateTetherService();
 
@@ -740,8 +739,8 @@
   VerifyTetherActiveStatus(false /* expected_active */);
 
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kEnabledByUser);
 
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
@@ -761,9 +760,8 @@
   VerifyTetherActiveStatus(true /* expected_active */);
 
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::
-          kUnavailableSuiteDisabled);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kUnavailableSuiteDisabled);
 
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_UNAVAILABLE,
@@ -848,9 +846,8 @@
   ASSERT_TRUE(tether_service);
 
   fake_multidevice_setup_client_impl_factory_->fake_multidevice_setup_client()
-      ->SetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-          chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      ->SetFeatureState(multidevice_setup::mojom::Feature::kInstantTethering,
+                        multidevice_setup::mojom::FeatureState::kEnabledByUser);
 
   base::RunLoop().RunUntilIdle();
   tether_service->Shutdown();
@@ -1147,7 +1144,7 @@
 TEST_F(TetherServiceTest, TestUserPrefChangesViaFeatureStateChange) {
   // Start with the feature disabled.
   initial_feature_state_ =
-      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser;
+      multidevice_setup::mojom::FeatureState::kDisabledByUser;
   profile_->GetPrefs()->SetBoolean(
       chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);
   CreateTetherService();
@@ -1156,8 +1153,8 @@
   profile_->GetPrefs()->SetBoolean(
       chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, true);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kEnabledByUser);
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
@@ -1172,8 +1169,8 @@
   profile_->GetPrefs()->SetBoolean(
       chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kDisabledByUser);
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
       network_state_handler()->GetTechnologyState(
@@ -1190,8 +1187,8 @@
   profile_->GetPrefs()->SetBoolean(
       chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, true);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kEnabledByUser);
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
@@ -1215,14 +1212,14 @@
 
   SetTetherTechnologyStateEnabled(false);
   fake_multidevice_setup_client_->InvokePendingSetFeatureEnabledStateCallback(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::Feature::kInstantTethering,
       false /* expected_enabled */, absl::nullopt /* expected_auth_token */,
       true /* success */);
   profile_->GetPrefs()->SetBoolean(
       chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, false);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kDisabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kDisabledByUser);
   EXPECT_EQ(
       chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_AVAILABLE,
       network_state_handler()->GetTechnologyState(
@@ -1234,14 +1231,14 @@
 
   SetTetherTechnologyStateEnabled(true);
   fake_multidevice_setup_client_->InvokePendingSetFeatureEnabledStateCallback(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::Feature::kInstantTethering,
       true /* expected_enabled */, absl::nullopt /* expected_auth_token */,
       false /* success */);
   profile_->GetPrefs()->SetBoolean(
       chromeos::multidevice_setup::kInstantTetheringEnabledPrefName, true);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      multidevice_setup::mojom::Feature::kInstantTethering,
+      multidevice_setup::mojom::FeatureState::kEnabledByUser);
   EXPECT_EQ(chromeos::NetworkStateHandler::TechnologyState::TECHNOLOGY_ENABLED,
             network_state_handler()->GetTechnologyState(
                 chromeos::NetworkTypePattern::Tether()));
diff --git a/chrome/browser/ash/web_applications/eche_app_info.cc b/chrome/browser/ash/web_applications/eche_app_info.cc
index c6e01aee..8cdf4904 100644
--- a/chrome/browser/ash/web_applications/eche_app_info.cc
+++ b/chrome/browser/ash/web_applications/eche_app_info.cc
@@ -63,7 +63,7 @@
 }
 
 bool EcheSystemAppDelegate::ShouldAllowResize() const {
-  return base::FeatureList::IsEnabled(chromeos::features::kEcheSWAResizing);
+  return false;
 }
 
 bool EcheSystemAppDelegate::ShouldAllowMaximize() const {
diff --git a/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
index df23034..574b244 100644
--- a/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/eche_app_integration_browsertest.cc
@@ -141,29 +141,3 @@
 
 INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
     EcheAppIntegrationTest);
-
-class EcheAppEnableResizingTest : public SystemWebAppIntegrationTest {
- public:
-  EcheAppEnableResizingTest() {
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{chromeos::features::kEcheSWA,
-                              chromeos::features::kEcheSWAResizing},
-        /*disabled_features=*/{});
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_P(EcheAppEnableResizingTest,
-                       WindowResizeableAfterEnableEcheSWAResizingFlag) {
-  WaitForTestSystemAppInstall();
-  Browser* browser;
-  LaunchApp(web_app::SystemAppType::ECHE, &browser);
-  BrowserView* const browser_view =
-      BrowserView::GetBrowserViewForBrowser(browser);
-  EXPECT_TRUE(browser_view->CanResize());
-}
-
-INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
-    EcheAppEnableResizingTest);
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index beb09c9..36cfb41 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -935,7 +935,7 @@
       chromeos::CrostiniUpgraderUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
-      chromeos::multidevice_setup::mojom::MultiDeviceSetup, chromeos::OobeUI,
+      ash::multidevice_setup::mojom::MultiDeviceSetup, chromeos::OobeUI,
       ash::multidevice::ProximityAuthUI,
       chromeos::multidevice_setup::MultiDeviceSetupDialogUI>(map);
 
@@ -944,7 +944,7 @@
       map);
 
   RegisterWebUIControllerInterfaceBinder<
-      chromeos::multidevice_setup::mojom::PrivilegedHostDeviceSetter,
+      ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter,
       chromeos::OobeUI>(map);
 
   RegisterWebUIControllerInterfaceBinder<
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 6dc388cc..65f6095 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -55,7 +55,6 @@
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/active_use_util.h"
 #include "chrome/browser/after_startup_task_utils.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_impl.h"
@@ -221,6 +220,7 @@
 #include "ash/components/arc/metrics/stability_metrics_manager.h"
 #include "ash/components/settings/cros_settings_names.h"
 #include "ash/constants/ash_switches.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
 #include "chrome/browser/ash/settings/hardware_data_usage_controller.h"
 #include "chrome/browser/ash/settings/stats_reporting_controller.h"
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.cc b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
index 469bda7..2ecd1adc 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.cc
@@ -10,6 +10,7 @@
 #include <memory>
 #include <set>
 #include <utility>
+#include <vector>
 
 #include "ash/components/arc/arc_prefs.h"
 #include "ash/components/disks/disk.h"
@@ -38,6 +39,7 @@
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
 #include "chrome/browser/ash/login/lock/screen_locker.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_features.h"
@@ -584,6 +586,12 @@
       chromeos::PowerManagerClient::Get();
   power_manager_client->RemoveObserver(device_event_router_.get());
 
+  if (base::FeatureList::IsEnabled(chromeos::features::kGuestOsFiles)) {
+    auto* registry = guest_os::GuestOsService::GetForProfile(profile_)
+                         ->MountProviderRegistry();
+    registry->RemoveObserver(this);
+  }
+
   profile_ = nullptr;
 }
 
@@ -676,6 +684,12 @@
   ash::TabletMode* tablet_mode = ash::TabletMode::Get();
   if (tablet_mode)
     tablet_mode->AddObserver(this);
+
+  if (base::FeatureList::IsEnabled(chromeos::features::kGuestOsFiles)) {
+    auto* registry = guest_os::GuestOsService::GetForProfile(profile_)
+                         ->MountProviderRegistry();
+    registry->AddObserver(this);
+  }
 }
 
 // File watch setup routines.
@@ -1287,5 +1301,32 @@
   // notification.
   notification_manager_->HandleIOTaskProgress(status);
 }
+void EventRouter::OnRegistered(guest_os::GuestOsMountProviderRegistry::Id id,
+                               guest_os::GuestOsMountProvider* provider) {
+  OnMountableGuestsChanged();
+}
+
+void EventRouter::OnUnregistered(
+    guest_os::GuestOsMountProviderRegistry::Id id) {
+  OnMountableGuestsChanged();
+}
+
+void EventRouter::OnMountableGuestsChanged() {
+  auto* registry = guest_os::GuestOsService::GetForProfile(profile_)
+                       ->MountProviderRegistry();
+  std::vector<file_manager_private::MountableGuest> guests;
+  for (const auto id : registry->List()) {
+    file_manager_private::MountableGuest guest;
+    auto* provider = registry->Get(id);
+    guest.id = id;
+    guest.display_name = provider->DisplayName();
+    guests.push_back(std::move(guest));
+  }
+  BroadcastEvent(
+      profile_,
+      extensions::events::FILE_MANAGER_PRIVATE_ON_IO_TASK_PROGRESS_STATUS,
+      file_manager_private::OnMountableGuestsChanged::kEventName,
+      file_manager_private::OnMountableGuestsChanged::Create(guests));
+}
 
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/extensions/file_manager/event_router.h b/chrome/browser/chromeos/extensions/file_manager/event_router.h
index 8692aeafa..12b1bc1 100644
--- a/chrome/browser/chromeos/extensions/file_manager/event_router.h
+++ b/chrome/browser/chromeos/extensions/file_manager/event_router.h
@@ -24,6 +24,8 @@
 #include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/ash/file_manager/volume_manager_observer.h"
 #include "chrome/browser/ash/guest_os/guest_os_share_path.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/chromeos/extensions/file_manager/device_event_router.h"
 #include "chrome/browser/chromeos/extensions/file_manager/drivefs_event_router.h"
@@ -57,7 +59,8 @@
       public drive::DriveIntegrationServiceObserver,
       public guest_os::GuestOsSharePath::Observer,
       public ash::TabletModeObserver,
-      public file_manager::io_task::IOTaskController::Observer {
+      public file_manager::io_task::IOTaskController::Observer,
+      public guest_os::GuestOsMountProviderRegistry::Observer {
  public:
   using DispatchDirectoryChangeEventImplCallback =
       base::RepeatingCallback<void(const base::FilePath& virtual_path,
@@ -195,6 +198,11 @@
   // IOTaskController::Observer:
   void OnIOTaskStatus(const io_task::ProgressStatus& status) override;
 
+  // guest_os::GuestOsMountProviderRegistry::Observer overrides.
+  void OnRegistered(guest_os::GuestOsMountProviderRegistry::Id id,
+                    guest_os::GuestOsMountProvider* provider) override;
+  void OnUnregistered(guest_os::GuestOsMountProviderRegistry::Id id) override;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(EventRouterTest, PopulateCrostiniEvent);
 
@@ -262,6 +270,9 @@
       const drivefs::mojom::DialogReason& reason,
       base::OnceCallback<void(drivefs::mojom::DialogResult)> callback);
 
+  // Called to refresh the list of guests and broadcast it.
+  void OnMountableGuestsChanged();
+
   base::Time last_copy_progress_event_;
 
   std::map<base::FilePath, std::unique_ptr<FileWatcher>> file_watchers_;
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_guest_os.h b/chrome/browser/chromeos/extensions/file_manager/private_api_guest_os.h
index 99b8a2d..bbce147 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_guest_os.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_guest_os.h
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// This file provides Guest OS API functions, which don't belong to
-// other files.
+// This file provides Guest OS API functions
 
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_GUEST_OS_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_GUEST_OS_H_
diff --git a/chrome/browser/download/background_download_service_factory.cc b/chrome/browser/download/background_download_service_factory.cc
index caa7d8f..8c05e591 100644
--- a/chrome/browser/download/background_download_service_factory.cc
+++ b/chrome/browser/download/background_download_service_factory.cc
@@ -17,7 +17,6 @@
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/plugin_vm/plugin_vm_image_download_client.h"
 #include "chrome/browser/download/deferred_client_wrapper.h"
 #include "chrome/browser/download/download_manager_utils.h"
 #include "chrome/browser/download/simple_download_manager_coordinator_factory.h"
@@ -52,6 +51,10 @@
 #include "chrome/browser/download/android/service/download_task_scheduler.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/plugin_vm/plugin_vm_image_download_client.h"
+#endif
+
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
 #include "chrome/browser/offline_pages/prefetch/offline_prefetch_download_client.h"
 #endif
diff --git a/chrome/browser/download/download_prefs_unittest.cc b/chrome/browser/download/download_prefs_unittest.cc
index 1d845ba..12bc295 100644
--- a/chrome/browser/download/download_prefs_unittest.cc
+++ b/chrome/browser/download/download_prefs_unittest.cc
@@ -451,9 +451,10 @@
   const base::FilePath drivefs_dir = base::FilePath(
       "/media/fuse/drivefs-" + base::MD5String(drivefs_profile_salt + "-" +
                                                account_id.GetAccountIdKey()));
+  const base::FilePath ash_resources_dir = base::FilePath("/opt/google/chrome");
   chrome::SetLacrosDefaultPaths(documents_path, default_dir, drivefs_dir,
                                 removable_media_dir, android_files_dir,
-                                linux_files_dir);
+                                linux_files_dir, ash_resources_dir);
 #endif
 
   // Test a valid subdirectory of downloads.
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
index 7da4d5e..b68d6ca 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc
@@ -16,7 +16,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/media/webrtc/capture_policy_utils.h"
-#include "chrome/browser/media/webrtc/desktop_media_list_ash.h"
 #include "chrome/browser/media/webrtc/desktop_media_picker_factory_impl.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/native_desktop_media_list.h"
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
index 072a48ca..26738cf 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.cc
@@ -44,7 +44,6 @@
 #include "components/translate/core/browser/translate_download_manager.h"
 #include "components/translate/core/browser/translate_prefs.h"
 #include "third_party/icu/source/i18n/unicode/coll.h"
-#include "ui/base/ime/ash/input_method_descriptor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_collator.h"
 
@@ -53,6 +52,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/ime/ash/component_extension_ime_manager.h"
 #include "ui/base/ime/ash/extension_ime_util.h"
+#include "ui/base/ime/ash/input_method_descriptor.h"
 #include "ui/base/ime/ash/input_method_manager.h"
 #include "ui/base/ime/ash/input_method_util.h"
 #endif
diff --git a/chrome/browser/extensions/forced_extensions/OWNERS b/chrome/browser/extensions/forced_extensions/OWNERS
index aa46929..449c761 100644
--- a/chrome/browser/extensions/forced_extensions/OWNERS
+++ b/chrome/browser/extensions/forced_extensions/OWNERS
@@ -2,4 +2,3 @@
 
 burunduk@chromium.org
 poromov@chromium.org
-swapnilgupta@google.com
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 39abb5e..04f113be 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -504,6 +504,11 @@
     "expiry_milestone": 105
   },
   {
+    "name": "autofill-password-rich-iph",
+    "owners": [ "christianxu", "bling-flags@google.com" ],
+    "expiry_milestone": 105
+  },
+  {
     "name": "autofill-save-card-dismiss-on-navigation",
     "owners": [ "sczs", "bling-flags@google.com" ],
     "expiry_milestone": 82
@@ -1476,11 +1481,6 @@
     "expiry_milestone": 105
   },
   {
-    "name": "eche-swa-resizing",
-    "owners": [ "dhnishi" ],
-    "expiry_milestone": 98
-  },
-  {
     "name": "edit-context",
     "owners": [ "shihken@microsoft.com", "snianu@microsoft.com", "yosin" ],
     "expiry_milestone": 104
@@ -3038,7 +3038,7 @@
   {
     "name": "enable-web-bluetooth-new-permissions-backend",
     "owners": [ "web-bluetooth@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "enable-web-filter-interstitial-refresh",
@@ -5703,7 +5703,7 @@
   {
     "name": "terminal-ssh",
     "owners": [ "lxj@google.com", "joelhockey", "//chrome/browser/ash/guest_os/OWNERS" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "terminal-tmux-integration",
@@ -6081,11 +6081,6 @@
     "expiry_milestone": 98
   },
   {
-    "name": "web-bluetooth-request-larger-mtu",
-    "owners": [ "web-bluetooth@google.com" ],
-    "expiry_milestone": 100
-  },
-  {
     "name": "web-bundles",
     "owners": [ "//content/browser/web_package/OWNERS" ],
     "expiry_milestone": 105
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 9116fdb..84c75c0 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3670,12 +3670,6 @@
     "Enables showing the voice search button in the top toolbar. Enabling "
     "Adaptive Button overrides this.";
 
-const char kWebBluetoothRequestLargerMtuName[] =
-    "Request larger MTU for Web Bluetooth";
-const char kWebBluetoothRequestLargerMtuDescription[] =
-    "Controls whether Web Bluetooth should request for a larger ATT MTU so "
-    "that more information can be exchanged per transmission.";
-
 const char kWebFeedName[] = "Web Feed";
 const char kWebFeedDescription[] =
     "Allows users to keep up with and consume web content.";
@@ -4733,10 +4727,6 @@
 const char kEcheCustomWidgetDescription[] =
     "Move the Eche App into a custom widget.";
 
-const char kEcheSWAResizingName[] = "Allow resizing Eche App.";
-const char kEcheSWAResizingDescription[] =
-    "Enable a naive resize for the Eche window";
-
 const char kEcheSWADebugModeName[] = "Enable Eche Debug Mode";
 const char kEcheSWADebugModeDescription[] = "Enable the Debug Mode of the Eche";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f9614164..cecbd1e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2095,9 +2095,6 @@
 extern const char kVoiceButtonInTopToolbarName[];
 extern const char kVoiceButtonInTopToolbarDescription[];
 
-extern const char kWebBluetoothRequestLargerMtuName[];
-extern const char kWebBluetoothRequestLargerMtuDescription[];
-
 extern const char kWebFeedName[];
 extern const char kWebFeedDescription[];
 
@@ -2712,9 +2709,6 @@
 extern const char kEcheCustomWidgetName[];
 extern const char kEcheCustomWidgetDescription[];
 
-extern const char kEcheSWAResizingName[];
-extern const char kEcheSWAResizingDescription[];
-
 extern const char kEcheSWADebugModeName[];
 extern const char kEcheSWADebugModeDescription[];
 
diff --git a/chrome/browser/media/webrtc/desktop_media_picker_controller.cc b/chrome/browser/media/webrtc/desktop_media_picker_controller.cc
index 33363eb2..ed593ea5 100644
--- a/chrome/browser/media/webrtc/desktop_media_picker_controller.cc
+++ b/chrome/browser/media/webrtc/desktop_media_picker_controller.cc
@@ -14,7 +14,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/media/webrtc/capture_policy_utils.h"
-#include "chrome/browser/media/webrtc/desktop_media_list_ash.h"
 #include "chrome/browser/media/webrtc/desktop_media_picker.h"
 #include "chrome/browser/media/webrtc/desktop_media_picker_factory_impl.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
diff --git a/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc b/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc
index 5d036b5d..02e12fd 100644
--- a/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc
+++ b/chrome/browser/media/webrtc/desktop_media_picker_factory_impl.cc
@@ -9,11 +9,14 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/media/webrtc/current_tab_desktop_media_list.h"
-#include "chrome/browser/media/webrtc/desktop_media_list_ash.h"
 #include "chrome/browser/media/webrtc/native_desktop_media_list.h"
 #include "chrome/browser/media/webrtc/tab_desktop_media_list.h"
 #include "content/public/browser/desktop_capture.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/media/webrtc/desktop_media_list_ash.h"
+#endif
+
 DesktopMediaPickerFactoryImpl::DesktopMediaPickerFactoryImpl() = default;
 
 DesktopMediaPickerFactoryImpl::~DesktopMediaPickerFactoryImpl() = default;
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.cc b/chrome/browser/metrics/chrome_metrics_service_accessor.cc
index 967fc42..cb12d9c 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.cc
@@ -7,7 +7,6 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/per_user_state_manager_chromeos.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/pref_names.h"
 #include "components/metrics/metrics_service.h"
@@ -17,6 +16,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
+#include "chrome/browser/metrics/per_user_state_manager_chromeos.h"
 // nogncheck needed for Lacros builds since header checker does not understand
 // preprocessor.
 #include "components/metrics/structured/neutrino_logging.h"  // nogncheck
diff --git a/chrome/browser/metrics/chromeos_metrics_provider.cc b/chrome/browser/metrics/chromeos_metrics_provider.cc
index e092e401..17ac7831 100644
--- a/chrome/browser/metrics/chromeos_metrics_provider.cc
+++ b/chrome/browser/metrics/chromeos_metrics_provider.cc
@@ -75,9 +75,9 @@
 bool IsFeatureEnabled(
     const chromeos::multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
         feature_states_map,
-    chromeos::multidevice_setup::mojom::Feature feature) {
+    ash::multidevice_setup::mojom::Feature feature) {
   return feature_states_map.find(feature)->second ==
-         chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser;
+         ash::multidevice_setup::mojom::FeatureState::kEnabledByUser;
 }
 
 }  // namespace
@@ -277,7 +277,7 @@
   const chromeos::multidevice_setup::MultiDeviceSetupClient::
       HostStatusWithDevice& host_status_with_device = client->GetHostStatus();
   if (host_status_with_device.first !=
-      chromeos::multidevice_setup::mojom::HostStatus::kHostVerified) {
+      ash::multidevice_setup::mojom::HostStatus::kHostVerified) {
     return;
   }
 
@@ -290,14 +290,12 @@
   const chromeos::multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
       feature_states_map = client->GetFeatureStates();
   linked_android_phone_data->set_is_smartlock_enabled(IsFeatureEnabled(
-      feature_states_map,
-      chromeos::multidevice_setup::mojom::Feature::kSmartLock));
+      feature_states_map, ash::multidevice_setup::mojom::Feature::kSmartLock));
   linked_android_phone_data->set_is_instant_tethering_enabled(IsFeatureEnabled(
       feature_states_map,
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering));
-  linked_android_phone_data->set_is_messages_enabled(
-      IsFeatureEnabled(feature_states_map,
-                       chromeos::multidevice_setup::mojom::Feature::kMessages));
+      ash::multidevice_setup::mojom::Feature::kInstantTethering));
+  linked_android_phone_data->set_is_messages_enabled(IsFeatureEnabled(
+      feature_states_map, ash::multidevice_setup::mojom::Feature::kMessages));
 }
 
 void ChromeOSMetricsProvider::UpdateMultiProfileUserCount(
diff --git a/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc b/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
index c3a8aa82..0b5599c 100644
--- a/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chromeos_metrics_provider_unittest.cc
@@ -47,7 +47,7 @@
   // NOTE: At most, one client should be created per-test.
   std::unique_ptr<chromeos::multidevice_setup::MultiDeviceSetupClient>
   CreateInstance(
-      mojo::PendingRemote<chromeos::multidevice_setup::mojom::MultiDeviceSetup>)
+      mojo::PendingRemote<ash::multidevice_setup::mojom::MultiDeviceSetup>)
       override {
     EXPECT_TRUE(fake_multidevice_setup_client_);
     return std::move(fake_multidevice_setup_client_);
@@ -187,18 +187,18 @@
 }
 
 TEST_F(ChromeOSMetricsProviderTest, HasLinkedAndroidPhoneAndEnabledFeatures) {
-  fake_multidevice_setup_client_->SetHostStatusWithDevice(std::make_pair(
-      chromeos::multidevice_setup::mojom::HostStatus::kHostVerified,
-      chromeos::multidevice::CreateRemoteDeviceRefForTest()));
+  fake_multidevice_setup_client_->SetHostStatusWithDevice(
+      std::make_pair(ash::multidevice_setup::mojom::HostStatus::kHostVerified,
+                     chromeos::multidevice::CreateRemoteDeviceRefForTest()));
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kInstantTethering,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      ash::multidevice_setup::mojom::Feature::kInstantTethering,
+      ash::multidevice_setup::mojom::FeatureState::kEnabledByUser);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kSmartLock,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      ash::multidevice_setup::mojom::Feature::kSmartLock,
+      ash::multidevice_setup::mojom::FeatureState::kEnabledByUser);
   fake_multidevice_setup_client_->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kMessages,
-      chromeos::multidevice_setup::mojom::FeatureState::kFurtherSetupRequired);
+      ash::multidevice_setup::mojom::Feature::kMessages,
+      ash::multidevice_setup::mojom::FeatureState::kFurtherSetupRequired);
 
   // |scoped_enabler| takes over the lifetime of |user_manager|.
   auto* user_manager = new ash::FakeChromeUserManager();
diff --git a/chrome/browser/notifications/profile_notification.cc b/chrome/browser/notifications/profile_notification.cc
index 7531b32..528c17f2e 100644
--- a/chrome/browser/notifications/profile_notification.cc
+++ b/chrome/browser/notifications/profile_notification.cc
@@ -9,11 +9,14 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
 #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
-#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "components/account_id/account_id.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#endif
+
 // static
 std::string ProfileNotification::GetProfileNotificationId(
     const std::string& delegate_id,
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 9182adf..363dae1 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -75,6 +75,7 @@
     "//components/sync/android:sync_java",
     "//components/sync/protocol:protocol_java",
     "//content/public/android:content_java",
+    "//third_party/android_deps:guava_android_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
@@ -147,6 +148,7 @@
     "//components/signin/public/android:java",
     "//components/sync/android:sync_java",
     "//components/sync/protocol:protocol_java",
+    "//third_party/android_deps:guava_android_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/junit",
diff --git a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeImpl.java b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeImpl.java
index c81f91fc..430f913 100644
--- a/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeImpl.java
+++ b/chrome/browser/password_manager/android/java/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeImpl.java
@@ -6,15 +6,18 @@
 
 import static org.chromium.chrome.browser.flags.ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID;
 
+import android.accounts.Account;
 import android.app.PendingIntent;
 
 import com.google.android.gms.common.api.ApiException;
 import com.google.android.gms.common.api.ResolvableApiException;
+import com.google.common.base.Optional;
 
 import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.components.signin.AccountUtils;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -57,8 +60,8 @@
     }
 
     @CalledByNative
-    void getAllLogins(@JobId int jobId, @PasswordStoreOperationTarget int target) {
-        mBackend.getAllLogins(target, passwords -> {
+    void getAllLogins(@JobId int jobId, String syncingAccount) {
+        mBackend.getAllLogins(getAccount(syncingAccount), passwords -> {
             if (mNativeBackendBridge == 0) return;
             PasswordStoreAndroidBackendBridgeImplJni.get().onCompleteWithLogins(
                     mNativeBackendBridge, jobId, passwords);
@@ -66,8 +69,8 @@
     }
 
     @CalledByNative
-    void getAutofillableLogins(@JobId int jobId) {
-        mBackend.getAutofillableLogins(passwords -> {
+    void getAutofillableLogins(@JobId int jobId, String syncingAccount) {
+        mBackend.getAutofillableLogins(getAccount(syncingAccount), passwords -> {
             if (mNativeBackendBridge == 0) return;
             PasswordStoreAndroidBackendBridgeImplJni.get().onCompleteWithLogins(
                     mNativeBackendBridge, jobId, passwords);
@@ -75,8 +78,8 @@
     }
 
     @CalledByNative
-    void getLoginsForSignonRealm(@JobId int jobId, String signonRealm) {
-        mBackend.getLoginsForSignonRealm(signonRealm, passwords -> {
+    void getLoginsForSignonRealm(@JobId int jobId, String signonRealm, String syncingAccount) {
+        mBackend.getLoginsForSignonRealm(signonRealm, getAccount(syncingAccount), passwords -> {
             if (mNativeBackendBridge == 0) return;
             PasswordStoreAndroidBackendBridgeImplJni.get().onCompleteWithLogins(
                     mNativeBackendBridge, jobId, passwords);
@@ -84,8 +87,8 @@
     }
 
     @CalledByNative
-    void addLogin(@JobId int jobId, byte[] pwdWithLocalData) {
-        mBackend.addLogin(pwdWithLocalData, () -> {
+    void addLogin(@JobId int jobId, byte[] pwdWithLocalData, String syncingAccount) {
+        mBackend.addLogin(pwdWithLocalData, getAccount(syncingAccount), () -> {
             if (mNativeBackendBridge == 0) return;
             PasswordStoreAndroidBackendBridgeImplJni.get().onLoginAdded(
                     mNativeBackendBridge, jobId, pwdWithLocalData);
@@ -93,8 +96,8 @@
     }
 
     @CalledByNative
-    void updateLogin(@JobId int jobId, byte[] pwdWithLocalData) {
-        mBackend.updateLogin(pwdWithLocalData, () -> {
+    void updateLogin(@JobId int jobId, byte[] pwdWithLocalData, String syncingAccount) {
+        mBackend.updateLogin(pwdWithLocalData, getAccount(syncingAccount), () -> {
             if (mNativeBackendBridge == 0) return;
             PasswordStoreAndroidBackendBridgeImplJni.get().onLoginUpdated(
                     mNativeBackendBridge, jobId, pwdWithLocalData);
@@ -102,9 +105,8 @@
     }
 
     @CalledByNative
-    void removeLogin(
-            @JobId int jobId, byte[] pwdSpecificsData, @PasswordStoreOperationTarget int target) {
-        mBackend.removeLogin(pwdSpecificsData, target, () -> {
+    void removeLogin(@JobId int jobId, byte[] pwdSpecificsData, String syncingAccount) {
+        mBackend.removeLogin(pwdSpecificsData, getAccount(syncingAccount), () -> {
             if (mNativeBackendBridge == 0) return;
             PasswordStoreAndroidBackendBridgeImplJni.get().onLoginDeleted(
                     mNativeBackendBridge, jobId, pwdSpecificsData);
@@ -146,6 +148,11 @@
                 mNativeBackendBridge, jobId, error, api_error_code);
     }
 
+    private Optional<Account> getAccount(String syncingAccount) {
+        if (syncingAccount == null) return Optional.absent();
+        return Optional.of(AccountUtils.createAccountFromName(syncingAccount));
+    }
+
     @CalledByNative
     private void destroy() {
         mNativeBackendBridge = 0;
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeTest.java
index 749ebb967..3eede87d 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordStoreAndroidBackendBridgeTest.java
@@ -11,12 +11,14 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.accounts.Account;
 import android.app.PendingIntent;
 
 import com.google.android.gms.common.api.ApiException;
 import com.google.android.gms.common.api.CommonStatusCodes;
 import com.google.android.gms.common.api.ResolvableApiException;
 import com.google.android.gms.common.api.Status;
+import com.google.common.base.Optional;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -36,6 +38,7 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.components.signin.AccountUtils;
 import org.chromium.components.sync.protocol.ListPasswordsResult;
 import org.chromium.components.sync.protocol.PasswordSpecificsData;
 import org.chromium.components.sync.protocol.PasswordWithLocalData;
@@ -64,6 +67,9 @@
     private static final ListPasswordsResult.Builder sTestLogins =
             ListPasswordsResult.newBuilder().addPasswordData(sTestPwdWithLocalData);
     private static final long sDummyNativePointer = 4;
+    private static final String sTestAccountEmail = "test@email.com";
+    private static final Optional<Account> sTestAccount =
+            Optional.of(AccountUtils.createAccountFromName(sTestAccountEmail));
 
     @Rule
     public JniMocker mJniMocker = new JniMocker();
@@ -88,11 +94,9 @@
         final int kTestTaskId = 1337;
 
         // Ensure the backend is called with a valid success callback.
-        mBackendBridge.getAllLogins(kTestTaskId, PasswordStoreOperationTarget.DEFAULT);
+        mBackendBridge.getAllLogins(kTestTaskId, sTestAccountEmail);
         ArgumentCaptor<Callback<byte[]>> successCallback = ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .getAllLogins(
-                        eq(PasswordStoreOperationTarget.DEFAULT), successCallback.capture(), any());
+        verify(mBackendMock).getAllLogins(eq(sTestAccount), successCallback.capture(), any());
         assertNotNull(successCallback.getValue());
 
         byte[] kExpectedList = sTestLogins.build().toByteArray();
@@ -106,12 +110,10 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getAllLogins(kTestTaskId, PasswordStoreOperationTarget.DEFAULT);
+        mBackendBridge.getAllLogins(kTestTaskId, null);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .getAllLogins(
-                        eq(PasswordStoreOperationTarget.DEFAULT), any(), failureCallback.capture());
+        verify(mBackendMock).getAllLogins(eq(Optional.absent()), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new Exception("Sample failure");
@@ -126,12 +128,10 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getAllLogins(kTestTaskId, PasswordStoreOperationTarget.LOCAL_STORAGE);
+        mBackendBridge.getAllLogins(kTestTaskId, sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .getAllLogins(eq(PasswordStoreOperationTarget.LOCAL_STORAGE), any(),
-                        failureCallback.capture());
+        verify(mBackendMock).getAllLogins(eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new PasswordStoreAndroidBackend.BackendException(
@@ -146,12 +146,10 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getAllLogins(kTestTaskId, PasswordStoreOperationTarget.DEFAULT);
+        mBackendBridge.getAllLogins(kTestTaskId, null);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .getAllLogins(
-                        eq(PasswordStoreOperationTarget.DEFAULT), any(), failureCallback.capture());
+        verify(mBackendMock).getAllLogins(eq(Optional.absent()), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new ApiException(new Status(CommonStatusCodes.ERROR, ""));
@@ -167,12 +165,10 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getAllLogins(kTestTaskId, PasswordStoreOperationTarget.DEFAULT);
+        mBackendBridge.getAllLogins(kTestTaskId, sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .getAllLogins(
-                        eq(PasswordStoreOperationTarget.DEFAULT), any(), failureCallback.capture());
+        verify(mBackendMock).getAllLogins(eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         PendingIntent pendingIntentMock = mock(PendingIntent.class);
@@ -192,12 +188,10 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getAllLogins(kTestTaskId, PasswordStoreOperationTarget.DEFAULT);
+        mBackendBridge.getAllLogins(kTestTaskId, null);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .getAllLogins(
-                        eq(PasswordStoreOperationTarget.DEFAULT), any(), failureCallback.capture());
+        verify(mBackendMock).getAllLogins(eq(Optional.absent()), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         PendingIntent pendingIntentMock = mock(PendingIntent.class);
@@ -215,9 +209,10 @@
         final int kTestTaskId = 1337;
 
         // Ensure the backend is called with a valid success callback.
-        mBackendBridge.getAutofillableLogins(kTestTaskId);
+        mBackendBridge.getAutofillableLogins(kTestTaskId, null);
         ArgumentCaptor<Callback<byte[]>> successCallback = ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock).getAutofillableLogins(successCallback.capture(), any());
+        verify(mBackendMock)
+                .getAutofillableLogins(eq(Optional.absent()), successCallback.capture(), any());
         assertNotNull(successCallback.getValue());
 
         byte[] kExpectedList = sTestLogins.build().toByteArray();
@@ -231,10 +226,11 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getAutofillableLogins(kTestTaskId);
+        mBackendBridge.getAutofillableLogins(kTestTaskId, sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock).getAutofillableLogins(any(), failureCallback.capture());
+        verify(mBackendMock)
+                .getAutofillableLogins(eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new Exception("Sample failure");
@@ -249,9 +245,11 @@
         final int kTestTaskId = 1337;
 
         // Ensure the backend is called with a valid success callback.
-        mBackendBridge.getLoginsForSignonRealm(kTestTaskId, "https://test_signon_realm.com");
+        mBackendBridge.getLoginsForSignonRealm(kTestTaskId, "https://test_signon_realm.com", null);
         ArgumentCaptor<Callback<byte[]>> successCallback = ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock).getLoginsForSignonRealm(any(), successCallback.capture(), any());
+        verify(mBackendMock)
+                .getLoginsForSignonRealm(
+                        any(), eq(Optional.absent()), successCallback.capture(), any());
         assertNotNull(successCallback.getValue());
 
         byte[] kExpectedList = sTestLogins.build().toByteArray();
@@ -265,10 +263,12 @@
         final int kTestTaskId = 42069;
 
         // Ensure the backend is called with a valid failure callback.
-        mBackendBridge.getLoginsForSignonRealm(kTestTaskId, "https://test_signon_realm.com");
+        mBackendBridge.getLoginsForSignonRealm(
+                kTestTaskId, "https://test_signon_realm.com", sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock).getLoginsForSignonRealm(any(), any(), failureCallback.capture());
+        verify(mBackendMock)
+                .getLoginsForSignonRealm(any(), eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new Exception("Sample failure");
@@ -284,9 +284,10 @@
 
         // Ensure the backend is called with a valid success callback.
         byte[] pwdWithLocalData = sTestPwdWithLocalData.build().toByteArray();
-        mBackendBridge.addLogin(kTestTaskId, pwdWithLocalData);
+        mBackendBridge.addLogin(kTestTaskId, pwdWithLocalData, null);
         ArgumentCaptor<Runnable> successCallback = ArgumentCaptor.forClass(Runnable.class);
-        verify(mBackendMock).addLogin(any(), successCallback.capture(), any());
+        verify(mBackendMock)
+                .addLogin(any(), eq(Optional.absent()), successCallback.capture(), any());
         assertNotNull(successCallback.getValue());
 
         successCallback.getValue().run();
@@ -299,10 +300,10 @@
 
         // Ensure the backend is called with a valid failure callback.
         byte[] pwdWithLocalData = sTestPwdWithLocalData.build().toByteArray();
-        mBackendBridge.addLogin(kTestTaskId, pwdWithLocalData);
+        mBackendBridge.addLogin(kTestTaskId, pwdWithLocalData, sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock).addLogin(any(), any(), failureCallback.capture());
+        verify(mBackendMock).addLogin(any(), eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new Exception("Sample failure");
@@ -318,9 +319,10 @@
 
         // Ensure the backend is called with a valid success callback.
         byte[] pwdWithLocalData = sTestPwdWithLocalData.build().toByteArray();
-        mBackendBridge.updateLogin(kTestTaskId, pwdWithLocalData);
+        mBackendBridge.updateLogin(kTestTaskId, pwdWithLocalData, null);
         ArgumentCaptor<Runnable> successCallback = ArgumentCaptor.forClass(Runnable.class);
-        verify(mBackendMock).updateLogin(any(), successCallback.capture(), any());
+        verify(mBackendMock)
+                .updateLogin(any(), eq(Optional.absent()), successCallback.capture(), any());
         assertNotNull(successCallback.getValue());
 
         successCallback.getValue().run();
@@ -333,10 +335,10 @@
 
         // Ensure the backend is called with a valid failure callback.
         byte[] pwdWithLocalData = sTestPwdWithLocalData.build().toByteArray();
-        mBackendBridge.updateLogin(kTestTaskId, pwdWithLocalData);
+        mBackendBridge.updateLogin(kTestTaskId, pwdWithLocalData, sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock).updateLogin(any(), any(), failureCallback.capture());
+        verify(mBackendMock).updateLogin(any(), eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new Exception("Sample failure");
@@ -352,12 +354,10 @@
 
         // Ensure the backend is called with a valid success callback.
         byte[] pwdSpecificsData = sTestProfile.build().toByteArray();
-        mBackendBridge.removeLogin(
-                kTestTaskId, pwdSpecificsData, PasswordStoreOperationTarget.SYNCING_STORAGE);
+        mBackendBridge.removeLogin(kTestTaskId, pwdSpecificsData, null);
         ArgumentCaptor<Runnable> successCallback = ArgumentCaptor.forClass(Runnable.class);
         verify(mBackendMock)
-                .removeLogin(any(), eq(PasswordStoreOperationTarget.SYNCING_STORAGE),
-                        successCallback.capture(), any());
+                .removeLogin(any(), eq(Optional.absent()), successCallback.capture(), any());
         assertNotNull(successCallback.getValue());
 
         successCallback.getValue().run();
@@ -370,13 +370,10 @@
 
         // Ensure the backend is called with a valid failure callback.
         byte[] pwdSpecificsData = sTestProfile.build().toByteArray();
-        mBackendBridge.removeLogin(
-                kTestTaskId, pwdSpecificsData, PasswordStoreOperationTarget.DEFAULT);
+        mBackendBridge.removeLogin(kTestTaskId, pwdSpecificsData, sTestAccountEmail);
         ArgumentCaptor<Callback<Exception>> failureCallback =
                 ArgumentCaptor.forClass(Callback.class);
-        verify(mBackendMock)
-                .removeLogin(any(), eq(PasswordStoreOperationTarget.DEFAULT), any(),
-                        failureCallback.capture());
+        verify(mBackendMock).removeLogin(any(), eq(sTestAccount), any(), failureCallback.capture());
         assertNotNull(failureCallback.getValue());
 
         Exception kExpectedException = new Exception("Sample failure");
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.cc b/chrome/browser/password_manager/android/password_store_android_backend.cc
index 59f2aba..3bead717 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend.cc
@@ -134,6 +134,15 @@
   return joined_logins;
 }
 
+PasswordStoreAndroidBackendBridge::Account GetAccount(
+    absl::optional<std::string> syncing_account) {
+  if (syncing_account.has_value()) {
+    return PasswordStoreAndroidBackendBridge::SyncingAccount(
+        syncing_account.value());
+  }
+  return PasswordStoreOperationTarget::kLocalStorage;
+}
+
 }  // namespace
 
 PasswordStoreAndroidBackend::MetricsRecorder::MetricsRecorder() = default;
@@ -250,9 +259,10 @@
     std::unique_ptr<SyncDelegate> sync_delegate)
     : lifecycle_helper_(std::make_unique<PasswordManagerLifecycleHelperImpl>()),
       bridge_(PasswordStoreAndroidBackendBridge::Create()),
+      sync_delegate_(std::move(sync_delegate)),
       sync_controller_delegate_(
           std::make_unique<PasswordSyncControllerDelegateAndroid>(
-              std::move(sync_delegate))) {
+              sync_delegate_.get())) {
   DCHECK(bridge_);
   bridge_->SetConsumer(weak_ptr_factory_.GetWeakPtr());
 }
@@ -264,9 +274,10 @@
     std::unique_ptr<SyncDelegate> sync_delegate)
     : lifecycle_helper_(std::move(lifecycle_helper)),
       bridge_(std::move(bridge)),
+      sync_delegate_(std::move(sync_delegate)),
       sync_controller_delegate_(
           std::make_unique<PasswordSyncControllerDelegateAndroid>(
-              std::move(sync_delegate))) {
+              sync_delegate_.get())) {
   DCHECK(bridge_);
   bridge_->SetConsumer(weak_ptr_factory_.GetWeakPtr());
 }
@@ -295,13 +306,14 @@
 
 void PasswordStoreAndroidBackend::GetAllLoginsAsync(
     LoginsOrErrorReply callback) {
-  GetAllLoginsForTarget(PasswordStoreOperationTarget::kDefault,
-                        std::move(callback));
+  GetAllLoginsForAccount(GetAccount(sync_delegate_->GetSyncingAccount()),
+                         std::move(callback));
 }
 
 void PasswordStoreAndroidBackend::GetAutofillableLoginsAsync(
     LoginsOrErrorReply callback) {
-  JobId job_id = bridge_->GetAutofillableLogins();
+  JobId job_id = bridge_->GetAutofillableLogins(
+      GetAccount(sync_delegate_->GetSyncingAccount()));
   QueueNewJob(job_id, std::move(callback),
               MetricInfix("GetAutofillableLoginsAsync"));
 }
@@ -341,22 +353,24 @@
 void PasswordStoreAndroidBackend::AddLoginAsync(
     const PasswordForm& form,
     PasswordStoreChangeListReply callback) {
-  JobId job_id = bridge_->AddLogin(form);
+  JobId job_id =
+      bridge_->AddLogin(form, GetAccount(sync_delegate_->GetSyncingAccount()));
   QueueNewJob(job_id, std::move(callback), MetricInfix("AddLoginAsync"));
 }
 
 void PasswordStoreAndroidBackend::UpdateLoginAsync(
     const PasswordForm& form,
     PasswordStoreChangeListReply callback) {
-  JobId job_id = bridge_->UpdateLogin(form);
+  JobId job_id = bridge_->UpdateLogin(
+      form, GetAccount(sync_delegate_->GetSyncingAccount()));
   QueueNewJob(job_id, std::move(callback), MetricInfix("UpdateLoginAsync"));
 }
 
 void PasswordStoreAndroidBackend::RemoveLoginAsync(
     const PasswordForm& form,
     PasswordStoreChangeListReply callback) {
-  RemoveLoginForTarget(form, PasswordStoreOperationTarget::kDefault,
-                       std::move(callback));
+  RemoveLoginForAccount(form, GetAccount(sync_delegate_->GetSyncingAccount()),
+                        std::move(callback));
 }
 
 void PasswordStoreAndroidBackend::FilterAndRemoveLogins(
@@ -500,7 +514,7 @@
               base::Unretained(raw_recorder));
 
           callbacks_chain = base::BindOnce(
-              &PasswordStoreAndroidBackend::RemoveLoginForTarget, weak_self,
+              &PasswordStoreAndroidBackend::RemoveLoginForAccount, weak_self,
               std::move(*login), PasswordStoreOperationTarget::kLocalStorage,
               std::move(record_removal_result)
                   .Then(std::move(callbacks_chain)));
@@ -511,8 +525,8 @@
       weak_ptr_factory_.GetWeakPtr(),
       MetricsRecorder(MetricInfix("ClearAllLocalPasswords")));
 
-  GetAllLoginsForTarget(PasswordStoreOperationTarget::kLocalStorage,
-                        std::move(cleaning_callback));
+  GetAllLoginsForAccount(PasswordStoreOperationTarget::kLocalStorage,
+                         std::move(cleaning_callback));
 }
 
 void PasswordStoreAndroidBackend::OnCompleteWithLogins(
@@ -584,7 +598,8 @@
                                                  bool include_psl,
                                                  LoginsOrErrorReply callback) {
   JobId job_id = bridge_->GetLoginsForSignonRealm(
-      FormToSignonRealmQuery(form, include_psl));
+      FormToSignonRealmQuery(form, include_psl),
+      GetAccount(sync_delegate_->GetSyncingAccount()));
   QueueNewJob(job_id,
               base::BindOnce(&ValidateSignonRealm, std::move(form), include_psl,
                              std::move(callback)),
@@ -667,18 +682,18 @@
       MetricsRecorder(metric_infix), std::move(callback));
 }
 
-void PasswordStoreAndroidBackend::GetAllLoginsForTarget(
-    PasswordStoreOperationTarget target,
+void PasswordStoreAndroidBackend::GetAllLoginsForAccount(
+    PasswordStoreAndroidBackendBridge::Account account,
     LoginsOrErrorReply callback) {
-  JobId job_id = bridge_->GetAllLogins(target);
+  JobId job_id = bridge_->GetAllLogins(std::move(account));
   QueueNewJob(job_id, std::move(callback), MetricInfix("GetAllLoginsAsync"));
 }
 
-void PasswordStoreAndroidBackend::RemoveLoginForTarget(
+void PasswordStoreAndroidBackend::RemoveLoginForAccount(
     const PasswordForm& form,
-    PasswordStoreOperationTarget target,
+    PasswordStoreAndroidBackendBridge::Account account,
     PasswordStoreChangeListReply callback) {
-  JobId job_id = bridge_->RemoveLogin(form, target);
+  JobId job_id = bridge_->RemoveLogin(form, std::move(account));
   QueueNewJob(job_id, std::move(callback), MetricInfix("RemoveLoginAsync"));
 }
 
diff --git a/chrome/browser/password_manager/android/password_store_android_backend.h b/chrome/browser/password_manager/android/password_store_android_backend.h
index 194607c1..1a298f0 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend.h
@@ -211,14 +211,15 @@
       PasswordStoreChangeListReply callback);
 
   // Returns the complete list of PasswordForms (regardless of their blocklist
-  // status) from specified storage.
-  void GetAllLoginsForTarget(PasswordStoreOperationTarget target,
-                             LoginsOrErrorReply callback);
+  // status) for |account|.
+  void GetAllLoginsForAccount(
+      PasswordStoreAndroidBackendBridge::Account account,
+      LoginsOrErrorReply callback);
 
-  // Removes |form| from specified storage.
-  void RemoveLoginForTarget(const PasswordForm& form,
-                            PasswordStoreOperationTarget target,
-                            PasswordStoreChangeListReply callback);
+  // Removes |form| from |account|.
+  void RemoveLoginForAccount(const PasswordForm& form,
+                             PasswordStoreAndroidBackendBridge::Account account,
+                             PasswordStoreChangeListReply callback);
 
   // Invoked synchronously by `lifecycle_helper_` when Chrome is foregrounded.
   // This should not cover the initial startup since the registration for the
@@ -243,6 +244,9 @@
   // This object is the proxy to the JNI bridge that performs the API requests.
   std::unique_ptr<PasswordStoreAndroidBackendBridge> bridge_;
 
+  // Delegate to obtain sync status, and syncing account.
+  std::unique_ptr<SyncDelegate> sync_delegate_;
+
   // Delegate to handle sync events.
   std::unique_ptr<PasswordSyncControllerDelegateAndroid>
       sync_controller_delegate_;
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_bridge.h b/chrome/browser/password_manager/android/password_store_android_backend_bridge.h
index ea35330..94353cf18 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_bridge.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend_bridge.h
@@ -24,6 +24,9 @@
 class PasswordStoreAndroidBackendBridge {
  public:
   using JobId = base::StrongAlias<struct JobIdTag, int>;
+  using SyncingAccount =
+      base::StrongAlias<struct SyncingAccountTag, std::string>;
+  using Account = absl::variant<PasswordStoreOperationTarget, SyncingAccount>;
 
   // Each bridge is created with a consumer that will be called when a job is
   // completed. In order to identify which request the response belongs to, the
@@ -60,39 +63,52 @@
 
   // Triggers an asynchronous request to retrieve all stored passwords. The
   // registered `Consumer` is notified with `OnCompleteWithLogins` when the
-  // job with the returned JobId succeeds.
-  [[nodiscard]] virtual JobId GetAllLogins(
-      PasswordStoreOperationTarget target) = 0;
+  // job with the returned JobId succeeds. `syncing_account` is used to decide
+  // which storage to use. If `syncing_account` is absl::nullopt local storage
+  // will be used.
+  [[nodiscard]] virtual JobId GetAllLogins(Account account) = 0;
 
   // Triggers an asynchronous request to retrieve all autofillable
   // (non-blocklisted) passwords. The registered `Consumer` is notified with
   // `OnCompleteWithLogins` when the job with the returned JobId succeeds.
-  [[nodiscard]] virtual JobId GetAutofillableLogins() = 0;
+  // `syncing_account` is used to decide which storage to use. If
+  // `syncing_account` is absl::nullopt local storage will be used.
+  [[nodiscard]] virtual JobId GetAutofillableLogins(Account account) = 0;
 
   // Triggers an asynchronous request to retrieve stored passwords with
   // matching |signon_realm|. The returned results must be validated (e.g
   // matching "sample.com" also returns logins for "not-sample.com").
   // The registered `Consumer` is notified with `OnCompleteWithLogins` when the
-  // job with the returned JobId succeeds.
+  // job with the returned JobId succeeds. `syncing_account` is used to decide
+  // which storage to use. If `syncing_account` is absl::nullopt local storage
+  // will be used.
   [[nodiscard]] virtual JobId GetLoginsForSignonRealm(
-      const std::string& signon_realm) = 0;
+      const std::string& signon_realm,
+      Account account) = 0;
 
   // Triggers an asynchronous request to add |form| to store. The
   // registered `Consumer` is notified with `OnLoginsChanged` when the
-  // job with the returned JobId succeeds.
-  [[nodiscard]] virtual JobId AddLogin(const PasswordForm& form) = 0;
+  // job with the returned JobId succeeds. `syncing_account` is used to decide
+  // which storage to use. If `syncing_account` is absl::nullopt local storage
+  // will be used.
+  [[nodiscard]] virtual JobId AddLogin(const PasswordForm& form,
+                                       Account account) = 0;
 
   // Triggers an asynchronous request to update |form| in store. The
   // registered `Consumer` is notified with `OnLoginsChanged` when the
-  // job with the returned JobId succeeds.
-  [[nodiscard]] virtual JobId UpdateLogin(const PasswordForm& form) = 0;
+  // job with the returned JobId succeeds. `syncing_account` is used to decide
+  // which storage to use. If `syncing_account` is absl::nullopt local storage
+  // will be used.
+  [[nodiscard]] virtual JobId UpdateLogin(const PasswordForm& form,
+                                          Account account) = 0;
 
   // Triggers an asynchronous request to remove |form| from store. The
   // registered `Consumer` is notified with `OnLoginsChanged` when the
-  // job with the returned JobId succeeds.
-  [[nodiscard]] virtual JobId RemoveLogin(
-      const PasswordForm& form,
-      PasswordStoreOperationTarget target) = 0;
+  // job with the returned JobId succeeds. `syncing_account` is used to decide
+  // which storage to use. If `syncing_account` is absl::nullopt local storage
+  // will be used.
+  [[nodiscard]] virtual JobId RemoveLogin(const PasswordForm& form,
+                                          Account account) = 0;
 
   // Factory function for creating the bridge. Implementation is pulled in by
   // including an implementation or by defining it explicitly in tests.
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.cc b/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.cc
index 81bdbe75..f4938e7 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.cc
@@ -59,6 +59,20 @@
   return result;
 }
 
+base::android::ScopedJavaLocalRef<jstring> GetJavaStringFromAccount(
+    PasswordStoreAndroidBackendBridgeImpl::Account account) {
+  if (absl::holds_alternative<password_manager::PasswordStoreOperationTarget>(
+          account)) {
+    DCHECK(password_manager::PasswordStoreOperationTarget::kLocalStorage ==
+           absl::get<password_manager::PasswordStoreOperationTarget>(account));
+    return nullptr;
+  }
+  return base::android::ConvertUTF8ToJavaString(
+      base::android::AttachCurrentThread(),
+      absl::get<PasswordStoreAndroidBackendBridgeImpl::SyncingAccount>(account)
+          .value());
+}
+
 }  // namespace
 
 namespace password_manager {
@@ -118,64 +132,71 @@
                      consumer_, JobId(job_id), std::move(error)));
 }
 
-JobId PasswordStoreAndroidBackendBridgeImpl::GetAllLogins(
-    password_manager::PasswordStoreOperationTarget target) {
+JobId PasswordStoreAndroidBackendBridgeImpl::GetAllLogins(Account account) {
   JobId job_id = GetNextJobId();
   Java_PasswordStoreAndroidBackendBridgeImpl_getAllLogins(
       base::android::AttachCurrentThread(), java_object_, job_id.value(),
-      static_cast<int>(target));
+      GetJavaStringFromAccount(std::move(account)));
   return job_id;
 }
 
-JobId PasswordStoreAndroidBackendBridgeImpl::GetAutofillableLogins() {
+JobId PasswordStoreAndroidBackendBridgeImpl::GetAutofillableLogins(
+    Account account) {
   JobId job_id = GetNextJobId();
   Java_PasswordStoreAndroidBackendBridgeImpl_getAutofillableLogins(
-      base::android::AttachCurrentThread(), java_object_, job_id.value());
+      base::android::AttachCurrentThread(), java_object_, job_id.value(),
+      GetJavaStringFromAccount(std::move(account)));
   return job_id;
 }
 
 JobId PasswordStoreAndroidBackendBridgeImpl::GetLoginsForSignonRealm(
-    const std::string& signon_realm) {
+    const std::string& signon_realm,
+    Account account) {
   JobId job_id = GetNextJobId();
   Java_PasswordStoreAndroidBackendBridgeImpl_getLoginsForSignonRealm(
       base::android::AttachCurrentThread(), java_object_, job_id.value(),
       base::android::ConvertUTF8ToJavaString(
-          base::android::AttachCurrentThread(), signon_realm));
+          base::android::AttachCurrentThread(), signon_realm),
+      GetJavaStringFromAccount(std::move(account)));
   return job_id;
 }
 
 JobId PasswordStoreAndroidBackendBridgeImpl::AddLogin(
-    const password_manager::PasswordForm& form) {
+    const password_manager::PasswordForm& form,
+    Account account) {
   JobId job_id = GetNextJobId();
   sync_pb::PasswordWithLocalData data = PasswordWithLocalDataFromPassword(form);
   Java_PasswordStoreAndroidBackendBridgeImpl_addLogin(
       base::android::AttachCurrentThread(), java_object_, job_id.value(),
       base::android::ToJavaByteArray(base::android::AttachCurrentThread(),
-                                     data.SerializeAsString()));
+                                     data.SerializeAsString()),
+      GetJavaStringFromAccount(std::move(account)));
   return job_id;
 }
 
 JobId PasswordStoreAndroidBackendBridgeImpl::UpdateLogin(
-    const password_manager::PasswordForm& form) {
+    const password_manager::PasswordForm& form,
+    Account account) {
   JobId job_id = GetNextJobId();
   sync_pb::PasswordWithLocalData data = PasswordWithLocalDataFromPassword(form);
   Java_PasswordStoreAndroidBackendBridgeImpl_updateLogin(
       base::android::AttachCurrentThread(), java_object_, job_id.value(),
       base::android::ToJavaByteArray(base::android::AttachCurrentThread(),
-                                     data.SerializeAsString()));
+                                     data.SerializeAsString()),
+      GetJavaStringFromAccount(std::move(account)));
   return job_id;
 }
 
 JobId PasswordStoreAndroidBackendBridgeImpl::RemoveLogin(
     const password_manager::PasswordForm& form,
-    password_manager::PasswordStoreOperationTarget target) {
+    Account account) {
   JobId job_id = GetNextJobId();
   sync_pb::PasswordSpecificsData data = SpecificsDataFromPassword(form);
   Java_PasswordStoreAndroidBackendBridgeImpl_removeLogin(
       base::android::AttachCurrentThread(), java_object_, job_id.value(),
       base::android::ToJavaByteArray(base::android::AttachCurrentThread(),
                                      data.SerializeAsString()),
-      static_cast<int>(target));
+      GetJavaStringFromAccount(std::move(account)));
   return job_id;
 }
 
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.h b/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.h
index 693ec34..7f85229 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.h
+++ b/chrome/browser/password_manager/android/password_store_android_backend_bridge_impl.h
@@ -59,18 +59,16 @@
  private:
   // Implements PasswordStoreAndroidBackendBridge interface.
   void SetConsumer(base::WeakPtr<Consumer> consumer) override;
-  [[nodiscard]] JobId GetAllLogins(
-      password_manager::PasswordStoreOperationTarget target) override;
-  [[nodiscard]] JobId GetAutofillableLogins() override;
-  [[nodiscard]] JobId GetLoginsForSignonRealm(
-      const std::string& signon_realm) override;
-  [[nodiscard]] JobId AddLogin(
-      const password_manager::PasswordForm& form) override;
-  [[nodiscard]] JobId UpdateLogin(
-      const password_manager::PasswordForm& form) override;
-  [[nodiscard]] JobId RemoveLogin(
-      const password_manager::PasswordForm& form,
-      password_manager::PasswordStoreOperationTarget target) override;
+  [[nodiscard]] JobId GetAllLogins(Account account) override;
+  [[nodiscard]] JobId GetAutofillableLogins(Account account) override;
+  [[nodiscard]] JobId GetLoginsForSignonRealm(const std::string& signon_realm,
+                                              Account account) override;
+  [[nodiscard]] JobId AddLogin(const password_manager::PasswordForm& form,
+                               Account account) override;
+  [[nodiscard]] JobId UpdateLogin(const password_manager::PasswordForm& form,
+                                  Account account) override;
+  [[nodiscard]] JobId RemoveLogin(const password_manager::PasswordForm& form,
+                                  Account account) override;
 
   [[nodiscard]] JobId GetNextJobId();
 
diff --git a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
index df42e9d..969eb0f 100644
--- a/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
+++ b/chrome/browser/password_manager/android/password_store_android_backend_unittest.cc
@@ -25,12 +25,14 @@
 using testing::_;
 using testing::ElementsAre;
 using testing::Eq;
+using testing::NiceMock;
 using testing::Optional;
 using testing::Return;
 using testing::StrictMock;
 using testing::WithArg;
 using JobId = PasswordStoreAndroidBackendBridge::JobId;
 
+const char kTestAccount[] = "test@gmail.com";
 const std::u16string kTestUsername(u"Todd Tester");
 const std::u16string kTestPassword(u"S3cr3t");
 constexpr char kTestUrl[] = "https://example.com";
@@ -41,6 +43,20 @@
          expectation == absl::get<PasswordStoreBackendError>(arg);
 }
 
+MATCHER_P(ExpectSyncingAccount, expectation, "") {
+  return absl::holds_alternative<
+             PasswordStoreAndroidBackendBridge::SyncingAccount>(arg) &&
+         expectation ==
+             absl::get<PasswordStoreAndroidBackendBridge::SyncingAccount>(arg)
+                 .value();
+}
+
+MATCHER(ExpectLocalAccount, "") {
+  return absl::holds_alternative<PasswordStoreOperationTarget>(arg) &&
+         (PasswordStoreOperationTarget::kLocalStorage ==
+          absl::get<PasswordStoreOperationTarget>(arg));
+}
+
 std::vector<std::unique_ptr<PasswordForm>> CreateTestLogins() {
   std::vector<std::unique_ptr<PasswordForm>> forms;
   forms.push_back(CreateEntry("Todd Tester", "S3cr3t",
@@ -107,15 +123,21 @@
     : public PasswordStoreAndroidBackendBridge {
  public:
   MOCK_METHOD(void, SetConsumer, (base::WeakPtr<Consumer>), (override));
-  MOCK_METHOD(JobId, GetAllLogins, (PasswordStoreOperationTarget), (override));
-  MOCK_METHOD(JobId, GetAutofillableLogins, (), (override));
-  MOCK_METHOD(JobId, GetLoginsForSignonRealm, (const std::string&), (override));
-  MOCK_METHOD(JobId, AddLogin, (const PasswordForm&), (override));
-  MOCK_METHOD(JobId, UpdateLogin, (const PasswordForm&), (override));
+  MOCK_METHOD(JobId, GetAllLogins, (Account), (override));
+  MOCK_METHOD(JobId, GetAutofillableLogins, (Account), (override));
   MOCK_METHOD(JobId,
-              RemoveLogin,
-              (const PasswordForm&, PasswordStoreOperationTarget),
+              GetLoginsForSignonRealm,
+              (const std::string&, Account),
               (override));
+  MOCK_METHOD(JobId, AddLogin, (const PasswordForm&, Account), (override));
+  MOCK_METHOD(JobId, UpdateLogin, (const PasswordForm&, Account), (override));
+  MOCK_METHOD(JobId, RemoveLogin, (const PasswordForm&, Account), (override));
+};
+
+class MockSyncDelegate : public PasswordStoreBackend::SyncDelegate {
+ public:
+  MOCK_METHOD(bool, IsSyncingPasswordsEnabled, (), (override));
+  MOCK_METHOD(absl::optional<std::string>, GetSyncingAccount, (), (override));
 };
 
 }  // namespace
@@ -126,7 +148,7 @@
     backend_ = std::make_unique<PasswordStoreAndroidBackend>(
         base::PassKey<class PasswordStoreAndroidBackendTest>(),
         CreateMockBridge(), CreateFakeLifecycleHelper(),
-        /*sync_delegate=*/nullptr);
+        CreateMockSyncDelegate());
   }
 
   ~PasswordStoreAndroidBackendTest() override {
@@ -138,12 +160,27 @@
   PasswordStoreAndroidBackendBridge::Consumer& consumer() { return *backend_; }
   MockPasswordStoreAndroidBackendBridge* bridge() { return bridge_; }
   FakeLifecycleHelper* lifecycle_helper() { return lifecycle_helper_; }
+  MockSyncDelegate* sync_delegate() { return sync_delegate_; }
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::TaskEnvironment::MainThreadType::UI,
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
+  void EnableSyncForTestAccount() {
+    EXPECT_CALL(*sync_delegate_, IsSyncingPasswordsEnabled)
+        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*sync_delegate_, GetSyncingAccount)
+        .WillRepeatedly(Return(kTestAccount));
+  }
+
+  void DisableSyncFeature() {
+    EXPECT_CALL(*sync_delegate_, IsSyncingPasswordsEnabled)
+        .WillRepeatedly(Return(false));
+    EXPECT_CALL(*sync_delegate_, GetSyncingAccount)
+        .WillRepeatedly(Return(absl::nullopt));
+  }
+
  private:
   std::unique_ptr<PasswordStoreAndroidBackendBridge> CreateMockBridge() {
     auto unique_bridge =
@@ -159,9 +196,16 @@
     return new_helper;
   }
 
+  std::unique_ptr<PasswordStoreBackend::SyncDelegate> CreateMockSyncDelegate() {
+    auto unique_delegate = std::make_unique<NiceMock<MockSyncDelegate>>();
+    sync_delegate_ = unique_delegate.get();
+    return unique_delegate;
+  }
+
   std::unique_ptr<PasswordStoreAndroidBackend> backend_;
   raw_ptr<StrictMock<MockPasswordStoreAndroidBackendBridge>> bridge_;
   raw_ptr<FakeLifecycleHelper> lifecycle_helper_;
+  raw_ptr<MockSyncDelegate> sync_delegate_;
 };
 
 TEST_F(PasswordStoreAndroidBackendTest, CallsCompletionCallbackAfterInit) {
@@ -172,11 +216,12 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, CallsBridgeForLogins) {
+  EnableSyncForTestAccount();
   backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::RepeatingClosure(), base::DoNothing());
   const JobId kJobId{1337};
   base::MockCallback<LoginsOrErrorReply> mock_reply;
-  EXPECT_CALL(*bridge(), GetAllLogins(PasswordStoreOperationTarget::kDefault))
+  EXPECT_CALL(*bridge(), GetAllLogins(ExpectSyncingAccount(kTestAccount)))
       .WillOnce(Return(kJobId));
   backend().GetAllLoginsAsync(mock_reply.Get());
 
@@ -316,6 +361,7 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, CallsBridgeForRemoveLogin) {
+  DisableSyncFeature();
   backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::RepeatingClosure(), base::DoNothing());
   const JobId kJobId{13388};
@@ -323,8 +369,7 @@
 
   PasswordForm form =
       CreateTestLogin(kTestUsername, kTestPassword, kTestUrl, kTestDateCreated);
-  EXPECT_CALL(*bridge(),
-              RemoveLogin(form, PasswordStoreOperationTarget::kDefault))
+  EXPECT_CALL(*bridge(), RemoveLogin(form, ExpectLocalAccount()))
       .WillOnce(Return(kJobId));
   backend().RemoveLoginAsync(form, mock_reply.Get());
 
@@ -447,6 +492,7 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, CallsBridgeForAddLogin) {
+  EnableSyncForTestAccount();
   backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::RepeatingClosure(), base::DoNothing());
   const JobId kJobId{13388};
@@ -454,7 +500,8 @@
 
   PasswordForm form =
       CreateTestLogin(kTestUsername, kTestPassword, kTestUrl, kTestDateCreated);
-  EXPECT_CALL(*bridge(), AddLogin(form)).WillOnce(Return(kJobId));
+  EXPECT_CALL(*bridge(), AddLogin(form, ExpectSyncingAccount(kTestAccount)))
+      .WillOnce(Return(kJobId));
   backend().AddLoginAsync(form, mock_reply.Get());
 
   PasswordStoreChangeList expected_changes;
@@ -466,6 +513,7 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, CallsBridgeForUpdateLogin) {
+  DisableSyncFeature();
   backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::RepeatingClosure(), base::DoNothing());
   const JobId kJobId{13388};
@@ -473,7 +521,8 @@
 
   PasswordForm form =
       CreateTestLogin(kTestUsername, kTestPassword, kTestUrl, kTestDateCreated);
-  EXPECT_CALL(*bridge(), UpdateLogin(form)).WillOnce(Return(kJobId));
+  EXPECT_CALL(*bridge(), UpdateLogin(form, ExpectLocalAccount()))
+      .WillOnce(Return(kJobId));
   backend().UpdateLoginAsync(form, mock_reply.Get());
 
   PasswordStoreChangeList expected_changes;
@@ -509,6 +558,7 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, DisableAutoSignInForOrigins) {
+  EnableSyncForTestAccount();
   base::HistogramTester histogram_tester;
   constexpr auto kLatencyDelta = base::Milliseconds(123u);
 
@@ -541,7 +591,8 @@
   const JobId kUpdateJobId1{13388};
   // Forms are updated in reverse order.
   EXPECT_CALL(*bridge(),
-              UpdateLogin(FormWithDisabledAutoSignIn(form_to_update2)))
+              UpdateLogin(FormWithDisabledAutoSignIn(form_to_update2),
+                          ExpectSyncingAccount(kTestAccount)))
       .WillOnce(Return(kUpdateJobId1));
 
   consumer().OnCompleteWithLogins(
@@ -561,7 +612,8 @@
                           FormWithDisabledAutoSignIn(form_to_update2)));
   const JobId kUpdateJobId2{13389};
   EXPECT_CALL(*bridge(),
-              UpdateLogin(FormWithDisabledAutoSignIn(form_to_update1)))
+              UpdateLogin(FormWithDisabledAutoSignIn(form_to_update1),
+                          ExpectSyncingAccount(kTestAccount)))
       .WillOnce(Return(kUpdateJobId2));
   consumer().OnLoginsChanged(kUpdateJobId1, change1);
   RunUntilIdle();
@@ -591,14 +643,13 @@
 TEST_F(PasswordStoreAndroidBackendTest, RemoveAllLocalLogins) {
   base::HistogramTester histogram_tester;
   constexpr auto kLatencyDelta = base::Milliseconds(123u);
-
+  EnableSyncForTestAccount();
   backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::RepeatingClosure(), base::DoNothing());
 
   base::MockCallback<LoginsReply> mock_logins_reply;
   const JobId kGetLoginsJobId{13387};
-  EXPECT_CALL(*bridge(),
-              GetAllLogins(PasswordStoreOperationTarget::kLocalStorage))
+  EXPECT_CALL(*bridge(), GetAllLogins(ExpectLocalAccount()))
       .WillOnce(Return(kGetLoginsJobId));
   backend().ClearAllLocalPasswords();
 
@@ -606,9 +657,7 @@
   const JobId kRemoveLoginJobId{13388};
   PasswordForm form_to_delete = CreateTestLogin(
       kTestUsername, kTestPassword, kTestUrl, base::Time::FromTimeT(1500));
-  EXPECT_CALL(
-      *bridge(),
-      RemoveLogin(form_to_delete, PasswordStoreOperationTarget::kLocalStorage))
+  EXPECT_CALL(*bridge(), RemoveLogin(form_to_delete, ExpectLocalAccount()))
       .WillOnce(Return(kRemoveLoginJobId));
 
   task_environment_.FastForwardBy(kLatencyDelta);
@@ -633,14 +682,14 @@
 }
 
 TEST_F(PasswordStoreAndroidBackendTest, RemoveAllLocalLoginsSuccessMetrics) {
+  EnableSyncForTestAccount();
   base::HistogramTester histogram_tester;
   backend().InitBackend(PasswordStoreAndroidBackend::RemoteChangesReceived(),
                         base::RepeatingClosure(), base::DoNothing());
 
   base::MockCallback<LoginsReply> mock_logins_reply;
   const JobId kGetLoginsJobId{13387};
-  EXPECT_CALL(*bridge(),
-              GetAllLogins(PasswordStoreOperationTarget::kLocalStorage))
+  EXPECT_CALL(*bridge(), GetAllLogins(ExpectLocalAccount()))
       .WillOnce(Return(kGetLoginsJobId));
   backend().ClearAllLocalPasswords();
 
diff --git a/chrome/browser/password_manager/android/password_sync_controller_delegate_android.cc b/chrome/browser/password_manager/android/password_sync_controller_delegate_android.cc
index 83183a25..f43b846c 100644
--- a/chrome/browser/password_manager/android/password_sync_controller_delegate_android.cc
+++ b/chrome/browser/password_manager/android/password_sync_controller_delegate_android.cc
@@ -13,8 +13,8 @@
 namespace password_manager {
 
 PasswordSyncControllerDelegateAndroid::PasswordSyncControllerDelegateAndroid(
-    std::unique_ptr<PasswordStoreBackend::SyncDelegate> sync_delegate)
-    : sync_delegate_(std::move(sync_delegate)) {}
+    PasswordStoreBackend::SyncDelegate* sync_delegate)
+    : sync_delegate_(sync_delegate) {}
 
 PasswordSyncControllerDelegateAndroid::
     ~PasswordSyncControllerDelegateAndroid() = default;
diff --git a/chrome/browser/password_manager/android/password_sync_controller_delegate_android.h b/chrome/browser/password_manager/android/password_sync_controller_delegate_android.h
index 35c3fb07..504e499 100644
--- a/chrome/browser/password_manager/android/password_sync_controller_delegate_android.h
+++ b/chrome/browser/password_manager/android/password_sync_controller_delegate_android.h
@@ -25,7 +25,7 @@
     : public syncer::ModelTypeControllerDelegate {
  public:
   explicit PasswordSyncControllerDelegateAndroid(
-      std::unique_ptr<PasswordStoreBackend::SyncDelegate> sync_delegate);
+      PasswordStoreBackend::SyncDelegate* sync_delegate);
   PasswordSyncControllerDelegateAndroid(
       const PasswordSyncControllerDelegateAndroid&) = delete;
   PasswordSyncControllerDelegateAndroid(
@@ -59,7 +59,7 @@
 
   base::WeakPtr<syncer::ModelTypeControllerDelegate> GetWeakPtrToBaseClass();
 
-  std::unique_ptr<PasswordStoreBackend::SyncDelegate> sync_delegate_;
+  raw_ptr<PasswordStoreBackend::SyncDelegate> sync_delegate_;
 
   // Current sync status, absl::nullopt until UpdateSyncStatusOnStartUp() is
   // called. This value is used to distinguish between sync setup on startup and
diff --git a/chrome/browser/password_manager/android/password_sync_controller_delegate_android_unittest.cc b/chrome/browser/password_manager/android/password_sync_controller_delegate_android_unittest.cc
index ecc8ada8..c74094b2 100644
--- a/chrome/browser/password_manager/android/password_sync_controller_delegate_android_unittest.cc
+++ b/chrome/browser/password_manager/android/password_sync_controller_delegate_android_unittest.cc
@@ -25,27 +25,21 @@
   PasswordSyncControllerDelegateAndroidTest() {
     sync_controller_delegate_ =
         std::make_unique<PasswordSyncControllerDelegateAndroid>(
-            CreateSyncDelegate());
+            &sync_delegate_);
   }
 
   ~PasswordSyncControllerDelegateAndroidTest() override = default;
 
   void RunUntilIdle() { task_environment_.RunUntilIdle(); }
 
-  MockPasswordBackendSyncDelegate* sync_delegate() { return sync_delegate_; }
+  MockPasswordBackendSyncDelegate* sync_delegate() { return &sync_delegate_; }
   PasswordSyncControllerDelegateAndroid* sync_controller_delegate() {
     return sync_controller_delegate_.get();
   }
 
  private:
-  std::unique_ptr<MockPasswordBackendSyncDelegate> CreateSyncDelegate() {
-    auto unique_delegate = std::make_unique<MockPasswordBackendSyncDelegate>();
-    sync_delegate_ = unique_delegate.get();
-    return unique_delegate;
-  }
-
   base::test::SingleThreadTaskEnvironment task_environment_;
-  raw_ptr<MockPasswordBackendSyncDelegate> sync_delegate_;
+  MockPasswordBackendSyncDelegate sync_delegate_;
   std::unique_ptr<PasswordSyncControllerDelegateAndroid>
       sync_controller_delegate_;
 };
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 4a5ff00..e00ba55 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -18,8 +18,6 @@
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/app_restore/full_restore_prefs.h"
-#include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/browsing_data/browsing_data_lifetime_policy_handler.h"
 #include "chrome/browser/first_party_sets/first_party_sets_pref_names.h"
 #include "chrome/browser/net/disk_cache_dir_policy_handler.h"
@@ -144,8 +142,10 @@
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
 #include "chrome/browser/apps/app_service/webapk/webapk_prefs.h"
 #include "chrome/browser/ash/accessibility/magnifier_type.h"
+#include "chrome/browser/ash/app_restore/full_restore_prefs.h"
 #include "chrome/browser/ash/borealis/borealis_prefs.h"
 #include "chrome/browser/ash/crostini/crostini_pref_names.h"
+#include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_policy_handler.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/ash/policy/handlers/configuration_policy_handler_ash.h"
diff --git a/chrome/browser/predictors/autocomplete_action_predictor.cc b/chrome/browser/predictors/autocomplete_action_predictor.cc
index 76f27818..ca84398 100644
--- a/chrome/browser/predictors/autocomplete_action_predictor.cc
+++ b/chrome/browser/predictors/autocomplete_action_predictor.cc
@@ -213,7 +213,7 @@
     PrerenderManager::CreateForWebContents(&web_contents);
     auto* prerender_manager = PrerenderManager::FromWebContents(&web_contents);
     search_prerender_handle_ =
-        prerender_manager->StartPrerenderAutocompleteMatch(match);
+        prerender_manager->StartPrerenderSearchSuggestion(match);
   } else if (prerender_utils::IsDirectUrlInputPrerenderEnabled()) {
     // Check whether preloading is enabled. If users disable this
     // setting, it means users do not want to preload pages.
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 901a9ff..ec49b39 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -37,14 +37,12 @@
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/permission_bubble_media_access_handler.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
 #include "chrome/browser/memory/enterprise_memory_limit_pref_observer.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
 #include "chrome/browser/net/net_error_tab_helper.h"
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/secure_dns_util.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/notifications/notification_channels_provider_android.h"
 #include "chrome/browser/notifications/notification_display_service_impl.h"
 #include "chrome/browser/notifications/notifier_state_tracker.h"
 #include "chrome/browser/notifications/platform_notification_service_impl.h"
@@ -228,6 +226,7 @@
 #include "chrome/browser/first_run/android/first_run_prefs.h"
 #include "chrome/browser/lens/android/lens_prefs.h"
 #include "chrome/browser/media/android/cdm/media_drm_origin_id_manager.h"
+#include "chrome/browser/notifications/notification_channels_provider_android.h"
 #include "chrome/browser/ssl/known_interception_disclosure_infobar_delegate.h"
 #include "chrome/browser/video_tutorials/prefs.h"
 #include "components/cdm/browser/media_drm_storage_impl.h"  // nogncheck crbug.com/1125897
@@ -399,6 +398,7 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_MAC)
+#include "chrome/browser/media/webrtc/system_media_capture_permissions_stats_mac.h"
 #include "chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.h"
 #include "chrome/browser/ui/cocoa/confirm_quit.h"
 #include "chrome/browser/web_applications/app_shim_registry_mac.h"
diff --git a/chrome/browser/prefs/chrome_command_line_pref_store.cc b/chrome/browser/prefs/chrome_command_line_pref_store.cc
index d66558a2..3fdef6b 100644
--- a/chrome/browser/prefs/chrome_command_line_pref_store.cc
+++ b/chrome/browser/prefs/chrome_command_line_pref_store.cc
@@ -11,7 +11,6 @@
 #include <utility>
 #include <vector>
 
-#include "ash/constants/ash_switches.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
@@ -20,8 +19,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/borealis/borealis_prefs.h"
-#include "chrome/browser/ash/borealis/borealis_switches.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/browser_sync/browser_sync_switches.h"
@@ -39,6 +36,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_switches.h"
+#include "chrome/browser/ash/borealis/borealis_prefs.h"
+#include "chrome/browser/ash/borealis/borealis_switches.h"
 #endif
 
 const CommandLinePrefStore::SwitchToPreferenceMapEntry
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index 528f482d5..d461e35 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -170,7 +170,7 @@
 }
 
 base::WeakPtr<content::PrerenderHandle>
-PrerenderManager::StartPrerenderAutocompleteMatch(
+PrerenderManager::StartPrerenderSearchSuggestion(
     const AutocompleteMatch& match) {
   DCHECK(AutocompleteMatch::IsSearchType(match.type));
 
@@ -216,7 +216,7 @@
 
   // Skip changing the prerender URL in tests as they may not have Profile or
   // TemplateURLServiceFactory. In that case, the callers of
-  // StartPrerenderAutocompleteMatch() should ensure the prerender URL is valid
+  // StartPrerenderSearchSuggestion() should ensure the prerender URL is valid
   // instead.
   if (!skip_template_url_service_for_testing_) {
     TemplateURLService* template_url_service =
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 4226e35b..68d581e 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -51,7 +51,7 @@
   // The entry of prerender.
   // Calling this method will lead to the cancellation of the previous prerender
   // if the given `match`'s search terms differ from the ongoing one's.
-  base::WeakPtr<content::PrerenderHandle> StartPrerenderAutocompleteMatch(
+  base::WeakPtr<content::PrerenderHandle> StartPrerenderSearchSuggestion(
       const AutocompleteMatch& match);
 
   // The entry of direct url input prerender.
diff --git a/chrome/browser/prerender/prerender_manager_unittest.cc b/chrome/browser/prerender/prerender_manager_unittest.cc
index ab8eb6ca..0312993 100644
--- a/chrome/browser/prerender/prerender_manager_unittest.cc
+++ b/chrome/browser/prerender/prerender_manager_unittest.cc
@@ -91,7 +91,7 @@
       *GetActiveWebContents());
   AutocompleteMatch match =
       CreateSearchSuggestionMatch("/title1.html", "pre", "prerender");
-  prerender_manager()->StartPrerenderAutocompleteMatch(match);
+  prerender_manager()->StartPrerenderSearchSuggestion(match);
   registry_observer.WaitForTrigger(prerendering_url);
   int prerender_host_id = prerender_helper().GetHostForUrl(prerendering_url);
   EXPECT_NE(prerender_host_id, content::RenderFrameHost::kNoFrameTreeNodeId);
@@ -106,7 +106,7 @@
       *GetActiveWebContents());
   AutocompleteMatch match =
       CreateSearchSuggestionMatch("/title1.html", "pre", "prefetch");
-  prerender_manager()->StartPrerenderAutocompleteMatch(match);
+  prerender_manager()->StartPrerenderSearchSuggestion(match);
 
   registry_observer.WaitForTrigger(prerendering_url);
   int prerender_host_id = prerender_helper().GetHostForUrl(prerendering_url);
@@ -116,7 +116,7 @@
   GURL prerendering_url2 =
       GetSearchSuggestionUrl("/title1.html", "prer", "prerender");
   match = CreateSearchSuggestionMatch("/title1.html", "prer", "prerender");
-  prerender_manager()->StartPrerenderAutocompleteMatch(match);
+  prerender_manager()->StartPrerenderSearchSuggestion(match);
   host_observer.WaitForDestroyed();
   registry_observer.WaitForTrigger(prerendering_url2);
   EXPECT_TRUE(prerender_manager()->search_prerender_handle_for_testing());
@@ -134,12 +134,12 @@
       *GetActiveWebContents());
   AutocompleteMatch match =
       CreateSearchSuggestionMatch("/title1.html", "pre", "prerender");
-  prerender_manager()->StartPrerenderAutocompleteMatch(match);
+  prerender_manager()->StartPrerenderSearchSuggestion(match);
   registry_observer.WaitForTrigger(prerendering_url);
   int prerender_host_id = prerender_helper().GetHostForUrl(prerendering_url);
   EXPECT_NE(prerender_host_id, content::RenderFrameHost::kNoFrameTreeNodeId);
   match = CreateSearchSuggestionMatch("/title1.html", "prer", "prerender");
-  prerender_manager()->StartPrerenderAutocompleteMatch(match);
+  prerender_manager()->StartPrerenderSearchSuggestion(match);
   EXPECT_TRUE(prerender_manager()->search_prerender_handle_for_testing());
 
   // The created prerender for `prerendering_url` still exists, so the
@@ -156,7 +156,7 @@
       *GetActiveWebContents());
   AutocompleteMatch match =
       CreateSearchSuggestionMatch("/title1.html", "pre", "prerende");
-  prerender_manager()->StartPrerenderAutocompleteMatch(match);
+  prerender_manager()->StartPrerenderSearchSuggestion(match);
 
   registry_observer.WaitForTrigger(prerendering_url);
   int prerender_host_id = prerender_helper().GetHostForUrl(prerendering_url);
diff --git a/chrome/browser/printing/print_backend_service_manager.cc b/chrome/browser/printing/print_backend_service_manager.cc
index e70d6d3b1..d462d06 100644
--- a/chrome/browser/printing/print_backend_service_manager.cc
+++ b/chrome/browser/printing/print_backend_service_manager.cc
@@ -27,6 +27,7 @@
 #include "printing/backend/print_backend.h"
 
 #if BUILDFLAG(IS_WIN)
+#include "printing/backend/win_helper.h"
 #include "printing/printed_page_win.h"
 #endif
 
@@ -467,12 +468,41 @@
   return clients_count;
 }
 
+#if BUILDFLAG(IS_WIN)
+bool PrintBackendServiceManager::PrinterDriverKnownToRequireElevatedPrivilege(
+    const std::string& printer_name,
+    ClientType client_type) {
+  // Any Windows printer driver which causes a UI dialog to be displayed does
+  // not work if printing is started from within a sandboxed environment.
+  // crbug.com/1243873
+  switch (client_type) {
+    case ClientType::kQuery:
+      return false;
+    case ClientType::kQueryWithUi:
+      // Guaranteed to display the system print dialog.
+      return true;
+    case ClientType::kPrintDocument:
+      // Drivers with a print port that results in saving to a file will cause
+      // a system dialog to be displayed.
+      return DoesDriverDisplayFileDialogForPrinting(printer_name);
+  }
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 const mojo::Remote<mojom::PrintBackendService>&
 PrintBackendServiceManager::GetService(const std::string& printer_name,
                                        ClientType client_type,
                                        bool* is_sandboxed) {
+  // Determine if sandboxing is appropriate.  This might be already known for
+  // certain drivers/configurations, or learned during runtime.
   bool should_sandbox =
       !PrinterDriverFoundToRequireElevatedPrivilege(printer_name);
+#if BUILDFLAG(IS_WIN)
+  bool avoid_sandbox =
+      PrinterDriverKnownToRequireElevatedPrivilege(printer_name, client_type);
+  if (avoid_sandbox)
+    should_sandbox = false;
+#endif
   *is_sandboxed = should_sandbox;
 
   if (sandboxed_service_remote_for_test_) {
@@ -489,10 +519,15 @@
   // be needed by client callers.
   DCHECK_GT(GetClientsRegisteredCount(), 0u);
 
-  // On the first print make note that so far no drivers have required fallback.
+  // On the first print make note that so far no drivers have been discovered
+  // to require fallback beyond any predetermined known cases.
   static bool first_print = true;
   if (first_print) {
+#if BUILDFLAG(IS_WIN)
+    DCHECK(should_sandbox || avoid_sandbox);
+#else
     DCHECK(should_sandbox);
+#endif
     first_print = false;
     base::UmaHistogramBoolean(
         kPrintBackendRequiresElevatedPrivilegeHistogramName, /*sample=*/false);
diff --git a/chrome/browser/printing/print_backend_service_manager.h b/chrome/browser/printing/print_backend_service_manager.h
index e2166c5..ad92c9f 100644
--- a/chrome/browser/printing/print_backend_service_manager.h
+++ b/chrome/browser/printing/print_backend_service_manager.h
@@ -238,6 +238,16 @@
   // Get the total number of clients registered.
   size_t GetClientsRegisteredCount() const;
 
+#if BUILDFLAG(IS_WIN)
+  // Query if printer driver has known reasons for requiring elevated
+  // privileges in order to operate.  In these cases relying upon fallback
+  // after an access-denied error is not preferable.  Any such reasons are
+  // platform specific.
+  bool PrinterDriverKnownToRequireElevatedPrivilege(
+      const std::string& printer_name,
+      ClientType client_type);
+#endif
+
   // Acquires a remote handle to the Print Backend Service instance, launching a
   // process to host the service if necessary. `is_sandboxed` is set to indicate
   // if the service was launched within a sandbox.
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index 8b00920c..62ac93f 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -40,7 +40,6 @@
 #include "base/version.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/background/background_contents_service_factory.h"
 #include "chrome/browser/background_fetch/background_fetch_delegate_factory.h"
 #include "chrome/browser/background_fetch/background_fetch_delegate_impl.h"
@@ -185,6 +184,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/components/account_manager/account_manager_factory.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
+#include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/ash/app_mode/app_launch_utils.h"
 #include "chrome/browser/ash/arc/session/arc_service_launcher.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 1db57b1..c8f667a 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -38,7 +38,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/accessibility/accessibility_labels_service.h"
 #include "chrome/browser/accessibility/accessibility_labels_service_factory.h"
-#include "chrome/browser/ash/account_manager/child_account_type_changed_user_data.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
@@ -145,6 +144,7 @@
 #include "ash/components/arc/session/arc_management_transition.h"
 #include "ash/constants/ash_switches.h"
 #include "chrome/browser/ash/account_manager/account_manager_policy_controller_factory.h"
+#include "chrome/browser/ash/account_manager/child_account_type_changed_user_data.h"
 #include "chrome/browser/ash/arc/policy/arc_policy_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager.cc b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
index 33baf6d4..a60ee48 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager.cc
@@ -312,8 +312,8 @@
 
   // Check if messages feature is enabled
   if (multidevice_setup_client->GetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kMessages) !=
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser) {
+          ash::multidevice_setup::mojom::Feature::kMessages) !=
+      ash::multidevice_setup::mojom::FeatureState::kEnabledByUser) {
     return false;
   }
 
diff --git a/chrome/browser/push_messaging/push_messaging_notification_manager_unittest.cc b/chrome/browser/push_messaging/push_messaging_notification_manager_unittest.cc
index 8375a05..ee48fe5 100644
--- a/chrome/browser/push_messaging/push_messaging_notification_manager_unittest.cc
+++ b/chrome/browser/push_messaging/push_messaging_notification_manager_unittest.cc
@@ -67,8 +67,8 @@
   auto fake_multidevice_setup_client = std::make_unique<
       chromeos::multidevice_setup::FakeMultiDeviceSetupClient>();
   fake_multidevice_setup_client->SetFeatureState(
-      chromeos::multidevice_setup::mojom::Feature::kMessages,
-      chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser);
+      ash::multidevice_setup::mojom::Feature::kMessages,
+      ash::multidevice_setup::mojom::FeatureState::kEnabledByUser);
 
   PushMessagingNotificationManager manager(profile());
   manager.SetTestMultiDeviceSetupClient(fake_multidevice_setup_client.get());
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index a7c6eb6..c397e5f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -62,6 +62,7 @@
 #include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_plugin_guest_manager.h"
 #include "content/public/browser/browser_thread.h"
@@ -621,8 +622,8 @@
     ASSERT_TRUE(provider->install_finalizer().CanUserUninstallWebApp(app_id));
     provider->install_finalizer().UninstallWebApp(
         app_id, webapps::WebappUninstallSource::kAppMenu,
-        base::BindLambdaForTesting([&](bool uninstalled) {
-          EXPECT_TRUE(uninstalled);
+        base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+          EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
           run_loop.Quit();
         }));
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index b598754..cfe5781 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -40,13 +40,10 @@
   "background/color.js",
   "background/command_handler_interface.js",
   "background/custom_automation_event.js",
-  "background/desktop_automation_interface.js",
   "background/editing/editable_line.js",
-  "background/editing/editing.js",
   "background/editing/intent_handler.js",
   "background/event_source.js",
   "background/gesture_command_data.js",
-  "background/gesture_interface.js",
   "background/injected_script_loader.js",
   "background/keyboard_handler.js",
   "background/keymaps/key_map.js",
@@ -120,13 +117,16 @@
   "background/braille_command_handler.js",
   "background/command_handler.js",
   "background/desktop_automation_handler.js",
+  "background/desktop_automation_interface.js",
   "background/download_handler.js",
   "background/earcon_engine.js",
   "background/earcons.js",
+  "background/editing/editing.js",
   "background/es6_loader.js",
   "background/find_handler.js",
   "background/focus_automation_handler.js",
   "background/gesture_command_handler.js",
+  "background/gesture_interface.js",
   "background/live_regions.js",
   "background/media_automation_handler.js",
   "background/range_automation_handler.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 459485d..0566f5f 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -7,6 +7,7 @@
 import {BrailleCommandHandler} from './braille_command_handler.js';
 import {CommandHandler} from './command_handler.js';
 import {DesktopAutomationHandler} from './desktop_automation_handler.js';
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 import {DownloadHandler} from './download_handler.js';
 import {Earcons} from './earcons.js';
 import {FindHandler} from './find_handler.js';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
index a78f7118..aff23f83 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -25,6 +25,9 @@
         'BrailleCommandHandler',
         '/chromevox/background/braille_command_handler.js');
     await importModule(
+        'DesktopAutomationInterface',
+        '/chromevox/background/desktop_automation_interface.js');
+    await importModule(
         'GestureCommandHandler',
         '/chromevox/background/gesture_command_handler.js');
     await importModule(
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
index cc8e66f..e79e77f0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille_command_handler.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview ChromeVox braille commands.
  */
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
index 16ffe1e..40b09ef82 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -5,6 +5,8 @@
 /**
  * @fileoverview ChromeVox commands.
  */
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
+import {GestureInterface} from './gesture_interface.js';
 import {SmartStickyMode} from './smart_sticky_mode.js';
 
 const ActionType = chrome.automation.ActionType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
index cdadf70..5e75335 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -5,6 +5,8 @@
 /**
  * @fileoverview Handles automation from a desktop automation node.
  */
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
+import {TextEditHandler} from './editing/editing.js';
 
 const ActionType = chrome.automation.ActionType;
 const AutomationNode = chrome.automation.AutomationNode;
@@ -22,7 +24,7 @@
 
     /**
      * The object that speaks changes to an editable text field.
-     * @type {editing.TextEditHandler}
+     * @type {TextEditHandler}
      * @private
      */
     this.textEditHandler_ = null;
@@ -120,7 +122,7 @@
     }.bind(this));
   }
 
-  /** @type {editing.TextEditHandler} */
+  /** @type {TextEditHandler} */
   get textEditHandler() {
     return this.textEditHandler_;
   }
@@ -784,7 +786,7 @@
     }
 
     if (!this.textEditHandler_ || this.textEditHandler_.node !== target) {
-      this.textEditHandler_ = editing.TextEditHandler.createForNode(target);
+      this.textEditHandler_ = TextEditHandler.createForNode(target);
     }
 
     return !!this.textEditHandler_;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
index 6d0d10fe..6f6264ba 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -19,6 +19,9 @@
     await importModule(
         'DesktopAutomationHandler',
         '/chromevox/background/desktop_automation_handler.js');
+    await importModule(
+        'DesktopAutomationInterface',
+        '/chromevox/background/desktop_automation_interface.js');
     await new Promise(r => {
       chrome.automation.getDesktop(desktop => {
         this.handler_ = DesktopAutomationInterface.instance;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
index ad4c0cc..33ee107 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_interface.js
@@ -5,14 +5,10 @@
 /**
  * @fileoverview Interface to prevent circular dependencies.
  */
+import {TextEditHandler} from './editing/editing.js';
 
-goog.provide('DesktopAutomationInterface');
-
-goog.require('BaseAutomationHandler');
-goog.require('editing.TextEditHandler');
-
-DesktopAutomationInterface = class extends BaseAutomationHandler {
-  /** @type {editing.TextEditHandler} */
+export class DesktopAutomationInterface extends BaseAutomationHandler {
+  /** @type {TextEditHandler} */
   get textEditHandler() {}
 
   /**
@@ -20,7 +16,7 @@
    * @param {boolean} val
    */
   ignoreDocumentSelectionFromAction(val) {}
-};
+}
 
 /**
  * @type {DesktopAutomationInterface}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
index d62d186..8bfa5e9 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing.js
@@ -7,23 +7,6 @@
  * appropriate spoken and braille feedback.
  */
 
-goog.provide('editing.TextEditHandler');
-
-goog.require('AutomationTreeWalker');
-goog.require('AutomationUtil');
-goog.require('Color');
-goog.require('IntentHandler');
-goog.require('Output');
-goog.require('OutputEventType');
-goog.require('TreePathRecoveryStrategy');
-goog.require('cursors.Cursor');
-goog.require('cursors.Range');
-goog.require('editing.EditableLine');
-goog.require('BrailleBackground');
-goog.require('ChromeVoxEditableTextBase');
-goog.require('LibLouis.FormType');
-
-goog.scope(function() {
 const AutomationEvent = chrome.automation.AutomationEvent;
 const AutomationIntent = chrome.automation.AutomationIntent;
 const AutomationNode = chrome.automation.AutomationNode;
@@ -41,7 +24,7 @@
  * A handler for automation events in a focused text field or editable root
  * such as a |contenteditable| subtree.
  */
-editing.TextEditHandler = class {
+export class TextEditHandler {
   /**
    * @param {!AutomationNode} node
    */
@@ -165,16 +148,16 @@
   /**
    * @param {!AutomationNode} node The root editable node, i.e. the root of a
    *     contenteditable subtree or a text field.
-   * @return {editing.TextEditHandler}
+   * @return {TextEditHandler}
    */
   static createForNode(node) {
     if (!node.state.editable) {
       throw new Error('Expected editable node.');
     }
 
-    return new editing.TextEditHandler(node);
+    return new TextEditHandler(node);
   }
-};
+}
 
 
 /**
@@ -976,7 +959,11 @@
     ChromeVoxState.addObserver(this);
   }
 
-  /** @override */
+  /**
+   * @param {cursors.Range} range
+   * @param {boolean=} opt_fromEditing
+   * @override
+   */
   onCurrentRangeChanged(range, opt_fromEditing) {
     const inputType = range && range.start.node.inputType;
     if (inputType === 'email' || inputType === 'url') {
@@ -994,4 +981,3 @@
  * @private {ChromeVoxStateObserver}
  */
 editing.observer_ = new editing.EditingChromeVoxStateObserver();
-});  // goog.scope
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
index d213e232..cda2cae2 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/editing/editing_test.js
@@ -15,6 +15,17 @@
     super();
   }
 
+  /** @override */
+  async setUpDeferred() {
+    await super.setUpDeferred();
+    await importModule(
+        'DesktopAutomationInterface',
+        '/chromevox/background/desktop_automation_interface.js');
+    await importModule(
+        'TextEditHandler', '/chromevox/background/editing/editing.js');
+    await super.setUpDeferred();
+  }
+
   press(keyCode, modifiers) {
     return function() {
       EventGenerator.sendKeyPress(keyCode, modifiers);
@@ -1927,7 +1938,7 @@
         let didThrow = false;
         let handler;
         try {
-          handler = editing.TextEditHandler(input);
+          handler = new TextEditHandler(input);
         } catch (e) {
           didThrow = true;
         }
@@ -1937,7 +1948,7 @@
         htmlAttributes = {};
         htmlTag = '';
         state = {editable: true};
-        handler = new editing.TextEditHandler(input);
+        handler = new TextEditHandler(input);
         assertEquals(
             'AutomationEditableText', handler.editableText_.constructor.name,
             'Incorrect backing object for simple editable.');
@@ -1946,7 +1957,7 @@
         htmlAttributes = {};
         htmlTag = '';
         state = {editable: true, multiline: true};
-        handler = new editing.TextEditHandler(input);
+        handler = new TextEditHandler(input);
         assertEquals(
             'AutomationEditableText', handler.editableText_.constructor.name,
             'Incorrect object for multiline editable.');
@@ -1955,7 +1966,7 @@
         htmlAttributes = {};
         htmlTag = 'textarea';
         state = {editable: true};
-        handler = new editing.TextEditHandler(input);
+        handler = new TextEditHandler(input);
         assertEquals(
             'AutomationRichEditableText',
             handler.editableText_.constructor.name,
@@ -1965,7 +1976,7 @@
         htmlAttributes = {};
         htmlTag = '';
         state = {editable: true, richlyEditable: true};
-        handler = new editing.TextEditHandler(input);
+        handler = new TextEditHandler(input);
         assertEquals(
             'AutomationRichEditableText',
             handler.editableText_.constructor.name,
@@ -1975,7 +1986,7 @@
         htmlAttributes = {contenteditable: ''};
         htmlTag = '';
         state = {editable: true};
-        handler = new editing.TextEditHandler(input);
+        handler = new TextEditHandler(input);
         assertEquals(
             'AutomationRichEditableText',
             handler.editableText_.constructor.name,
@@ -1986,7 +1997,7 @@
         htmlAttributes = {contenteditable: 'true'};
         htmlTag = '';
         state = {editable: true};
-        handler = new editing.TextEditHandler(input);
+        handler = new TextEditHandler(input);
         assertEquals(
             'AutomationRichEditableText',
             handler.editableText_.constructor.name,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
index 142fc68..66110e0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_command_handler.js
@@ -5,6 +5,7 @@
 /**
  * @fileoverview Handles gesture-based commands.
  */
+import {GestureInterface} from './gesture_interface.js';
 import {PointerHandler} from './pointer_handler.js';
 
 const RoleType = chrome.automation.RoleType;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js
index 551ae2df..2dd239f55 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/gesture_interface.js
@@ -7,11 +7,8 @@
  * CommandHandler and GestureCommandHandler.
  */
 
-goog.provide('GestureInterface');
+export const GestureInterface = {};
 
-goog.require('GestureGranularity');
-
-goog.scope(function() {
 /** @return {GestureGranularity} */
 GestureInterface.getGranularity = function() {
   if (GestureInterface.granularityGetter) {
@@ -35,4 +32,3 @@
 
 /** @public {?function(GestureGranularity)} */
 GestureInterface.granularitySetter = null;
-});  // goog.scope
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 4121444..bf4db0da 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -7,11 +7,13 @@
  */
 
 goog.require('AutomationPredicate');
+goog.require('AutomationTreeWalker');
 goog.require('AutomationUtil');
 goog.require('AutoScrollHandler');
 goog.require('AutomationObjectConstructorInstaller');
 goog.require('BackgroundKeyboardHandler');
 goog.require('BaseAutomationHandler');
+goog.require('BrailleBackground');
 goog.require('BrailleCommandData');
 goog.require('BrailleKeyCommand');
 goog.require('ChromeVox');
@@ -25,15 +27,15 @@
 goog.require('CommandHandlerInterface');
 goog.require('CommandStore');
 goog.require('CustomAutomationEvent');
-goog.require('DesktopAutomationInterface');
-goog.require('editing.TextEditHandler');
 goog.require('EventGenerator');
 goog.require('EventSourceState');
 goog.require('ExtensionBridge');
 goog.require('GestureCommandData');
-goog.require('GestureInterface');
+goog.require('GestureGranularity');
+goog.require('IntentHandler');
 goog.require('JaPhoneticMap');
 goog.require('KeyCode');
+goog.require('LibLouis.FormType');
 goog.require('LocaleOutputHelper');
 goog.require('LogStore');
 goog.require('MathHandler');
@@ -46,3 +48,5 @@
 goog.require('TreePathRecoveryStrategy');
 goog.require('constants');
 goog.require('cursors.Cursor');
+goog.require('cursors.Range');
+goog.require('editing.EditableLine');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
index 657247a..6718222 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
@@ -6,6 +6,7 @@
  * @fileoverview ChromeVox pointer handler. A pointer, in this context, is
  * either user touch or mouse input.
  */
+import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 
 const AutomationEvent = chrome.automation.AutomationEvent;
 const EventType = chrome.automation.EventType;
diff --git a/chrome/browser/resources/chromeos/drive_internals.html b/chrome/browser/resources/chromeos/drive_internals.html
index 7f93740..277adc3 100644
--- a/chrome/browser/resources/chromeos/drive_internals.html
+++ b/chrome/browser/resources/chromeos/drive_internals.html
@@ -34,6 +34,12 @@
         </label>
       </div>
       <div>
+        <label>
+          Mirroring
+          <input type="checkbox" id="mirroring-toggle">
+        </label>
+      </div>
+      <div>
         Tracing
         <button id="button-enable-tracing">Enable</button>
         <button id="button-disable-tracing">Disable</button>
diff --git a/chrome/browser/resources/chromeos/drive_internals.js b/chrome/browser/resources/chromeos/drive_internals.js
index 82198c9..50a03da 100644
--- a/chrome/browser/resources/chromeos/drive_internals.js
+++ b/chrome/browser/resources/chromeos/drive_internals.js
@@ -95,6 +95,10 @@
   $('verbose-logging-toggle').checked = enabled;
 }
 
+function updateMirroring(enabled) {
+  $('mirroring-toggle').checked = enabled;
+}
+
 function updateStartupArguments(args) {
   $('startup-arguments-input').value = args;
 }
@@ -305,6 +309,10 @@
     chrome.send('setVerboseLoggingEnabled', [e.target.checked]);
   });
 
+  $('mirroring-toggle').addEventListener('change', function(e) {
+    chrome.send('setMirroringEnabled', [e.target.checked]);
+  });
+
   $('startup-arguments-form').addEventListener('submit', function(e) {
     e.preventDefault();
     $('arguments-status-text').textContent = 'applying...';
diff --git a/chrome/browser/resources/chromeos/login/screens/common/multidevice_setup.js b/chrome/browser/resources/chromeos/login/screens/common/multidevice_setup.js
index 3756b25..a7bcec30 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/multidevice_setup.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/multidevice_setup.js
@@ -13,7 +13,7 @@
 
   constructor() {
     /**
-     * @private {?chromeos.multideviceSetup.mojom.
+     * @private {?ash.multideviceSetup.mojom.
      *               PrivilegedHostDeviceSetterRemote}
      */
     this.remote_ = null;
@@ -31,7 +31,8 @@
     assert(!opt_authToken);
 
     if (!this.remote_) {
-      this.remote_ = chromeos.multideviceSetup.mojom.PrivilegedHostDeviceSetter.getRemote();
+      this.remote_ =
+          ash.multideviceSetup.mojom.PrivilegedHostDeviceSetter.getRemote();
     }
 
     return /** @type {!Promise<{success: boolean}>} */ (
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
index 12c2af9..212dd8a5 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
@@ -44,22 +44,28 @@
         border-top: var(--cr-separator-line);
         margin: var(--cr-form-field-bottom-spacing) 0;
       }
+
+      :host([dialog-mode='password_view']) cr-input,
+      :host([dialog-mode='password_view']) settings-textarea {
+        --cr-input-background-color: transparent;
+        --cr-input-readonly-opacity: 1;
+      }
     </style>
     <cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
-      <div slot="title" id="title">[[getTitle_(dialogMode_)]]</div>
+      <div slot="title" id="title">[[getTitle_(dialogMode)]]</div>
       <div slot="body">
         <div hidden="[[!shouldShowStorageDetails_(isAccountStoreUser,
-            dialogMode_)]]" id="storageDetails">
-          [[getStorageDetailsMessage_(dialogMode_)]]
+            dialogMode)]]" id="storageDetails">
+          [[getStorageDetailsMessage_(dialogMode)]]
         </div>
         <select class="md-select" id="storePicker"
             autofocus="[[shouldShowStorePicker_(isAccountStoreUser,
-                dialogMode_)]]"
+                dialogMode)]]"
             aria-description="$i18n{addPasswordStorePickerA11yDescription}"
             hidden="[[!shouldShowStorePicker_(isAccountStoreUser,
-                dialogMode_)]]">
+                dialogMode)]]">
           <option value="[[storeOptionAccountValue]]">
-            [[getStoreOptionAccountText_(accountEmail, dialogMode_)]]
+            [[getStoreOptionAccountText_(accountEmail, dialogMode)]]
           </option>
           <option value="[[storeOptionDeviceValue]]">
             $i18n{addPasswordStoreOptionDevice}
@@ -68,45 +74,71 @@
         <cr-input id="websiteInput" label="$i18n{editPasswordWebsiteLabel}"
             class$="[[getClassForWebsiteInput_(websiteInputErrorMessage_)]]"
             autofocus="[[shouldAutofocusWebsiteInput_(isAccountStoreUser,
-                dialogMode_)]]"
-            value="[[getWebsite_(dialogMode_, existingEntry.urls.link)]]"
+                dialogMode)]]"
+            value="[[getWebsite_(dialogMode, existingEntry.urls.link)]]"
             on-blur="onWebsiteInputBlur_" placeholder="example.com"
             on-input="validateWebsite_"
-            required="[[isWebsiteEditable_(dialogMode_)]]"
+            required="[[isWebsiteEditable_(dialogMode)]]"
             invalid="[[websiteInputInvalid_]]"
             error-message="[[websiteInputErrorMessage_]]"
-            readonly="[[!isWebsiteEditable_(dialogMode_)]]">
+            readonly="[[!isWebsiteEditable_(dialogMode)]]"
+            hidden="[[isInPasswordViewMode_]]">
         </cr-input>
         <cr-input id="usernameInput" label="$i18n{editPasswordUsernameLabel}"
-            readonly="[[isInViewMode_]]" invalid="[[usernameInputInvalid_]]"
+            readonly="[[isInViewMode_]]"
+            invalid="[[usernameInputInvalid_]]"
             value="{{username_}}"
             error-message="[[getUsernameErrorMessage_(websiteUrls_.shown)]]">
+        <template is="dom-if" if="[[isInPasswordViewMode_]]">
+          <cr-icon-button id="copyUsernameButton"
+              class="icon-copy-content"
+              slot="suffix"
+              title="$i18n{copyUsername}"
+              on-click="onCopyUsernameButtonClick_">
+          </cr-icon-button>
+        </template>
         </cr-input>
         <a id="viewExistingPasswordLink" is="action-link"
             on-click="onViewExistingPasswordClick_"
             aria-description="[[getViewExistingPasswordAriaDescription_(
                 websiteUrls_.shown, username_)]]"
-            hidden="[[!usernameInputInvalid_]]">$i18n{viewExistingPassword}</a>
+            hidden="[[!usernameInputInvalid_]]">
+          $i18n{viewExistingPassword}
+        </a>
         <cr-input id="passwordInput" label="$i18n{editPasswordPasswordLabel}"
-            type="[[getPasswordInputType_(isInViewMode_, isPasswordVisible_)]]"
-            value="{{password_}}" class="password-input"
-            readonly="[[isInViewMode_]]" required="[[!isInViewMode_]]"
-            auto-validate="[[!isInViewMode_]]">
-          <template is="dom-if" if="[[!isInViewMode_]]">
+            type="[[getPasswordInputType_(
+                isInFederatedViewMode_, isPasswordVisible_)]]"
+            value="{{password_}}"
+            class$="password-input"
+            readonly="[[isInViewMode_]]"
+            required="[[!isInFederatedViewMode_]]"
+            auto-validate="[[!isInFederatedViewMode_]]">
+          <template is="dom-if" if="[[!isInFederatedViewMode_]]">
             <cr-icon-button id="showPasswordButton"
                 class$="[[getIconClass_(isPasswordVisible_)]]"
-                slot="suffix" on-click="onShowPasswordButtonClick_"
-                title="[[showPasswordTitle_(isPasswordVisible_)]]">
+                slot="suffix"
+                title="[[showPasswordTitle_(isPasswordVisible_)]]"
+                on-click="onShowPasswordButtonClick_">
+            </cr-icon-button>
+          </template>
+          <template is="dom-if" if="[[isInPasswordViewMode_]]">
+            <cr-icon-button id="copyPasswordButton"
+                class="icon-copy-content"
+                slot="suffix"
+                title="$i18n{copyPassword}"
+                on-click="onCopyPasswordButtonClick_">
             </cr-icon-button>
           </template>
         </cr-input>
         <div id="footnote" hidden="[[isInViewMode_]]">
-          [[getFootnote_(dialogMode_, existingEntry.urls.shown)]]
+          [[getFootnote_(dialogMode, existingEntry.urls.shown)]]
         </div>
-        <template is="dom-if" if="[[isPasswordNotesEnabled_]]">
+        <template is="dom-if" if="[[shouldShowNote_(dialogMode)]]" restamp>
           <div class="divider"></div>
           <settings-textarea label="$i18n{passwordNoteLabel}"
               value=""
+              id="note"
+              readonly$="[[isInViewMode_]]"
               on-value-changed="onNoteChanged_"
               maxlength="1000">
           </settings-textarea>
@@ -117,10 +149,17 @@
             hidden="[[isInViewMode_]]">
           $i18n{cancel}
         </cr-button>
+        <template is="dom-if" if="[[isInPasswordViewMode_]]" restamp>
+          <cr-button id="switchToEditButton" class="cancel-button"
+              on-click="onSwitchToEditButtonClick_">
+            $i18n{edit}
+          </cr-button>
+        </template>
         <cr-button id="actionButton" class="action-button"
             on-click="onActionButtonClick_"
             disabled="[[isSaveButtonDisabled_]]">
-          [[getActionButtonName_(isInViewMode_)]]
+          [[getActionButtonName_(isInFederatedViewMode_,
+          isInPasswordViewMode_)]]
         </cr-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
index c785389f..3cc7639 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.ts
@@ -54,12 +54,14 @@
 
 /**
  * Contains the possible modes for 'password-edit-dialog'.
- * VIEW: entry is an existing federation credential
- * EDIT: entry is an existing password
+ * FEDERATED_VIEW: entry is an existing federated credential
+ * PASSWORD_VIEW: entry is an existing password and in view mode
+ * EDIT: entry is an existing password and in edit mode
  * ADD: no existing entry
  */
-enum PasswordDialogMode {
-  VIEW = 'view',
+export enum PasswordDialogMode {
+  FEDERATED_VIEW = 'federated_view',
+  PASSWORD_VIEW = 'password_view',
   EDIT = 'edit',
   ADD = 'add',
 }
@@ -102,7 +104,7 @@
   static get properties() {
     return {
       /**
-       * Has value for dialog in VIEW and EDIT modes.
+       * Has value for dialog in FEDERATED_VIEW, PASSWORD_VIEW and EDIT modes.
        */
       existingEntry: {type: Object, value: null},
 
@@ -130,17 +132,37 @@
       tokenRequestManager: {type: Object, value: null},
       // </if>
 
-      dialogMode_: {
+      requestedDialogMode: {type: Object, value: null},
+
+      dialogMode: {
         type: String,
-        computed: 'computeDialogMode_(existingEntry)',
+        computed: 'computeDialogMode_(existingEntry, requestedDialogMode)',
+        reflectToAttribute: true,
+      },
+
+      /**
+       * True if existing entry is opened in password view mode.
+       */
+      isInPasswordViewMode_: {
+        type: Boolean,
+        computed: 'computeIsInPasswordViewMode_(dialogMode)',
       },
 
       /**
        * True if existing entry is a federated credential.
        */
+      isInFederatedViewMode_: {
+        type: Boolean,
+        computed: 'computeIsInFederatedViewMode_(dialogMode)',
+      },
+
+      /**
+       * True if existing entry is only for viewing in the current dialog.
+       */
       isInViewMode_: {
         type: Boolean,
-        computed: 'computeIsInViewMode_(dialogMode_)',
+        computed:
+            'computeIsInViewMode_(isInPasswordViewMode_, isInFederatedViewMode_)',
       },
 
       /**
@@ -217,7 +239,10 @@
   tokenRequestManager: BlockingRequestManager|null;
   // </if>
   private usernamesByOrigin_: Map<string, Set<string>>|null = null;
-  private dialogMode_: PasswordDialogMode;
+  requestedDialogMode: PasswordDialogMode|null;
+  dialogMode: PasswordDialogMode;
+  private isInPasswordViewMode_: boolean;
+  private isInFederatedViewMode_: boolean;
   private isInViewMode_: boolean;
   private isPasswordVisible_: boolean;
   private websiteUrls_: chrome.passwordsPrivate.UrlCollection|null;
@@ -240,7 +265,7 @@
       this.username_ = this.existingEntry.username;
     }
     this.password_ = this.getPassword_();
-    if (!this.isInViewMode_) {
+    if (!this.isInFederatedViewMode_) {
       this.usernamesByOrigin_ = this.getUsernamesByOrigin_();
     }
     if (this.shouldShowStorePicker_()) {
@@ -251,7 +276,8 @@
                 this.storeOptionDeviceValue;
           });
     }
-    this.isPasswordVisible_ = false;
+    this.isPasswordVisible_ =
+        this.dialogMode === PasswordDialogMode.PASSWORD_VIEW;
   }
 
   /** Closes the dialog. */
@@ -260,16 +286,28 @@
   }
 
   private computeDialogMode_(): PasswordDialogMode {
+    if (this.isPasswordNotesEnabled_ && this.requestedDialogMode) {
+      return this.requestedDialogMode;
+    }
     if (this.existingEntry) {
-      return this.existingEntry.federationText ? PasswordDialogMode.VIEW :
-                                                 PasswordDialogMode.EDIT;
+      return this.existingEntry.federationText ?
+          PasswordDialogMode.FEDERATED_VIEW :
+          PasswordDialogMode.EDIT;
     }
 
     return PasswordDialogMode.ADD;
   }
 
+  private computeIsInPasswordViewMode_(): boolean {
+    return this.dialogMode === PasswordDialogMode.PASSWORD_VIEW;
+  }
+
+  private computeIsInFederatedViewMode_(): boolean {
+    return this.dialogMode === PasswordDialogMode.FEDERATED_VIEW;
+  }
+
   private computeIsInViewMode_(): boolean {
-    return this.dialogMode_ === PasswordDialogMode.VIEW;
+    return this.isInFederatedViewMode_ || this.isInPasswordViewMode_;
   }
 
   private computeIsSaveButtonDisabled_(): boolean {
@@ -277,6 +315,11 @@
         this.usernameInputInvalid_ || !this.password_.length;
   }
 
+  private shouldShowNote_(): boolean {
+    return this.isPasswordNotesEnabled_ &&
+        this.dialogMode !== PasswordDialogMode.FEDERATED_VIEW;
+  }
+
   /**
    * Handler for tapping the 'cancel' button. Should just dismiss the dialog.
    */
@@ -290,8 +333,8 @@
    * the content (federation text) is always visible.
    */
   private getPasswordInputType_(): string {
-    // VIEW mode implies |existingEntry| is a federated credential.
-    if (this.isInViewMode_) {
+    // FEDERATED_VIEW mode implies |existingEntry| is a federated credential.
+    if (this.isInFederatedViewMode_) {
       return 'text';
     }
 
@@ -302,7 +345,7 @@
    * Gets the title text for the show/hide icon.
    */
   private showPasswordTitle_(): string {
-    assert(!this.isInViewMode_);
+    assert(!this.isInFederatedViewMode_);
     return this.isPasswordVisible_ ? this.i18n('hidePassword') :
                                      this.i18n('showPassword');
   }
@@ -311,7 +354,7 @@
    * Get the right icon to display when hiding/showing a password.
    */
   private getIconClass_(): string {
-    assert(!this.isInViewMode_);
+    assert(!this.isInFederatedViewMode_);
     return this.isPasswordVisible_ ? 'icon-visibility-off' : 'icon-visibility';
   }
 
@@ -319,22 +362,24 @@
    * Gets the initial text to show in the website input.
    */
   private getWebsite_(): string {
-    return this.dialogMode_ === PasswordDialogMode.ADD ?
+    return this.dialogMode === PasswordDialogMode.ADD ?
         '' :
         this.existingEntry!.urls.link;
   }
 
   /**
    * Gets the initial text to show in the password input: the password for a
-   * regular credential, the federation text for a federated credential or empty
+   * regular credential, the federated text for a federated credential or empty
    * string in the ADD mode.
    */
   private getPassword_(): string {
-    switch (this.dialogMode_) {
-      case PasswordDialogMode.VIEW:
-        // VIEW mode implies |existingEntry| is a federated credential.
+    switch (this.dialogMode) {
+      case PasswordDialogMode.FEDERATED_VIEW:
+        // FEDERATED_VIEW mode implies |existingEntry| is a federated
+        // credential.
         return this.existingEntry!.federationText!;
       case PasswordDialogMode.EDIT:
+      case PasswordDialogMode.PASSWORD_VIEW:
         return this.existingEntry!.password;
       case PasswordDialogMode.ADD:
         return '';
@@ -348,18 +393,19 @@
    * Handler for tapping the show/hide button.
    */
   private onShowPasswordButtonClick_() {
-    assert(!this.isInViewMode_);
+    assert(!this.isInFederatedViewMode_);
     this.isPasswordVisible_ = !this.isPasswordVisible_;
   }
 
   /**
-   * Handler for tapping the 'done' or 'save' button depending on |dialogMode_|.
+   * Handler for tapping the 'done' or 'save' button depending on |dialogMode|.
    * For 'save' button it should save new password. After pressing action button
    * the edit dialog should be closed.
    */
   private onActionButtonClick_() {
-    switch (this.dialogMode_) {
-      case PasswordDialogMode.VIEW:
+    switch (this.dialogMode) {
+      case PasswordDialogMode.FEDERATED_VIEW:
+      case PasswordDialogMode.PASSWORD_VIEW:
         this.close();
         return;
       case PasswordDialogMode.EDIT:
@@ -372,6 +418,29 @@
         assertNotReached();
     }
   }
+  /**
+   * Handler to switch into edit mode from password view mode.
+   */
+  private onSwitchToEditButtonClick_() {
+    assert(this.isInPasswordViewMode_);
+    this.requestedDialogMode = PasswordDialogMode.EDIT;
+    this.$.dialog.focus();
+  }
+
+  /**
+   * Handler to copy the username from the username field.
+   */
+  private onCopyUsernameButtonClick_() {
+    navigator.clipboard.writeText(this.username_);
+  }
+
+  /**
+   * Handler to copy the password from the password field.
+   */
+  private onCopyPasswordButtonClick_() {
+    assert(!this.isInFederatedViewMode_);
+    navigator.clipboard.writeText(this.password_);
+  }
 
   private addPassword_() {
     const useAccountStore = !this.$.storePicker.hidden ?
@@ -434,7 +503,7 @@
    * is stored.
    */
   private getStorageDetailsMessage_(): string {
-    if (this.dialogMode_ === PasswordDialogMode.ADD) {
+    if (this.dialogMode === PasswordDialogMode.ADD) {
       // Storage message is not shown in the ADD mode.
       return '';
     }
@@ -448,7 +517,7 @@
   }
 
   private getStoreOptionAccountText_(): string {
-    if (this.dialogMode_ !== PasswordDialogMode.ADD) {
+    if (this.dialogMode !== PasswordDialogMode.ADD) {
       // Store picker is only shown in the ADD mode.
       return '';
     }
@@ -457,13 +526,15 @@
   }
 
   private getTitle_(): string {
-    switch (this.dialogMode_) {
+    switch (this.dialogMode) {
       case PasswordDialogMode.ADD:
         return this.i18n('addPasswordTitle');
       case PasswordDialogMode.EDIT:
         return this.i18n('editPasswordTitle');
-      case PasswordDialogMode.VIEW:
+      case PasswordDialogMode.FEDERATED_VIEW:
         return this.i18n('passwordDetailsTitle');
+      case PasswordDialogMode.PASSWORD_VIEW:
+        return this.existingEntry!.urls.shown;
       default:
         assertNotReached();
         return '';
@@ -471,21 +542,21 @@
   }
 
   private shouldShowStorageDetails_(): boolean {
-    return this.dialogMode_ !== PasswordDialogMode.ADD &&
+    return this.dialogMode !== PasswordDialogMode.ADD &&
         this.isAccountStoreUser;
   }
 
   private shouldShowStorePicker_(): boolean {
-    return this.dialogMode_ === PasswordDialogMode.ADD &&
+    return this.dialogMode === PasswordDialogMode.ADD &&
         this.isAccountStoreUser;
   }
 
   private isWebsiteEditable_(): boolean {
-    return this.dialogMode_ === PasswordDialogMode.ADD;
+    return this.dialogMode === PasswordDialogMode.ADD;
   }
 
   private shouldAutofocusWebsiteInput_(): boolean {
-    return this.dialogMode_ === PasswordDialogMode.ADD &&
+    return this.dialogMode === PasswordDialogMode.ADD &&
         !this.isAccountStoreUser;
   }
 
@@ -493,9 +564,16 @@
    * @return The text to be displayed as the dialog's footnote.
    */
   private getFootnote_(): string {
-    return this.dialogMode_ === PasswordDialogMode.ADD ?
-        this.i18n('addPasswordFootnote') :
-        this.i18n('editPasswordFootnote', this.existingEntry!.urls.shown);
+    switch (this.dialogMode) {
+      case PasswordDialogMode.ADD:
+        return this.i18n('addPasswordFootnote');
+      case PasswordDialogMode.EDIT:
+      case PasswordDialogMode.FEDERATED_VIEW:
+        return this.i18n(
+            'editPasswordFootnote', this.existingEntry!.urls.shown);
+      default:
+        return '';
+    }
   }
 
   private getClassForWebsiteInput_(): string {
@@ -508,7 +586,7 @@
    * Helper function that checks whether the entered url is valid.
    */
   private validateWebsite_() {
-    assert(this.dialogMode_ === PasswordDialogMode.ADD);
+    assert(this.dialogMode === PasswordDialogMode.ADD);
     if (!this.$.websiteInput.value.length) {
       this.websiteUrls_ = null;
       this.websiteInputErrorMessage_ = null;
@@ -585,10 +663,11 @@
    * Checks whether edited username is not used for the same website.
    */
   private computeUsernameInputInvalid_(): boolean {
-    if (this.isInViewMode_ || !this.websiteUrls_ || !this.usernamesByOrigin_) {
+    if (this.isInFederatedViewMode_ || !this.websiteUrls_ ||
+        !this.usernamesByOrigin_) {
       return false;
     }
-    if (this.dialogMode_ === PasswordDialogMode.EDIT &&
+    if (this.dialogMode === PasswordDialogMode.EDIT &&
         this.username_ === this.existingEntry!.username) {
       // The value hasn't changed.
       return false;
@@ -598,7 +677,7 @@
         this.usernamesByOrigin_.get(this.websiteUrls_.origin)!.has(
             this.username_);
 
-    if (isDuplicate && this.dialogMode_ === PasswordDialogMode.ADD) {
+    if (isDuplicate && this.dialogMode === PasswordDialogMode.ADD) {
       chrome.metricsPrivate.recordEnumerationValue(
           'PasswordManager.AddCredentialFromSettings.UserAction',
           AddCredentialFromSettingsUserInteractions
@@ -613,8 +692,8 @@
    * Used for the fast check whether edited username is already used.
    */
   private getUsernamesByOrigin_(): Map<string, Set<string>> {
-    assert(!this.isInViewMode_);
-    const relevantPasswords = this.dialogMode_ === PasswordDialogMode.EDIT ?
+    assert(!this.isInFederatedViewMode_);
+    const relevantPasswords = this.dialogMode === PasswordDialogMode.EDIT ?
         // In EDIT mode entries considered duplicates only if in the same store.
         this.savedPasswords.filter(item => {
           return item.isPresentOnDevice() ===
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
index 6317596c4..508025d 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -487,11 +487,6 @@
   onInitialPageContentDataFetched_(newData) {
     this.onPageContentDataChanged_(newData);
 
-    if (this.pageContentData.notificationAccessStatus !==
-        settings.PhoneHubNotificationAccessStatus.AVAILABLE_BUT_NOT_GRANTED) {
-      return;
-    }
-
     // Show the notification access dialog if the url contains the correct
     // param.
     const urlParams = settings.Router.getInstance().getQueryParameters();
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
index 80ec186..9e69f7f 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/fake_page_handler.js
@@ -16,7 +16,7 @@
 export class FakePageHandler {
   /**
    * @param {Object=} options
-   * @return {!Object<number, Permission>}
+   * @return {!Object<number, appManagement.mojom.Permission>}
    */
   static createWebPermissions(options) {
     const permissionTypes = [
@@ -47,7 +47,7 @@
 
   /**
    * @param {Array<number>=} optIds
-   * @return {!Object<number, Permission>}
+   * @return {!Object<number, appManagement.mojom.Permission>}
    */
   static createArcPermissions(optIds) {
     const permissionTypes = optIds || [
@@ -71,7 +71,7 @@
 
   /**
    * @param {apps.mojom.AppType} appType
-   * @return {!Object<number, Permission>}
+   * @return {!Object<number, appManagement.mojom.Permission>}
    */
   static createPermissions(appType) {
     switch (appType) {
@@ -234,7 +234,7 @@
 
   /**
    * @param {string} appId
-   * @param {Permission} permission
+   * @param {appManagement.mojom.Permission} permission
    */
   setPermission(appId, permission) {
     const app = AppManagementStore.getInstance().data.apps[appId];
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/types.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/types.js
index b46588ee..9b0ab0e 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/types.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/types.js
@@ -17,7 +17,7 @@
 let ExtensionAppPermissionMessage;
 
 /**
- * @typedef {apps.mojom.Permission}
+ * @typedef {appManagement.mojom.Permission}
  */
 let Permission;
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
index 10af3c6..9f3e574 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_notifications_page/app_notification_row.js
@@ -10,6 +10,7 @@
 import '/app-management/image.mojom-lite.js';
 import '/app-management/safe_base_name.mojom-lite.js';
 import '/app-management/types.mojom-lite.js';
+import '/app-management/app_management.mojom-lite.js';
 import '/os_apps_page/app_notification_handler.mojom-lite.js';
 
 import {createBoolPermissionValue, createTriStatePermissionValue, isBoolValue, isPermissionEnabled, isTriStateValue} from '//resources/cr_components/app_management/permission_util.js';
@@ -72,8 +73,8 @@
           createBoolPermissionValue(this.checked_ ? false : true);
     } else if (isTriStateValue(permission.value)) {
       permission.value = createTriStatePermissionValue(
-          this.checked_ ? apps.mojom.TriState.kBlock :
-                          apps.mojom.TriState.kAllow);
+          this.checked_ ? appManagement.mojom.TriState.kBlock :
+                          appManagement.mojom.TriState.kAllow);
     }
 
     this.mojoInterfaceProvider_.setNotificationPermission(
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
index d26a582..1b97031 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/os_apps_page.js
@@ -48,16 +48,17 @@
  */
 export function isAppInstalled(app) {
   switch (app.readiness) {
-    case apps.mojom.Readiness.kReady:
-    case apps.mojom.Readiness.kDisabledByBlocklist:
-    case apps.mojom.Readiness.kDisabledByPolicy:
-    case apps.mojom.Readiness.kDisabledByUser:
-    case apps.mojom.Readiness.kTerminated:
+    case chromeos.settings.appNotification.mojom.Readiness.kReady:
+    case chromeos.settings.appNotification.mojom.Readiness.kDisabledByBlocklist:
+    case chromeos.settings.appNotification.mojom.Readiness.kDisabledByPolicy:
+    case chromeos.settings.appNotification.mojom.Readiness.kDisabledByUser:
+    case chromeos.settings.appNotification.mojom.Readiness.kTerminated:
       return true;
-    case apps.mojom.Readiness.kUninstalledByUser:
-    case apps.mojom.Readiness.kUninstalledByMigration:
-    case apps.mojom.Readiness.kRemoved:
-    case apps.mojom.Readiness.kUnknown:
+    case chromeos.settings.appNotification.mojom.Readiness.kUninstalledByUser:
+    case chromeos.settings.appNotification.mojom.Readiness
+        .kUninstalledByMigration:
+    case chromeos.settings.appNotification.mojom.Readiness.kRemoved:
+    case chromeos.settings.appNotification.mojom.Readiness.kUnknown:
       return false;
   }
   assertNotReached();
diff --git a/chrome/browser/resources/settings/controls/settings_textarea.html b/chrome/browser/resources/settings/controls/settings_textarea.html
index 40e2f1c0..18818836 100644
--- a/chrome/browser/resources/settings/controls/settings_textarea.html
+++ b/chrome/browser/resources/settings/controls/settings_textarea.html
@@ -23,6 +23,6 @@
       value="{{value::input}}" aria-label$="[[label]]"
       on-focus="onInputFocusChange_" on-blur="onInputFocusChange_"
       on-change="onInputChange_" disabled="[[disabled]]"
-      maxlength$="[[maxlength]]"></textarea>
+      maxlength$="[[maxlength]]" readonly$="[[readonly]]"></textarea>
   <div id="underline"></div>
 </div>
diff --git a/chrome/browser/resources/settings/controls/settings_textarea.ts b/chrome/browser/resources/settings/controls/settings_textarea.ts
index 69619bef..ec96f9cc 100644
--- a/chrome/browser/resources/settings/controls/settings_textarea.ts
+++ b/chrome/browser/resources/settings/controls/settings_textarea.ts
@@ -57,6 +57,12 @@
         type: Number,
       },
 
+      /**
+       * Whether the text area is read only. If read-only, content cannot be
+       * changed.
+       */
+      readonly: Boolean,
+
       /** Number of rows (lines) of the text area. */
       rows: {
         type: Number,
diff --git a/chrome/browser/resources/settings/lazy_load.ts b/chrome/browser/resources/settings/lazy_load.ts
index b7d77e72..418ff44c 100644
--- a/chrome/browser/resources/settings/lazy_load.ts
+++ b/chrome/browser/resources/settings/lazy_load.ts
@@ -83,7 +83,7 @@
 export {SettingsPasswordCheckEditDialogElement} from './autofill_page/password_check_edit_dialog.js';
 export {SettingsPasswordEditDisclaimerDialogElement} from './autofill_page/password_check_edit_disclaimer_dialog.js';
 export {PasswordCheckListItemElement} from './autofill_page/password_check_list_item.js';
-export {PasswordEditDialogElement} from './autofill_page/password_edit_dialog.js';
+export {PasswordDialogMode, PasswordEditDialogElement} from './autofill_page/password_edit_dialog.js';
 export {PasswordListItemElement} from './autofill_page/password_list_item.js';
 export {PasswordMoveMultiplePasswordsToAccountDialogElement} from './autofill_page/password_move_multiple_passwords_to_account_dialog.js';
 export {PasswordMoveToAccountDialogElement} from './autofill_page/password_move_to_account_dialog.js';
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc
index 4bd423c..9aa8a79 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc
@@ -223,9 +223,9 @@
   // the registry and as measured by |ReporterRunner|.
   void ReportRuntime(const base::TimeDelta& reporter_running_time,
                      const base::TimeDelta& running_time_without_sleep) const {
-    RecordLongTimesHistogram("SoftwareReporter.RunningTimeAccordingToChrome",
+    RecordLongTimesHistogram("SoftwareReporter.RunningTimeAccordingToChrome2",
                              reporter_running_time);
-    RecordLongTimesHistogram("SoftwareReporter.RunningTimeWithoutSleep",
+    RecordLongTimesHistogram("SoftwareReporter.RunningTimeWithoutSleep2",
                              running_time_without_sleep);
 
     // TODO(b/641081): This should only have KEY_QUERY_VALUE and KEY_SET_VALUE.
@@ -283,6 +283,10 @@
                                REPORTER_LOGS_UPLOAD_RESULT_ERROR_MAX);
   }
 
+  void ReportCreateJobResult(DWORD result) {
+    RecordSparseHistogram("SoftwareReporter.CreateJobResult", result);
+  }
+
  private:
   using Sample = base::HistogramBase::Sample;
 
@@ -474,10 +478,6 @@
   }
 
  private:
-  // The type returned by QueryUnbiasedInterruptTime, which does not include
-  // time spent in sleep or hibernation.
-  using TimestampWithoutSleep = ULONGLONG;
-
   // Keeps track of last and upcoming reporter runs and logs uploading.
   //
   // Periodic runs are allowed to start if the last time the reporter ran was
@@ -565,14 +565,12 @@
         g_testing_delegate_ ? g_testing_delegate_->BlockingTaskRunner()
                             : blocking_task_runner_.get();
 
-    TimestampWithoutSleep now_without_sleep;
-    ::QueryUnbiasedInterruptTime(&now_without_sleep);
-
     auto launch_and_wait =
         base::BindOnce(&LaunchAndWaitForExit, next_invocation);
-    auto reporter_done =
-        base::BindOnce(&ReporterRunner::ReporterDone, base::Unretained(this),
-                       Now(), now_without_sleep, next_invocation);
+    // Unretained is safe because ReporterRunner deletes itself after all
+    // invocations are finished.
+    auto reporter_done = base::BindOnce(
+        &ReporterRunner::ReporterDone, base::Unretained(this), next_invocation);
     base::PostTaskAndReplyWithResult(task_runner, FROM_HERE,
                                      std::move(launch_and_wait),
                                      std::move(reporter_done));
@@ -581,10 +579,8 @@
   // This method is called on the UI thread when an invocation of the reporter
   // has completed. This is run as a task posted from an interruptible worker
   // thread so should be resilient to unexpected shutdown.
-  void ReporterDone(const base::Time& reporter_start_time,
-                    TimestampWithoutSleep start_time_without_sleep,
-                    SwReporterInvocation finished_invocation,
-                    int exit_code) {
+  void ReporterDone(SwReporterInvocation finished_invocation,
+                    ReporterRunResult result) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK_EQ(instance_, this);
 
@@ -593,29 +589,18 @@
     // start.
     base::ScopedClosureRunner scoped_runner(base::BindOnce(
         &ReporterRunner::SendResultAndDeleteSelf, base::Unretained(this),
-        ExitCodeToInvocationResult(exit_code)));
+        ExitCodeToInvocationResult(result.exit_code)));
 
     UMAHistogramReporter uma(finished_invocation.suffix());
 
     // Don't continue the current queue of reporters if one failed to launch.
     // If the reporter failed to launch, do not process the results.
-    if (exit_code == kReporterNotLaunchedExitCode) {
-      uma.ReportExitCode(exit_code);
+    if (result.exit_code == kReporterNotLaunchedExitCode) {
+      uma.ReportExitCode(result.exit_code);
       NotifySequenceDone(SwReporterInvocationResult::kProcessFailedToLaunch);
       return;
     }
 
-    TimestampWithoutSleep now_without_sleep;
-    ::QueryUnbiasedInterruptTime(&now_without_sleep);
-
-    base::Time now = Now();
-    base::TimeDelta reporter_running_time = now - reporter_start_time;
-
-    // QueryUnbiasedInterruptTime returns units of 100 nanoseconds. See
-    // https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryunbiasedinterrupttime
-    base::TimeDelta running_time_without_sleep =
-        base::Nanoseconds(100 * (now_without_sleep - start_time_without_sleep));
-
     // Tries to run the next invocation in the queue.
     if (!invocations_.container().empty()) {
       // If there are other invocations to start, then we shouldn't finalize
@@ -626,7 +611,7 @@
     }
 
     uma.ReportVersion(invocations_.version());
-    uma.ReportExitCode(exit_code);
+    uma.ReportExitCode(result.exit_code);
     uma.ReportEngineErrorCode();
     uma.ReportFoundUwS();
 
@@ -634,12 +619,14 @@
     if (local_state) {
       if (finished_invocation.BehaviourIsSupported(
               SwReporterInvocation::BEHAVIOUR_LOG_EXIT_CODE_TO_PREFS)) {
-        local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code);
+        local_state->SetInteger(prefs::kSwReporterLastExitCode,
+                                result.exit_code);
       }
+      const base::Time now = Now();
       local_state->SetInt64(prefs::kSwReporterLastTimeTriggered,
                             now.ToInternalValue());
     }
-    uma.ReportRuntime(reporter_running_time, running_time_without_sleep);
+    uma.ReportRuntime(result.running_time, result.running_time_without_sleep);
     uma.ReportMemoryUsage();
     if (finished_invocation.reporter_logs_upload_enabled())
       uma.RecordLogsUploadResult();
@@ -661,7 +648,7 @@
 
     // Do not accept reboot required or post-reboot exit codes, since they
     // should not be sent out by the reporter.
-    if (exit_code != chrome_cleaner::kSwReporterCleanupNeeded) {
+    if (result.exit_code != chrome_cleaner::kSwReporterCleanupNeeded) {
       RecordPromptNotShownWithReasonHistogram(NO_PROMPT_REASON_NOTHING_FOUND);
       return;
     }
@@ -871,18 +858,18 @@
 // wait for termination to collect its exit code. This task could be
 // interrupted by a shutdown at any time, so it shouldn't depend on anything
 // external that could be shut down beforehand.
-int LaunchAndWaitForExit(const SwReporterInvocation& invocation) {
+ReporterRunResult LaunchAndWaitForExit(const SwReporterInvocation& invocation) {
   TRACE_EVENT("safe_browsing", "ReporterRunner::LaunchAndWaitForExit");
 
   // This exit code is used to identify that a reporter run didn't happen, so
   // the result should be ignored and a rerun scheduled for the usual delay.
-  int exit_code = kReporterNotLaunchedExitCode;
+  ReporterRunResult result{.exit_code = kReporterNotLaunchedExitCode};
 
   UMAHistogramReporter uma(invocation.suffix());
 
   base::FilePath tmpdir;
   if (!base::GetTempDir(&tmpdir)) {
-    return exit_code;
+    return result;
   }
 
   // The reporter runs from the system tmp directory. This is to avoid
@@ -896,14 +883,22 @@
   base::win::ScopedHandle job;
   if (ReporterTerminatesOnBrowserExit()) {
     job.Set(::CreateJobObject(nullptr, nullptr));
+    if (job.IsValid()) {
+      base::SetJobObjectLimitFlags(job.Get(),
+                                   JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE);
+      launch_options.job_handle = job.Get();
+      uma.ReportCreateJobResult(ERROR_SUCCESS);
+    } else {
+      uma.ReportCreateJobResult(::GetLastError());
+    }
   }
-  if (job.IsValid()) {
-    base::SetJobObjectLimitFlags(job.Get(), JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE);
-    launch_options.job_handle = job.Get();
-  } else {
-    PLOG(WARNING) << "The Chrome Cleanup Tool's reporter process is not "
-                     "attached to a job and may outlive the browser.";
-  }
+
+  base::Time start_time = Now();
+
+  // QueryUnbiasedInterruptTime does not include time spent in sleep or
+  // hibernation.
+  ULONGLONG start_time_without_sleep;
+  ::QueryUnbiasedInterruptTime(&start_time_without_sleep);
 
   base::Process reporter_process =
       g_testing_delegate_
@@ -912,7 +907,7 @@
           : base::LaunchProcess(invocation.command_line(), launch_options);
 
   if (!reporter_process.IsValid()) {
-    return exit_code;
+    return result;
   }
 
   constexpr base::TimeDelta kPollingTime = base::Seconds(10);
@@ -921,18 +916,28 @@
     TRACE_EVENT("safe_browsing", "ReporterRunning");
     if (g_testing_delegate_) {
       exited = g_testing_delegate_->WaitForReporterExit(
-          reporter_process, kPollingTime, &exit_code);
+          reporter_process, kPollingTime, &result.exit_code);
     } else {
-      exited =
-          reporter_process.WaitForExitWithTimeout(kPollingTime, &exit_code);
+      exited = reporter_process.WaitForExitWithTimeout(kPollingTime,
+                                                       &result.exit_code);
       // Wait should only fail on a timeout.
       DCHECK(exited || reporter_process.IsRunning());
     }
   }
 
+  result.running_time = Now() - start_time;
+
+  ULONGLONG now_without_sleep;
+  ::QueryUnbiasedInterruptTime(&now_without_sleep);
+
+  // QueryUnbiasedInterruptTime returns units of 100 nanoseconds. See
+  // https://docs.microsoft.com/en-us/windows/win32/api/realtimeapiset/nf-realtimeapiset-queryunbiasedinterrupttime
+  result.running_time_without_sleep =
+      base::Nanoseconds(100 * (now_without_sleep - start_time_without_sleep));
+
   // After the reporter process has exited the job object is no longer needed.
   // It will be closed when it goes out of scope here.
-  return exit_code;
+  return result;
 }
 
 }  // namespace internal
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h
index ac990c30..82ec65930 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h
+++ b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.h
@@ -57,6 +57,18 @@
   virtual void CreateChromeCleanerDialogController() = 0;
 };
 
+// Stores the exit code and other data about a reporter run.
+struct ReporterRunResult {
+  // Exit code of the reporter process.
+  int exit_code = 0;
+
+  // Running time of the process (including time the computer is asleep).
+  base::TimeDelta running_time;
+
+  // Running time of the process, not including time the computer is asleep.
+  base::TimeDelta running_time_without_sleep;
+};
+
 // Set a delegate for testing. The implementation will not take ownership of
 // |delegate| - it must remain valid until this function is called again to
 // reset the delegate. If |delegate| is nullptr, any previous delegate is
@@ -67,9 +79,9 @@
 // on this version of Windows. Exposed for testing.
 bool ReporterTerminatesOnBrowserExit();
 
-// Launches a reporter process based on the command-line in |invocation| and
-// returns its exit code. Exposed for testing.
-int LaunchAndWaitForExit(const SwReporterInvocation& invocation);
+// Launches a reporter process based on the command-line in `invocation` and
+// returns its exit code and running time. Exposed for testing.
+ReporterRunResult LaunchAndWaitForExit(const SwReporterInvocation& invocation);
 
 }  // namespace internal
 
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
index 88e6b88..d3148f8 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -1080,56 +1080,3 @@
                 IDS_PLUGIN_VM_APP_NAME)},
             GetModelContent(model_updater_.get()));
 }
-
-class BorealisAppTest : public AppServiceAppModelBuilderTest {
- public:
-  void SetUp() override {
-    testing_profile_ = std::make_unique<TestingProfile>();
-    web_app::FakeWebAppProvider::Get(testing_profile_.get())->Start();
-    CreateBuilder(/*guest_mode=*/false);
-  }
-
-  void TearDown() override { ResetBuilder(); }
-
- protected:
-  void CreateBuilder(bool guest_mode) {
-    ResetBuilder();  // Destroy any existing builder in the correct order.
-
-    app_service_test_.UninstallAllApps(testing_profile_.get());
-    testing_profile_->SetGuestSession(guest_mode);
-    app_service_test_.SetUp(testing_profile_.get());
-    model_updater_ = std::make_unique<FakeAppListModelUpdater>(
-        /*profile=*/nullptr, /*reorder_delegate=*/nullptr);
-    controller_ = std::make_unique<test::TestAppListControllerDelegate>();
-    builder_ = std::make_unique<AppServiceAppModelBuilder>(controller_.get());
-    scoped_callback_ = std::make_unique<
-        AppServiceAppModelBuilder::ScopedAppPositionInitCallbackForTest>(
-        builder_.get(), base::BindRepeating(&InitAppPosition));
-    builder_->Initialize(nullptr, testing_profile_.get(), model_updater_.get());
-
-    RemoveApps(apps::mojom::AppType::kBorealis, testing_profile_.get(),
-               model_updater_.get());
-  }
-
-  std::unique_ptr<TestingProfile> testing_profile_;
-};
-
-TEST_F(BorealisAppTest, BorealisDisallowed) {
-  ASSERT_NE(borealis::BorealisService::GetForProfile(testing_profile_.get())
-                ->Features()
-                .MightBeAllowed(),
-            borealis::BorealisFeatures::AllowStatus::kAllowed);
-  EXPECT_EQ(std::vector<std::string>{}, GetModelContent(model_updater_.get()));
-}
-
-TEST_F(BorealisAppTest, BorealisAllowed) {
-  borealis::ScopedAllowBorealis allow_borealis(testing_profile_.get(),
-                                               /*also_enable=*/false);
-  // Reset the AppModelBuilder, so that it is created in a state where
-  // Borealis was enabled.
-  CreateBuilder(/*guest_mode=*/false);
-
-  EXPECT_EQ(
-      std::vector<std::string>{l10n_util::GetStringUTF8(IDS_BOREALIS_APP_NAME)},
-      GetModelContent(model_updater_.get()));
-}
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
index 49b5fbbc..f56a86d 100644
--- a/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result.cc
@@ -55,6 +55,7 @@
   return ChromeSearchResult::IconInfo(icon, dimension);
 }
 
+// Creates a TextItem without tags.
 TextItem CreateTextItem(const std::u16string& text) {
   TextItem text_item(TextType::kString);
   text_item.SetText(text);
@@ -62,17 +63,10 @@
   return text_item;
 }
 
-std::u16string GetAdditionalText(const SuggestionAnswer::ImageLine& line) {
-  if (line.additional_text()) {
-    const auto& additional_text = line.additional_text()->text();
-    if (!additional_text.empty())
-      return additional_text;
-  }
-  return std::u16string();
-}
+TextItem TextFieldToTextItem(const SuggestionAnswer::TextField& text_field) {
+  TextItem text_item(TextType::kString);
+  text_item.SetText(text_field.text());
 
-ash::SearchResultTags TextFieldToTags(
-    const SuggestionAnswer::TextField& text_field) {
   ash::SearchResultTags tags;
   const auto length = text_field.text().length();
   switch (text_field.style()) {
@@ -85,24 +79,56 @@
     default:
       break;
   }
-  return tags;
+  text_item.SetTextTags(tags);
+
+  return text_item;
 }
 
+// Converts an ImageLine to TextVector, ignoring any additional text.
 std::vector<TextItem> ImageLineToTextVector(
     const SuggestionAnswer::ImageLine& line) {
   std::vector<TextItem> text_vector;
   for (const auto& text_field : line.text_fields()) {
-    if (!text_vector.empty())
+    if (!text_vector.empty()) {
       text_vector.push_back(CreateTextItem(u" "));
-
-    TextItem text_item(TextType::kString);
-    text_item.SetText(text_field.text());
-    text_item.SetTextTags(TextFieldToTags(text_field));
-    text_vector.push_back(text_item);
+    }
+    text_vector.push_back(TextFieldToTextItem(text_field));
   }
   return text_vector;
 }
 
+// Converts the line's additional text into a TextItem and appends it to the
+// supplied TextVector.
+void AppendAdditionalText(const SuggestionAnswer::ImageLine& line,
+                          std::vector<TextItem>& text_vector) {
+  if (!line.additional_text() || line.additional_text()->text().empty())
+    return;
+
+  if (!text_vector.empty()) {
+    text_vector.push_back(CreateTextItem(u" "));
+  }
+
+  text_vector.push_back(TextFieldToTextItem(*line.additional_text()));
+}
+
+// Converts AutocompleteMatch fields to a TextItem.
+std::vector<TextItem> MatchFieldsToTextVector(
+    const std::u16string& text,
+    const ACMatchClassifications& classifications) {
+  TextItem text_item(TextType::kString);
+  text_item.SetText(text);
+
+  ash::SearchResultTags tags;
+  // Classifications include URL tags, which we need to convert, and MATCH tags,
+  // which we should ignore since the query highlighter will perform all the
+  // matching instead.
+  ACMatchClassificationsToTags(text, classifications, &tags,
+                               /*ignore_match=*/true);
+  text_item.SetTextTags(tags);
+
+  return {text_item};
+}
+
 std::vector<TextItem> AddBoldTags(std::vector<TextItem> text_vector) {
   std::vector<TextItem> bolded_vector;
   for (const auto& old_text : text_vector) {
@@ -207,12 +233,14 @@
     // Calculator results come in two forms:
     // 1) Answer in |match.contents|, empty description,
     // 2) Query in |match.contents|, answer in |match.description|.
-    std::vector<TextItem> contents_vector = {CreateTextItem(match_.contents)};
+    std::vector<TextItem> contents_vector =
+        MatchFieldsToTextVector(match_.contents, match_.contents_class);
     if (match_.description.empty()) {
       SetTitleTextVector(contents_vector);
       SetDetailsTextVector({CreateTextItem(query_)});
     } else {
-      SetTitleTextVector({CreateTextItem(match_.description)});
+      SetTitleTextVector(MatchFieldsToTextVector(match_.description,
+                                                 match_.description_class));
       SetDetailsTextVector(contents_vector);
     }
   } else if (IsWeatherResult()) {
@@ -222,25 +250,22 @@
     // TODO(crbug.com/1250154): Put additional weather text into the title
     // field instead of match contents, once the information becomes available
     // from the Suggest server.
-    SetTitleTextVector({CreateTextItem(match_.contents)});
-    SetDetailsTextVector({CreateTextItem(GetAdditionalText(second_line))});
+    SetTitleTextVector(
+        MatchFieldsToTextVector(match_.contents, match_.contents_class));
+
+    std::vector<TextItem> details_vector;
+    AppendAdditionalText(second_line, details_vector);
+    SetDetailsTextVector(details_vector);
   } else {
     const auto& second_line = match_.answer->second_line();
     auto title_vector = ImageLineToTextVector(second_line);
-    const auto& additional_title = GetAdditionalText(second_line);
-    if (!additional_title.empty()) {
-      title_vector.push_back(CreateTextItem(u" "));
-      title_vector.push_back(CreateTextItem(additional_title));
-    }
+    AppendAdditionalText(second_line, title_vector);
     SetTitleTextVector(title_vector);
 
     const auto& first_line = match_.answer->first_line();
-    std::vector<TextItem> details_vector = {CreateTextItem(match_.contents)};
-    const auto& additional_details = GetAdditionalText(first_line);
-    if (!additional_details.empty()) {
-      details_vector.push_back(CreateTextItem(u" "));
-      details_vector.push_back(CreateTextItem(additional_details));
-    }
+    std::vector<TextItem> details_vector =
+        MatchFieldsToTextVector(match_.contents, match_.contents_class);
+    AppendAdditionalText(first_line, details_vector);
     SetDetailsTextVector(details_vector);
   }
 
@@ -250,11 +275,6 @@
   std::u16string accessible_name = ComputeAccessibleName(
       {big_title_text_vector(), title_text_vector(), details_text_vector()});
   SetAccessibleName(accessible_name);
-
-  // TODO(crbug.com/1250154): Remove these once the migration to TextVectors
-  // is completed.
-  SetTitle(TextVectorToString(title_text_vector()));
-  SetDetails(TextVectorToString(details_text_vector()));
 }
 
 void OmniboxAnswerResult::UpdateClassicTitleAndDetails() {
@@ -271,16 +291,16 @@
                                  &details_tags);
     SetDetailsTags(details_tags);
   } else {
-    const auto& additional_text =
-        GetAdditionalText(match_.answer->first_line());
-    // TODO(crbug.com/1130372): Use placeholders or a l10n-friendly way to
-    // construct this string instead of concatenation. This currently only
-    // happens for stock ticker symbols.
-    SetTitle(!additional_text.empty()
-                 ? base::JoinString({match_.contents, additional_text}, u" ")
-                 : match_.contents);
-    SetDetails(TextVectorToString(
-        ImageLineToTextVector(match_.answer->second_line())));
+    const auto* additional = match_.answer->first_line().additional_text();
+    const std::u16string title =
+        additional && !additional->text().empty()
+            ? base::JoinString({match_.contents, additional->text()}, u" ")
+            : match_.contents;
+    SetTitle(title);
+
+    auto details_vector = ImageLineToTextVector(match_.answer->second_line());
+    AppendAdditionalText(match_.answer->second_line(), details_vector);
+    SetDetails(TextVectorToString(details_vector));
   }
 }
 
diff --git a/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc b/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc
new file mode 100644
index 0000000..3c1a976
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/omnibox_answer_result_unittest.cc
@@ -0,0 +1,271 @@
+// 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/ui/app_list/search/omnibox_answer_result.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/app_list/app_list_metrics.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
+#include "base/json/json_reader.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/values.h"
+#include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_match_type.h"
+#include "components/omnibox/browser/suggestion_answer.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/range/range.h"
+
+namespace app_list {
+namespace {
+
+using Tag = ash::SearchResultTag;
+
+class ClassicOmniboxAnswerResultTest : public testing::Test {
+ public:
+  ClassicOmniboxAnswerResultTest() {
+    scoped_feature_list_.InitAndDisableFeature(
+        ash::features::kProductivityLauncher);
+  }
+  ~ClassicOmniboxAnswerResultTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+class OmniboxAnswerResultTest : public testing::Test {
+ public:
+  OmniboxAnswerResultTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        ash::features::kProductivityLauncher);
+  }
+  ~OmniboxAnswerResultTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+MATCHER_P(TagEquals, tag, "") {
+  bool styles_match = arg.styles == tag.styles;
+  bool range_match = arg.range == tag.range;
+  return styles_match && range_match;
+}
+
+}  // namespace
+
+TEST_F(ClassicOmniboxAnswerResultTest, ClassicCalculatorResult) {
+  AutocompleteMatch match;
+  match.type = AutocompleteMatchType::CALCULATOR;
+  match.contents = u"2+2";
+  match.description = u"4";
+
+  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query");
+  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kList);
+  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
+  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_CALCULATOR);
+  EXPECT_EQ(result.title(), u"2+2");
+  EXPECT_EQ(result.details(), u"4");
+}
+
+TEST_F(ClassicOmniboxAnswerResultTest, ClassicAnswerResult) {
+  SuggestionAnswer answer;
+  std::string json =
+      "{ \"l\": ["
+      "  { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], "
+      "              \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, "
+      "  { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 5 }], "
+      "              \"at\": { \"t\": \"additional two\", \"tt\": 6 } } } "
+      "] }";
+  absl::optional<base::Value> value = base::JSONReader::Read(json);
+  ASSERT_TRUE(value && value->is_dict());
+  ASSERT_TRUE(SuggestionAnswer::ParseAnswer(value->GetDict(), u"-1", &answer));
+
+  AutocompleteMatch match;
+  match.answer = answer;
+  match.contents = u"contents";
+
+  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query");
+  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kList);
+  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
+  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER);
+  EXPECT_EQ(result.title(), u"contents additional one");
+  EXPECT_EQ(result.details(), u"text two additional two");
+}
+
+TEST_F(OmniboxAnswerResultTest, CalculatorResult) {
+  AutocompleteMatch match;
+  match.type = AutocompleteMatchType::CALCULATOR;
+  match.contents = u"2+2";
+  match.description = u"4";
+
+  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query");
+  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
+  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_CALCULATOR);
+
+  ASSERT_EQ(result.title_text_vector().size(), 1);
+  const auto& title = result.title_text_vector()[0];
+  ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title.GetText(), u"4");
+  EXPECT_THAT(title.GetTextTags(),
+              testing::UnorderedElementsAre(TagEquals(
+                  Tag(Tag::Style::MATCH, 0, title.GetText().length()))));
+
+  ASSERT_EQ(result.details_text_vector().size(), 1);
+  const auto& details = result.details_text_vector()[0];
+  ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details.GetText(), u"2+2");
+  EXPECT_TRUE(details.GetTextTags().empty());
+}
+
+TEST_F(OmniboxAnswerResultTest, CalculatorResultNoDescription) {
+  AutocompleteMatch match;
+  match.type = AutocompleteMatchType::CALCULATOR;
+  match.contents = u"4";
+
+  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"2+2");
+  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
+  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_CALCULATOR);
+
+  ASSERT_EQ(result.title_text_vector().size(), 1);
+  const auto& title = result.title_text_vector()[0];
+  ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title.GetText(), u"4");
+  EXPECT_THAT(title.GetTextTags(),
+              testing::UnorderedElementsAre(TagEquals(
+                  Tag(Tag::Style::MATCH, 0, title.GetText().length()))));
+
+  ASSERT_EQ(result.details_text_vector().size(), 1);
+  const auto& details = result.details_text_vector()[0];
+  ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details.GetText(), u"2+2");
+  EXPECT_TRUE(details.GetTextTags().empty());
+}
+
+TEST_F(OmniboxAnswerResultTest, WeatherResult) {
+  // This comes from SuggestionAnswer::AnswerType::ANSWER_TYPE_WEATHER.
+  const std::u16string kWeatherType = u"8";
+
+  SuggestionAnswer answer;
+  std::string json =
+      "{ \"l\": ["
+      "  { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], "
+      "              \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, "
+      "  { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 8 }], "
+      "              \"at\": { \"t\": \"additional two\", \"tt\": 42 } } } "
+      "] }";
+  absl::optional<base::Value> value = base::JSONReader::Read(json);
+  ASSERT_TRUE(value && value->is_dict());
+  ASSERT_TRUE(
+      SuggestionAnswer::ParseAnswer(value->GetDict(), kWeatherType, &answer));
+
+  AutocompleteMatch match;
+  match.answer = answer;
+  match.contents = u"contents";
+  match.description = u"description";
+
+  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query");
+  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
+  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER);
+
+  ASSERT_EQ(result.big_title_text_vector().size(), 1);
+  const auto& big_title = result.big_title_text_vector()[0];
+  ASSERT_EQ(big_title.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(big_title.GetText(), u"text two");
+  EXPECT_TRUE(big_title.GetTextTags().empty());
+
+  ASSERT_EQ(result.title_text_vector().size(), 1);
+  const auto& title = result.title_text_vector()[0];
+  ASSERT_EQ(title.GetType(), ash::SearchResultTextItemType::kString);
+  // TODO(crbug.com/1250154): Once the weather description is enabled from the
+  // Suggest server, this should be updated to display the weather description
+  // instead of |match.contents|.
+  EXPECT_EQ(title.GetText(), u"contents");
+  EXPECT_THAT(title.GetTextTags(),
+              testing::UnorderedElementsAre(TagEquals(
+                  Tag(Tag::Style::MATCH, 0, title.GetText().length()))));
+
+  ASSERT_EQ(result.details_text_vector().size(), 1);
+  const auto& details = result.details_text_vector()[0];
+  ASSERT_EQ(details.GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details.GetText(), u"additional two");
+  EXPECT_TRUE(details.GetTextTags().empty());
+}
+
+TEST_F(OmniboxAnswerResultTest, AnswerResult) {
+  // This comes from SuggestionAnswer::AnswerType::ANSWER_TYPE_FINANCE.
+  const std::u16string kWeatherType = u"2";
+
+  SuggestionAnswer answer;
+  // Text tags ("tt") 5 and 6 are SuggestionAnswer::TextType::NEGATIVE and
+  // SuggestionAnswer::TextType::POSITIVE respectively.
+  std::string json =
+      "{ \"l\": ["
+      "  { \"il\": { \"t\": [{ \"t\": \"text one\", \"tt\": 8 }], "
+      "              \"at\": { \"t\": \"additional one\", \"tt\": 42 } } }, "
+      "  { \"il\": { \"t\": [{ \"t\": \"text two\", \"tt\": 5 }], "
+      "              \"at\": { \"t\": \"additional two\", \"tt\": 6 } } } "
+      "] }";
+  absl::optional<base::Value> value = base::JSONReader::Read(json);
+  ASSERT_TRUE(value && value->is_dict());
+  ASSERT_TRUE(
+      SuggestionAnswer::ParseAnswer(value->GetDict(), kWeatherType, &answer));
+
+  AutocompleteMatch match;
+  match.answer = answer;
+  match.contents = u"contents";
+  match.description = u"description";
+
+  OmniboxAnswerResult result(nullptr, nullptr, nullptr, match, u"query");
+  EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kOmnibox);
+  EXPECT_EQ(result.metrics_type(), ash::OMNIBOX_ANSWER);
+
+  // All title fields should have the MATCH tag, and there should be a space
+  // delimiter added between each field. Additionally, the NEGATIVE text type
+  // should have RED tags, and the POSITIVE text type should have GREEN tags.
+  const auto& title = result.title_text_vector();
+  ASSERT_EQ(title.size(), 3);
+  ASSERT_EQ(title[0].GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title[0].GetText(), u"text two");
+  size_t length = title[0].GetText().length();
+  EXPECT_THAT(title[0].GetTextTags(),
+              testing::UnorderedElementsAre(
+                  TagEquals(Tag(Tag::Style::MATCH, 0, length)),
+                  TagEquals(Tag(Tag::Style::RED, 0, length))));
+
+  ASSERT_EQ(title[1].GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title[1].GetText(), u" ");
+  length = title[1].GetText().length();
+  EXPECT_THAT(title[1].GetTextTags(), testing::UnorderedElementsAre(TagEquals(
+                                          Tag(Tag::Style::MATCH, 0, length))));
+
+  ASSERT_EQ(title[2].GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(title[2].GetText(), u"additional two");
+  length = title[2].GetText().length();
+  EXPECT_THAT(title[2].GetTextTags(),
+              testing::UnorderedElementsAre(
+                  TagEquals(Tag(Tag::Style::MATCH, 0, length)),
+                  TagEquals(Tag(Tag::Style::GREEN, 0, length))));
+
+  // Details text should not have tags.
+  const auto& details = result.details_text_vector();
+  ASSERT_EQ(details.size(), 3);
+  ASSERT_EQ(details[0].GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details[0].GetText(), u"contents");
+  EXPECT_TRUE(details[0].GetTextTags().empty());
+
+  ASSERT_EQ(details[1].GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details[1].GetText(), u" ");
+  EXPECT_TRUE(details[1].GetTextTags().empty());
+
+  ASSERT_EQ(details[2].GetType(), ash::SearchResultTextItemType::kString);
+  EXPECT_EQ(details[2].GetText(), u"additional one");
+  EXPECT_TRUE(details[2].GetTextTags().empty());
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/omnibox_provider.cc b/chrome/browser/ui/app_list/search/omnibox_provider.cc
index e4779c5..07d0e78 100644
--- a/chrome/browser/ui/app_list/search/omnibox_provider.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_provider.cc
@@ -37,6 +37,10 @@
 
 using chromeos::string_matching::TokenizedString;
 
+// Some omnibox answers overtrigger on short queries. This controls the minimum
+// query length before they are displayed.
+constexpr size_t kMinQueryLengthForCommonAnswers = 4u;
+
 bool IsDriveUrl(const GURL& url) {
   // Returns true if the |url| points to a Drive Web host.
   const std::string& host = url.host();
@@ -49,6 +53,25 @@
          match.type == AutocompleteMatchType::CALCULATOR;
 }
 
+// Some answer result types overtrigger on short queries. Returns true if an
+// answer should be filtered.
+bool ShouldFilterAnswer(const AutocompleteMatch& match,
+                        const std::u16string& query) {
+  // TODO(crbug.com/1258415): Move this to the filtering ranker once more
+  // detailed result subtype info is exposed by ChromeSearchResult.
+  if (query.size() >= kMinQueryLengthForCommonAnswers || !match.answer) {
+    return false;
+  }
+
+  switch (match.answer.value().type()) {
+    case SuggestionAnswer::ANSWER_TYPE_DICTIONARY:
+    case SuggestionAnswer::ANSWER_TYPE_TRANSLATION:
+      return true;
+    default:
+      return false;
+  }
+}
+
 int ProviderTypes() {
   // We use all the default providers except for the document provider, which
   // suggests Drive files on enterprise devices. This is disabled to avoid
@@ -165,7 +188,8 @@
       continue;
     }
 
-    if (!is_zero_state_input_ && IsAnswer(match)) {
+    if (!is_zero_state_input_ && IsAnswer(match) &&
+        !ShouldFilterAnswer(match, last_query_)) {
       new_results.emplace_back(std::make_unique<OmniboxAnswerResult>(
           profile_, list_controller_, controller_.get(), match, last_query_));
     } else if (match.type == AutocompleteMatchType::OPEN_TAB) {
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index d83f5c2..9b0ac6f0 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -291,7 +291,6 @@
 }
 
 void OmniboxResult::UpdateTitleAndDetails() {
-  // TODO(crbug.com/1258415): We can remove the tags logic.
   if (!IsUrlResultWithDescription()) {
     SetTitle(match_.contents);
     ChromeSearchResult::Tags title_tags;
diff --git a/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc b/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
index 458088a..57efe087 100644
--- a/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
+++ b/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
@@ -45,11 +45,18 @@
 }
 
 bool ShouldIgnoreResult(const ChromeSearchResult* result) {
+  // Ignore:
+  // - answer results: ANSWER and CALCULATOR.
+  // - 'magnifying glass' results: WEB_QUERY, SEARCH_SUGGEST,
+  //   SEARCH_SUGGEST_PERSONALIZED.
+  //
   // TODO(crbug.com/1258415): We should have a more robust way of determining
   // omnibox subtypes than using the metrics type.
-  return result->metrics_type() == ash::OMNIBOX_WEB_QUERY ||
-         result->metrics_type() == ash::OMNIBOX_ANSWER ||
-         result->metrics_type() == ash::OMNIBOX_CALCULATOR;
+  return result->metrics_type() == ash::OMNIBOX_ANSWER ||
+         result->metrics_type() == ash::OMNIBOX_CALCULATOR ||
+         result->metrics_type() == ash::OMNIBOX_WEB_QUERY ||
+         result->metrics_type() == ash::OMNIBOX_SEARCH_SUGGEST ||
+         result->metrics_type() == ash::OMNIBOX_SUGGEST_PERSONALIZED;
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/app_list/search/search_tags_util.cc b/chrome/browser/ui/app_list/search/search_tags_util.cc
index ab43f87d..8a273b4 100644
--- a/chrome/browser/ui/app_list/search/search_tags_util.cc
+++ b/chrome/browser/ui/app_list/search/search_tags_util.cc
@@ -26,7 +26,8 @@
 
 void ACMatchClassificationsToTags(const std::u16string& text,
                                   const ACMatchClassifications& text_classes,
-                                  ChromeSearchResult::Tags* tags) {
+                                  ChromeSearchResult::Tags* tags,
+                                  const bool ignore_match) {
   int tag_styles = ash::SearchResultTag::NONE;
   size_t tag_start = 0;
 
@@ -40,11 +41,17 @@
       tag_styles = ash::SearchResultTag::NONE;
     }
 
-    if (text_class.style == ACMatchClassification::NONE)
+    int style = text_class.style;
+    if (ignore_match) {
+      style &= ~ACMatchClassification::MATCH;
+    }
+
+    if (style == ACMatchClassification::NONE) {
       continue;
+    }
 
     tag_start = text_class.offset;
-    tag_styles = ACMatchStyleToTagStyle(text_class.style);
+    tag_styles = ACMatchStyleToTagStyle(style);
   }
 
   if (tag_styles != ash::SearchResultTag::NONE) {
diff --git a/chrome/browser/ui/app_list/search/search_tags_util.h b/chrome/browser/ui/app_list/search/search_tags_util.h
index c79b484d..341d7d7 100644
--- a/chrome/browser/ui/app_list/search/search_tags_util.h
+++ b/chrome/browser/ui/app_list/search/search_tags_util.h
@@ -13,7 +13,8 @@
 // Translates ACMatchClassifications into ChromeSearchResult tags.
 void ACMatchClassificationsToTags(const std::u16string& text,
                                   const ACMatchClassifications& text_classes,
-                                  ChromeSearchResult::Tags* tags);
+                                  ChromeSearchResult::Tags* tags,
+                                  const bool ignore_match = false);
 
 // TODO(crbug.com/1258415): Remove this and its references once the productivity
 // launcher is enabled.
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 48f0a5d7..e1f3b2d 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -179,7 +179,7 @@
 }
 
 void ChromeShellDelegate::BindMultiDeviceSetup(
-    mojo::PendingReceiver<chromeos::multidevice_setup::mojom::MultiDeviceSetup>
+    mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
         receiver) {
   chromeos::multidevice_setup::MultiDeviceSetupService* service =
       chromeos::multidevice_setup::MultiDeviceSetupServiceFactory::
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 07155ed..1ff456f 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -45,9 +45,8 @@
   void BindFingerprint(
       mojo::PendingReceiver<device::mojom::Fingerprint> receiver) override;
   void BindMultiDeviceSetup(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup> receiver)
-      override;
+      mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
+          receiver) override;
   media_session::MediaSessionService* GetMediaSessionService() override;
   bool IsSessionRestoreInProgress() const override;
   void SetUpEnvironmentForLockedFullscreen(bool locked) override;
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
index 61b94bf..f2d6590 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/apps/app_service/menu_util.h"
 #include "chrome/browser/ash/app_restore/full_restore_service.h"
 #include "chrome/browser/ash/arc/app_shortcuts/arc_app_shortcuts_menu_builder.h"
+#include "chrome/browser/ash/borealis/borealis_window_manager.h"
 #include "chrome/browser/ash/crosapi/browser_manager.h"
 #include "chrome/browser/ash/crostini/crostini_manager.h"
 #include "chrome/browser/ash/crostini/crostini_shelf_utils.h"
@@ -89,10 +90,11 @@
     const ash::ShelfItem* item,
     int64_t display_id)
     : ShelfContextMenu(controller, item, display_id) {
-  if (crostini::IsUnmatchedCrostiniShelfAppId(item->id.app_id)) {
-    // For Crostini app_id with the prefix "crostini:", set app_type as Unknown
-    // to skip the ArcAppShelfId valid. App type can't be set as Crostini,
-    // because the pin item should not be added for it.
+  if (crostini::IsUnmatchedCrostiniShelfAppId(item->id.app_id) ||
+      borealis::BorealisWindowManager::IsAnonymousAppId(item->id.app_id)) {
+    // Sometimes GuestOS runs applications that are not registered with the apps
+    // service. These "anonymous" apps should not be pinnable, so we set type
+    // "unknown" to avoid the ARC check below.
     app_type_ = apps::AppType::kUnknown;
     return;
   }
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index fed4fb82..e66d40d 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/media/webrtc/permission_bubble_media_access_handler.h"
-#include "chrome/browser/media/webrtc/system_media_capture_permissions_mac.h"
 #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h"
 #include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 3069f81..2020e21 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -40,8 +40,6 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/platform_apps/app_load_service.h"
 #include "chrome/browser/apps/platform_apps/platform_app_launch.h"
-#include "chrome/browser/ash/app_mode/kiosk_app_types.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
 #include "chrome/browser/extensions/startup_helper.h"
@@ -95,7 +93,9 @@
 #include "ash/components/cryptohome/cryptohome_parameters.h"
 #include "ash/constants/ash_switches.h"
 #include "chrome/browser/ash/app_mode/app_launch_utils.h"
+#include "chrome/browser/ash/app_mode/kiosk_app_types.h"
 #include "chrome/browser/ash/app_restore/full_restore_service.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "components/user_manager/user_manager.h"
 #else
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
index 504549eb..52b46704 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view_browsertest.cc
@@ -38,6 +38,7 @@
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/common/referrer.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
@@ -270,11 +271,12 @@
     base::RunLoop run_loop;
     web_app::WebAppProvider::GetForTest(browser()->profile())
         ->install_finalizer()
-        .UninstallWebApp(app_id, webapps::WebappUninstallSource::kAppMenu,
-                         base::BindLambdaForTesting([&](bool uninstalled) {
-                           EXPECT_TRUE(uninstalled);
-                           run_loop.Quit();
-                         }));
+        .UninstallWebApp(
+            app_id, webapps::WebappUninstallSource::kAppMenu,
+            base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+              EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
+              run_loop.Quit();
+            }));
     run_loop.Run();
 
     web_app::SetInstallBounceMetricTimeForTesting(absl::nullopt);
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_content_settings_container.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_content_settings_container.cc
index 8860d26..6cf0fd3 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_content_settings_container.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_content_settings_container.cc
@@ -71,8 +71,11 @@
 void WebAppContentSettingsContainer::FadeIn() {
   if (GetVisible())
     return;
+
+  // The layer may have been destroyed since SetUpForFadeIn() was called.
+  SetPaintToLayer();
+
   SetVisible(true);
-  DCHECK_EQ(layer()->opacity(), 0);
   ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
   settings.SetTransitionDuration(kContentSettingsFadeInDuration);
   layer()->SetOpacity(1);
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
index 7ab14ba..eb6cca8 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -13,7 +13,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
@@ -48,7 +47,6 @@
 #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/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/theme_change_waiter.h"
 #include "extensions/test/test_extension_dir.h"
@@ -466,22 +464,14 @@
         browser(), std::move(web_app_info), start_url);
   }
 
-  void ToggleWindowControlsOverlayAndWaitHelper(
-      content::WebContents* web_contents,
-      BrowserView* browser_view) {
+  void ToggleWindowControlsOverlayAndWait() {
+    auto* web_contents = helper()->browser_view()->GetActiveWebContents();
     helper()->SetupGeometryChangeCallback(web_contents);
     content::TitleWatcher title_watcher(web_contents, u"ongeometrychange");
-    browser_view->ToggleWindowControlsOverlayEnabled();
+    helper()->browser_view()->ToggleWindowControlsOverlayEnabled();
     std::ignore = title_watcher.WaitAndGetTitle();
   }
 
-  // When toggling the WCO app initialized by the helper class.
-  void ToggleWindowControlsOverlayAndWait() {
-    ToggleWindowControlsOverlayAndWaitHelper(
-        helper()->browser_view()->GetActiveWebContents(),
-        helper()->browser_view());
-  }
-
   bool GetWindowControlOverlayVisibility() {
     auto* web_contents = helper()->browser_view()->GetActiveWebContents();
     return EvalJs(web_contents,
@@ -559,24 +549,6 @@
         "rect");
   }
 
-  // Opens a new popup window from |web_contents| on |target_url| and returns
-  // the Browser it opened in.
-  Browser* OpenPopup(content::WebContents* web_contents,
-                     const std::string& target_url) {
-    GURL target_gurl(target_url);
-    content::TestNavigationObserver nav_observer(target_gurl);
-    nav_observer.StartWatchingNewWebContents();
-
-    std::string script =
-        "window.open('" + target_url + "', '_blank', 'popup');";
-    EXPECT_TRUE(content::ExecuteScript(web_contents, script));
-    nav_observer.Wait();
-
-    Browser* popup = chrome::FindLastActive();
-    EXPECT_TRUE(popup);
-    return popup;
-  }
-
  protected:
   content::test::FencedFrameTestHelper fenced_frame_helper_;
 
@@ -685,64 +657,6 @@
   EXPECT_EQ(u"onresize", title_watcher2.WaitAndGetTitle());
 }
 
-// Test to ensure crbug.com/1298226 won't reproduce.
-IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay,
-                       PopupFromWcoAppToItself) {
-  InstallAndLaunchWebApp();
-  auto* wco_web_contents = helper()->browser_view()->GetActiveWebContents();
-
-  Browser* popup = OpenPopup(
-      wco_web_contents,
-      EvalJs(wco_web_contents, "window.location.href").ExtractString());
-
-  BrowserView* popup_browser_view =
-      BrowserView::GetBrowserViewForBrowser(popup);
-  content::WebContents* popup_web_contents =
-      popup_browser_view->GetActiveWebContents();
-
-  EXPECT_TRUE(
-      content::WaitForRenderFrameReady(popup_web_contents->GetMainFrame()));
-  EXPECT_FALSE(popup_browser_view->IsWindowControlsOverlayEnabled());
-  EXPECT_FALSE(EvalJs(popup_web_contents,
-                      "window.navigator.windowControlsOverlay.visible")
-                   .ExtractBool());
-
-  // When popup is opened (from a WCO app) pointing to itself, the popup also
-  // has WCO which can be toggled.
-  ToggleWindowControlsOverlayAndWaitHelper(popup_web_contents,
-                                           popup_browser_view);
-  EXPECT_TRUE(popup_browser_view->IsWindowControlsOverlayEnabled());
-  EXPECT_TRUE(EvalJs(popup_web_contents,
-                     "window.navigator.windowControlsOverlay.visible")
-                  .ExtractBool());
-}
-
-// Test to ensure crbug.com/1298237 won't reproduce.
-IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay,
-                       PopupFromWcoAppToAnyOtherWebsite) {
-  InstallAndLaunchWebApp();
-  // The initial WCO state doesn't matter, but to highlight that it's different,
-  // the script is run with the WCO initially toggled on.
-  ToggleWindowControlsOverlayAndWait();
-  EXPECT_TRUE(GetWindowControlOverlayVisibility());
-
-  Browser* popup = OpenPopup(helper()->browser_view()->GetActiveWebContents(),
-                             "https://google.com");
-  BrowserView* popup_browser_view =
-      BrowserView::GetBrowserViewForBrowser(popup);
-  content::WebContents* popup_web_contents =
-      popup_browser_view->GetActiveWebContents();
-  EXPECT_TRUE(
-      content::WaitForRenderFrameReady(popup_web_contents->GetMainFrame()));
-
-  // When popup is opened pointing to any other site, it will not know whether
-  // the popup app uses WCO or not. This test also ensures it does not crash.
-  EXPECT_FALSE(popup_browser_view->IsWindowControlsOverlayEnabled());
-  EXPECT_FALSE(EvalJs(popup_web_contents,
-                      "window.navigator.windowControlsOverlay.visible")
-                   .ExtractBool());
-}
-
 IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay,
                        WindowControlsOverlayRTL) {
   base::test::ScopedRestoreICUDefaultLocale test_locale("ar");
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
index 1b1c8bb..8c553aa 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_controller.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_params.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/system_app_accessible_name.h"
-#include "chrome/browser/ui/views/web_apps/frame_toolbar/terminal_system_app_menu_button_chromeos.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_content_settings_container.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_utils.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h"
@@ -33,6 +32,7 @@
 #include "ui/views/window/hit_test_utils.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ui/views/web_apps/frame_toolbar/terminal_system_app_menu_button_chromeos.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_delegate.h"
 #endif
 
@@ -279,6 +279,9 @@
 }
 
 void WebAppToolbarButtonContainer::FadeInContentSettingIcons() {
+  if (!GetAnimate())
+    return;
+
   if (content_settings_container_)
     content_settings_container_->FadeIn();
 }
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index f5003dff..7e345de4 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -73,6 +73,7 @@
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -487,8 +488,8 @@
         DCHECK(app->CanUserUninstallWebApp());
         provider->install_finalizer().UninstallWebApp(
             app_id, webapps::WebappUninstallSource::kAppsPage,
-            base::BindLambdaForTesting([&](bool uninstalled) {
-              EXPECT_TRUE(uninstalled);
+            base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+              EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
               run_loop.Quit();
             }));
         run_loop.Run();
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
index 2c2383ab..b101d88 100644
--- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.cc
@@ -24,6 +24,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/clear_site_data_utils.h"
 #include "extensions/browser/extension_dialog_auto_confirm.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -288,7 +289,7 @@
     view_->CancelDialog();
 }
 
-base::OnceCallback<void(bool uninstalled)>
+base::OnceCallback<void(webapps::UninstallResultCode code)>
 WebAppUninstallDialogViews::UninstallStarted() {
   DCHECK(closed_callback_);
   // Next OnWebAppWillBeUninstalled should be ignored. Unsubscribe:
@@ -296,7 +297,12 @@
   // The view can now be destroyed without us knowing, so clear it to prevent
   // UAF in the destructor.
   view_ = nullptr;
-  return std::move(closed_callback_);
+  return base::BindOnce(
+      [](OnWebAppUninstallDialogClosed callback,
+         webapps::UninstallResultCode code) {
+        std::move(callback).Run(code == webapps::UninstallResultCode::kSuccess);
+      },
+      std::move(closed_callback_));
 }
 
 void WebAppUninstallDialogViews::UninstallCancelled() {
diff --git a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h
index 3c3b084..a6304c9 100644
--- a/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h
+++ b/chrome/browser/ui/views/web_apps/web_app_uninstall_dialog_view.h
@@ -30,6 +30,7 @@
 class WebAppUninstallDialogViews;
 
 namespace webapps {
+enum class UninstallResultCode;
 enum class WebappUninstallSource;
 }
 
@@ -110,7 +111,8 @@
 
   // Called when the view is triggering an uninstallation with the
   // WebAppProvider system. Returns a callback to be passed to this system.
-  base::OnceCallback<void(bool uninstalled)> UninstallStarted();
+  base::OnceCallback<void(webapps::UninstallResultCode code)>
+  UninstallStarted();
 
   // Called to signify that the uninstall has been cancelled.
   void UninstallCancelled();
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
index cca15a3..6fd2828 100644
--- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -15,7 +15,6 @@
 #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/launch_utils.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator.h"
@@ -35,6 +34,7 @@
 #include "ui/display/scoped_display_for_new_windows.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #endif
 
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 d0fe0df0..2cbd98c 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
@@ -47,6 +47,7 @@
 #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 "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
 #include "content/public/test/browser_test_utils.h"
@@ -356,7 +357,14 @@
   DCHECK(provider);
   DCHECK(provider->install_finalizer().CanUserUninstallWebApp(app_id));
   provider->install_finalizer().UninstallWebApp(
-      app_id, webapps::WebappUninstallSource::kAppMenu, std::move(callback));
+      app_id, webapps::WebappUninstallSource::kAppMenu,
+      base::BindOnce(
+          [](UninstallWebAppCallback callback,
+             webapps::UninstallResultCode code) {
+            std::move(callback).Run(code ==
+                                    webapps::UninstallResultCode::kSuccess);
+          },
+          std::move(callback)));
 }
 
 BrowserWaiter::BrowserWaiter(Browser* filter) : filter_(filter) {
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 35adb51c..02f7923 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -75,6 +75,7 @@
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/common/content_features.h"
 #include "content/public/test/background_color_change_waiter.h"
 #include "content/public/test/browser_test.h"
@@ -1222,8 +1223,8 @@
   base::RunLoop run_loop_uninstall;
   provider->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
-        DCHECK(uninstalled);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop_uninstall.Quit();
       }));
   run_loop_uninstall.Run();
@@ -1311,8 +1312,8 @@
   base::RunLoop run_loop_uninstall;
   WebAppProvider::GetForTest(profile())->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
-        DCHECK(uninstalled);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop_uninstall.Quit();
       }));
   run_loop_uninstall.Run();
@@ -1375,8 +1376,8 @@
   base::RunLoop run_loop_uninstall;
   provider->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
-        DCHECK(uninstalled);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop_uninstall.Quit();
       }));
   run_loop_uninstall.Run();
@@ -2017,8 +2018,8 @@
   base::RunLoop run_loop_uninstall;
   WebAppProvider::GetForTest(profile())->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppsPage,
-      base::BindLambdaForTesting([&](bool uninstalled) {
-        DCHECK(uninstalled);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop_uninstall.Quit();
       }));
   run_loop_uninstall.Run();
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
index 840a000..4d7e73a 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl_browsertest.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/test/browser_test.h"
 #include "url/gurl.h"
 
@@ -128,8 +129,8 @@
   DCHECK(provider->install_finalizer().CanUserUninstallWebApp(foo_app_id));
   provider->install_finalizer().UninstallWebApp(
       foo_app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool success) {
-        EXPECT_TRUE(success);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop.Quit();
       }));
   run_loop.Run();
diff --git a/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc b/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
index 92769cd..9c186794 100644
--- a/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_uninstall_browsertest.cc
@@ -25,6 +25,7 @@
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -46,8 +47,8 @@
     DCHECK(provider->install_finalizer().CanUserUninstallWebApp(app_id));
     provider->install_finalizer().UninstallWebApp(
         app_id, webapps::WebappUninstallSource::kAppMenu,
-        base::BindLambdaForTesting([&](bool uninstalled) {
-          EXPECT_TRUE(uninstalled);
+        base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+          EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
           run_loop.Quit();
         }));
 
@@ -158,7 +159,7 @@
   DCHECK(provider->install_finalizer().CanUserUninstallWebApp(app_id));
   provider->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
         if (quit_run_loop)
           run_loop.Quit();
         quit_run_loop = true;
@@ -172,7 +173,7 @@
   // Trigger second uninstall call and wait for result.
   provider->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
         if (quit_run_loop)
           run_loop.Quit();
         quit_run_loop = true;
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 1345bc3..cf66d2b 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -246,11 +246,10 @@
 #endif
 }
 
-void AppManagementPageHandler::SetPermission(
-    const std::string& app_id,
-    apps::mojom::PermissionPtr permission) {
+void AppManagementPageHandler::SetPermission(const std::string& app_id,
+                                             apps::PermissionPtr permission) {
   apps::AppServiceProxyFactory::GetForProfile(profile_)->SetPermission(
-      app_id, std::move(permission));
+      app_id, apps::ConvertPermissionToMojomPermission(permission));
 }
 
 void AppManagementPageHandler::SetResizeLocked(const std::string& app_id,
@@ -353,21 +352,25 @@
 
 app_management::mojom::AppPtr AppManagementPageHandler::CreateUIAppPtr(
     const apps::AppUpdate& update) {
-  base::flat_map<apps::mojom::PermissionType, apps::mojom::PermissionPtr>
-      permissions;
+  auto app = app_management::mojom::App::New();
+  app->id = update.AppId();
+  app->type = update.AppType();
+  app->title = update.Name();
+
   for (const auto& permission : update.Permissions()) {
     if (permission->permission_type == apps::mojom::PermissionType::kStorage &&
         ShouldHideStoragePermission(update.AppId())) {
       continue;
     }
-    permissions[permission->permission_type] = permission->Clone();
+    app_management::mojom::PermissionType permission_type =
+        mojo::EnumTraits<app_management::mojom::PermissionType,
+                         apps::PermissionType>::
+            ToMojom(apps::ConvertMojomPermissionTypeToPermissionType(
+                permission->permission_type));
+    app->permissions[permission_type] =
+        apps::ConvertMojomPermissionToPermission(permission);
   }
 
-  auto app = app_management::mojom::App::New();
-  app->id = update.AppId();
-  app->type = update.AppType();
-  app->title = update.Name();
-  app->permissions = std::move(permissions);
   app->install_reason = update.InstallReason();
   app->install_source = update.InstallSource();
 
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.h b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
index 358ff20f..53c966d 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.h
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.h
@@ -8,17 +8,21 @@
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h"
 #include "chrome/browser/web_applications/app_registrar_observer.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
+#include "components/services/app_service/public/cpp/permission.h"
 #include "components/services/app_service/public/cpp/preferred_apps_list_handle.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/webui/resources/cr_components/app_management/app_management.mojom-forward.h"
+#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h"
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.h"
+#endif
 
 class Profile;
 
@@ -61,7 +65,7 @@
   void SetPinned(const std::string& app_id,
                  apps::mojom::OptionalBool pinned) override;
   void SetPermission(const std::string& app_id,
-                     apps::mojom::PermissionPtr permission) override;
+                     apps::PermissionPtr permission) override;
   void SetResizeLocked(const std::string& app_id, bool locked) override;
   void Uninstall(const std::string& app_id) override;
   void OpenNativeSettings(const std::string& app_id) override;
diff --git a/chrome/browser/ui/webui/bookmarks/OWNERS b/chrome/browser/ui/webui/bookmarks/OWNERS
index b963a3f..e69de29 100644
--- a/chrome/browser/ui/webui/bookmarks/OWNERS
+++ b/chrome/browser/ui/webui/bookmarks/OWNERS
@@ -1 +0,0 @@
-calamity@chromium.org
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index e5e9dec..b686163f 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -602,7 +602,7 @@
 
 void BindMultiDeviceSetup(
     Profile* profile,
-    mojo::PendingReceiver<chromeos::multidevice_setup::mojom::MultiDeviceSetup>
+    mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
         receiver) {
   chromeos::multidevice_setup::MultiDeviceSetupService* service = chromeos::
       multidevice_setup::MultiDeviceSetupServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
index de3b403f..91e9c7f 100644
--- a/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/crostini_installer/crostini_installer_ui.cc
@@ -74,8 +74,6 @@
       {"configureContainerMessage",
        IDS_CROSTINI_INSTALLER_CONFIGURE_CONTAINER_MESSAGE},
       {"setupContainerMessage", IDS_CROSTINI_INSTALLER_SETUP_CONTAINER_MESSAGE},
-      {"fetchSshKeysMessage", IDS_CROSTINI_INSTALLER_FETCH_SSH_KEYS_MESSAGE},
-      {"mountContainerMessage", IDS_CROSTINI_INSTALLER_MOUNT_CONTAINER_MESSAGE},
       {"cancelingMessage", IDS_CROSTINI_INSTALLER_CANCELING},
 
       {"configureMessage", IDS_CROSTINI_INSTALLER_CONFIGURE_MESSAGE},
diff --git a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
index 29e3768..0f21a75 100644
--- a/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/drive_internals_ui.cc
@@ -277,6 +277,10 @@
             &DriveInternalsWebUIHandler::SetVerboseLoggingEnabled,
             weak_ptr_factory_.GetWeakPtr()));
     web_ui()->RegisterMessageCallback(
+        "setMirroringEnabled",
+        base::BindRepeating(&DriveInternalsWebUIHandler::SetMirroringEnabled,
+                            weak_ptr_factory_.GetWeakPtr()));
+    web_ui()->RegisterMessageCallback(
         "enableTracing",
         base::BindRepeating(&DriveInternalsWebUIHandler::SetTracingEnabled,
                             weak_ptr_factory_.GetWeakPtr(), true));
@@ -469,6 +473,10 @@
     MaybeCallJavascript("updateVerboseLogging",
                         base::Value(verbose_logging_enabled));
 
+    bool mirroring_enabled = profile()->GetPrefs()->GetBoolean(
+        drive::prefs::kDriveFsEnableMirrorSync);
+    MaybeCallJavascript("updateMirroring", base::Value(mirroring_enabled));
+
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
         base::BindOnce(GetDeveloperMode),
@@ -533,6 +541,7 @@
         drive::prefs::kDriveFsWasLaunchedAtLeastOnce,
         drive::prefs::kDriveFsPinnedMigrated,
         drive::prefs::kDriveFsEnableVerboseLogging,
+        drive::prefs::kDriveFsEnableMirrorSync,
     };
 
     PrefService* pref_service = profile()->GetPrefs();
@@ -661,6 +670,21 @@
     }
   }
 
+  void SetMirroringEnabled(const base::Value::List& args) {
+    AllowJavascript();
+    drive::DriveIntegrationService* integration_service =
+        GetIntegrationService();
+    if (!integration_service) {
+      return;
+    }
+
+    if (args.size() == 1 && args[0].is_bool()) {
+      bool enabled = args[0].GetBool();
+      profile()->GetPrefs()->SetBoolean(drive::prefs::kDriveFsEnableMirrorSync,
+                                        enabled);
+    }
+  }
+
   // Called when the "Startup Arguments" field on the page is submitted.
   void SetStartupArguments(const base::Value::List& args) {
     AllowJavascript();
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
index d4a4da8..5088e1ba 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -137,6 +137,11 @@
 
 namespace chromeos {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace multidevice_setup {
+namespace mojom = ::ash::multidevice_setup::mojom;
+}
+
 namespace {
 
 const char* kKnownDisplayTypes[] = {
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
index 35a04a5..b77f478 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.h
@@ -151,13 +151,13 @@
   // Instantiates implementor of the mojom::MultiDeviceSetup mojo interface
   // passing the pending receiver that will be internally bound.
   void BindInterface(
-      mojo::PendingReceiver<multidevice_setup::mojom::MultiDeviceSetup>
+      mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
           receiver);
   // Instantiates implementor of the mojom::PrivilegedHostDeviceSetter mojo
   // interface passing the pending receiver that will be internally bound.
   void BindInterface(
       mojo::PendingReceiver<
-          multidevice_setup::mojom::PrivilegedHostDeviceSetter> receiver);
+          ash::multidevice_setup::mojom::PrivilegedHostDeviceSetter> receiver);
   // Instantiates implementor of the mojom::CrosNetworkConfig mojo
   // interface passing the pending receiver that will be internally bound.
   void BindInterface(
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
index 7a65a2ec..53f0ae9e 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.cc
@@ -128,7 +128,7 @@
 MultiDeviceSetupDialogUI::~MultiDeviceSetupDialogUI() = default;
 
 void MultiDeviceSetupDialogUI::BindInterface(
-    mojo::PendingReceiver<chromeos::multidevice_setup::mojom::MultiDeviceSetup>
+    mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
         receiver) {
   MultiDeviceSetupService* service =
       MultiDeviceSetupServiceFactory::GetForProfile(
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h
index 702b724..765e1ca7 100644
--- a/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h
+++ b/chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h
@@ -68,8 +68,8 @@
   // Instantiates implementor of the mojom::MultiDeviceSetup mojo interface
   // passing the pending receiver that will be internally bound.
   void BindInterface(
-      mojo::PendingReceiver<
-          chromeos::multidevice_setup::mojom::MultiDeviceSetup> receiver);
+      mojo::PendingReceiver<ash::multidevice_setup::mojom::MultiDeviceSetup>
+          receiver);
 
  private:
   WEB_UI_CONTROLLER_TYPE_DECL();
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
index 207e061..2fdb5e3c5 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler.cc
@@ -79,6 +79,7 @@
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/web_ui.h"
 #include "extensions/browser/app_sorting.h"
@@ -911,24 +912,35 @@
       return;
     }
 
-    auto uninstall_success_callback = base::BindOnce(
-        [](base::WeakPtr<AppLauncherHandler> app_launcher_handler,
-           bool success) {
-          if (app_launcher_handler)
-            app_launcher_handler->CleanupAfterUninstall();
-        },
-        weak_ptr_factory_.GetWeakPtr());
-
     extension_id_prompting_ = extension_id;
     const auto& list = args->GetListDeprecated();
     const bool dont_confirm =
         list.size() >= 2 && list[1].is_bool() && list[1].GetBool();
+
     if (dont_confirm) {
+      auto uninstall_success_callback = base::BindOnce(
+          [](base::WeakPtr<AppLauncherHandler> app_launcher_handler,
+             webapps::UninstallResultCode code) {
+            if (app_launcher_handler) {
+              app_launcher_handler->CleanupAfterUninstall();
+            }
+          },
+          weak_ptr_factory_.GetWeakPtr());
+
       base::AutoReset<bool> auto_reset(&ignore_changes_, true);
       web_app_provider_->install_finalizer().UninstallWebApp(
           extension_id_prompting_, webapps::WebappUninstallSource::kAppsPage,
           std::move(uninstall_success_callback));
     } else {
+      auto uninstall_success_callback = base::BindOnce(
+          [](base::WeakPtr<AppLauncherHandler> app_launcher_handler,
+             bool success) {
+            if (app_launcher_handler) {
+              app_launcher_handler->CleanupAfterUninstall();
+            }
+          },
+          weak_ptr_factory_.GetWeakPtr());
+
       Browser* browser =
           chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
       web_app::WebAppUiManagerImpl::Get(web_app_provider_)
diff --git a/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc b/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc
index 636c5e57..2063da8 100644
--- a/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc
+++ b/chrome/browser/ui/webui/settings/ash/os_apps_page/app_notification_handler.cc
@@ -34,9 +34,7 @@
   app->id = update.AppId();
   app->title = update.Name();
   app->notification_permission = std::move(permission_copy);
-  app->readiness =
-      mojo::EnumTraits<app_notification::mojom::Readiness,
-                       apps::Readiness>::ToMojom(update.Readiness());
+  app->readiness = update.Readiness();
 
   return app;
 }
diff --git a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn
index c7bacea..5f555c2 100644
--- a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn
+++ b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/BUILD.gn
@@ -11,23 +11,21 @@
   sources = [ "app_notification_handler.mojom" ]
 
   public_deps = [
-    "//components/services/app_service/public/mojom",
     "//mojo/public/mojom/base",
+    "//ui/webui/resources/cr_components/app_management:mojo_bindings",
   ]
 
   cpp_typemaps = [
     {
       types = [
         {
-          mojom = "chromeos.settings.app_notification.mojom.Permission"
-          cpp = "::apps::PermissionPtr"
-          move_only = true
+          mojom = "chromeos.settings.app_notification.mojom.Readiness"
+          cpp = "::apps::Readiness"
         },
       ]
       traits_headers = [
         "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h",
         "//components/services/app_service/public/cpp/app_types.h",
-        "//components/services/app_service/public/cpp/permission.h",
       ]
       traits_sources = [ "//chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.cc" ]
       traits_public_deps =
diff --git a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom
index 29345ec..3c13cc74 100644
--- a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom
+++ b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom
@@ -4,6 +4,8 @@
 
 module chromeos.settings.app_notification.mojom;
 
+import "ui/webui/resources/cr_components/app_management/app_management.mojom";
+
 // Whether an app is ready to launch, i.e. installed.
 enum Readiness {
   kUnknown = 0,
@@ -17,36 +19,6 @@
   kUninstalledByMigration,
 };
 
-// The types of permission.
-enum PermissionType {
-  kUnknown         = 0,
-  kCamera          = 1,
-  kLocation        = 2,
-  kMicrophone      = 3,
-  kNotifications   = 4,
-  kContacts        = 5,
-  kStorage         = 6,
-  kPrinting        = 7,
-};
-
-enum TriState {
-  kAllow,
-  kBlock,
-  kAsk,
-};
-
-union PermissionValue {
-  bool bool_value;
-  TriState tristate_value;
-};
-
-struct Permission {
-  PermissionType permission_type;
-  PermissionValue value;
-  // If the permission is managed by an enterprise policy.
-  bool is_managed;
-};
-
 // Implementation of App
 // Contains the app's id, title, and only the notification permission, as this
 // is the only permission used in the AppNotificationsHandler.
@@ -63,7 +35,7 @@
   Readiness readiness;
 
   // Contains the current permission state of the App's notification.
-  Permission notification_permission;
+  app_management.mojom.Permission notification_permission;
 };
 
 // Browser interface.
@@ -77,7 +49,8 @@
   AddObserver(pending_remote<AppNotificationsObserver> observer);
 
   // Updates the permission of the specified app (via app_id).
-  SetNotificationPermission(string app_id, Permission permission);
+  SetNotificationPermission(string app_id,
+                            app_management.mojom.Permission permission);
 
   // Get the list of installed apps.
   GetApps() => (array<App> apps);
diff --git a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.cc b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.cc
index 8a4fecad..8790c55 100644
--- a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.cc
+++ b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h"
 
-#include <utility>
-
 namespace mojo {
 
 Readiness EnumTraits<Readiness, apps::Readiness>::ToMojom(
@@ -66,131 +64,4 @@
   }
 }
 
-bool StructTraits<PermissionDataView, apps::PermissionPtr>::Read(
-    PermissionDataView data,
-    apps::PermissionPtr* out) {
-  apps::PermissionType permission_type;
-  if (!data.ReadPermissionType(&permission_type))
-    return false;
-
-  apps::PermissionValuePtr value;
-  if (!data.ReadValue(&value))
-    return false;
-
-  *out = std::make_unique<apps::Permission>(permission_type, std::move(value),
-                                            data.is_managed());
-  return true;
-}
-
-PermissionType EnumTraits<PermissionType, apps::PermissionType>::ToMojom(
-    apps::PermissionType input) {
-  switch (input) {
-    case apps::PermissionType::kUnknown:
-      return PermissionType::kUnknown;
-    case apps::PermissionType::kCamera:
-      return PermissionType::kCamera;
-    case apps::PermissionType::kLocation:
-      return PermissionType::kLocation;
-    case apps::PermissionType::kMicrophone:
-      return PermissionType::kMicrophone;
-    case apps::PermissionType::kNotifications:
-      return PermissionType::kNotifications;
-    case apps::PermissionType::kContacts:
-      return PermissionType::kContacts;
-    case apps::PermissionType::kStorage:
-      return PermissionType::kStorage;
-    case apps::PermissionType::kPrinting:
-      return PermissionType::kPrinting;
-  }
-}
-
-bool EnumTraits<PermissionType, apps::PermissionType>::FromMojom(
-    PermissionType input,
-    apps::PermissionType* output) {
-  switch (input) {
-    case PermissionType::kUnknown:
-      *output = apps::PermissionType::kUnknown;
-      return true;
-    case PermissionType::kCamera:
-      *output = apps::PermissionType::kCamera;
-      return true;
-    case PermissionType::kLocation:
-      *output = apps::PermissionType::kLocation;
-      return true;
-    case PermissionType::kMicrophone:
-      *output = apps::PermissionType::kMicrophone;
-      return true;
-    case PermissionType::kNotifications:
-      *output = apps::PermissionType::kNotifications;
-      return true;
-    case PermissionType::kContacts:
-      *output = apps::PermissionType::kContacts;
-      return true;
-    case PermissionType::kStorage:
-      *output = apps::PermissionType::kStorage;
-      return true;
-    case PermissionType::kPrinting:
-      *output = apps::PermissionType::kPrinting;
-      return true;
-  }
-}
-
-TriState EnumTraits<TriState, apps::TriState>::ToMojom(apps::TriState input) {
-  switch (input) {
-    case apps::TriState::kAllow:
-      return TriState::kAllow;
-    case apps::TriState::kBlock:
-      return TriState::kBlock;
-    case apps::TriState::kAsk:
-      return TriState::kAsk;
-  }
-}
-
-bool EnumTraits<TriState, apps::TriState>::FromMojom(TriState input,
-                                                     apps::TriState* output) {
-  switch (input) {
-    case TriState::kAllow:
-      *output = apps::TriState::kAllow;
-      return true;
-    case TriState::kBlock:
-      *output = apps::TriState::kBlock;
-      return true;
-    case TriState::kAsk:
-      *output = apps::TriState::kAsk;
-      return true;
-  }
-}
-
-PermissionValueDataView::Tag
-UnionTraits<PermissionValueDataView, apps::PermissionValuePtr>::GetTag(
-    const apps::PermissionValuePtr& r) {
-  if (r->bool_value.has_value()) {
-    return PermissionValueDataView::Tag::BOOL_VALUE;
-  } else if (r->tristate_value.has_value()) {
-    return PermissionValueDataView::Tag::TRISTATE_VALUE;
-  }
-  NOTREACHED();
-  return PermissionValueDataView::Tag::BOOL_VALUE;
-}
-
-bool UnionTraits<PermissionValueDataView, apps::PermissionValuePtr>::Read(
-    PermissionValueDataView data,
-    apps::PermissionValuePtr* out) {
-  switch (data.tag()) {
-    case PermissionValueDataView::Tag::BOOL_VALUE: {
-      *out = std::make_unique<apps::PermissionValue>(data.bool_value());
-      return true;
-    }
-    case PermissionValueDataView::Tag::TRISTATE_VALUE: {
-      apps::TriState tristate_value;
-      if (!data.ReadTristateValue(&tristate_value))
-        return false;
-      *out = std::make_unique<apps::PermissionValue>(tristate_value);
-      return true;
-    }
-  }
-  NOTREACHED();
-  return false;
-}
-
 }  // namespace mojo
diff --git a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h
index 780affc..77269cff 100644
--- a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h
+++ b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h
@@ -8,20 +8,12 @@
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom.h"
 #include "components/services/app_service/public/cpp/app_types.h"
 #include "components/services/app_service/public/cpp/permission.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace mojo {
 
 namespace {
 
 using Readiness = chromeos::settings::app_notification::mojom::Readiness;
-using PermissionDataView =
-    chromeos::settings::app_notification::mojom::PermissionDataView;
-using PermissionType =
-    chromeos::settings::app_notification::mojom::PermissionType;
-using TriState = chromeos::settings::app_notification::mojom::TriState;
-using PermissionValueDataView =
-    chromeos::settings::app_notification::mojom::PermissionValueDataView;
 
 }  // namespace
 
@@ -32,60 +24,12 @@
 };
 
 template <>
-struct StructTraits<PermissionDataView, apps::PermissionPtr> {
-  static apps::PermissionType permission_type(const apps::PermissionPtr& r) {
-    return r->permission_type;
-  }
-
-  static const apps::PermissionValuePtr& value(const apps::PermissionPtr& r) {
-    return r->value;
-  }
-
-  static bool is_managed(const apps::PermissionPtr& r) { return r->is_managed; }
-
-  static bool Read(PermissionDataView, apps::PermissionPtr* out);
-};
-
-template <>
 struct CloneTraits<apps::PermissionPtr> {
   static apps::PermissionPtr Clone(const apps::PermissionPtr& input) {
     return input->Clone();
   }
 };
 
-template <>
-struct EnumTraits<PermissionType, apps::PermissionType> {
-  static PermissionType ToMojom(apps::PermissionType input);
-  static bool FromMojom(PermissionType input, apps::PermissionType* output);
-};
-
-template <>
-struct EnumTraits<TriState, apps::TriState> {
-  static TriState ToMojom(apps::TriState input);
-  static bool FromMojom(TriState input, apps::TriState* output);
-};
-
-template <>
-struct UnionTraits<PermissionValueDataView, apps::PermissionValuePtr> {
-  static PermissionValueDataView::Tag GetTag(const apps::PermissionValuePtr& r);
-
-  static bool IsNull(const apps::PermissionValuePtr& r) {
-    return !r->bool_value.has_value() && !r->tristate_value.has_value();
-  }
-
-  static void SetToNull(apps::PermissionValuePtr* out) { out->reset(); }
-
-  static bool bool_value(const apps::PermissionValuePtr& r) {
-    return r->bool_value.value();
-  }
-
-  static apps::TriState tristate_value(const apps::PermissionValuePtr& r) {
-    return r->tristate_value.value();
-  }
-
-  static bool Read(PermissionValueDataView data, apps::PermissionValuePtr* out);
-};
-
 }  // namespace mojo
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_ASH_OS_APPS_PAGE_MOJOM_APP_TYPE_MOJOM_TRAITS_H_
diff --git a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits_unittest.cc b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits_unittest.cc
index 63dddef..6cd4132 100644
--- a/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits_unittest.cc
+++ b/chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits_unittest.cc
@@ -2,12 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <utility>
-
-#include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom.h"
 #include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_type_mojom_traits.h"
+#include "chrome/browser/ui/webui/settings/ash/os_apps_page/mojom/app_notification_handler.mojom.h"
 #include "components/services/app_service/public/cpp/app_types.h"
-#include "components/services/app_service/public/cpp/permission.h"
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -38,94 +35,3 @@
     EXPECT_EQ(readiness_in, readiness_out);
   }
 }
-
-TEST(AppTypeMojomTraitsTest, RoundTripPermissions) {
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kUnknown,
-        std::make_unique<apps::PermissionValue>(true),
-        /*is_managed=*/false);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kCamera,
-        std::make_unique<apps::PermissionValue>(true),
-        /*is_managed=*/true);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kLocation,
-        std::make_unique<apps::PermissionValue>(apps::TriState::kAllow),
-        /*is_managed=*/false);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kMicrophone,
-        std::make_unique<apps::PermissionValue>(apps::TriState::kBlock),
-        /*is_managed=*/true);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kNotifications,
-        std::make_unique<apps::PermissionValue>(apps::TriState::kAsk),
-        /*is_managed=*/false);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kContacts,
-        std::make_unique<apps::PermissionValue>(apps::TriState::kAllow),
-        /*is_managed=*/true);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kStorage,
-        std::make_unique<apps::PermissionValue>(apps::TriState::kBlock),
-        /*is_managed=*/false);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-  {
-    auto permission = std::make_unique<apps::Permission>(
-        apps::PermissionType::kPrinting,
-        std::make_unique<apps::PermissionValue>(apps::TriState::kBlock),
-        /*is_managed=*/false);
-    apps::PermissionPtr output;
-    ASSERT_TRUE(mojo::test::SerializeAndDeserialize<
-                chromeos::settings::app_notification::mojom::Permission>(
-        permission, output));
-    EXPECT_EQ(*permission, *output);
-  }
-}
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index d2f2c19..92673b0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -31,6 +31,11 @@
 
 namespace chromeos {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace multidevice_setup {
+namespace mojom = ::ash::multidevice_setup::mojom;
+}
+
 namespace settings {
 
 namespace {
@@ -435,14 +440,14 @@
       kAndroidSmsInfoOriginKey,
       ContentSettingsPattern::FromURLNoWildcard(*app_url).ToString());
 
-  chromeos::multidevice_setup::mojom::FeatureState messages_state =
+  multidevice_setup::mojom::FeatureState messages_state =
       multidevice_setup_client_->GetFeatureState(
-          chromeos::multidevice_setup::mojom::Feature::kMessages);
+          multidevice_setup::mojom::Feature::kMessages);
   bool enabled_state =
       messages_state ==
-          chromeos::multidevice_setup::mojom::FeatureState::kEnabledByUser ||
-      messages_state == chromeos::multidevice_setup::mojom::FeatureState::
-                            kFurtherSetupRequired;
+          multidevice_setup::mojom::FeatureState::kEnabledByUser ||
+      messages_state ==
+          multidevice_setup::mojom::FeatureState::kFurtherSetupRequired;
   android_sms_info->SetBoolKey(kAndroidSmsInfoEnabledKey, enabled_state);
 
   return android_sms_info;
@@ -489,11 +494,11 @@
   DCHECK(features::IsEchePhoneHubPermissionsOnboarding());
   DCHECK(!apps_access_operation_);
 
-  ash::eche_app::AppsAccessManager::AccessStatus apps_access_status =
+  phonehub::MultideviceFeatureAccessManager::AccessStatus apps_access_status =
       apps_access_manager_->GetAccessStatus();
 
-  if (apps_access_status !=
-      ash::eche_app::AppsAccessManager::AccessStatus::kAvailableButNotGranted) {
+  if (apps_access_status != phonehub::MultideviceFeatureAccessManager::
+                                AccessStatus::kAvailableButNotGranted) {
     PA_LOG(WARNING) << "Cannot request apps access setup flow; current "
                     << "status: " << apps_access_status;
     return;
@@ -635,13 +640,14 @@
   page_content_dictionary->SetInteger(
       kCameraRollAccessStatus, static_cast<int32_t>(camera_roll_access_status));
 
-  ash::eche_app::AppsAccessManager::AccessStatus apps_access_status =
-      ash::eche_app::AppsAccessManager::AccessStatus::kAvailableButNotGranted;
+  phonehub::MultideviceFeatureAccessManager::AccessStatus apps_access_status =
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::
+          kAvailableButNotGranted;
   if (apps_access_manager_)
     apps_access_status = apps_access_manager_->GetAccessStatus();
   bool is_apps_access_granted =
       apps_access_status ==
-      ash::eche_app::AppsAccessManager::AccessStatus::kAccessGranted;
+      phonehub::MultideviceFeatureAccessManager::AccessStatus::kAccessGranted;
 
   page_content_dictionary->SetBoolKey(kIsPhoneHubAppsAccessGranted,
                                       is_apps_access_granted);
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index c6f095a..29ecff2 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -8,11 +8,11 @@
 
 #include "ash/components/phonehub/fake_camera_roll_manager.h"
 #include "ash/components/phonehub/fake_multidevice_feature_access_manager.h"
+#include "ash/components/phonehub/multidevice_feature_access_manager.h"
 #include "ash/constants/ash_features.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_android_sms_pairing_state_tracker.h"
 #include "ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
 #include "ash/services/multidevice_setup/public/cpp/prefs.h"
-#include "ash/webui/eche_app_ui/apps_access_manager.h"
 #include "ash/webui/eche_app_ui/fake_apps_access_manager.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ash/android_sms/android_sms_urls.h"
@@ -32,6 +32,11 @@
 
 namespace chromeos {
 
+// TODO(https://crbug.com/1164001): remove after migrating to ash.
+namespace multidevice_setup {
+namespace mojom = ::ash::multidevice_setup::mojom;
+}
+
 namespace settings {
 
 namespace {
@@ -225,7 +230,7 @@
         std::make_unique<android_sms::FakeAndroidSmsAppManager>();
     fake_apps_access_manager_ =
         std::make_unique<ash::eche_app::FakeAppsAccessManager>(
-            ash::eche_app::AppsAccessManager::AccessStatus::
+            phonehub::MultideviceFeatureAccessManager::AccessStatus::
                 kAvailableButNotGranted);
     fake_camera_roll_manager_ =
         std::make_unique<ash::phonehub::FakeCameraRollManager>();
@@ -333,10 +338,10 @@
 
   void CallAttemptAppsSetup(bool has_access_been_granted) {
     fake_apps_access_manager()->SetAccessStatusInternal(
-        has_access_been_granted
-            ? ash::eche_app::AppsAccessManager::AccessStatus::kAccessGranted
-            : ash::eche_app::AppsAccessManager::AccessStatus::
-                  kAvailableButNotGranted);
+        has_access_been_granted ? phonehub::MultideviceFeatureAccessManager::
+                                      AccessStatus::kAccessGranted
+                                : phonehub::MultideviceFeatureAccessManager::
+                                      AccessStatus::kAvailableButNotGranted);
     base::ListValue empty_args;
     test_web_ui()->HandleReceivedMessage("attemptAppsSetup", &empty_args);
   }
@@ -446,11 +451,11 @@
   void SimulateAppsAccessStatusChanged(bool has_access_been_granted) {
     size_t call_data_count_before_call = test_web_ui()->call_data().size();
 
-    ash::eche_app::AppsAccessManager::AccessStatus apps_access_status =
-        has_access_been_granted
-            ? ash::eche_app::AppsAccessManager::AccessStatus::kAccessGranted
-            : ash::eche_app::AppsAccessManager::AccessStatus::
-                  kAvailableButNotGranted;
+    phonehub::MultideviceFeatureAccessManager::AccessStatus apps_access_status =
+        has_access_been_granted ? phonehub::MultideviceFeatureAccessManager::
+                                      AccessStatus::kAccessGranted
+                                : phonehub::MultideviceFeatureAccessManager::
+                                      AccessStatus::kAvailableButNotGranted;
     fake_apps_access_manager()->SetAccessStatusInternal(apps_access_status);
     expected_is_phone_hub_apps_access_granted_ = has_access_been_granted;
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index 767f6c34..22fd44a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -42,7 +42,9 @@
 namespace settings {
 namespace {
 
-using FeatureState = multidevice_setup::mojom::FeatureState;
+using Feature = ::ash::multidevice_setup::mojom::Feature;
+using FeatureState = ::ash::multidevice_setup::mojom::FeatureState;
+using HostStatus = ::ash::multidevice_setup::mojom::HostStatus;
 
 // TODO(https://crbug.com/1164001): remove after migrating to namespace ash.
 namespace phonehub = ::ash::phonehub;
@@ -299,10 +301,9 @@
   html_source->AddLocalizedStrings(kLocalizedStrings);
 }
 
-bool IsOptedIn(multidevice_setup::mojom::HostStatus host_status) {
-  return host_status ==
-             multidevice_setup::mojom::HostStatus::kHostSetButNotYetVerified ||
-         host_status == multidevice_setup::mojom::HostStatus::kHostVerified;
+bool IsOptedIn(HostStatus host_status) {
+  return host_status == HostStatus::kHostSetButNotYetVerified ||
+         host_status == HostStatus::kHostVerified;
 }
 
 }  // namespace
@@ -746,27 +747,25 @@
   updater.RemoveSearchTags(GetMultiDeviceOptedInWifiSyncSearchConcepts());
   updater.RemoveSearchTags(GetMultiDeviceOptedInPhoneHubAppsSearchConcepts());
 
-  if (feature_states_map.at(multidevice_setup::mojom::Feature::kSmartLock) ==
-      multidevice_setup::mojom::FeatureState::kEnabledByUser) {
+  if (feature_states_map.at(Feature::kSmartLock) ==
+      FeatureState::kEnabledByUser) {
     updater.AddSearchTags(GetSmartLockOptionsSearchConcepts());
   }
-  if (IsFeatureSupported(multidevice_setup::mojom::Feature::kPhoneHub)) {
+  if (IsFeatureSupported(Feature::kPhoneHub)) {
     updater.AddSearchTags(GetMultiDeviceOptedInPhoneHubSearchConcepts());
     if (features::IsPhoneHubCameraRollEnabled() &&
-        IsFeatureSupported(
-            multidevice_setup::mojom::Feature::kPhoneHubCameraRoll)) {
+        IsFeatureSupported(Feature::kPhoneHubCameraRoll)) {
       updater.AddSearchTags(
           GetMultiDeviceOptedInPhoneHubCameraRollSearchConcepts());
     }
   }
-  if (IsFeatureSupported(multidevice_setup::mojom::Feature::kWifiSync))
+  if (IsFeatureSupported(Feature::kWifiSync))
     updater.AddSearchTags(GetMultiDeviceOptedInWifiSyncSearchConcepts());
-  if (IsFeatureSupported(multidevice_setup::mojom::Feature::kEche))
+  if (IsFeatureSupported(Feature::kEche))
     updater.AddSearchTags(GetMultiDeviceOptedInPhoneHubAppsSearchConcepts());
 }
 
-bool MultiDeviceSection::IsFeatureSupported(
-    multidevice_setup::mojom::Feature feature) {
+bool MultiDeviceSection::IsFeatureSupported(Feature feature) {
   const FeatureState feature_state =
       multidevice_setup_client_->GetFeatureState(feature);
   return feature_state != FeatureState::kNotSupportedByPhone &&
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
index eb110e39..a8e17e5 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
@@ -67,7 +67,7 @@
   // Nearby Share enabled pref change observer.
   void OnNearbySharingEnabledChanged();
 
-  bool IsFeatureSupported(multidevice_setup::mojom::Feature feature);
+  bool IsFeatureSupported(ash::multidevice_setup::mojom::Feature feature);
   void RefreshNearbyBackgroundScanningShareSearchConcepts();
 
   // nearby_share::mojom::NearbyShareSettingsObserver:
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 4027e8e..77279d04 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -17,7 +17,6 @@
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -106,6 +105,7 @@
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/chromeos/devicetype_utils.h"
@@ -1022,6 +1022,7 @@
        IDS_SETTINGS_PASSWORD_VIEW_EXISTING_PASSWORD_ARIA_DESCRIPTION},
       {"copyPassword", IDS_SETTINGS_PASSWORD_COPY},
       {"sendPassword", IDS_SETTINGS_PASSWORD_SEND},
+      {"copyUsername", IDS_SETTINGS_USERNAME_COPY},
       {"passwordStoredOnDevice", IDS_SETTINGS_PASSWORD_STORED_ON_DEVICE},
       {"passwordStoredInAccount", IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT},
       {"passwordStoredInAccountAndOnDevice",
diff --git a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
index 0740cc5..a707204 100644
--- a/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/externally_managed_app_install_task_unittest.cc
@@ -46,6 +46,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -129,11 +130,11 @@
   }
 
   void SetNextUninstallExternalWebAppResult(const GURL& app_url,
-                                            bool uninstalled) {
+                                            webapps::UninstallResultCode code) {
     DCHECK(!base::Contains(next_uninstall_external_web_app_results_, app_url));
 
     next_uninstall_external_web_app_results_[app_url] = {
-        GetAppIdForUrl(app_url), uninstalled};
+        GetAppIdForUrl(app_url), code};
   }
 
   const std::vector<WebAppInstallInfo>& web_app_info_list() {
@@ -195,7 +196,8 @@
     UnregisterApp(app_id);
 
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), /*uninstalled=*/true));
+        FROM_HERE, base::BindOnce(std::move(callback),
+                                  webapps::UninstallResultCode::kSuccess));
   }
 
   void UninstallExternalWebAppByUrl(
@@ -206,18 +208,17 @@
     uninstall_external_web_app_urls_.push_back(app_url);
 
     AppId app_id;
-    bool uninstalled;
-    std::tie(app_id, uninstalled) =
-        next_uninstall_external_web_app_results_[app_url];
+    webapps::UninstallResultCode code;
+    std::tie(app_id, code) = next_uninstall_external_web_app_results_[app_url];
     next_uninstall_external_web_app_results_.erase(app_url);
 
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::BindLambdaForTesting(
-            [&, app_id, uninstalled, callback = std::move(callback)]() mutable {
-              if (uninstalled)
+            [&, app_id, code, callback = std::move(callback)]() mutable {
+              if (code == webapps::UninstallResultCode::kSuccess)
                 UnregisterApp(app_id);
-              std::move(callback).Run(uninstalled);
+              std::move(callback).Run(code);
             }));
   }
 
@@ -272,7 +273,7 @@
 
   // Maps app URLs to the id of the app that would have been installed for that
   // url and the result of trying to uninstall it.
-  std::map<GURL, std::pair<AppId, bool>>
+  std::map<GURL, std::pair<AppId, webapps::UninstallResultCode>>
       next_uninstall_external_web_app_results_;
 };
 
@@ -736,7 +737,8 @@
   // Replace the placeholder with a real app.
   options.reinstall_placeholder = true;
   auto task = GetInstallationTaskWithTestMocks(options);
-  finalizer()->SetNextUninstallExternalWebAppResult(kWebAppUrl, true);
+  finalizer()->SetNextUninstallExternalWebAppResult(
+      kWebAppUrl, webapps::UninstallResultCode::kSuccess);
   url_loader().SetPrepareForLoadResultLoaded();
   url_loader().SetNextLoadUrlResult(kWebAppUrl,
                                     WebAppUrlLoader::Result::kUrlLoaded);
@@ -795,7 +797,8 @@
   options.reinstall_placeholder = true;
   auto task = GetInstallationTaskWithTestMocks(options);
 
-  finalizer()->SetNextUninstallExternalWebAppResult(kWebAppUrl, false);
+  finalizer()->SetNextUninstallExternalWebAppResult(
+      kWebAppUrl, webapps::UninstallResultCode::kError);
   url_loader().SetPrepareForLoadResultLoaded();
   url_loader().SetNextLoadUrlResult(kWebAppUrl,
                                     WebAppUrlLoader::Result::kUrlLoaded);
diff --git a/chrome/browser/web_applications/externally_managed_app_install_task.cc b/chrome/browser/web_applications/externally_managed_app_install_task.cc
index 42d4774..2065672 100644
--- a/chrome/browser/web_applications/externally_managed_app_install_task.cc
+++ b/chrome/browser/web_applications/externally_managed_app_install_task.cc
@@ -27,6 +27,7 @@
 #include "components/webapps/browser/installable/installable_manager.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "components/webapps/browser/installable/installable_params.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 
@@ -191,8 +192,8 @@
 void ExternallyManagedAppInstallTask::OnPlaceholderUninstalled(
     content::WebContents* web_contents,
     ResultCallback result_callback,
-    bool uninstalled) {
-  if (!uninstalled) {
+    webapps::UninstallResultCode code) {
+  if (code != webapps::UninstallResultCode::kSuccess) {
     LOG(ERROR) << "Failed to uninstall placeholder for: "
                << install_options_.install_url;
     std::move(result_callback)
diff --git a/chrome/browser/web_applications/externally_managed_app_install_task.h b/chrome/browser/web_applications/externally_managed_app_install_task.h
index c9cd70c..3091d3b 100644
--- a/chrome/browser/web_applications/externally_managed_app_install_task.h
+++ b/chrome/browser/web_applications/externally_managed_app_install_task.h
@@ -28,6 +28,7 @@
 
 namespace webapps {
 enum class InstallResultCode;
+enum class UninstallResultCode;
 }
 
 namespace web_app {
@@ -103,7 +104,7 @@
                                ResultCallback result_callback);
   void OnPlaceholderUninstalled(content::WebContents* web_contents,
                                 ResultCallback result_callback,
-                                bool uninstalled);
+                                webapps::UninstallResultCode code);
   void ContinueWebAppInstall(content::WebContents* web_contents,
                              ResultCallback result_callback);
   void OnWebAppInstalled(bool is_placeholder,
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl.cc
index aebdbcb4..7d8f4df 100644
--- a/chrome/browser/web_applications/externally_managed_app_manager_impl.cc
+++ b/chrome/browser/web_applications/externally_managed_app_manager_impl.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "chrome/common/chrome_features.h"
 #include "components/webapps/browser/install_result_code.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/url_constants.h"
 
@@ -86,7 +87,10 @@
         url, ConvertExternalInstallSourceToUninstallSource(install_source),
         base::BindOnce(
             [](const UninstallCallback& callback, const GURL& app_url,
-               bool uninstalled) { callback.Run(app_url, uninstalled); },
+               webapps::UninstallResultCode code) {
+              callback.Run(app_url,
+                           code == webapps::UninstallResultCode::kSuccess);
+            },
             callback, url));
   }
 }
diff --git a/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc b/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc
index 0fcce091..2dfc5eab1 100644
--- a/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc
+++ b/chrome/browser/web_applications/externally_managed_app_manager_impl_unittest.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/webapps/browser/install_result_code.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/url_constants.h"
@@ -1430,7 +1431,8 @@
   auto web_app = test::CreateWebApp(kFooWebAppUrl, Source::kPolicy);
   controller().RegisterApp(std::move(web_app));
 
-  install_finalizer().SetNextUninstallExternalWebAppResult(kFooWebAppUrl, true);
+  install_finalizer().SetNextUninstallExternalWebAppResult(
+      kFooWebAppUrl, webapps::UninstallResultCode::kSuccess);
   UninstallAppsResults results = UninstallAppsAndWait(
       &externally_managed_app_manager_impl(),
       ExternalInstallSource::kExternalPolicy, std::vector<GURL>{kFooWebAppUrl});
@@ -1443,8 +1445,8 @@
 
 TEST_F(ExternallyManagedAppManagerImplTest, UninstallApps_Fails) {
   const GURL kFooWebAppUrl("https://foo.example");
-  install_finalizer().SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
-                                                           false);
+  install_finalizer().SetNextUninstallExternalWebAppResult(
+      kFooWebAppUrl, webapps::UninstallResultCode::kError);
   UninstallAppsResults results = UninstallAppsAndWait(
       &externally_managed_app_manager_impl(),
       ExternalInstallSource::kExternalPolicy, std::vector<GURL>{kFooWebAppUrl});
@@ -1462,8 +1464,10 @@
   web_app = test::CreateWebApp(kBarWebAppUrl, Source::kPolicy);
   controller().RegisterApp(std::move(web_app));
 
-  install_finalizer().SetNextUninstallExternalWebAppResult(kFooWebAppUrl, true);
-  install_finalizer().SetNextUninstallExternalWebAppResult(kBarWebAppUrl, true);
+  install_finalizer().SetNextUninstallExternalWebAppResult(
+      kFooWebAppUrl, webapps::UninstallResultCode::kSuccess);
+  install_finalizer().SetNextUninstallExternalWebAppResult(
+      kBarWebAppUrl, webapps::UninstallResultCode::kSuccess);
   UninstallAppsResults results =
       UninstallAppsAndWait(&externally_managed_app_manager_impl(),
                            ExternalInstallSource::kExternalPolicy,
@@ -1493,8 +1497,8 @@
             run_loop.Quit();
           }));
 
-  install_finalizer().SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
-                                                           false);
+  install_finalizer().SetNextUninstallExternalWebAppResult(
+      kFooWebAppUrl, webapps::UninstallResultCode::kError);
   UninstallAppsResults uninstall_results = UninstallAppsAndWait(
       &externally_managed_app_manager_impl(),
       ExternalInstallSource::kExternalPolicy, std::vector<GURL>{kFooWebAppUrl});
@@ -1526,8 +1530,8 @@
     externally_managed_app_manager_impl().SetNextInstallationTaskResult(
         kFooWebAppUrl, webapps::InstallResultCode::kSuccessNewInstall,
         /*did_install_placeholder=*/false);
-    install_finalizer().SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
-                                                             true);
+    install_finalizer().SetNextUninstallExternalWebAppResult(
+        kFooWebAppUrl, webapps::UninstallResultCode::kSuccess);
 
     auto [url, code] =
         InstallAndWait(&externally_managed_app_manager_impl(), install_options);
@@ -1637,8 +1641,8 @@
         kFooWebAppUrl, webapps::InstallResultCode::kSuccessNewInstall,
         /*did_install_placeholder=*/false);
     ui_manager().SetNumWindowsForApp(GenerateFakeAppId(kFooWebAppUrl), 1);
-    install_finalizer().SetNextUninstallExternalWebAppResult(kFooWebAppUrl,
-                                                             true);
+    install_finalizer().SetNextUninstallExternalWebAppResult(
+        kFooWebAppUrl, webapps::UninstallResultCode::kSuccess);
 
     auto [url, code] =
         InstallAndWait(&externally_managed_app_manager_impl(), install_options);
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index e7fbe24..5385d05f 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -56,6 +56,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/url_loader_interceptor.h"
 #include "extensions/browser/extension_registry.h"
@@ -656,8 +657,8 @@
   UpdateCheckResultAwaiter awaiter(browser(), url);
   GetProvider().install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
-        EXPECT_TRUE(uninstalled);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop.Quit();
       }));
 
diff --git a/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc
index def00635..2b83244 100644
--- a/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_apps/system_web_app_manager.cc
@@ -20,14 +20,11 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/version.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/web_applications/system_web_apps/system_web_app_background_task.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-// TODO(b/174811949): Hide behind ChromeOS build flag.
-#include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_constants.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/external_install_options.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/system_web_apps/system_web_app_background_task.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_delegate.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_id.h"
@@ -50,6 +47,7 @@
 #include "content/public/browser/url_data_source.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/web_preferences/web_preferences.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_base_features.h"
@@ -69,6 +67,7 @@
 #include "ash/webui/shimless_rma/url_constants.h"
 #include "ash/webui/shortcut_customization_ui/url_constants.h"
 #include "chrome/browser/ash/web_applications/camera_app/camera_system_web_app_info.h"
+#include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_constants.h"
 #include "chrome/browser/ash/web_applications/connectivity_diagnostics_system_web_app_info.h"
 #include "chrome/browser/ash/web_applications/crosh_system_web_app_info.h"
 #include "chrome/browser/ash/web_applications/diagnostics_system_web_app_info.h"
diff --git a/chrome/browser/web_applications/test/fake_install_finalizer.cc b/chrome/browser/web_applications/test/fake_install_finalizer.cc
index 6957a23..f046a9e 100644
--- a/chrome/browser/web_applications/test/fake_install_finalizer.cc
+++ b/chrome/browser/web_applications/test/fake_install_finalizer.cc
@@ -18,6 +18,7 @@
 #include "components/crx_file/id_util.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 
 namespace web_app {
 
@@ -52,7 +53,8 @@
     UninstallWebAppCallback callback) {
   user_uninstalled_external_apps_.erase(app_id);
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), /*uninstalled=*/true));
+      FROM_HERE, base::BindOnce(std::move(callback),
+                                webapps::UninstallResultCode::kSuccess));
 }
 
 void FakeInstallFinalizer::UninstallExternalWebAppByUrl(
@@ -65,7 +67,7 @@
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindLambdaForTesting(
                      [this, app_url, callback = std::move(callback)]() mutable {
-                       bool result =
+                       webapps::UninstallResultCode result =
                            next_uninstall_external_web_app_results_[app_url];
                        next_uninstall_external_web_app_results_.erase(app_url);
                        std::move(callback).Run(result);
@@ -124,9 +126,9 @@
 
 void FakeInstallFinalizer::SetNextUninstallExternalWebAppResult(
     const GURL& app_url,
-    bool uninstalled) {
+    webapps::UninstallResultCode code) {
   DCHECK(!base::Contains(next_uninstall_external_web_app_results_, app_url));
-  next_uninstall_external_web_app_results_[app_url] = uninstalled;
+  next_uninstall_external_web_app_results_[app_url] = code;
 }
 
 void FakeInstallFinalizer::SimulateExternalAppUninstalledByUser(
diff --git a/chrome/browser/web_applications/test/fake_install_finalizer.h b/chrome/browser/web_applications/test/fake_install_finalizer.h
index 4fcd059..fe353e36 100644
--- a/chrome/browser/web_applications/test/fake_install_finalizer.h
+++ b/chrome/browser/web_applications/test/fake_install_finalizer.h
@@ -61,7 +61,7 @@
   void SetNextFinalizeInstallResult(const AppId& app_id,
                                     webapps::InstallResultCode code);
   void SetNextUninstallExternalWebAppResult(const GURL& app_url,
-                                            bool uninstalled);
+                                            webapps::UninstallResultCode code);
 
   // Uninstall the app and add |app_id| to the map of external extensions
   // uninstalled by the user. May be called on an app that isn't installed to
@@ -93,7 +93,8 @@
 
   absl::optional<AppId> next_app_id_;
   absl::optional<webapps::InstallResultCode> next_result_code_;
-  std::map<GURL, bool> next_uninstall_external_web_app_results_;
+  std::map<GURL, webapps::UninstallResultCode>
+      next_uninstall_external_web_app_results_;
   std::set<AppId> user_uninstalled_external_apps_;
 
   int num_reparent_tab_calls_ = 0;
diff --git a/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc b/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc
index 674adb59..d40866c 100644
--- a/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc
+++ b/chrome/browser/web_applications/test/fake_web_app_registry_controller.cc
@@ -45,7 +45,8 @@
                                          /*icon_manager=*/nullptr);
   translation_manager_ = std::make_unique<WebAppTranslationManager>(
       profile, base::MakeRefCounted<TestFileUtils>());
-  translation_manager_->SetSubsystems(/*install_manager=*/nullptr);
+  translation_manager_->SetSubsystems(/*install_manager=*/nullptr,
+                                      mutable_registrar_.get());
 
   fake_externally_managed_app_manager_ =
       std::make_unique<FakeExternallyManagedAppManager>(profile);
diff --git a/chrome/browser/web_applications/test/web_app_install_test_utils.cc b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
index 2641900..4bf2adcf 100644
--- a/chrome/browser/web_applications/test/web_app_install_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
@@ -19,6 +19,7 @@
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_switches.h"
 #include "components/webapps/browser/install_result_code.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -114,8 +115,8 @@
   DCHECK(provider->install_finalizer().CanUserUninstallWebApp(app_id));
   provider->install_finalizer().UninstallWebApp(
       app_id, webapps::WebappUninstallSource::kAppMenu,
-      base::BindLambdaForTesting([&](bool uninstalled) {
-        EXPECT_TRUE(uninstalled);
+      base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+        EXPECT_EQ(code, webapps::UninstallResultCode::kSuccess);
         run_loop.Quit();
       }));
 
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 737edab0..dfb0b17c 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -281,7 +281,9 @@
     LOG(WARNING) << "Couldn't uninstall web app with url " << app_url
                  << "; No corresponding web app for url.";
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), /*uninstalled=*/false));
+        FROM_HERE,
+        base::BindOnce(std::move(callback),
+                       webapps::UninstallResultCode::kNoAppToUninstall));
     return;
   }
 
@@ -481,7 +483,7 @@
     UninstallWebAppCallback callback) {
   if (registrar_->GetAppById(app_id) == nullptr ||
       base::Contains(pending_uninstalls_, app_id)) {
-    std::move(callback).Run(false);
+    std::move(callback).Run(webapps::UninstallResultCode::kNoAppToUninstall);
     return;
   }
   auto uninstall_task = std::make_unique<WebAppUninstallJob>(
@@ -507,7 +509,7 @@
     base::UmaHistogramBoolean("Webapp.SyncInitiatedUninstallResult",
                               code == webapps::UninstallResultCode::kSuccess);
   }
-  std::move(callback).Run(code == webapps::UninstallResultCode::kSuccess);
+  std::move(callback).Run(code);
 }
 
 void WebAppInstallFinalizer::UninstallExternalWebAppOrRemoveSource(
@@ -517,8 +519,9 @@
   const WebApp* app = GetWebAppRegistrar().GetAppById(app_id);
   if (!app) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  /*uninstalled=*/false));
+        FROM_HERE,
+        base::BindOnce(std::move(callback),
+                       webapps::UninstallResultCode::kNoAppToUninstall));
     return;
   }
 
@@ -554,7 +557,7 @@
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback),
-                                /*uninstalled=*/true));
+                                webapps::UninstallResultCode::kSuccess));
 }
 
 void WebAppInstallFinalizer::SetWebAppManifestFieldsAndWriteData(
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 93172e9..94078c5 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -50,9 +50,11 @@
       base::OnceCallback<void(const AppId& app_id,
                               webapps::InstallResultCode code,
                               OsHooksErrors os_hooks_errors)>;
-  using UninstallWebAppCallback = base::OnceCallback<void(bool uninstalled)>;
+  using UninstallWebAppCallback =
+      base::OnceCallback<void(webapps::UninstallResultCode code)>;
   using RepeatingUninstallCallback =
-      base::RepeatingCallback<void(const AppId& app_id, bool uninstalled)>;
+      base::RepeatingCallback<void(const AppId& app_id,
+                                   webapps::UninstallResultCode code)>;
 
   struct FinalizeOptions {
     FinalizeOptions();
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index b11ce52..2b87f13 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -29,6 +29,7 @@
 #include "chrome/common/chrome_features.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -371,8 +372,15 @@
   if (!started_)
     return;
 
-  finalizer_->UninstallWithoutRegistryUpdateFromSync(std::move(web_apps),
-                                                     std::move(callback));
+  finalizer_->UninstallWithoutRegistryUpdateFromSync(
+      std::move(web_apps),
+      base::BindRepeating(
+          [](RepeatingUninstallCallback callback, const web_app::AppId& app_id,
+             webapps::UninstallResultCode code) {
+            callback.Run(app_id,
+                         code == webapps::UninstallResultCode::kSuccess);
+          },
+          std::move(callback)));
 }
 
 void WebAppInstallManager::RetryIncompleteUninstalls(
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
index ab47c2e..da234dc 100644
--- a/chrome/browser/web_applications/web_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -40,10 +40,12 @@
 #include "chrome/browser/web_applications/web_app_install_task.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_uninstall_job.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/uninstall_result_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
@@ -385,29 +387,29 @@
     return num_apps;
   }
 
-  bool UninstallExternalWebAppByUrl(
+  webapps::UninstallResultCode UninstallExternalWebAppByUrl(
       const GURL& app_url,
       ExternalInstallSource external_install_source) {
-    bool result = false;
+    webapps::UninstallResultCode result;
     base::RunLoop run_loop;
     finalizer().UninstallExternalWebAppByUrl(
         app_url,
         ConvertExternalInstallSourceToUninstallSource(external_install_source),
-        base::BindLambdaForTesting([&](bool uninstalled) {
-          result = uninstalled;
+        base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+          result = code;
           run_loop.Quit();
         }));
     run_loop.Run();
     return result;
   }
 
-  bool UninstallWebApp(const AppId& app_id) {
-    bool result = false;
+  webapps::UninstallResultCode UninstallWebApp(const AppId& app_id) {
+    webapps::UninstallResultCode result;
     base::RunLoop run_loop;
     finalizer().UninstallWebApp(
         app_id, webapps::WebappUninstallSource::kAppMenu,
-        base::BindLambdaForTesting([&](bool uninstalled) {
-          result = uninstalled;
+        base::BindLambdaForTesting([&](webapps::UninstallResultCode code) {
+          result = code;
           run_loop.Quit();
         }));
     run_loop.Run();
@@ -993,12 +995,15 @@
       }));
 
   // Unknown url fails.
-  EXPECT_FALSE(UninstallExternalWebAppByUrl(
-      GURL("https://example.org/"), ExternalInstallSource::kExternalPolicy));
+  EXPECT_EQ(
+      webapps::UninstallResultCode::kNoAppToUninstall,
+      UninstallExternalWebAppByUrl(GURL("https://example.org/"),
+                                   ExternalInstallSource::kExternalPolicy));
 
   // Uninstall policy app first.
-  EXPECT_TRUE(UninstallExternalWebAppByUrl(
-      external_app_url, ExternalInstallSource::kExternalPolicy));
+  EXPECT_EQ(webapps::UninstallResultCode::kSuccess,
+            UninstallExternalWebAppByUrl(
+                external_app_url, ExternalInstallSource::kExternalPolicy));
 
   EXPECT_TRUE(registrar().GetAppById(app_id));
   EXPECT_FALSE(observer_uninstall_called);
@@ -1030,12 +1035,15 @@
       }));
 
   // Unknown url fails.
-  EXPECT_FALSE(UninstallExternalWebAppByUrl(
-      GURL("https://example.org/"), ExternalInstallSource::kExternalPolicy));
+  EXPECT_EQ(
+      webapps::UninstallResultCode::kNoAppToUninstall,
+      UninstallExternalWebAppByUrl(GURL("https://example.org/"),
+                                   ExternalInstallSource::kExternalPolicy));
 
   // Uninstall policy app first.
-  EXPECT_TRUE(UninstallExternalWebAppByUrl(
-      external_app_url, ExternalInstallSource::kExternalPolicy));
+  EXPECT_EQ(webapps::UninstallResultCode::kSuccess,
+            UninstallExternalWebAppByUrl(
+                external_app_url, ExternalInstallSource::kExternalPolicy));
 
   EXPECT_TRUE(registrar().GetAppById(app_id));
   EXPECT_FALSE(observer_uninstall_called);
@@ -1071,7 +1079,7 @@
 
   file_utils().SetNextDeleteFileRecursivelyResult(true);
 
-  EXPECT_TRUE(UninstallWebApp(app_id));
+  EXPECT_EQ(webapps::UninstallResultCode::kSuccess, UninstallWebApp(app_id));
 
   EXPECT_FALSE(registrar().GetAppById(app_id));
   EXPECT_TRUE(observer_uninstalled_called);
@@ -1108,7 +1116,7 @@
 
   file_utils().SetNextDeleteFileRecursivelyResult(true);
 
-  EXPECT_TRUE(UninstallWebApp(app_id));
+  EXPECT_EQ(webapps::UninstallResultCode::kSuccess, UninstallWebApp(app_id));
 
   EXPECT_FALSE(registrar().GetAppById(app_id));
   EXPECT_TRUE(observer_uninstalled_called);
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 08962cb9..ec93e6b 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -304,7 +304,7 @@
 
   sync_bridge_->SetSubsystems(database_factory_.get(), install_manager_.get());
   icon_manager_->SetSubsystems(registrar_.get(), install_manager_.get());
-  translation_manager_->SetSubsystems(install_manager_.get());
+  translation_manager_->SetSubsystems(install_manager_.get(), registrar_.get());
   install_finalizer_->SetSubsystems(
       install_manager_.get(), registrar_.get(), ui_manager_.get(),
       sync_bridge_.get(), os_integration_manager_.get(), icon_manager_.get(),
diff --git a/chrome/browser/web_applications/web_app_translation_manager.cc b/chrome/browser/web_applications/web_app_translation_manager.cc
index 574d811..8b11e19 100644
--- a/chrome/browser/web_applications/web_app_translation_manager.cc
+++ b/chrome/browser/web_applications/web_app_translation_manager.cc
@@ -134,8 +134,10 @@
 WebAppTranslationManager::~WebAppTranslationManager() = default;
 
 void WebAppTranslationManager::SetSubsystems(
-    base::raw_ptr<WebAppInstallManager> install_manager) {
+    base::raw_ptr<WebAppInstallManager> install_manager,
+    base::raw_ptr<WebAppRegistrar> registrar) {
   install_manager_ = install_manager;
+  registrar_ = registrar;
 }
 
 void WebAppTranslationManager::Start() {
@@ -146,12 +148,6 @@
   }
 }
 
-// TODO(crbug.com/1259777): Consider adding to cache when writing a translation
-// to avoid reading everything again here.
-void WebAppTranslationManager::OnWebAppInstalled(const AppId& app_id) {
-  ReadTranslations(base::DoNothing());
-}
-
 void WebAppTranslationManager::OnWebAppUninstalled(const AppId& app_id) {
   DeleteTranslations(app_id, base::DoNothing());
 }
@@ -165,20 +161,30 @@
     const base::flat_map<Locale, blink::Manifest::TranslationItem>&
         translations,
     WriteCallback callback) {
-  if (base::FeatureList::IsEnabled(
+  if (!base::FeatureList::IsEnabled(
           blink::features::kWebAppEnableTranslations)) {
-    base::ThreadPool::PostTaskAndReplyWithResult(
-        FROM_HERE, kTaskTraits,
-        base::BindOnce(WriteTranslationsBlocking, utils_, web_apps_directory_,
-                       std::move(app_id), std::move(translations)),
-        std::move(callback));
-  } else {
     std::move(callback).Run(true);
+    return;
   }
+
+  const std::string& locale = g_browser_process->GetApplicationLocale();
+  // TODO(crbug.com/1259777): Check other matching locales. Eg if no name
+  // defined in en-US, check en.
+  auto it = translations.find(base::UTF8ToUTF16(locale));
+  if (it != translations.end()) {
+    translation_cache_[app_id] = it->second;
+  }
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, kTaskTraits,
+      base::BindOnce(WriteTranslationsBlocking, utils_, web_apps_directory_,
+                     std::move(app_id), std::move(translations)),
+      std::move(callback));
 }
 
 void WebAppTranslationManager::DeleteTranslations(const AppId& app_id,
                                                   WriteCallback callback) {
+  translation_cache_.erase(app_id);
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, kTaskTraits,
       base::BindOnce(DeleteTranslationsBlocking, utils_, web_apps_directory_,
@@ -198,7 +204,7 @@
     ReadCallback callback,
     const AllTranslations& proto) {
   translation_cache_.clear();
-  std::string locale = g_browser_process->GetApplicationLocale();
+  const std::string& locale = g_browser_process->GetApplicationLocale();
 
   for (const auto& id_to_translations : proto.id_to_translations_map()) {
     const AppId& app_id = id_to_translations.first;
@@ -216,4 +222,24 @@
   std::move(callback).Run(translation_cache_);
 }
 
+std::string WebAppTranslationManager::GetName(const AppId& app_id) {
+  auto it = translation_cache_.find(app_id);
+  if (it != translation_cache_.end() && it->second.name) {
+    return it->second.name.value();
+  }
+
+  const WebApp* web_app = registrar_->GetAppById(app_id);
+  return web_app ? web_app->name() : std::string();
+}
+
+std::string WebAppTranslationManager::GetDescription(const AppId& app_id) {
+  auto it = translation_cache_.find(app_id);
+  if (it != translation_cache_.end() && it->second.description) {
+    return it->second.description.value();
+  }
+
+  const WebApp* web_app = registrar_->GetAppById(app_id);
+  return web_app ? web_app->description() : std::string();
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_translation_manager.h b/chrome/browser/web_applications/web_app_translation_manager.h
index fb41fb8..67f6367 100644
--- a/chrome/browser/web_applications/web_app_translation_manager.h
+++ b/chrome/browser/web_applications/web_app_translation_manager.h
@@ -16,6 +16,7 @@
 #include "chrome/browser/web_applications/web_app_id.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"
 
 namespace web_app {
 
@@ -33,7 +34,8 @@
   WebAppTranslationManager& operator=(const WebAppTranslationManager&) = delete;
   ~WebAppTranslationManager() override;
 
-  void SetSubsystems(base::raw_ptr<WebAppInstallManager> install_manager);
+  void SetSubsystems(base::raw_ptr<WebAppInstallManager> install_manager,
+                     base::raw_ptr<WebAppRegistrar> registrar);
 
   void Start();
 
@@ -45,11 +47,11 @@
   void DeleteTranslations(const AppId& app_id, WriteCallback callback);
   void ReadTranslations(ReadCallback callback);
 
-  // TODO(crbug.com/1259777): Add methods to get the name, short_name and
-  // description.
+  std::string GetName(const AppId& app_id);
+  std::string GetDescription(const AppId& app_id);
+  // TODO(crbug.com/1212519): Add a method to get the short_name.
 
   // WebAppInstallManager:
-  void OnWebAppInstalled(const AppId& app_id) override;
   void OnWebAppUninstalled(const AppId& app_id) override;
   void OnWebAppInstallManagerDestroyed() override;
 
@@ -57,8 +59,10 @@
   void OnTranslationsRead(ReadCallback callback, const AllTranslations& proto);
 
   base::raw_ptr<WebAppInstallManager> install_manager_;
+  base::raw_ptr<WebAppRegistrar> registrar_;
   base::FilePath web_apps_directory_;
   scoped_refptr<FileUtilsWrapper> utils_;
+  // Cache of the translations on disk for the current device language.
   std::map<AppId, blink::Manifest::TranslationItem> translation_cache_;
 
   base::ScopedObservation<WebAppInstallManager, WebAppInstallManagerObserver>
diff --git a/chrome/browser/web_applications/web_app_translation_manager_unittest.cc b/chrome/browser/web_applications/web_app_translation_manager_unittest.cc
index 609abf9..3c947c08 100644
--- a/chrome/browser/web_applications/web_app_translation_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_translation_manager_unittest.cc
@@ -34,7 +34,7 @@
 
     translation_manager_ =
         std::make_unique<WebAppTranslationManager>(profile(), file_utils_);
-    translation_manager_->SetSubsystems(&install_manager());
+    translation_manager_->SetSubsystems(&install_manager(), registrar());
   }
 
  protected:
@@ -91,7 +91,7 @@
   WebAppInstallManager& install_manager() { return *install_manager_; }
 
   FakeWebAppProvider& provider() { return *provider_; }
-
+  WebAppRegistrar* registrar() { return &controller().registrar(); }
   WebAppTranslationManager& translation_manager() {
     return *translation_manager_;
   }
@@ -113,8 +113,13 @@
 TEST_F(WebAppTranslationManagerTest, WriteReadAndDelete) {
   auto web_app1 = test::CreateWebApp(GURL("https://example.com/path"));
   const AppId app_id1 = web_app1->app_id();
+  web_app1->SetName("App1 name");
+  controller().RegisterApp(std::move(web_app1));
+
   auto web_app2 = test::CreateWebApp(GURL("https://example.com/path2"));
   const AppId app_id2 = web_app2->app_id();
+  web_app2->SetName("App2 name");
+  controller().RegisterApp(std::move(web_app2));
 
   g_browser_process->SetApplicationLocale("en");
 
@@ -133,7 +138,7 @@
   translations1[u"fr"] = item2;
 
   blink::Manifest::TranslationItem item3;
-  item3.name = "name 3";
+  item3.short_name = "short name 3";
   translations2[u"en"] = item3;
 
   blink::Manifest::TranslationItem item4;
@@ -152,11 +157,23 @@
     ASSERT_EQ(cache.size(), static_cast<size_t>(2));
     EXPECT_EQ(cache.find(app_id1)->second, item1);
     EXPECT_EQ(cache.find(app_id2)->second, item3);
+
+    EXPECT_EQ(translation_manager().GetName(app_id1), item1.name);
+    EXPECT_EQ(translation_manager().GetDescription(app_id1), item1.description);
+
+    EXPECT_EQ(translation_manager().GetName(app_id2), "App2 name");
+    EXPECT_EQ(translation_manager().GetDescription(app_id2), "");
   }
 
   // Delete translations for web_app1.
   AwaitDeleteTranslations(app_id1);
 
+  EXPECT_EQ(translation_manager().GetName(app_id1), "App1 name");
+  EXPECT_EQ(translation_manager().GetDescription(app_id1), "");
+
+  EXPECT_EQ(translation_manager().GetName(app_id2), "App2 name");
+  EXPECT_EQ(translation_manager().GetDescription(app_id2), "");
+
   // Read translations to ensure web_app1 deleted.
   {
     std::map<AppId, blink::Manifest::TranslationItem> cache =
@@ -169,6 +186,7 @@
 TEST_F(WebAppTranslationManagerTest, UpdateTranslations) {
   auto web_app1 = test::CreateWebApp(GURL("https://example.com/path"));
   const AppId app_id1 = web_app1->app_id();
+  controller().RegisterApp(std::move(web_app1));
 
   g_browser_process->SetApplicationLocale("en");
 
@@ -189,17 +207,47 @@
   // Write translations for the app.
   AwaitWriteTranslations(app_id1, translations1);
 
+  // Check the translations set correctly.
+  EXPECT_EQ(translation_manager().GetName(app_id1), item1.name);
+  EXPECT_EQ(translation_manager().GetDescription(app_id1), item1.description);
+
   // Update the translations for the app.
   AwaitWriteTranslations(app_id1, translations2);
 
   // Check the translations have correctly updated.
-  std::map<AppId, blink::Manifest::TranslationItem> cache =
-      AwaitReadTranslations();
-  ASSERT_EQ(cache.size(), static_cast<size_t>(1));
-  EXPECT_EQ(cache.find(app_id1)->second, item2);
+  EXPECT_EQ(translation_manager().GetName(app_id1), item2.name);
+  EXPECT_EQ(translation_manager().GetDescription(app_id1), item2.description);
 }
 
-// TODO(crbug.com/1259777): Add a test for installing and uninstalling an app.
+TEST_F(WebAppTranslationManagerTest, InstallAndUninstall) {
+  g_browser_process->SetApplicationLocale("en");
+
+  base::flat_map<Locale, blink::Manifest::TranslationItem> translations;
+
+  blink::Manifest::TranslationItem item1;
+  item1.name = "name 1";
+  item1.short_name = "short name 1";
+  item1.description = "description 1";
+  translations[u"en"] = item1;
+
+  auto app_info = std::make_unique<WebAppInstallInfo>();
+  app_info->start_url = GURL("https://example.com/path");
+  app_info->scope = GURL("https://example.com/path");
+  app_info->title = u"Web App";
+  app_info->translations = translations;
+
+  // Install app
+  AppId app_id = web_app::test::InstallWebApp(profile(), std::move(app_info));
+
+  // Check translations are stored
+  EXPECT_EQ(provider().translation_manager().GetName(app_id), item1.name);
+
+  // Uninstall app
+  web_app::test::UninstallWebApp(profile(), app_id);
+
+  // Check translations were deleted
+  EXPECT_EQ(provider().translation_manager().GetName(app_id), std::string());
+}
 
 // TODO(crbug.com/1259777): Add a test for an app which is installed before the
 // translation manager is started.
diff --git a/chrome/browser/web_applications/web_app_uninstall_job.cc b/chrome/browser/web_applications/web_app_uninstall_job.cc
index 873da9ff..a1b215cf 100644
--- a/chrome/browser/web_applications/web_app_uninstall_job.cc
+++ b/chrome/browser/web_applications/web_app_uninstall_job.cc
@@ -90,8 +90,9 @@
   delete_option_ = ModifyAppRegistry::kNo;
 }
 
-void WebAppUninstallJob::OnSubAppUninstalled(bool success) {
-  errors_ = errors_ || !success;
+void WebAppUninstallJob::OnSubAppUninstalled(
+    webapps::UninstallResultCode code) {
+  errors_ = errors_ || (code != webapps::UninstallResultCode::kSuccess);
   num_pending_sub_app_uninstalls_--;
   DCHECK_GE(num_pending_sub_app_uninstalls_, 0u);
   MaybeFinishUninstall();
diff --git a/chrome/browser/web_applications/web_app_uninstall_job.h b/chrome/browser/web_applications/web_app_uninstall_job.h
index 8f5cbf63..4d31f83 100644
--- a/chrome/browser/web_applications/web_app_uninstall_job.h
+++ b/chrome/browser/web_applications/web_app_uninstall_job.h
@@ -72,7 +72,7 @@
   void StopAppRegistryModification();
 
  private:
-  void OnSubAppUninstalled(bool success);
+  void OnSubAppUninstalled(webapps::UninstallResultCode code);
   void OnOsHooksUninstalled(OsHooksErrors errors);
   void OnIconDataDeleted(bool success);
   void MaybeFinishUninstall();
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index a9d20dc..c62b9b9 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1646351850-e130df75a1db05108bce9392a7ef7802dcecbcbf.profdata
+chrome-linux-main-1646373280-d759981c54f6e4157d2426e93d85d8d71495eaeb.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 42ac70f4..477a5f2 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1646351850-162d9ea153d3a2328281d56a551c09dec83c924b.profdata
+chrome-mac-arm-main-1646373280-8212e605c155d54914a28d19d8e047af54d93de5.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 7d0fff1..60d528e3 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1646351850-8d66e9fd4499fb5b0e45259a4a140c4e769b4f3a.profdata
+chrome-mac-main-1646373280-3ef5ea47feb2462021e0bef1b80feaa79bb4420e.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 731b8f54..d0a9103a 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1646340877-f57bfe90c41df81b369842a8d3231b77c3ca4071.profdata
+chrome-win32-main-1646373280-e45bebbd99d5bf552fe3a08469e0de1c8a26c6a9.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index aa05e6e..4b55bf0 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1646351850-f9222b04fae451f4416b18f11de544e1a08062fb.profdata
+chrome-win64-main-1646373280-691bb757f4d6595c876cd4a444ce9cf26cb2856e.profdata
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index 97f406b0..105c48c5 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -42,6 +42,10 @@
 #include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/common/chrome_paths_lacros.h"  // nogncheck
+#endif
+
 namespace {
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
@@ -362,6 +366,24 @@
 #endif
       break;
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    case chrome::FILE_FALLBACK_RESOURCES_PACK:
+      if (!base::PathService::Get(base::DIR_ASSETS, &cur))
+        return false;
+      cur = cur.Append(FILE_PATH_LITERAL("resources_fallback.pak"));
+      break;
+    case chrome::FILE_ASH_RESOURCES_PACK:
+      if (!chrome::GetAshResourcesPath(&cur))
+        return false;
+      cur = cur.Append("resources.pak");
+      break;
+    case chrome::FILE_RESOURCES_MAP:
+      if (!base::PathService::Get(base::DIR_ASSETS, &cur))
+        return false;
+      cur = cur.Append(FILE_PATH_LITERAL("resources.map"));
+      break;
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     case chrome::DIR_CHROMEOS_WALLPAPERS:
       if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h
index 751f3a6..ac143fae 100644
--- a/chrome/common/chrome_paths.h
+++ b/chrome/common/chrome_paths.h
@@ -94,6 +94,15 @@
                         // This includes data for internal pages (e.g., html
                         // files and images), unless these resources are
                         // purposefully split into a separate file.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  FILE_FALLBACK_RESOURCES_PACK,  // Full path to the fallback.pak file
+                                 // containing binary data. This includes
+                                 // resources in FILE_RESOURCES_PACK which are
+                                 // not included in ASH_RESOURCES_PACK.
+  FILE_ASH_RESOURCES_PACK,       // Full path to ash resources.pak file.
+  FILE_RESOURCES_MAP,            // Full path to mapping table which maps
+                                 // lacros resources id to ash resources.
+#endif
   FILE_DEV_UI_RESOURCES_PACK,  // Full path to the .pak file containing
                                // binary data for internal pages (e.g., html
                                // files and images).
diff --git a/chrome/common/chrome_paths_lacros.cc b/chrome/common/chrome_paths_lacros.cc
index 1545a49e..4480c71c 100644
--- a/chrome/common/chrome_paths_lacros.cc
+++ b/chrome/common/chrome_paths_lacros.cc
@@ -24,6 +24,7 @@
   base::FilePath removable_media_dir;
   base::FilePath android_files_dir;
   base::FilePath linux_files_dir;
+  base::FilePath ash_resources_dir;
 };
 
 DefaultPaths& GetDefaultPaths() {
@@ -38,7 +39,8 @@
                            const base::FilePath& drivefs,
                            const base::FilePath& removable_media_dir,
                            const base::FilePath& android_files_dir,
-                           const base::FilePath& linux_files_dir) {
+                           const base::FilePath& linux_files_dir,
+                           const base::FilePath& ash_resources_dir) {
   DCHECK(!documents_dir.empty());
   DCHECK(documents_dir.IsAbsolute());
   GetDefaultPaths().documents_dir = documents_dir;
@@ -51,6 +53,7 @@
   GetDefaultPaths().removable_media_dir = removable_media_dir;
   GetDefaultPaths().android_files_dir = android_files_dir;
   GetDefaultPaths().linux_files_dir = linux_files_dir;
+  GetDefaultPaths().ash_resources_dir = ash_resources_dir;
 }
 
 void SetLacrosDefaultPathsFromInitParams(
@@ -71,10 +74,13 @@
     base::FilePath linux_files_dir;
     if (init_params->default_paths->linux_files.has_value())
       linux_files_dir = init_params->default_paths->linux_files.value();
-    chrome::SetLacrosDefaultPaths(init_params->default_paths->documents,
-                                  init_params->default_paths->downloads,
-                                  drivefs_dir, removable_media_dir,
-                                  android_files_dir, linux_files_dir);
+    base::FilePath ash_resources_dir;
+    if (init_params->default_paths->ash_resources.has_value())
+      ash_resources_dir = init_params->default_paths->ash_resources.value();
+    chrome::SetLacrosDefaultPaths(
+        init_params->default_paths->documents,
+        init_params->default_paths->downloads, drivefs_dir, removable_media_dir,
+        android_files_dir, linux_files_dir, ash_resources_dir);
   }
 }
 
@@ -170,4 +176,11 @@
   return true;
 }
 
+bool GetAshResourcesPath(base::FilePath* result) {
+  if (GetDefaultPaths().ash_resources_dir.empty())
+    return false;
+  *result = GetDefaultPaths().ash_resources_dir;
+  return true;
+}
+
 }  // namespace chrome
diff --git a/chrome/common/chrome_paths_lacros.h b/chrome/common/chrome_paths_lacros.h
index 87f6ce3f..581ec73 100644
--- a/chrome/common/chrome_paths_lacros.h
+++ b/chrome/common/chrome_paths_lacros.h
@@ -22,7 +22,8 @@
                            const base::FilePath& drivefs,
                            const base::FilePath& removable_media_dir,
                            const base::FilePath& android_files_dir,
-                           const base::FilePath& linux_files_dir);
+                           const base::FilePath& linux_files_dir,
+                           const base::FilePath& ash_resources_dir);
 
 // Sets the default paths from BrowserInitParams received from ash on startup.
 void SetLacrosDefaultPathsFromInitParams(
@@ -40,6 +41,7 @@
 bool GetRemovableMediaPath(base::FilePath* result);
 bool GetAndroidFilesPath(base::FilePath* result);
 bool GetLinuxFilesPath(base::FilePath* result);
+bool GetAshResourcesPath(base::FilePath* result);
 }  // namespace chrome
 
 #endif  // CHROME_COMMON_CHROME_PATHS_LACROS_H_
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 48810316..0829fa4 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -442,7 +442,7 @@
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
-    BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_CHROMEOS_ASH)
 const char kChromeUIConnectorsInternalsHost[] = "connectors-internals";
 #endif
 
@@ -681,7 +681,7 @@
     kChromeUIAssistantOptInHost,
 #endif
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
-    BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_CHROMEOS_ASH)
     kChromeUIConnectorsInternalsHost,
 #endif
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 67c7d02..79013c2 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -379,7 +379,7 @@
 #endif
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
-    BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_CHROMEOS_ASH)
 extern const char kChromeUIConnectorsInternalsHost[];
 #endif
 
diff --git a/chrome/installer/util/registry_util_unittest.cc b/chrome/installer/util/registry_util_unittest.cc
index 65e3216..abc9e7b 100644
--- a/chrome/installer/util/registry_util_unittest.cc
+++ b/chrome/installer/util/registry_util_unittest.cc
@@ -6,13 +6,13 @@
 
 #include <Aclapi.h>
 
+#include <iterator>
 #include <memory>
 #include <string>
 #include <utility>
 
 #include "base/base_paths.h"
 #include "base/command_line.h"
-#include "base/cxx17_backports.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -360,7 +360,7 @@
   // Tests where the expected file exists.
   static const char data[] = "data";
   ASSERT_TRUE(base::CreateDirectory(some_long_dir));
-  ASSERT_NE(-1, base::WriteFile(expect, data, base::size(data) - 1));
+  ASSERT_NE(-1, base::WriteFile(expect, data, std::size(data) - 1));
   // Paths don't match.
   EXPECT_FALSE(ProgramCompare(expect).Evaluate(L"\"" + other.value() + L"\""));
   // Paths match exactly.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index fb26cbf..0d10af0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4007,6 +4007,7 @@
         "//ui/display:display_manager_test_api",
         "//ui/display/manager",
         "//ui/events/devices:test_support",
+        "//ui/webui/resources/cr_components/app_management:mojo_bindings",
         "//ui/wm/public",
         "//url",
       ]
@@ -6920,6 +6921,7 @@
       "../browser/ui/app_list/search/keyboard_shortcut_provider_unittest.cc",
       "../browser/ui/app_list/search/keyboard_shortcut_result_unittest.cc",
       "../browser/ui/app_list/search/mixer_unittest.cc",
+      "../browser/ui/app_list/search/omnibox_answer_result_unittest.cc",
       "../browser/ui/app_list/search/omnibox_result_unittest.cc",
       "../browser/ui/app_list/search/open_tab_result_unittest.cc",
       "../browser/ui/app_list/search/ranking/answer_ranker_unittest.cc",
@@ -7053,6 +7055,7 @@
       "//components/arc/common",
       "//components/services/app_service/public/cpp:preferred_apps",
       "//components/soda:soda",
+      "//ui/webui/resources/cr_components/app_management:unit_tests",
     ]
   }
 
diff --git a/chrome/test/base/chrome_test_suite.cc b/chrome/test/base/chrome_test_suite.cc
index 55167f3..df00b1c 100644
--- a/chrome/test/base/chrome_test_suite.cc
+++ b/chrome/test/base/chrome_test_suite.cc
@@ -122,7 +122,8 @@
                                 /*drivefs=*/base::FilePath(),
                                 /*removable_media_dir*/ base::FilePath(),
                                 /*android_files_dir*/ base::FilePath(),
-                                /*linux_files_dir*/ base::FilePath());
+                                /*linux_files_dir*/ base::FilePath(),
+                                /*ash_resources_dir*/ base::FilePath());
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
diff --git a/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts b/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts
index b4c1d46..7d69578 100644
--- a/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts
+++ b/chrome/test/data/webui/app_settings/test_app_management_browser_proxy.ts
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {App, PageCallbackRouter, PageHandlerInterface, PageRemote} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import {App, PageCallbackRouter, PageHandlerInterface, PageRemote, Permission} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
 import {BrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js';
-import {OptionalBool, Permission, RunOnOsLoginMode, WindowMode} from 'chrome://resources/cr_components/app_management/types.mojom-webui.js';
+import {OptionalBool, RunOnOsLoginMode, WindowMode} from 'chrome://resources/cr_components/app_management/types.mojom-webui.js';
 
 export class FakePageHandler implements PageHandlerInterface {
   private app_: App;
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 993a9e6..3da34878 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
@@ -27,6 +27,8 @@
   const kUserCertId = 'test-cert-id';
   const kTestVpnName = 'test-vpn';
   const kTestVpnHost = 'test-vpn-host';
+  const kTestUsername = 'test-username';
+  const kTestPassword = 'test-password';
 
   suiteSetup(function() {
     mojoApi_ = new FakeNetworkConfig();
@@ -205,23 +207,38 @@
 
       networkConfig.set('vpnType_', 'IKEv2');
       Polymer.dom.flush();
+      assertEquals(3, networkConfig.get('ipsecAuthTypeItems_').length);
       assertTrue(!!networkConfig.$$('#ipsec-auth-type'));
       assertFalse(!!networkConfig.$$('#l2tp-username-input'));
 
-      // The authentication type is default to PSK. The PSK input should appear
-      // and the dropdowns for server CA and user certificate should be hidden.
-      assertEquals('PSK', networkConfig.ipsecAuthType_);
+      assertEquals('EAP', networkConfig.ipsecAuthType_);
+      assertFalse(!!networkConfig.$$('#ipsec-psk-input'));
+      assertTrue(!!networkConfig.$$('#vpnServerCa'));
+      assertFalse(!!networkConfig.$$('#vpnUserCert'));
+      assertTrue(!!networkConfig.$$('#ipsec-eap-username-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-eap-password-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-local-id-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-remote-id-input'));
+
+      networkConfig.set('ipsecAuthType_', 'PSK');
+      Polymer.dom.flush();
       assertTrue(!!networkConfig.$$('#ipsec-psk-input'));
       assertFalse(!!networkConfig.$$('#vpnServerCa'));
       assertFalse(!!networkConfig.$$('#vpnUserCert'));
+      assertFalse(!!networkConfig.$$('#ipsec-eap-username-input'));
+      assertFalse(!!networkConfig.$$('#ipsec-eap-password-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-local-id-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-remote-id-input'));
 
-      // Switch the authentication type to Cert. The PSK input should be hidden
-      // and the dropdowns for server CA and user certificate should appear.
       networkConfig.set('ipsecAuthType_', 'Cert');
       Polymer.dom.flush();
       assertFalse(!!networkConfig.$$('#ipsec-psk-input'));
       assertTrue(!!networkConfig.$$('#vpnServerCa'));
       assertTrue(!!networkConfig.$$('#vpnUserCert'));
+      assertFalse(!!networkConfig.$$('#ipsec-eap-username-input'));
+      assertFalse(!!networkConfig.$$('#ipsec-eap-password-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-local-id-input'));
+      assertTrue(!!networkConfig.$$('#ipsec-remote-id-input'));
     });
 
     test('No Certs', function() {
@@ -292,7 +309,7 @@
       configProperties.typeConfig.vpn.ipSec.psk = 'test-psk';
       assertTrue(networkConfig.vpnIsConfigured_());
 
-      const props = networkConfig.getPropertiesToSet_();
+      let props = networkConfig.getPropertiesToSet_();
       const mojom = chromeos.networkConfig.mojom;
       assertEquals(kTestVpnName, props.name);
       assertEquals(kTestVpnHost, props.typeConfig.vpn.host);
@@ -301,10 +318,18 @@
       assertEquals(2, props.typeConfig.vpn.ipSec.ikeVersion);
       assertFalse(props.typeConfig.vpn.ipSec.saveCredentials);
       assertEquals('test-psk', props.typeConfig.vpn.ipSec.psk);
+      assertEquals('', props.typeConfig.vpn.ipSec.localIdentity);
+      assertEquals('', props.typeConfig.vpn.ipSec.remoteIdentity);
 
       networkConfig.set('vpnSaveCredentials_', true);
       assertTrue(networkConfig.getPropertiesToSet_()
                      .typeConfig.vpn.ipSec.saveCredentials);
+
+      configProperties.typeConfig.vpn.ipSec.localIdentity = 'local-id';
+      configProperties.typeConfig.vpn.ipSec.remoteIdentity = 'remote-id';
+      props = networkConfig.getPropertiesToSet_();
+      assertEquals('local-id', props.typeConfig.vpn.ipSec.localIdentity);
+      assertEquals('remote-id', props.typeConfig.vpn.ipSec.remoteIdentity);
     });
 
     // Checks if values are read correctly for an existing service of PSK
@@ -318,6 +343,8 @@
       ikev2.typeProperties.vpn.ipSec = {
         authenticationType: {activeValue: 'PSK'},
         ikeVersion: {activeValue: 2},
+        localIdentity: {activeValue: 'local-id'},
+        remoteIdentity: {activeValue: 'remote-id'},
         saveCredentials: {activeValue: true},
       };
       setNetworkConfig(ikev2);
@@ -336,6 +363,8 @@
         assertEquals(mojom.VpnType.kIKEv2, props.typeConfig.vpn.type.value);
         assertEquals('PSK', props.typeConfig.vpn.ipSec.authenticationType);
         assertEquals(2, props.typeConfig.vpn.ipSec.ikeVersion);
+        assertEquals('local-id', props.typeConfig.vpn.ipSec.localIdentity);
+        assertEquals('remote-id', props.typeConfig.vpn.ipSec.remoteIdentity);
         assertTrue(props.typeConfig.vpn.ipSec.saveCredentials);
       });
     });
@@ -417,6 +446,98 @@
         });
       });
     });
+
+    test('EAP', function() {
+      initNetworkConfigWithCerts(
+          /* hasServerCa= */ true, /* hasUserCert= */ false);
+      networkConfig.set('vpnType_', 'IKEv2');
+      networkConfig.set('ipsecAuthType_', 'EAP');
+      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
+        return flushAsync().then(() => {
+          // Server CA should be selected.
+          assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
+
+          setMandatoryFields();
+          assertFalse(networkConfig.vpnIsConfigured_());
+          const eapProperties = networkConfig.get('eapProperties_');
+          eapProperties.identity = kTestUsername;
+          eapProperties.password = kTestPassword;
+          assertTrue(networkConfig.vpnIsConfigured_());
+
+          // Server CA is also mandatory when using EAP.
+          networkConfig.set('selectedServerCaHash_', '');
+          assertFalse(networkConfig.vpnIsConfigured_());
+          networkConfig.set('selectedServerCaHash_', kCaHash);
+
+          let props = networkConfig.getPropertiesToSet_();
+          const mojom = chromeos.networkConfig.mojom;
+          assertEquals(kTestVpnName, props.name);
+          assertEquals(kTestVpnHost, props.typeConfig.vpn.host);
+          assertEquals(mojom.VpnType.kIKEv2, props.typeConfig.vpn.type.value);
+          assertEquals('EAP', props.typeConfig.vpn.ipSec.authenticationType);
+          assertEquals(2, props.typeConfig.vpn.ipSec.ikeVersion);
+          assertEquals(1, props.typeConfig.vpn.ipSec.serverCaPems.length);
+          assertEquals(kCaPem, props.typeConfig.vpn.ipSec.serverCaPems[0]);
+          assertEquals('MSCHAPv2', props.typeConfig.vpn.ipSec.eap.outer);
+          assertEquals(kTestUsername, props.typeConfig.vpn.ipSec.eap.identity);
+          assertEquals(kTestPassword, props.typeConfig.vpn.ipSec.eap.password);
+          assertFalse(props.typeConfig.vpn.ipSec.saveCredentials);
+          assertFalse(props.typeConfig.vpn.ipSec.eap.saveCredentials);
+
+          networkConfig.set('vpnSaveCredentials_', true);
+          props = networkConfig.getPropertiesToSet_();
+          assertTrue(props.typeConfig.vpn.ipSec.saveCredentials);
+          assertTrue(props.typeConfig.vpn.ipSec.eap.saveCredentials);
+        });
+      });
+    });
+
+    test('Existing EAP', function() {
+      const mojom = chromeos.networkConfig.mojom;
+      const ikev2 = OncMojo.getDefaultManagedProperties(
+          mojom.NetworkType.kVPN, 'someguid', kTestVpnName);
+      ikev2.typeProperties.vpn.type = mojom.VpnType.kIKEv2;
+      ikev2.typeProperties.vpn.host = {activeValue: kTestVpnHost};
+      ikev2.typeProperties.vpn.ipSec = {
+        authenticationType: {activeValue: 'EAP'},
+        eap: {
+          domainSuffixMatch: {activeValue: []},
+          identity: {activeValue: kTestUsername},
+          outer: {activeValue: 'MSCHAPv2'},
+          saveCredentials: {activeValue: true},
+          subjectAltNameMatch: {activeValue: []},
+          useSystemCas: {activeValue: false},
+        },
+        ikeVersion: {activeValue: 2},
+        saveCredentials: {activeValue: true},
+        serverCaPems: {activeValue: [kCaPem]},
+      };
+      setNetworkConfig(ikev2);
+      initNetworkConfigWithCerts(
+          /* hasServerCa= */ true, /* hasUserCert= */ false);
+      return mojoApi_.whenCalled('getNetworkCertificates').then(() => {
+        return flushAsync().then(() => {
+          assertEquals('IKEv2', networkConfig.get('vpnType_'));
+          assertEquals('EAP', networkConfig.get('ipsecAuthType_'));
+          assertEquals(kCaHash, networkConfig.selectedServerCaHash_);
+
+          const props = networkConfig.getPropertiesToSet_();
+          const mojom = chromeos.networkConfig.mojom;
+          assertEquals('someguid', props.guid);
+          assertEquals(kTestVpnName, props.name);
+          assertEquals(kTestVpnHost, props.typeConfig.vpn.host);
+          assertEquals(mojom.VpnType.kIKEv2, props.typeConfig.vpn.type.value);
+          assertEquals('EAP', props.typeConfig.vpn.ipSec.authenticationType);
+          assertEquals(2, props.typeConfig.vpn.ipSec.ikeVersion);
+          assertEquals(1, props.typeConfig.vpn.ipSec.serverCaPems.length);
+          assertEquals(kCaPem, props.typeConfig.vpn.ipSec.serverCaPems[0]);
+          assertEquals('MSCHAPv2', props.typeConfig.vpn.ipSec.eap.outer);
+          assertEquals(kTestUsername, props.typeConfig.vpn.ipSec.eap.identity);
+          assertTrue(props.typeConfig.vpn.ipSec.saveCredentials);
+          assertTrue(props.typeConfig.vpn.ipSec.eap.saveCredentials);
+        });
+      });
+    });
   });
 
   suite('L2TP/IPsec', function() {
@@ -435,7 +556,7 @@
       const configProperties = networkConfig.get('configProperties_');
       configProperties.name = kTestVpnName;
       configProperties.typeConfig.vpn.host = kTestVpnHost;
-      configProperties.typeConfig.vpn.l2tp.username = 'test-username';
+      configProperties.typeConfig.vpn.l2tp.username = kTestUsername;
     }
 
     test('Switch Authentication Type', function() {
@@ -446,7 +567,10 @@
       // certificate should be hidden.
       networkConfig.set('vpnType_', 'L2TP_IPsec');
       Polymer.dom.flush();
+      assertEquals(2, networkConfig.get('ipsecAuthTypeItems_').length);
       assertEquals('PSK', networkConfig.ipsecAuthType_);
+      assertFalse(!!networkConfig.$$('#ipsec-local-id-input'));
+      assertFalse(!!networkConfig.$$('#ipsec-remote-id-input'));
       assertTrue(!!networkConfig.$$('#ipsec-auth-type'));
       assertTrue(!!networkConfig.$$('#l2tp-username-input'));
       assertTrue(!!networkConfig.$$('#ipsec-psk-input'));
@@ -462,6 +586,14 @@
       assertTrue(!!networkConfig.$$('#l2tp-username-input'));
       assertTrue(!!networkConfig.$$('#vpnServerCa'));
       assertTrue(!!networkConfig.$$('#vpnUserCert'));
+
+      // Switch VPN type to IKEv2 and auth type to EAP, and then back to
+      // L2TP/IPsec. The auth type should be reset to PSK since EAP is not a
+      // valid value.
+      networkConfig.set('vpnType_', 'IKEv2');
+      networkConfig.set('ipsecAuthType_', 'EAP');
+      networkConfig.set('vpnType_', 'L2TP_IPsec');
+      assertEquals('PSK', networkConfig.ipsecAuthType_);
     });
 
     test('No Certs', function() {
diff --git a/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js
index e25e4b61..c6345f0 100644
--- a/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/app_notifications_subpage_tests.js
@@ -107,7 +107,7 @@
   }
 
   /**
-   * @return {!apps.mojom.Permission}
+   * @return {!appManagement.mojom.Permission}
    */
   getLastUpdatedPermission() {
     return this.lastUpdatedAppPermission_;
@@ -148,7 +148,7 @@
 
   /**
    * @param {string} id
-   * @param {!apps.mojom.Permission} permission
+   * @param {!appManagement.mojom.Permission} permission
    */
   setNotificationPermission(id, permission) {
     return new Promise(resolve => {
@@ -223,8 +223,8 @@
   /**
    * @param {string} id
    * @param {string} title
-   * @param {!apps.mojom.Permission} permission
-   * @param {?apps.mojom.Readiness} readiness
+   * @param {!appManagement.mojom.Permission} permission
+   * @param {?chromeos.settings.appNotification.mojom.Readiness} readiness
    * @return {!chromeos.settings.appNotification.mojom.App}
    */
   function createApp(
diff --git a/chrome/test/data/webui/settings/chromeos/apps_page_test.js b/chrome/test/data/webui/settings/chromeos/apps_page_test.js
index deec541..6d463a8 100644
--- a/chrome/test/data/webui/settings/chromeos/apps_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/apps_page_test.js
@@ -166,7 +166,7 @@
 
   /**
    * @param {string} id
-   * @param {!apps.mojom.Permission} permission
+   * @param {!appManagement.mojom.Permission} permission
    */
   setNotificationPermission(id, permission) {
     return new Promise(resolve => {
@@ -197,8 +197,8 @@
   /**
    * @param {string} id
    * @param {string} title
-   * @param {!apps.mojom.Permission} permission
-   * @param {?apps.mojom.Readiness} readiness
+   * @param {!appManagement.mojom.Permission} permission
+   * @param {?chromeos.settings.appNotification.mojom.Readiness} readiness
    * @return {!chromeos.settings.appNotification.mojom.App}
    */
   function createApp(
diff --git a/chrome/test/data/webui/settings/password_edit_dialog_test.ts b/chrome/test/data/webui/settings/password_edit_dialog_test.ts
index 533ff75..6d50d6e4 100644
--- a/chrome/test/data/webui/settings/password_edit_dialog_test.ts
+++ b/chrome/test/data/webui/settings/password_edit_dialog_test.ts
@@ -7,8 +7,9 @@
 // clang-format off
 import 'chrome://settings/lazy_load.js';
 
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {PasswordEditDialogElement} from 'chrome://settings/lazy_load.js';
+import {CrInputElement, PasswordDialogMode, PasswordEditDialogElement} from 'chrome://settings/lazy_load.js';
 import {PasswordManagerImpl} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
@@ -29,16 +30,24 @@
  * Helper function to test if all components of edit dialog are shown correctly.
  */
 function assertEditDialogParts(passwordDialog: PasswordEditDialogElement) {
+  assertEquals(PasswordDialogMode.EDIT, passwordDialog.dialogMode);
+
   assertEquals(
       passwordDialog.i18n('editPasswordTitle'),
       passwordDialog.$.title.textContent!.trim());
   assertTrue(!!passwordDialog.$.websiteInput.readonly);
+
   assertFalse(!!passwordDialog.$.usernameInput.readonly);
+
   assertFalse(!!passwordDialog.$.passwordInput.readonly);
   assertTrue(!!passwordDialog.$.passwordInput.required);
+
   assertFalse(isElementVisible(passwordDialog.$.storePicker));
+
   assertTrue(!!passwordDialog.shadowRoot!.querySelector('#showPasswordButton'));
+
   assertTrue(isElementVisible(passwordDialog.$.footnote));
+
   assertTrue(isElementVisible(passwordDialog.$.cancel));
   assertEquals(
       passwordDialog.i18n('save'),
@@ -50,18 +59,27 @@
  * Helper function to test if all components of details dialog are shown
  * correctly.
  */
-function assertDetailsDialogParts(passwordDialog: PasswordEditDialogElement) {
+function assertFederatedDialogParts(passwordDialog: PasswordEditDialogElement) {
+  assertEquals(PasswordDialogMode.FEDERATED_VIEW, passwordDialog.dialogMode);
+
   assertEquals(
       passwordDialog.i18n('passwordDetailsTitle'),
       passwordDialog.$.title.textContent!.trim());
+
   assertTrue(!!passwordDialog.$.websiteInput.readonly);
+
   assertTrue(!!passwordDialog.$.usernameInput.readonly);
+
   assertTrue(!!passwordDialog.$.passwordInput.readonly);
   assertFalse(!!passwordDialog.$.passwordInput.required);
+
   assertFalse(isElementVisible(passwordDialog.$.storePicker));
+
   assertFalse(
       !!passwordDialog.shadowRoot!.querySelector('#showPasswordButton'));
+
   assertFalse(isElementVisible(passwordDialog.$.footnote));
+
   assertFalse(isElementVisible(passwordDialog.$.cancel));
   assertEquals(
       passwordDialog.i18n('done'),
@@ -70,19 +88,61 @@
 }
 
 /**
+ * Helper function to test if all components of view dialog are shown
+ * correctly.
+ */
+function assertViewDialogParts(
+    passwordDialog: PasswordEditDialogElement, titleText: string) {
+  assertEquals(PasswordDialogMode.PASSWORD_VIEW, passwordDialog.dialogMode);
+
+  assertEquals(titleText, passwordDialog.$.title.textContent!.trim());
+
+  assertTrue(!!passwordDialog.$.websiteInput.hidden);
+
+  assertTrue(!!passwordDialog.$.usernameInput.readonly);
+  assertTrue(!!passwordDialog.shadowRoot!.querySelector('#copyUsernameButton'));
+
+  assertTrue(!!passwordDialog.$.passwordInput.readonly);
+  assertTrue(!!passwordDialog.$.passwordInput.required);
+
+  assertTrue(!!passwordDialog.shadowRoot!.querySelector('#copyPasswordButton'));
+  assertTrue(!!passwordDialog.shadowRoot!.querySelector('#showPasswordButton'));
+
+  assertFalse(isElementVisible(passwordDialog.$.storePicker));
+
+  assertFalse(isElementVisible(passwordDialog.$.footnote));
+
+  assertFalse(isElementVisible(passwordDialog.$.cancel));
+  assertTrue(!!passwordDialog.shadowRoot!.querySelector('#switchToEditButton'));
+  assertEquals(
+      passwordDialog.i18n('done'),
+      passwordDialog.$.actionButton.textContent!.trim());
+  assertFalse(passwordDialog.$.actionButton.disabled);
+}
+
+/**
  * Helper function to test if all components of add dialog are shown correctly.
  */
 function assertAddDialogParts(passwordDialog: PasswordEditDialogElement) {
+  assertEquals(PasswordDialogMode.ADD, passwordDialog.dialogMode);
+
   assertEquals(
       passwordDialog.i18n('addPasswordTitle'),
       passwordDialog.$.title.textContent!.trim());
+
   assertFalse(!!passwordDialog.$.websiteInput.readonly);
+
   assertFalse(!!passwordDialog.$.usernameInput.readonly);
+
   assertFalse(!!passwordDialog.$.passwordInput.readonly);
   assertTrue(!!passwordDialog.$.passwordInput.required);
+
   assertFalse(isElementVisible(passwordDialog.$.storageDetails));
+
   assertTrue(!!passwordDialog.shadowRoot!.querySelector('#showPasswordButton'));
+
   assertTrue(isElementVisible(passwordDialog.$.footnote));
+
   assertTrue(isElementVisible(passwordDialog.$.cancel));
   assertEquals(
       passwordDialog.i18n('save'),
@@ -197,7 +257,7 @@
         {federationText: 'with chromium.org', username: 'bart', deviceId: 42});
     const passwordDialog =
         elementFactory.createPasswordEditDialog(federationEntry);
-    assertDetailsDialogParts(passwordDialog);
+    assertFederatedDialogParts(passwordDialog);
     assertEquals(
         federationEntry.urls.link, passwordDialog.$.websiteInput.value);
     assertEquals(
@@ -584,6 +644,85 @@
         assertEquals(existingEntry.password, addDialog.$.passwordInput.value);
       });
 
+  test('hasCorrectInitialStateWhenEditModeWhenNotesEnabled', async function() {
+    loadTimeData.overrideValues({enablePasswordNotes: true});
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialog = elementFactory.createPasswordEditDialog(commonEntry);
+    assertEditDialogParts(passwordDialog);
+    const noteElement =
+        passwordDialog.shadowRoot!.querySelector<CrInputElement>('#note');
+    assertTrue(!!noteElement);
+    assertTrue(!noteElement.readonly);
+  });
+
+  test('hasCorrectInitialStateWhenViewModeWhenNotesEnabled', async function() {
+    loadTimeData.overrideValues({enablePasswordNotes: true});
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialog = elementFactory.createPasswordEditDialog(
+        commonEntry, [], false, PasswordDialogMode.PASSWORD_VIEW);
+    assertViewDialogParts(passwordDialog, commonEntry.urls.shown);
+    assertTrue(passwordDialog.$.passwordInput.type === 'text');
+    const noteElement =
+        passwordDialog.shadowRoot!.querySelector<CrInputElement>('#note');
+    assertTrue(!!noteElement);
+    assertTrue(!!noteElement.readonly);
+  });
+
+  test('noChangesWhenNotesIsNotEnabled', async function() {
+    loadTimeData.overrideValues({enablePasswordNotes: false});
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialog = elementFactory.createPasswordEditDialog(commonEntry);
+    assertEditDialogParts(passwordDialog);
+    assertFalse(
+        !!passwordDialog.shadowRoot!.querySelector<CrInputElement>('#note'));
+  });
+
+  test('clickingEditInPasswordViewModeChangesDialogMode', async function() {
+    loadTimeData.overrideValues({enablePasswordNotes: true});
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialog = elementFactory.createPasswordEditDialog(
+        commonEntry, [], false, PasswordDialogMode.PASSWORD_VIEW);
+    assertViewDialogParts(passwordDialog, commonEntry.urls.shown);
+    const button = passwordDialog.shadowRoot!.querySelector<HTMLButtonElement>(
+        '#switchToEditButton')!;
+    button.click();
+    flush();
+    assertEditDialogParts(passwordDialog);
+  });
+
+  test('federatedCredentialDoesntHaveNotes', async function() {
+    loadTimeData.overrideValues({enablePasswordNotes: true});
+    const federationEntry = createMultiStorePasswordEntry(
+        {federationText: 'with chromium.org', username: 'bart', deviceId: 42});
+    const passwordDialog =
+        elementFactory.createPasswordEditDialog(federationEntry);
+    assertFederatedDialogParts(passwordDialog);
+    assertFalse(
+        !!passwordDialog.shadowRoot!.querySelector<CrInputElement>('#note'));
+  });
+
+  // When "--auto-open-devtools-for-tabs" option is enabled, this test fails
+  // with "Document is not focused." exception.
+  test('clickingCopyButtonsInViewModeCopiesText', async function() {
+    loadTimeData.overrideValues({enablePasswordNotes: true});
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialog = elementFactory.createPasswordEditDialog(
+        commonEntry, [], false, PasswordDialogMode.PASSWORD_VIEW);
+    passwordDialog.$.passwordInput.value = 'password';
+    assertViewDialogParts(passwordDialog, commonEntry.urls.shown);
+    passwordDialog.shadowRoot!
+        .querySelector<HTMLButtonElement>('#copyUsernameButton')!.click();
+    assertEquals('bart', await navigator.clipboard.readText());
+    passwordDialog.shadowRoot!
+        .querySelector<HTMLButtonElement>('#copyPasswordButton')!.click();
+    assertEquals('password', await navigator.clipboard.readText());
+  });
+
   // <if expr="not chromeos_ash and not chromeos_lacros">
   // On ChromeOS/Lacros the behavior is different (on failure we request token
   // and retry).
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
index 287e3b6d..f5aa280 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
@@ -5,7 +5,7 @@
 // clang-format off
 import {assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {AutofillManagerProxy, PasswordEditDialogElement, PasswordListItemElement, PasswordMoveMultiplePasswordsToAccountDialogElement, PasswordsExportDialogElement, PasswordsSectionElement, PaymentsManagerProxy, PersonalDataChangedListener} from 'chrome://settings/lazy_load.js';
+import {AutofillManagerProxy, PasswordDialogMode, PasswordEditDialogElement, PasswordListItemElement, PasswordMoveMultiplePasswordsToAccountDialogElement, PasswordsExportDialogElement, PasswordsSectionElement, PaymentsManagerProxy, PersonalDataChangedListener} from 'chrome://settings/lazy_load.js';
 import {MultiStoreExceptionEntry, MultiStorePasswordUiEntry, PasswordManagerProxy} from 'chrome://settings/settings.js';
 import {assertEquals} from 'chrome://webui-test/chai_assert.js';
 
@@ -385,9 +385,12 @@
   createPasswordEditDialog(
       passwordEntry: MultiStorePasswordUiEntry|null = null,
       passwords?: MultiStorePasswordUiEntry[],
-      isAccountStoreUser: boolean = false): PasswordEditDialogElement {
+      isAccountStoreUser: boolean = false,
+      requestedDialogMode: PasswordDialogMode|
+      null = null): PasswordEditDialogElement {
     const passwordDialog = this.document.createElement('password-edit-dialog');
     passwordDialog.existingEntry = passwordEntry;
+    passwordDialog.requestedDialogMode = requestedDialogMode;
     if (passwordEntry && !passwordEntry.federationText) {
       // Edit dialog is always opened with plaintext password for non-federated
       // credentials since user authentication is required before opening the
diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc
index 3af59403..48fdd6f 100644
--- a/chrome/test/ppapi/ppapi_browsertest.cc
+++ b/chrome/test/ppapi/ppapi_browsertest.cc
@@ -291,7 +291,6 @@
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
-  IgnoreNetworkServiceCrashes();
   network_service_test->CrashOnResolveHost("crash.com");
 
   RunTestViaHTTP(STRIP_PREFIXES(TCPSocketPrivateCrash_Resolve));
@@ -1282,7 +1281,6 @@
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
-  IgnoreNetworkServiceCrashes();
   network_service_test->CrashOnResolveHost("crash.com");
 
   RunTestViaHTTP(STRIP_PREFIXES(HostResolverCrash_Basic));
diff --git a/chrome/tools/build/win/OWNERS b/chrome/tools/build/win/OWNERS
index 9b598b1..7e577dc1 100644
--- a/chrome/tools/build/win/OWNERS
+++ b/chrome/tools/build/win/OWNERS
@@ -5,4 +5,3 @@
 
 # FILES.cfg OWNERS as in parent owner file.
 per-file FILES.cfg=kerz@chromium.org
-per-file FILES.cfg=mmoss@chromium.org
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 0b21021a..8e0f8b2a 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -1299,12 +1299,21 @@
       <message name="IDS_ONC_VPN_TYPE" desc="ONC Property label for VPN.Type">
         Provider type
       </message>
-      <message name="IDS_ONC_VPN_AUTH_TYPE" desc="ONC Porperty label for VPN.IPsec.AuthenticationType">
+      <message name="IDS_ONC_VPN_AUTH_TYPE" desc="ONC Property label for VPN.IPsec.AuthenticationType">
         Authentication type
       </message>
+      <message name="IDS_ONC_VPN_AUTH_TYPE_USERNAME" desc="ONC Property label for VPN.IPsec.AuthenticationType.EAP">
+        Username and password
+      </message>
       <message name="IDS_ONC_VPN_IPSEC_GROUP" desc="ONC Property label for VPN.IPSec.Group">
         Group name
       </message>
+      <message name="IDS_ONC_VPN_IPSEC_LOCAL_IDENTITY" desc="ONC Property label for VPN.IPSec.LocalIdentity">
+        Local identity
+      </message>
+      <message name="IDS_ONC_VPN_IPSEC_REMOTE_IDENTITY" desc="ONC Property label for VPN.IPSec.RemoteIdentity">
+        Remote identity
+      </message>
       <message name="IDS_ONC_VPN_THIRD_PARTY_VPN_PROVIDER_NAME" desc="ONC Property label for VPN.ThirdPartyVPN.ProviderName">
         Provider name
       </message>
@@ -2012,7 +2021,7 @@
         Use this photo
       </message>
       <message name="IDS_PERSONALIZATION_APP_AVATAR_CONFIRM_VIDEO" desc="Label for the button to confirm webcam video">
-        Use this video 
+        Use this video
       </message>
       <message name="IDS_PERSONALIZATION_APP_AVATAR_REJECT_PHOTO" desc="Label for the button to reject webcam photo or video">
         Retake
diff --git a/chromeos/chromeos_strings_grd/IDS_ONC_VPN_AUTH_TYPE_USERNAME.png.sha1 b/chromeos/chromeos_strings_grd/IDS_ONC_VPN_AUTH_TYPE_USERNAME.png.sha1
new file mode 100644
index 0000000..c1d8c24
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_ONC_VPN_AUTH_TYPE_USERNAME.png.sha1
@@ -0,0 +1 @@
+e8fd67ae43efebad7687e53cb8326fca6a36d2fc
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_ONC_VPN_IPSEC_LOCAL_IDENTITY.png.sha1 b/chromeos/chromeos_strings_grd/IDS_ONC_VPN_IPSEC_LOCAL_IDENTITY.png.sha1
new file mode 100644
index 0000000..d723a0e8
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_ONC_VPN_IPSEC_LOCAL_IDENTITY.png.sha1
@@ -0,0 +1 @@
+41d40b1275204a08a3f83cfeafe2dcce1104ceea
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_ONC_VPN_IPSEC_REMOTE_IDENTITY.png.sha1 b/chromeos/chromeos_strings_grd/IDS_ONC_VPN_IPSEC_REMOTE_IDENTITY.png.sha1
new file mode 100644
index 0000000..047daa5
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_ONC_VPN_IPSEC_REMOTE_IDENTITY.png.sha1
@@ -0,0 +1 @@
+fb9605745796f5129d15043f3a97bc12f4364f88
\ No newline at end of file
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index c05a655..977eb9b5 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -514,6 +514,9 @@
 // Default directories on the system. We send the full path for each value for
 // future compatibility, to avoid assumptions about where on disk the directory
 // is located.
+//
+// Next version: 33
+// Next id: 8
 [Stable]
 struct DefaultPaths {
   // The default (non-configurable) directory for documents. For example,
@@ -543,6 +546,10 @@
   // The (non-configurable) path at which Crostini's home directory is
   // mounted. For example, /media/fuse/crostini_<hash>_termina_penguin.
   [MinVersion=31] mojo_base.mojom.FilePath? linux_files@6;
+
+  // The (non-coniggurable) directory for ash resources. For example,
+  // /opt/google/chrome.
+  [MinVersion=32] mojo_base.mojom.FilePath? ash_resources@7;
 };
 
 // The device specific data needed in Lacros.
diff --git a/components/autofill_assistant/browser/client_context.cc b/components/autofill_assistant/browser/client_context.cc
index 962d06c..e3cbab6 100644
--- a/components/autofill_assistant/browser/client_context.cc
+++ b/components/autofill_assistant/browser/client_context.cc
@@ -78,11 +78,6 @@
   proto_.set_screen_orientation(client_->GetScreenOrientation());
 }
 
-void ClientContextImpl::SetPaymentsClientToken(
-    const std::string& client_token) {
-  proto_.set_payments_client_token(client_token);
-}
-
 ClientContextProto ClientContextImpl::AsProto() const {
   return proto_;
 }
diff --git a/components/autofill_assistant/browser/client_context.h b/components/autofill_assistant/browser/client_context.h
index d798fdb..e6cede8 100644
--- a/components/autofill_assistant/browser/client_context.h
+++ b/components/autofill_assistant/browser/client_context.h
@@ -18,8 +18,6 @@
   virtual ~ClientContext() = default;
   // Updates the client context based on the current state of the client.
   virtual void Update(const TriggerContext& trigger_context) = 0;
-  // Updates the payments client token. This is not part of the normal update.
-  virtual void SetPaymentsClientToken(const std::string& client_token) = 0;
   // Returns the proto representation of this client context.
   virtual ClientContextProto AsProto() const = 0;
 };
@@ -31,7 +29,6 @@
   ClientContextImpl(const Client* client);
   ~ClientContextImpl() override = default;
   void Update(const TriggerContext& trigger_context) override;
-  void SetPaymentsClientToken(const std::string& client_token) override;
   ClientContextProto AsProto() const override;
 
  private:
@@ -45,7 +42,6 @@
   EmptyClientContext() = default;
   ~EmptyClientContext() override = default;
   void Update(const TriggerContext& trigger_context) override {}
-  void SetPaymentsClientToken(const std::string& client_token) override {}
   ClientContextProto AsProto() const override;
 };
 
diff --git a/components/autofill_assistant/browser/mock_client_context.h b/components/autofill_assistant/browser/mock_client_context.h
index 69a09db..cc9a593c 100644
--- a/components/autofill_assistant/browser/mock_client_context.h
+++ b/components/autofill_assistant/browser/mock_client_context.h
@@ -19,7 +19,6 @@
   ~MockClientContext() override;
 
   MOCK_METHOD1(Update, void(const TriggerContext& trigger_context));
-  MOCK_METHOD1(SetPaymentsClientToken, void(const std::string&));
   MOCK_CONST_METHOD0(AsProto, ClientContextProto());
 };
 
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index c387bc1..f9dca962 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -913,13 +913,28 @@
 
 // static
 std::string ProtocolUtils::CreateGetUserDataRequest(
-    const CollectUserDataOptions& options) {
+    bool request_name,
+    bool request_email,
+    bool request_phone,
+    bool request_shipping,
+    bool request_payment_methods,
+    const std::vector<std::string>& supported_card_networks,
+    const std::string& client_token) {
   GetUserDataRequestProto request_proto;
-  request_proto.set_request_name(options.request_payer_name);
-  request_proto.set_request_email(options.request_payer_email);
-  request_proto.set_request_phone(options.request_phone_number_separately);
-  request_proto.set_request_addresses(options.request_shipping);
-  request_proto.set_request_payment_methods(options.request_payment_method);
+  request_proto.set_request_name(request_name);
+  request_proto.set_request_email(request_email);
+  request_proto.set_request_phone(request_phone);
+  request_proto.set_request_addresses(request_shipping);
+
+  if (request_payment_methods) {
+    auto* payment_methods_request =
+        request_proto.mutable_request_payment_methods();
+    payment_methods_request->set_client_token(client_token);
+    for (const std::string& supported_card_network : supported_card_networks) {
+      payment_methods_request->add_supported_card_networks(
+          supported_card_network);
+    }
+  }
 
   std::string serialized_request_proto;
   bool success = request_proto.SerializeToString(&serialized_request_proto);
diff --git a/components/autofill_assistant/browser/protocol_utils.h b/components/autofill_assistant/browser/protocol_utils.h
index 51d737a..b8c9ec0 100644
--- a/components/autofill_assistant/browser/protocol_utils.h
+++ b/components/autofill_assistant/browser/protocol_utils.h
@@ -16,7 +16,6 @@
 #include "components/autofill_assistant/browser/script_parameters.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/trigger_scripts/trigger_script.h"
-#include "components/autofill_assistant/browser/user_data.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
@@ -75,7 +74,13 @@
 
   // Create request to get user data.
   static std::string CreateGetUserDataRequest(
-      const CollectUserDataOptions& options);
+      bool request_name,
+      bool request_email,
+      bool request_phone,
+      bool request_shipping,
+      bool request_payment_methods,
+      const std::vector<std::string>& supported_card_networks,
+      const std::string& client_token);
 
   // Create an action from the |action|.
   static std::unique_ptr<Action> CreateAction(ActionDelegate* delegate,
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index c8e75746..6b08e29f 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -585,21 +585,30 @@
 }
 
 TEST_F(ProtocolUtilsTest, CreateGetUserDataRequest) {
-  CollectUserDataOptions options;
-  options.request_payer_name = true;
-  options.request_payer_email = true;
-  options.request_phone_number_separately = true;
-  options.request_shipping = true;
-  options.request_payment_method = true;
-
   GetUserDataRequestProto request;
-  EXPECT_TRUE(request.ParseFromString(
-      ProtocolUtils::CreateGetUserDataRequest(options)));
+  EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetUserDataRequest(
+      /* request_name= */ true, /* request_email= */ true,
+      /* request_phone= */ true, /* request_shipping= */ true,
+      /* request_payment_methods= */ false,
+      /* supported_card_networks= */ std::vector<std::string>(),
+      /* client_token= */ std::string())));
   EXPECT_TRUE(request.request_name());
   EXPECT_TRUE(request.request_email());
   EXPECT_TRUE(request.request_phone());
   EXPECT_TRUE(request.request_addresses());
-  EXPECT_TRUE(request.request_payment_methods());
+  EXPECT_FALSE(request.has_request_payment_methods());
+
+  EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetUserDataRequest(
+      /* request_name= */ true, /* request_email= */ true,
+      /* request_phone= */ true, /* request_shipping= */ true,
+      /* request_payment_methods= */ true,
+      /* supported_card_networks= */
+      std::vector<std::string>({"VISA", "MASTERCARD"}),
+      /* client_token= */ "token")));
+  EXPECT_TRUE(request.has_request_payment_methods());
+  EXPECT_EQ(request.request_payment_methods().client_token(), "token");
+  EXPECT_THAT(request.request_payment_methods().supported_card_networks(),
+              ElementsAre("VISA", "MASTERCARD"));
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 6f2b6ca..e2432eb 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -125,8 +125,7 @@
   }
   optional ScreenOrientation screen_orientation = 16;
 
-  // An opaque client token for interactions with payments' systems.
-  optional bytes payments_client_token = 19;
+  reserved 19;
 }
 
 // Get the list of scripts that can potentially be run on a url.
@@ -222,6 +221,13 @@
 
 // Request to get user data.
 message GetUserDataRequestProto {
+  message PaymentMethodRequest {
+    // The client token, generated by GMS Core.
+    optional bytes client_token = 1;
+    // The list of supported card networks.
+    repeated string supported_card_networks = 2;
+  }
+
   // For logging, to know which run this request has originated from.
   optional uint64 run_id = 1;
 
@@ -229,7 +235,9 @@
   optional bool request_email = 3;
   optional bool request_phone = 4;
   optional bool request_addresses = 5;
-  optional bool request_payment_methods = 6;
+  optional PaymentMethodRequest request_payment_methods = 7;
+
+  reserved 6;
 }
 
 // Response with user data.
diff --git a/components/autofill_assistant/browser/service/service_impl.cc b/components/autofill_assistant/browser/service/service_impl.cc
index 11337e82..39cab503 100644
--- a/components/autofill_assistant/browser/service/service_impl.cc
+++ b/components/autofill_assistant/browser/service/service_impl.cc
@@ -104,40 +104,6 @@
                              ResponseCallback callback) {
   DCHECK(!script_path.empty());
   client_context_->Update(trigger_context);
-  if (client_context_->AsProto().payments_client_token().empty() &&
-      base::FeatureList::IsEnabled(
-          features::kAutofillAssistantGetPaymentsClientToken)) {
-    client_->FetchPaymentsClientToken(base::BindOnce(
-        &ServiceImpl::OnFetchPaymentsClientToken,
-        weak_ptr_factory_.GetWeakPtr(), script_path, url,
-        std::make_unique<TriggerContext>(
-            std::vector<const TriggerContext*>{&trigger_context}),
-        global_payload, script_payload, std::move(callback)));
-  } else {
-    SendGetActions(script_path, url, trigger_context, global_payload,
-                   script_payload, std::move(callback));
-  }
-}
-
-void ServiceImpl::OnFetchPaymentsClientToken(
-    const std::string& script_path,
-    const GURL& url,
-    std::unique_ptr<TriggerContext> trigger_context,
-    const std::string& global_payload,
-    const std::string& script_payload,
-    ResponseCallback callback,
-    const std::string& client_token) {
-  client_context_->SetPaymentsClientToken(client_token);
-  SendGetActions(script_path, url, *trigger_context, global_payload,
-                 script_payload, std::move(callback));
-}
-
-void ServiceImpl::SendGetActions(const std::string& script_path,
-                                 const GURL& url,
-                                 const TriggerContext& trigger_context,
-                                 const std::string& global_payload,
-                                 const std::string& script_payload,
-                                 ResponseCallback callback) {
   request_sender_->SendRequest(
       script_action_server_url_,
       ProtocolUtils::CreateInitialScriptActionsRequest(
@@ -165,9 +131,41 @@
 
 void ServiceImpl::GetUserData(const CollectUserDataOptions& options,
                               ResponseCallback callback) {
-  request_sender_->SendRequest(user_data_url_,
-                               ProtocolUtils::CreateGetUserDataRequest(options),
-                               std::move(callback), RpcType::GET_USER_DATA);
+  if (options.request_payment_method) {
+    // We do not cache the payments client token. It could go stale (in practice
+    // it currently doesn't). Getting the token is little overhead.
+    client_->FetchPaymentsClientToken(base::BindOnce(
+        &ServiceImpl::SendUserDataRequest, weak_ptr_factory_.GetWeakPtr(),
+        options.request_payer_name, options.request_payer_email,
+        options.request_payer_phone || options.request_phone_number_separately,
+        options.request_shipping, options.request_payment_method,
+        options.supported_basic_card_networks, std::move(callback)));
+    return;
+  }
+
+  SendUserDataRequest(
+      options.request_payer_name, options.request_payer_email,
+      options.request_payer_phone || options.request_phone_number_separately,
+      options.request_shipping, options.request_payment_method,
+      options.supported_basic_card_networks, std::move(callback),
+      std::string());
+}
+
+void ServiceImpl::SendUserDataRequest(
+    bool request_name,
+    bool request_email,
+    bool request_phone,
+    bool request_shipping,
+    bool request_payment_methods,
+    const std::vector<std::string>& supported_card_networks,
+    ResponseCallback callback,
+    const std::string& client_token) {
+  request_sender_->SendRequest(
+      user_data_url_,
+      ProtocolUtils::CreateGetUserDataRequest(
+          request_name, request_email, request_phone, request_shipping,
+          request_payment_methods, supported_card_networks, client_token),
+      std::move(callback), RpcType::GET_USER_DATA);
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service_impl.h b/components/autofill_assistant/browser/service/service_impl.h
index dd7116b..ade812a 100644
--- a/components/autofill_assistant/browser/service/service_impl.h
+++ b/components/autofill_assistant/browser/service/service_impl.h
@@ -87,20 +87,15 @@
                    ResponseCallback callback) override;
 
  private:
-  void OnFetchPaymentsClientToken(
-      const std::string& script_path,
-      const GURL& url,
-      std::unique_ptr<TriggerContext> trigger_context,
-      const std::string& global_payload,
-      const std::string& script_payload,
+  void SendUserDataRequest(
+      bool request_name,
+      bool request_email,
+      bool request_phone,
+      bool request_shipping,
+      bool request_payment_methods,
+      const std::vector<std::string>& supported_card_networks,
       ResponseCallback callback,
       const std::string& client_token);
-  void SendGetActions(const std::string& script_path,
-                      const GURL& url,
-                      const TriggerContext& trigger_context,
-                      const std::string& global_payload,
-                      const std::string& script_payload,
-                      ResponseCallback callback);
 
   Client* const client_;
 
diff --git a/components/autofill_assistant/browser/service/service_impl_unittest.cc b/components/autofill_assistant/browser/service/service_impl_unittest.cc
index b21bf64..82d5c8e 100644
--- a/components/autofill_assistant/browser/service/service_impl_unittest.cc
+++ b/components/autofill_assistant/browser/service/service_impl_unittest.cc
@@ -73,9 +73,6 @@
 
 TEST_F(ServiceImplTest, GetActions) {
   EXPECT_CALL(*mock_client_context_, Update);
-  EXPECT_CALL(mock_client_, FetchPaymentsClientToken)
-      .WillOnce(RunOnceCallback<0>("token"));
-  EXPECT_CALL(*mock_client_context_, SetPaymentsClientToken("token"));
   EXPECT_CALL(*mock_request_sender_,
               OnSendRequest(GURL(kActionServerUrl), _, _, RpcType::GET_ACTIONS))
       .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
@@ -90,9 +87,6 @@
 
 TEST_F(ServiceImplTest, GetActionsForwardsScriptStoreConfig) {
   EXPECT_CALL(*mock_client_context_, Update);
-  EXPECT_CALL(mock_client_, FetchPaymentsClientToken)
-      .WillOnce(RunOnceCallback<0>("token"));
-  EXPECT_CALL(*mock_client_context_, SetPaymentsClientToken("token"));
 
   ScriptActionRequestProto expected_get_actions;
   ScriptStoreConfig* config = expected_get_actions.mutable_initial_request()
@@ -131,29 +125,6 @@
       features::kAutofillAssistantGetPaymentsClientToken);
 
   EXPECT_CALL(*mock_client_context_, Update);
-  EXPECT_CALL(mock_client_, FetchPaymentsClientToken).Times(0);
-  EXPECT_CALL(*mock_client_context_, SetPaymentsClientToken).Times(0);
-  EXPECT_CALL(*mock_request_sender_,
-              OnSendRequest(GURL(kActionServerUrl), _, _, RpcType::GET_ACTIONS))
-      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
-  EXPECT_CALL(mock_response_callback_,
-              Run(net::HTTP_OK, std::string("response")));
-
-  service_->GetActions(
-      std::string("fake_script_path"), GURL("https://www.example.com"),
-      TriggerContext(), std::string("fake_global_payload"),
-      std::string("fake_script_payload"), mock_response_callback_.Get());
-}
-
-TEST_F(ServiceImplTest, GetActionsDoesNotReloadClientToken) {
-  ClientContextProto client_context_proto;
-  client_context_proto.set_payments_client_token("token");
-  ON_CALL(*mock_client_context_, AsProto)
-      .WillByDefault(Return(client_context_proto));
-
-  EXPECT_CALL(*mock_client_context_, Update);
-  EXPECT_CALL(mock_client_, FetchPaymentsClientToken).Times(0);
-  EXPECT_CALL(*mock_client_context_, SetPaymentsClientToken).Times(0);
   EXPECT_CALL(*mock_request_sender_,
               OnSendRequest(GURL(kActionServerUrl), _, _, RpcType::GET_ACTIONS))
       .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
@@ -181,15 +152,30 @@
       mock_response_callback_.Get());
 }
 
-TEST_F(ServiceImplTest, GetUserData) {
+TEST_F(ServiceImplTest, GetUserDataWithPayments) {
+  EXPECT_CALL(mock_client_, FetchPaymentsClientToken)
+      .WillOnce(RunOnceCallback<0>("token"));
   EXPECT_CALL(*mock_request_sender_, OnSendRequest(GURL(kUserDataServerUrl), _,
                                                    _, RpcType::GET_USER_DATA))
       .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
   EXPECT_CALL(mock_response_callback_,
               Run(net::HTTP_OK, std::string("response")));
 
-  service_->GetUserData(CollectUserDataOptions(),
-                        mock_response_callback_.Get());
+  CollectUserDataOptions options;
+  options.request_payment_method = true;
+  service_->GetUserData(options, mock_response_callback_.Get());
+}
+
+TEST_F(ServiceImplTest, GetUserDataWithoutPayments) {
+  EXPECT_CALL(mock_client_, FetchPaymentsClientToken).Times(0);
+  EXPECT_CALL(*mock_request_sender_, OnSendRequest(GURL(kUserDataServerUrl), _,
+                                                   _, RpcType::GET_USER_DATA))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK, std::string("response")));
+  EXPECT_CALL(mock_response_callback_,
+              Run(net::HTTP_OK, std::string("response")));
+
+  CollectUserDataOptions options;
+  service_->GetUserData(options, mock_response_callback_.Get());
 }
 
 }  // namespace
diff --git a/components/cronet/cronet_url_request.cc b/components/cronet/cronet_url_request.cc
index cc0e2aad..43e05ed 100644
--- a/components/cronet/cronet_url_request.cc
+++ b/components/cronet/cronet_url_request.cc
@@ -13,6 +13,7 @@
 #include "build/build_config.h"
 #include "components/cronet/cronet_context.h"
 #include "net/base/idempotency.h"
+#include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_states.h"
 #include "net/base/net_errors.h"
diff --git a/components/security_interstitials/core/https_only_mode_ui_util.cc b/components/security_interstitials/core/https_only_mode_ui_util.cc
index a3ffc0e..c6b59a9 100644
--- a/components/security_interstitials/core/https_only_mode_ui_util.cc
+++ b/components/security_interstitials/core/https_only_mode_ui_util.cc
@@ -11,8 +11,6 @@
 
 void PopulateHttpsOnlyModeStringsForBlockingPage(base::Value* load_time_data,
                                                  const GURL& url) {
-  PopulateHttpsOnlyModeStringsForSharedHTML(load_time_data);
-
   load_time_data->SetStringKey(
       "tabTitle", l10n_util::GetStringUTF16(IDS_HTTPS_ONLY_MODE_TITLE));
   load_time_data->SetStringKey(
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index da7f83c..aabaa6c 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -156,19 +156,8 @@
     template = "//components/variations/android/java_templates/VariationsSwitches.java.tmpl"
   }
 
-  java_cpp_features("java_features_srcjar") {
-    # External code should depend on ":variations_java" instead.
-    visibility = [ ":*" ]
-    sources = [
-      "fake_crash.cc",
-      "variations_features.cc",
-    ]
-    template = "//components/variations/android/java_templates/VariationsFeatures.java.tmpl"
-  }
-
   android_library("variations_java") {
     srcjar_deps = [
-      ":java_features_srcjar",
       ":java_switches_srcjar",
       ":load_seed_result_enum_srcjar",
     ]
diff --git a/components/variations/android/java_templates/VariationsFeatures.java.tmpl b/components/variations/android/java_templates/VariationsFeatures.java.tmpl
deleted file mode 100644
index 9761ee5b..0000000
--- a/components/variations/android/java_templates/VariationsFeatures.java.tmpl
+++ /dev/null
@@ -1,16 +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.
-
-package org.chromium.components.variations;
-
-/**
- * Contains features that are specific to the variations component.
- */
-public final class VariationsFeatures {{
-
-{NATIVE_FEATURES}
-
-    // Prevents instantiation.
-    private VariationsFeatures() {{}}
-}}
diff --git a/components/viz/common/frame_sinks/blit_request.h b/components/viz/common/frame_sinks/blit_request.h
index 4aa8fb6..b236231c 100644
--- a/components/viz/common/frame_sinks/blit_request.h
+++ b/components/viz/common/frame_sinks/blit_request.h
@@ -10,7 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "base/cxx17_backports.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
 #include "components/viz/common/viz_common_export.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
@@ -81,7 +80,7 @@
   }
 
   const gpu::MailboxHolder& mailbox(size_t i) const {
-    CHECK(i < base::size(mailboxes_));
+    CHECK(i < std::size(mailboxes_));
     return mailboxes_[i];
   }
 
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetController.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetController.java
index 0c3a3d5..da03b510 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetController.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetController.java
@@ -4,7 +4,6 @@
 
 package org.chromium.components.webapps.bottomsheet;
 
-import android.app.Activity;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.util.Pair;
@@ -46,7 +45,7 @@
 @JNINamespace("webapps")
 public class PwaBottomSheetController
         implements UnownedUserData, AddToHomescreenViewDelegate, View.OnClickListener {
-    private final Activity mActivity;
+    private final Context mContext;
 
     /** A pointer to the native version of this class. It's lifetime is controlled by this class. */
     private long mNativePwaBottomSheetController;
@@ -158,10 +157,10 @@
 
     /**
      * Constructs a PwaBottomSheetController.
-     * @param activity The current activity.
+     * @param context The current context.
      */
-    public PwaBottomSheetController(Activity activity) {
-        mActivity = activity;
+    public PwaBottomSheetController(Context context) {
+        mContext = context;
     }
 
     // AddToHomescreenViewDelegate:
@@ -219,9 +218,9 @@
             return;
         }
 
-        mScreenshotAdapter = new ScreenshotsAdapter(mActivity);
+        mScreenshotAdapter = new ScreenshotsAdapter(mContext);
         PwaInstallBottomSheetView view =
-                new PwaInstallBottomSheetView(mActivity, mScreenshotAdapter);
+                new PwaInstallBottomSheetView(mContext, mScreenshotAdapter);
         mPwaBottomSheetContent = new PwaInstallBottomSheetContent(view, this);
         mModel = new PropertyModel.Builder(AddToHomescreenProperties.ALL_KEYS)
                          .with(AddToHomescreenProperties.ICON, new Pair<>(icon, isAdaptiveIcon))
diff --git a/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetControllerFactory.java b/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetControllerFactory.java
index f0837d5d..5b99edeb 100644
--- a/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetControllerFactory.java
+++ b/components/webapps/browser/android/java/src/org/chromium/components/webapps/bottomsheet/PwaBottomSheetControllerFactory.java
@@ -4,7 +4,7 @@
 
 package org.chromium.components.webapps.bottomsheet;
 
-import android.app.Activity;
+import android.content.Context;
 
 import org.chromium.ui.base.WindowAndroid;
 
@@ -12,8 +12,8 @@
  * A factory for producing a {@link PwaBottomSheetController}.
  */
 public class PwaBottomSheetControllerFactory {
-    public static PwaBottomSheetController createPwaBottomSheetController(Activity activity) {
-        return new PwaBottomSheetController(activity);
+    public static PwaBottomSheetController createPwaBottomSheetController(Context context) {
+        return new PwaBottomSheetController(context);
     }
 
     public static void attach(WindowAndroid windowAndroid, PwaBottomSheetController controller) {
diff --git a/components/webapps/browser/uninstall_result_code.h b/components/webapps/browser/uninstall_result_code.h
index 5b201c21..e130234 100644
--- a/components/webapps/browser/uninstall_result_code.h
+++ b/components/webapps/browser/uninstall_result_code.h
@@ -8,10 +8,11 @@
 namespace webapps {
 
 enum class UninstallResultCode {
-  kSuccess = 0,
-  kError = 1,
+  kSuccess,
+  kNoAppToUninstall,
+  kCancelled,
+  kError,
 };
-
 }
 
 #endif  // COMPONENTS_WEBAPPS_BROWSER_UNINSTALL_RESULT_CODE_H_
diff --git a/content/browser/attribution_reporting/attribution_internals_browsertest.cc b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
index 037a9a52..9c75e5d 100644
--- a/content/browser/attribution_reporting/attribution_internals_browsertest.cc
+++ b/content/browser/attribution_reporting/attribution_internals_browsertest.cc
@@ -402,49 +402,49 @@
                .SetPriority(13)
                .Build()}));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kPriorityTooLow,
+      AttributionTrigger::EventLevelResult::kPriorityTooLow,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(1))
           .SetPriority(11)
           .Build()));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kDroppedForNoise,
+      AttributionTrigger::EventLevelResult::kDroppedForNoise,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(2))
           .SetPriority(12)
           .Build()));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kExcessiveAttributions,
+      AttributionTrigger::EventLevelResult::kExcessiveAttributions,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(6))
           .SetPriority(-3)
           .Build()));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kExcessiveReportingOrigins,
+      AttributionTrigger::EventLevelResult::kExcessiveReportingOrigins,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(7))
           .SetPriority(-4)
           .Build()));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kDeduplicated,
+      AttributionTrigger::EventLevelResult::kDeduplicated,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(8))
           .SetPriority(-5)
           .Build()));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kNoCapacityForConversionDestination,
+      AttributionTrigger::EventLevelResult::kNoCapacityForConversionDestination,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(9))
           .SetPriority(-6)
           .Build()));
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kInternalError,
+      AttributionTrigger::EventLevelResult::kInternalError,
       ReportBuilder(
           AttributionInfoBuilder(SourceBuilder(now).BuildStored()).Build())
           .SetReportTime(now + base::Hours(10))
@@ -453,7 +453,8 @@
 
   // This shouldn't result in a row, as registration succeeded.
   manager_.NotifyTriggerHandled(CreateReportResult(
-      AttributionTrigger::Result::kSuccess, /*dropped_report=*/absl::nullopt,
+      AttributionTrigger::EventLevelResult::kSuccess,
+      /*dropped_report=*/absl::nullopt,
       /*dropped_report_source_deactivation_reason=*/absl::nullopt,
       /*new_report=*/
       ReportBuilder(
@@ -463,9 +464,9 @@
   // These shouldn't result in a row, as `CreateReportResult::dropped_report()`
   // is null.
   manager_.NotifyTriggerHandled(
-      CreateReportResult(AttributionTrigger::Result::kInternalError));
-  manager_.NotifyTriggerHandled(
-      CreateReportResult(AttributionTrigger::Result::kNoMatchingImpressions));
+      CreateReportResult(AttributionTrigger::EventLevelResult::kInternalError));
+  manager_.NotifyTriggerHandled(CreateReportResult(
+      AttributionTrigger::EventLevelResult::kNoMatchingImpressions));
 
   {
     static constexpr char wait_script[] = R"(
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index 78c61f5..14304131 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -292,40 +292,41 @@
     const CreateReportResult& result) {
   mojom::WebUIAttributionReport::Status status;
   switch (result.status()) {
-    case AttributionTrigger::Result::kSuccessDroppedLowerPriority:
-    case AttributionTrigger::Result::kPriorityTooLow:
+    case AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority:
+    case AttributionTrigger::EventLevelResult::kPriorityTooLow:
       status = mojom::WebUIAttributionReport::Status::kDroppedDueToLowPriority;
       break;
-    case AttributionTrigger::Result::kDroppedForNoise:
+    case AttributionTrigger::EventLevelResult::kDroppedForNoise:
       status = mojom::WebUIAttributionReport::Status::kDroppedForNoise;
       break;
-    case AttributionTrigger::Result::kExcessiveAttributions:
+    case AttributionTrigger::EventLevelResult::kExcessiveAttributions:
       status = mojom::WebUIAttributionReport::Status::
           kDroppedDueToExcessiveAttributions;
       break;
-    case AttributionTrigger::Result::kExcessiveReportingOrigins:
+    case AttributionTrigger::EventLevelResult::kExcessiveReportingOrigins:
       status = mojom::WebUIAttributionReport::Status::
           kDroppedDueToExcessiveReportingOrigins;
       break;
-    case AttributionTrigger::Result::kDeduplicated:
+    case AttributionTrigger::EventLevelResult::kDeduplicated:
       status = mojom::WebUIAttributionReport::Status::kDeduplicated;
       break;
-    case AttributionTrigger::Result::kNoCapacityForConversionDestination:
+    case AttributionTrigger::EventLevelResult::
+        kNoCapacityForConversionDestination:
       status = mojom::WebUIAttributionReport::Status::
           kNoReportCapacityForDestinationSite;
       break;
-    case AttributionTrigger::Result::kNoMatchingEventTriggers:
+    case AttributionTrigger::EventLevelResult::kNoMatchingEventTriggers:
       status = mojom::WebUIAttributionReport::Status::kNoMatchingEventTriggers;
       break;
-    case AttributionTrigger::Result::kInternalError:
+    case AttributionTrigger::EventLevelResult::kInternalError:
       // `kInternalError` doesn't always have a dropped report.
       if (!result.dropped_report().has_value())
         return;
 
       status = mojom::WebUIAttributionReport::Status::kInternalError;
       break;
-    case AttributionTrigger::Result::kSuccess:
-    case AttributionTrigger::Result::kNoMatchingImpressions:
+    case AttributionTrigger::EventLevelResult::kSuccess:
+    case AttributionTrigger::EventLevelResult::kNoMatchingImpressions:
       // TODO(apaseltiner): Surface `kNoMatchingImpressions` in internals UI.
       return;
   }
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc
index e3e7e65..6cbd647 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -86,7 +86,7 @@
   return false;
 }
 
-void RecordCreateReportStatus(AttributionTrigger::Result status) {
+void RecordCreateReportStatus(AttributionTrigger::EventLevelResult status) {
   base::UmaHistogramEnumeration("Conversions.CreateReportStatus", status);
 }
 
@@ -437,7 +437,7 @@
     MaybeSendDebugReport(std::move(*new_report));
   }
 
-  if (result.status() != AttributionTrigger::Result::kInternalError) {
+  if (result.status() != AttributionTrigger::EventLevelResult::kInternalError) {
     // Sources are changed here because storing a report can cause sources to be
     // deleted or become associated with a dedup key.
     NotifySourcesChanged();
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index 2d42c86d..65712e44 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -750,7 +750,7 @@
     InSequence seq;
 
     EXPECT_CALL(observer, OnTriggerHandled(CreateReportStatusIs(
-                              AttributionTrigger::Result::kSuccess)))
+                              AttributionTrigger::EventLevelResult::kSuccess)))
         .Times(3);
 
     EXPECT_CALL(checkpoint, Call(1));
@@ -759,8 +759,8 @@
         observer,
         OnTriggerHandled(AllOf(
             DroppedReportIs(Optional(EventLevelDataIs(TriggerPriorityIs(1)))),
-            CreateReportStatusIs(
-                AttributionTrigger::Result::kSuccessDroppedLowerPriority))));
+            CreateReportStatusIs(AttributionTrigger::EventLevelResult::
+                                     kSuccessDroppedLowerPriority))));
 
     EXPECT_CALL(checkpoint, Call(2));
 
@@ -769,7 +769,7 @@
         OnTriggerHandled(AllOf(
             DroppedReportIs(Optional(EventLevelDataIs(TriggerPriorityIs(-5)))),
             CreateReportStatusIs(
-                AttributionTrigger::Result::kPriorityTooLow))));
+                AttributionTrigger::EventLevelResult::kPriorityTooLow))));
 
     EXPECT_CALL(checkpoint, Call(3));
 
@@ -777,14 +777,14 @@
         observer,
         OnTriggerHandled(AllOf(
             DroppedReportIs(Optional(EventLevelDataIs(TriggerPriorityIs(2)))),
-            CreateReportStatusIs(
-                AttributionTrigger::Result::kSuccessDroppedLowerPriority))));
+            CreateReportStatusIs(AttributionTrigger::EventLevelResult::
+                                     kSuccessDroppedLowerPriority))));
     EXPECT_CALL(
         observer,
         OnTriggerHandled(AllOf(
             DroppedReportIs(Optional(EventLevelDataIs(TriggerPriorityIs(3)))),
-            CreateReportStatusIs(
-                AttributionTrigger::Result::kSuccessDroppedLowerPriority))));
+            CreateReportStatusIs(AttributionTrigger::EventLevelResult::
+                                     kSuccessDroppedLowerPriority))));
   }
 
   attribution_manager_->HandleSource(
@@ -1036,7 +1036,7 @@
   EXPECT_THAT(StoredReports(), IsEmpty());
   histograms.ExpectUniqueSample(
       "Conversions.CreateReportStatus",
-      AttributionTrigger::Result::kNoMatchingImpressions, 1);
+      AttributionTrigger::EventLevelResult::kNoMatchingImpressions, 1);
 }
 
 TEST_F(AttributionManagerImplTest, OnReportSent_NotifiesObservers) {
diff --git a/content/browser/attribution_reporting/attribution_observer_types.cc b/content/browser/attribution_reporting/attribution_observer_types.cc
index a329cc5..2cb5bba 100644
--- a/content/browser/attribution_reporting/attribution_observer_types.cc
+++ b/content/browser/attribution_reporting/attribution_observer_types.cc
@@ -12,7 +12,7 @@
 namespace content {
 
 CreateReportResult::CreateReportResult(
-    AttributionTrigger::Result status,
+    AttributionTrigger::EventLevelResult status,
     absl::optional<AttributionReport> dropped_report,
     absl::optional<DeactivatedSource::Reason>
         dropped_report_source_deactivation_reason,
@@ -22,19 +22,20 @@
       dropped_report_source_deactivation_reason_(
           dropped_report_source_deactivation_reason),
       new_report_(std::move(new_report)) {
-  DCHECK((status_ == AttributionTrigger::Result::kSuccess &&
+  DCHECK((status_ == AttributionTrigger::EventLevelResult::kSuccess &&
           !dropped_report_.has_value()) ||
-         status_ == AttributionTrigger::Result::kNoMatchingImpressions ||
-         status_ == AttributionTrigger::Result::kInternalError ||
+         status_ ==
+             AttributionTrigger::EventLevelResult::kNoMatchingImpressions ||
+         status_ == AttributionTrigger::EventLevelResult::kInternalError ||
          dropped_report_.has_value());
 
   DCHECK(dropped_report_.has_value() ||
          !dropped_report_source_deactivation_reason_);
 
-  DCHECK_EQ(
-      status_ == AttributionTrigger::Result::kSuccess ||
-          status_ == AttributionTrigger::Result::kSuccessDroppedLowerPriority,
-      new_report_.has_value());
+  DCHECK_EQ(status_ == AttributionTrigger::EventLevelResult::kSuccess ||
+                status_ == AttributionTrigger::EventLevelResult::
+                               kSuccessDroppedLowerPriority,
+            new_report_.has_value());
 }
 
 CreateReportResult::~CreateReportResult() = default;
diff --git a/content/browser/attribution_reporting/attribution_observer_types.h b/content/browser/attribution_reporting/attribution_observer_types.h
index 2b0c20d..d9f4600 100644
--- a/content/browser/attribution_reporting/attribution_observer_types.h
+++ b/content/browser/attribution_reporting/attribution_observer_types.h
@@ -35,7 +35,7 @@
 class CONTENT_EXPORT CreateReportResult {
  public:
   explicit CreateReportResult(
-      AttributionTrigger::Result status,
+      AttributionTrigger::EventLevelResult status,
       absl::optional<AttributionReport> dropped_report = absl::nullopt,
       absl::optional<DeactivatedSource::Reason>
           dropped_report_source_deactivation_reason = absl::nullopt,
@@ -48,7 +48,7 @@
   CreateReportResult& operator=(const CreateReportResult&);
   CreateReportResult& operator=(CreateReportResult&&);
 
-  AttributionTrigger::Result status() const { return status_; }
+  AttributionTrigger::EventLevelResult status() const { return status_; }
 
   const absl::optional<AttributionReport>& dropped_report() const {
     return dropped_report_;
@@ -63,10 +63,10 @@
   absl::optional<DeactivatedSource> GetDeactivatedSource() const;
 
  private:
-  AttributionTrigger::Result status_;
+  AttributionTrigger::EventLevelResult status_;
 
-  // `AttributionTrigger::Result::kInternalError` is only associated with a
-  // dropped report if the browser succeeded in running the
+  // `AttributionTrigger::EventLevelResult::kInternalError` is only associated
+  // with a dropped report if the browser succeeded in running the
   // source-to-attribute logic.
   absl::optional<AttributionReport> dropped_report_;
 
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index c940be9..d2db6ef 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -605,7 +605,7 @@
   // possible for there to be a matching source if there's no DB.
   if (!LazyInit(DbCreationPolicy::kIgnoreIfAbsent)) {
     return CreateReportResult(
-        AttributionTrigger::Result::kNoMatchingImpressions);
+        AttributionTrigger::EventLevelResult::kNoMatchingImpressions);
   }
 
   const net::SchemefulSite& conversion_destination =
@@ -640,8 +640,8 @@
   if (!matching_sources_statement.Step()) {
     return CreateReportResult(
         matching_sources_statement.Succeeded()
-            ? AttributionTrigger::Result::kNoMatchingImpressions
-            : AttributionTrigger::Result::kInternalError);
+            ? AttributionTrigger::EventLevelResult::kNoMatchingImpressions
+            : AttributionTrigger::EventLevelResult::kInternalError);
   }
 
   // The first one returned will be attributed; it has the highest priority.
@@ -656,14 +656,16 @@
   }
   // Exit early if the last statement wasn't valid.
   if (!matching_sources_statement.Succeeded()) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError);
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError);
   }
 
   absl::optional<StoredSourceData> source_to_attribute =
       ReadSourceToAttribute(db_.get(), source_id_to_attribute);
   // This is only possible if there is a corrupt DB.
   if (!source_to_attribute.has_value()) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError);
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError);
   }
 
   const CommonSourceInfo::SourceType source_type =
@@ -719,7 +721,7 @@
   // attribution.
   if (event_trigger == trigger.event_triggers().end()) {
     return CreateReportResult(
-        AttributionTrigger::Result::kNoMatchingEventTriggers,
+        AttributionTrigger::EventLevelResult::kNoMatchingEventTriggers,
         std::move(report));
   }
 
@@ -727,23 +729,26 @@
     case ReportAlreadyStoredStatus::kNotStored:
       break;
     case ReportAlreadyStoredStatus::kStored:
-      return CreateReportResult(AttributionTrigger::Result::kDeduplicated,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kDeduplicated,
+          std::move(report));
     case ReportAlreadyStoredStatus::kError:
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
   }
 
   switch (CapacityForStoringReport(serialized_conversion_destination)) {
     case ConversionCapacityStatus::kHasCapacity:
       break;
     case ConversionCapacityStatus::kNoCapacity:
-      return CreateReportResult(
-          AttributionTrigger::Result::kNoCapacityForConversionDestination,
-          std::move(report));
-    case ConversionCapacityStatus::kError:
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
+      return CreateReportResult(AttributionTrigger::EventLevelResult::
+                                    kNoCapacityForConversionDestination,
                                 std::move(report));
+    case ConversionCapacityStatus::kError:
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
   }
 
   const AttributionInfo& attribution_info = report.attribution_info();
@@ -754,11 +759,12 @@
       break;
     case RateLimitResult::kNotAllowed:
       return CreateReportResult(
-          AttributionTrigger::Result::kExcessiveAttributions,
+          AttributionTrigger::EventLevelResult::kExcessiveAttributions,
           std::move(report));
     case RateLimitResult::kError:
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
   }
 
   switch (rate_limit_table_.AttributionAllowedForReportingOriginLimit(
@@ -767,17 +773,19 @@
       break;
     case RateLimitResult::kNotAllowed:
       return CreateReportResult(
-          AttributionTrigger::Result::kExcessiveReportingOrigins,
+          AttributionTrigger::EventLevelResult::kExcessiveReportingOrigins,
           std::move(report));
     case RateLimitResult::kError:
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
   }
 
   sql::Transaction transaction(db_.get());
   if (!transaction.Begin()) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                              std::move(report));
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError,
+        std::move(report));
   }
 
   absl::optional<AttributionReport> replaced_report;
@@ -787,8 +795,9 @@
           replaced_report);
   if (maybe_replace_lower_priority_report_result ==
       MaybeReplaceLowerPriorityEventLevelReportResult::kError) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                              std::move(report));
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError,
+        std::move(report));
   }
 
   if (maybe_replace_lower_priority_report_result ==
@@ -797,11 +806,13 @@
           MaybeReplaceLowerPriorityEventLevelReportResult::
               kDropNewReportSourceDeactivated) {
     if (!transaction.Commit()) {
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
     }
     return CreateReportResult(
-        AttributionTrigger::Result::kPriorityTooLow, std::move(report),
+        AttributionTrigger::EventLevelResult::kPriorityTooLow,
+        std::move(report),
         maybe_replace_lower_priority_report_result ==
                 MaybeReplaceLowerPriorityEventLevelReportResult::
                     kDropNewReportSourceDeactivated
@@ -820,8 +831,9 @@
     if (!StoreReport(attribution_info.source.source_id(), trigger_data,
                      attribution_info.time, report.report_time(), priority,
                      report.external_report_id(), trigger.debug_key())) {
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
     }
   }
 
@@ -837,8 +849,9 @@
                                          *attribution_info.source.source_id());
     insert_dedup_key_statement.BindInt64(1, SerializeUint64(*dedup_key));
     if (!insert_dedup_key_statement.Run()) {
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
     }
   }
 
@@ -856,15 +869,17 @@
     impression_update_statement.BindInt64(0,
                                           *attribution_info.source.source_id());
     if (!impression_update_statement.Run()) {
-      return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                                std::move(report));
+      return CreateReportResult(
+          AttributionTrigger::EventLevelResult::kInternalError,
+          std::move(report));
     }
   }
 
   // Delete all unattributed sources.
   if (!DeleteSources(source_ids_to_delete)) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                              std::move(report));
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError,
+        std::move(report));
   }
 
   // Based on the deletion logic here and the fact that we delete sources
@@ -876,25 +891,28 @@
 
   if (create_report && !rate_limit_table_.AddRateLimitForAttribution(
                            db_.get(), attribution_info)) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                              std::move(report));
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError,
+        std::move(report));
   }
 
   if (!transaction.Commit()) {
-    return CreateReportResult(AttributionTrigger::Result::kInternalError,
-                              std::move(report));
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kInternalError,
+        std::move(report));
   }
 
   if (!create_report) {
-    return CreateReportResult(AttributionTrigger::Result::kDroppedForNoise,
-                              std::move(report));
+    return CreateReportResult(
+        AttributionTrigger::EventLevelResult::kDroppedForNoise,
+        std::move(report));
   }
 
   return CreateReportResult(
       maybe_replace_lower_priority_report_result ==
               MaybeReplaceLowerPriorityEventLevelReportResult::kReplaceOldReport
-          ? AttributionTrigger::Result::kSuccessDroppedLowerPriority
-          : AttributionTrigger::Result::kSuccess,
+          ? AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority
+          : AttributionTrigger::EventLevelResult::kSuccess,
       std::move(replaced_report),
       /*dropped_report_source_deactivation_reason=*/absl::nullopt,
       std::move(report));
diff --git a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
index ee3f6be..1faf75d 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql_unittest.cc
@@ -99,7 +99,7 @@
     EXPECT_EQ(expected, rows);
   }
 
-  AttributionTrigger::Result MaybeCreateAndStoreReport(
+  AttributionTrigger::EventLevelResult MaybeCreateAndStoreEventLevelReport(
       const AttributionTrigger& conversion) {
     return storage_->MaybeCreateAndStoreReport(conversion).status();
   }
@@ -233,16 +233,16 @@
   storage()->StoreSource(impression);
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Use a time range that targets all conversions.
   storage()->ClearData(
@@ -275,12 +275,12 @@
   storage()->StoreSource(impression);
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Use a time range that only intersects the last conversion.
   storage()->ClearData(
@@ -311,12 +311,12 @@
   storage()->StoreSource(impression);
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Use a time range that only intersects the last conversion.
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
@@ -347,11 +347,11 @@
     task_environment_.FastForwardBy(base::Days(1));
   }
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
   storage()->ClearData(base::Time::Min(), base::Time::Max(), null_filter);
@@ -570,8 +570,8 @@
   storage()->StoreSource(SourceBuilder().Build());
   storage()->StoreSource(SourceBuilder().Build());
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   CloseDatabase();
   sql::Database raw_db;
@@ -588,12 +588,13 @@
   OpenDatabase();
   delegate()->set_max_attributions_per_origin(2);
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
-  EXPECT_EQ(AttributionTrigger::Result::kNoCapacityForConversionDestination,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
+  EXPECT_EQ(
+      AttributionTrigger::EventLevelResult::kNoCapacityForConversionDestination,
+      MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   CloseDatabase();
   sql::Database raw_db;
@@ -626,8 +627,8 @@
 
   task_environment_.FastForwardBy(base::Days(1));
   EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(
+      AttributionTrigger::EventLevelResult::kSuccess,
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
               .SetConversionDestination(net::SchemefulSite(conversion_origin))
               .SetReportingOrigin(reporting_origin)
@@ -638,8 +639,8 @@
   // in a different window.
   delegate()->set_report_delay(base::Milliseconds(1));
   EXPECT_EQ(
-      AttributionTrigger::Result::kPriorityTooLow,
-      MaybeCreateAndStoreReport(
+      AttributionTrigger::EventLevelResult::kPriorityTooLow,
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
               .SetConversionDestination(net::SchemefulSite(conversion_origin))
               .SetReportingOrigin(reporting_origin)
@@ -684,8 +685,8 @@
 
   task_environment_.FastForwardBy(base::Days(1));
   EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(
+      AttributionTrigger::EventLevelResult::kSuccess,
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
               .SetConversionDestination(net::SchemefulSite(conversion_origin))
               .SetReportingOrigin(reporting_origin)
@@ -696,8 +697,8 @@
   // in a different window.
   delegate()->set_report_delay(base::Milliseconds(1));
   EXPECT_EQ(
-      AttributionTrigger::Result::kPriorityTooLow,
-      MaybeCreateAndStoreReport(
+      AttributionTrigger::EventLevelResult::kPriorityTooLow,
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
               .SetConversionDestination(net::SchemefulSite(conversion_origin))
               .SetReportingOrigin(reporting_origin)
@@ -734,7 +735,7 @@
 
   // These calls should be no-ops.
   storage->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
             storage->MaybeCreateAndStoreReport(DefaultTrigger()).status());
 }
 
@@ -748,7 +749,7 @@
 
   // The directory should be created, and the database opened.
   storage->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
             storage->MaybeCreateAndStoreReport(DefaultTrigger()).status());
 }
 
@@ -778,8 +779,8 @@
               ElementsAre(CommonSourceInfoIs(impression.common_info())));
 
   EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(
+      AttributionTrigger::EventLevelResult::kSuccess,
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
               .SetTriggerData(kMaxUint64)
               .SetConversionDestination(
@@ -840,8 +841,8 @@
 
   storage()->StoreSource(
       SourceBuilder().SetExpiry(base::Milliseconds(3)).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Milliseconds(3));
   // Store another impression to trigger the expiry logic.
@@ -877,8 +878,8 @@
 
   storage()->StoreSource(
       SourceBuilder().SetExpiry(base::Milliseconds(3)).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Milliseconds(3));
   // Advance past the default report time.
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index c926156d..ceccf14 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -88,8 +88,9 @@
 
   // Given a |conversion|, returns the expected conversion report properties at
   // the current timestamp.
-  AttributionReport GetExpectedReport(const StoredSource& source,
-                                      const AttributionTrigger& conversion) {
+  AttributionReport GetExpectedEventLevelReport(
+      const StoredSource& source,
+      const AttributionTrigger& conversion) {
     // TOO(apaseltiner): Replace this logic with explicit setting of expected
     // values.
     auto event_trigger = base::ranges::find(
@@ -106,7 +107,7 @@
         .Build();
   }
 
-  AttributionTrigger::Result MaybeCreateAndStoreReport(
+  AttributionTrigger::EventLevelResult MaybeCreateAndStoreEventLevelReport(
       const AttributionTrigger& conversion) {
     return storage_->MaybeCreateAndStoreReport(conversion).status();
   }
@@ -146,7 +147,7 @@
 
   // Test all public methods on AttributionStorage.
   EXPECT_NO_FATAL_FAILURE(storage->StoreSource(SourceBuilder().Build()));
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
             storage->MaybeCreateAndStoreReport(DefaultTrigger()).status());
   EXPECT_THAT(storage->GetAttributionReports(base::Time::Now()), IsEmpty());
   EXPECT_THAT(storage->GetActiveSources(), IsEmpty());
@@ -182,24 +183,25 @@
 
 TEST_F(AttributionStorageTest,
        GetWithNoMatchingImpressions_NoImpressionsReturned) {
-  EXPECT_THAT(storage()->MaybeCreateAndStoreReport(DefaultTrigger()),
-              AllOf(CreateReportStatusIs(
-                        AttributionTrigger::Result::kNoMatchingImpressions),
-                    NewReportIs(absl::nullopt)));
+  EXPECT_THAT(
+      storage()->MaybeCreateAndStoreReport(DefaultTrigger()),
+      AllOf(CreateReportStatusIs(
+                AttributionTrigger::EventLevelResult::kNoMatchingImpressions),
+            NewReportIs(absl::nullopt)));
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), IsEmpty());
 }
 
 TEST_F(AttributionStorageTest, GetWithMatchingImpression_ImpressionReturned) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest, MultipleImpressionsForConversion_OneConverts) {
   storage()->StoreSource(SourceBuilder().Build());
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest,
@@ -210,8 +212,8 @@
           .Build();
   storage()->StoreSource(impression);
   EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(
+      AttributionTrigger::EventLevelResult::kSuccess,
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder()
               .SetConversionDestination(
                   net::SchemefulSite(GURL("https://a.test")))
@@ -224,8 +226,8 @@
       SourceBuilder()
           .SetSourceType(CommonSourceInfo::SourceType::kEvent)
           .Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetEventSourceTriggerData(456).Build()));
 
   task_environment_.FastForwardBy(kReportDelay);
@@ -239,8 +241,8 @@
       SourceBuilder().SetExpiry(base::Milliseconds(2)).Build());
   task_environment_.FastForwardBy(base::Milliseconds(2));
 
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest, ImpressionExpired_ConversionsStoredPrior) {
@@ -249,13 +251,13 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(3));
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(base::Milliseconds(5));
 
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest,
@@ -263,14 +265,15 @@
   storage()->StoreSource(SourceBuilder().Build());
 
   for (int i = 0; i < kMaxConversions; i++) {
-    EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-              MaybeCreateAndStoreReport(DefaultTrigger()));
+    EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+              MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   }
 
   // No additional conversion reports should be created.
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(DefaultTrigger()),
-      AllOf(CreateReportStatusIs(AttributionTrigger::Result::kPriorityTooLow),
+      AllOf(CreateReportStatusIs(
+                AttributionTrigger::EventLevelResult::kPriorityTooLow),
             DroppedReportIs(IsTrue()), DeactivatedSourceIs(absl::nullopt)));
 }
 
@@ -278,11 +281,11 @@
   auto conversion = DefaultTrigger();
 
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   AttributionReport expected_report =
-      GetExpectedReport(SourceBuilder().BuildStored(), conversion);
+      GetExpectedEventLevelReport(SourceBuilder().BuildStored(), conversion);
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -297,8 +300,8 @@
                             url::Origin::Create(GURL("https://different.test")))
                         .Build();
   storage()->StoreSource(impression);
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -312,8 +315,8 @@
                             url::Origin::Create(GURL("https://different.test")))
                         .Build();
   storage()->StoreSource(impression);
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -322,8 +325,8 @@
 
 TEST_F(AttributionStorageTest, ConversionReportDeleted_RemovedFromStorage) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -345,14 +348,14 @@
   }
 
   for (int i = 0; i < kMaxConversions; i++) {
-    EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-              MaybeCreateAndStoreReport(DefaultTrigger()));
+    EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+              MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   }
 
   // No additional conversion reports should be created for any of the
   // impressions.
-  EXPECT_EQ(AttributionTrigger::Result::kPriorityTooLow,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kPriorityTooLow,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest,
@@ -368,8 +371,8 @@
   // The first impression should be active because even though
   // <reporting_origin, conversion_origin> matches, it has not converted yet.
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(2));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 }
 
@@ -380,8 +383,8 @@
 TEST_F(AttributionStorageTest,
        NewImpressionForConvertedImpression_MarkedInactive) {
   storage()->StoreSource(SourceBuilder().SetSourceEventId(0).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -395,10 +398,10 @@
 
   // Only the new impression should convert.
   auto conversion = DefaultTrigger();
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
   AttributionReport expected_report =
-      GetExpectedReport(builder.BuildStored(), conversion);
+      GetExpectedEventLevelReport(builder.BuildStored(), conversion);
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -413,8 +416,8 @@
   storage()->StoreSource(builder.Build());
 
   auto conversion = DefaultTrigger();
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -428,11 +431,11 @@
                              .Build());
 
   // The first impression should still be active and able to convert.
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   AttributionReport expected_report =
-      GetExpectedReport(builder.BuildStored(), conversion);
+      GetExpectedEventLevelReport(builder.BuildStored(), conversion);
 
   // Verify it was the first impression that converted.
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()),
@@ -456,9 +459,9 @@
   storage()->StoreSource(builder.Build());
 
   AttributionReport third_expected_conversion =
-      GetExpectedReport(builder.BuildStored(), conversion);
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+      GetExpectedEventLevelReport(builder.BuildStored(), conversion);
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -478,8 +481,8 @@
   task_environment_.FastForwardBy(base::Milliseconds(2));
   storage()->StoreSource(SourceBuilder().Build());
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Advance to the first impression's report time and verify only its report is
   // available.
@@ -492,8 +495,8 @@
 
 TEST_F(AttributionStorageTest, GetAttributionReportsMultipleTimes_SameResult) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   task_environment_.FastForwardBy(kReportDelay);
 
   std::vector<AttributionReport> first_call_reports =
@@ -568,13 +571,14 @@
   delegate()->set_max_attributions_per_origin(1);
   storage()->StoreSource(SourceBuilder().Build());
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   // Verify that MaxConversionsPerOrigin is enforced.
   auto result = storage()->MaybeCreateAndStoreReport(
       TriggerBuilder().SetTriggerData(5).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kNoCapacityForConversionDestination,
-            result.status());
+  EXPECT_EQ(
+      AttributionTrigger::EventLevelResult::kNoCapacityForConversionDestination,
+      result.status());
   EXPECT_THAT(result.dropped_report(),
               Optional(EventLevelDataIs(TriggerDataIs(5))));
 }
@@ -585,8 +589,8 @@
   storage()->StoreSource(impression);
   storage()->ClearData(
       now, now, GetMatcher(url::Origin::Create(GURL("https://no-match.com"))));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest, ClearDataOutsideRange_NoDelete) {
@@ -597,8 +601,8 @@
   storage()->ClearData(
       now + base::Minutes(10), now + base::Minutes(20),
       GetMatcher(impression.common_info().impression_origin()));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest, ClearDataImpression) {
@@ -610,8 +614,8 @@
     storage()->ClearData(
         now, now + base::Minutes(20),
         GetMatcher(impression.common_info().conversion_origin()));
-    EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
-              MaybeCreateAndStoreReport(DefaultTrigger()));
+    EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
+              MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   }
 }
 
@@ -621,8 +625,8 @@
   auto conversion = DefaultTrigger();
 
   storage()->StoreSource(impression);
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   storage()->ClearData(
       now - base::Minutes(20), now + base::Minutes(20),
@@ -651,8 +655,8 @@
   for (int i = 0; i < 5; i++) {
     auto origin =
         url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
-    EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-              MaybeCreateAndStoreReport(
+    EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+              MaybeCreateAndStoreEventLevelReport(
                   TriggerBuilder()
                       .SetConversionDestination(net::SchemefulSite(origin))
                       .SetReportingOrigin(origin)
@@ -662,8 +666,8 @@
   for (int i = 5; i < 10; i++) {
     auto origin =
         url::Origin::Create(GURL(base::StringPrintf("https://%d.com/", i)));
-    EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-              MaybeCreateAndStoreReport(
+    EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+              MaybeCreateAndStoreEventLevelReport(
                   TriggerBuilder()
                       .SetConversionDestination(net::SchemefulSite(origin))
                       .SetReportingOrigin(origin)
@@ -682,8 +686,8 @@
 
   storage()->StoreSource(impression);
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
   storage()->ClearData(
       base::Time::Now(), base::Time::Now(),
       GetMatcher(impression.common_info().impression_origin()));
@@ -706,10 +710,10 @@
   task_environment_.FastForwardBy(base::Days(1));
 
   const AttributionReport expected_report =
-      GetExpectedReport(builder.BuildStored(), conversion);
+      GetExpectedEventLevelReport(builder.BuildStored(), conversion);
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   storage()->ClearData(
       start + base::Minutes(1), start + base::Minutes(10),
@@ -732,8 +736,8 @@
   storage()->StoreSource(impression2);
   storage()->StoreSource(impression3);
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Only the first impression should overlap with this time range, but all the
   // impressions should share the origin.
@@ -751,11 +755,11 @@
     task_environment_.FastForwardBy(base::Days(1));
   }
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
   storage()->ClearData(base::Time::Min(), base::Time::Max(), null_filter);
@@ -774,11 +778,11 @@
     task_environment_.FastForwardBy(base::Days(1));
   }
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   task_environment_.FastForwardBy(base::Days(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   auto null_filter = base::RepeatingCallback<bool(const url::Origin&)>();
   storage()->ClearData(base::Time(), base::Time::Max(), null_filter);
@@ -799,17 +803,18 @@
   auto conversion = DefaultTrigger();
 
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
-  EXPECT_THAT(storage()->MaybeCreateAndStoreReport(conversion),
-              AllOf(CreateReportStatusIs(
-                        AttributionTrigger::Result::kExcessiveAttributions),
-                    DroppedReportIs(IsTrue())));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
+  EXPECT_THAT(
+      storage()->MaybeCreateAndStoreReport(conversion),
+      AllOf(CreateReportStatusIs(
+                AttributionTrigger::EventLevelResult::kExcessiveAttributions),
+            DroppedReportIs(IsTrue())));
 
   const AttributionReport expected_report =
-      GetExpectedReport(SourceBuilder().BuildStored(), conversion);
+      GetExpectedEventLevelReport(SourceBuilder().BuildStored(), conversion);
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()),
               ElementsAre(expected_report, expected_report));
@@ -829,16 +834,16 @@
       SourceBuilder()
           .SetSourceType(CommonSourceInfo::SourceType::kNavigation)
           .Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   storage()->StoreSource(
       SourceBuilder()
           .SetSourceType(CommonSourceInfo::SourceType::kEvent)
           .Build());
   // This would fail if the source types had separate limits.
-  EXPECT_EQ(AttributionTrigger::Result::kExcessiveAttributions,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kExcessiveAttributions,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 }
 
 TEST_F(AttributionStorageTest, NeverAttributeImpression_ReportNotStored) {
@@ -849,8 +854,8 @@
   storage()->StoreSource(SourceBuilder().Build());
   delegate()->set_randomized_response(absl::nullopt);
 
-  EXPECT_EQ(AttributionTrigger::Result::kDroppedForNoise,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDroppedForNoise,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -865,14 +870,14 @@
   storage()->StoreSource(SourceBuilder().SetSourceEventId(3).Build());
   delegate()->set_randomized_response(absl::nullopt);
 
-  EXPECT_EQ(AttributionTrigger::Result::kDroppedForNoise,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDroppedForNoise,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   storage()->StoreSource(SourceBuilder().SetSourceEventId(5).Build());
 
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetTriggerData(7).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetTriggerData(7).Build()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -896,21 +901,21 @@
   delegate()->set_randomized_response(absl::nullopt);
 
   const auto conversion = DefaultTrigger();
-  EXPECT_EQ(AttributionTrigger::Result::kDroppedForNoise,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDroppedForNoise,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   SourceBuilder builder;
   builder.SetSourceEventId(7);
   storage()->StoreSource(builder.Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   storage()->StoreSource(SourceBuilder().SetSourceEventId(9).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kExcessiveAttributions,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kExcessiveAttributions,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   const AttributionReport expected_report =
-      GetExpectedReport(builder.BuildStored(), conversion);
+      GetExpectedEventLevelReport(builder.BuildStored(), conversion);
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -930,10 +935,10 @@
   delegate()->set_randomized_response(absl::nullopt);
 
   const auto conversion = DefaultTrigger();
-  EXPECT_EQ(AttributionTrigger::Result::kDroppedForNoise,
-            MaybeCreateAndStoreReport(conversion));
-  EXPECT_EQ(AttributionTrigger::Result::kDroppedForNoise,
-            MaybeCreateAndStoreReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDroppedForNoise,
+            MaybeCreateAndStoreEventLevelReport(conversion));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDroppedForNoise,
+            MaybeCreateAndStoreEventLevelReport(conversion));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -1032,15 +1037,15 @@
           .SetConversionDestination(net::SchemefulSite(origin_a))
           .Build();
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(trigger));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(trigger));
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));
 
   // Force the impression to be deactivated by ensuring that the next report is
   // in a different window.
   delegate()->set_report_delay(kReportDelay + base::Milliseconds(1));
-  EXPECT_EQ(AttributionTrigger::Result::kPriorityTooLow,
-            MaybeCreateAndStoreReport(trigger));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kPriorityTooLow,
+            MaybeCreateAndStoreEventLevelReport(trigger));
   EXPECT_THAT(storage()->GetActiveSources(), IsEmpty());
 
   delegate()->set_max_destinations_per_source_site_reporting_origin(1);
@@ -1065,8 +1070,8 @@
   storage()->StoreSource(SourceBuilder().SetSourceEventId(5).Build());
 
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(3));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -1088,8 +1093,8 @@
       SourceBuilder().SetPriority(200).SetSourceEventId(7).Build());
 
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(3));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -1104,8 +1109,8 @@
       SourceBuilder().SetSourceEventId(5).SetPriority(1).Build());
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(2));
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Because the impression with data 5 has the highest priority, it is selected
   // for attribution. The unselected impression with data 3 should be
@@ -1149,8 +1154,8 @@
 
   // The falsely attributed impression should not be eligible for further
   // attribution.
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingImpressions,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingImpressions,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()),
               ElementsAre(expected_report));
@@ -1192,7 +1197,8 @@
 
   EXPECT_THAT(storage()->MaybeCreateAndStoreReport(
                   TriggerBuilder().SetPriority(0).SetTriggerData(20).Build()),
-              AllOf(CreateReportStatusIs(AttributionTrigger::Result::kSuccess),
+              AllOf(CreateReportStatusIs(
+                        AttributionTrigger::EventLevelResult::kSuccess),
                     DroppedReportIs(absl::nullopt)));
 
   // This conversion should replace the one above because it has a higher
@@ -1200,22 +1206,23 @@
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(
           TriggerBuilder().SetPriority(2).SetTriggerData(21).Build()),
-      AllOf(CreateReportStatusIs(
-                AttributionTrigger::Result::kSuccessDroppedLowerPriority),
+      AllOf(CreateReportStatusIs(AttributionTrigger::EventLevelResult::
+                                     kSuccessDroppedLowerPriority),
             DroppedReportIs(Optional(EventLevelDataIs(TriggerDataIs(20u))))));
 
   storage()->StoreSource(
       SourceBuilder().SetSourceEventId(7).SetPriority(2).Build());
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetPriority(1).SetTriggerData(22).Build()));
   // This conversion should be dropped because it has a lower priority than the
   // one above.
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(
           TriggerBuilder().SetPriority(0).SetTriggerData(23).Build()),
-      AllOf(CreateReportStatusIs(AttributionTrigger::Result::kPriorityTooLow),
+      AllOf(CreateReportStatusIs(
+                AttributionTrigger::EventLevelResult::kPriorityTooLow),
             DroppedReportIs(Optional(EventLevelDataIs(TriggerDataIs(23u))))));
 
   task_environment_.FastForwardBy(kReportDelay);
@@ -1233,15 +1240,16 @@
   storage()->StoreSource(SourceBuilder().Build());
 
   int i = 0;
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetPriority(i).SetTriggerData(i).Build()));
   i++;
 
   for (; i < 10; i++) {
-    EXPECT_EQ(AttributionTrigger::Result::kSuccessDroppedLowerPriority,
-              MaybeCreateAndStoreReport(
-                  TriggerBuilder().SetPriority(i).SetTriggerData(i).Build()));
+    EXPECT_EQ(
+        AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority,
+        MaybeCreateAndStoreEventLevelReport(
+            TriggerBuilder().SetPriority(i).SetTriggerData(i).Build()));
   }
 
   task_environment_.FastForwardBy(kReportDelay);
@@ -1255,27 +1263,27 @@
 
   storage()->StoreSource(SourceBuilder().Build());
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetPriority(1).SetTriggerData(3).Build()));
 
   task_environment_.FastForwardBy(base::Milliseconds(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetPriority(1).SetTriggerData(2).Build()));
 
   // This report should not be stored, as even though it has the same priority
   // as the previous two, it is the most recent.
   task_environment_.FastForwardBy(base::Milliseconds(1));
-  EXPECT_EQ(AttributionTrigger::Result::kPriorityTooLow,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kPriorityTooLow,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetPriority(1).SetTriggerData(8).Build()));
 
   // This report should be stored by replacing the one with `trigger_data ==
   // 2`, which is the most recent of the two with `priority == 1`.
   task_environment_.FastForwardBy(base::Milliseconds(1));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccessDroppedLowerPriority,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetPriority(2).SetTriggerData(5).Build()));
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()),
@@ -1292,8 +1300,8 @@
       SourceBuilder().SetSourceEventId(5).SetPriority(1).Build());
   EXPECT_THAT(storage()->GetActiveSources(), SizeIs(2));
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   // Because the impression with data 5 has the highest priority, it is selected
   // for attribution. The unselected impression with data 3 should be
@@ -1305,8 +1313,9 @@
 
   // This conversion should not be stored because all reports for the attributed
   // impression were in an earlier window.
-  EXPECT_EQ(AttributionTrigger::Result::kPriorityTooLow,
-            MaybeCreateAndStoreReport(TriggerBuilder().SetPriority(2).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kPriorityTooLow,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetPriority(2).Build()));
 
   // As a result, the impression with data 5 should also be deactivated.
   EXPECT_THAT(storage()->GetActiveSources(), IsEmpty());
@@ -1326,8 +1335,8 @@
   EXPECT_THAT(storage()->GetActiveSources(),
               ElementsAre(DedupKeysAre(IsEmpty()), DedupKeysAre(IsEmpty())));
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetConversionDestination(net::SchemefulSite(
                         url::Origin::Create(GURL("https://a.example"))))
@@ -1337,8 +1346,8 @@
 
   // Should be stored because dedup key doesn't match even though conversion
   // destination does.
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetConversionDestination(net::SchemefulSite(
                         url::Origin::Create(GURL("https://a.example"))))
@@ -1348,8 +1357,8 @@
 
   // Should be stored because conversion destination doesn't match even though
   // dedup key does.
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetConversionDestination(net::SchemefulSite(
                         url::Origin::Create(GURL("https://b.example"))))
@@ -1365,13 +1374,14 @@
           .SetDedupKey(11)
           .SetTriggerData(74)
           .Build());
-  EXPECT_EQ(AttributionTrigger::Result::kDeduplicated, result.status());
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDeduplicated,
+            result.status());
   EXPECT_THAT(result.dropped_report(),
               Optional(EventLevelDataIs(TriggerDataIs(74))));
 
   // Shouldn't be stored because conversion destination and dedup key match.
-  EXPECT_EQ(AttributionTrigger::Result::kDeduplicated,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDeduplicated,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetConversionDestination(net::SchemefulSite(
                         url::Origin::Create(GURL("https://b.example"))))
@@ -1400,8 +1410,8 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(1));
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetConversionDestination(net::SchemefulSite(
                         url::Origin::Create(GURL("https://a.example"))))
@@ -1422,8 +1432,8 @@
 
   // This report shouldn't be stored, as it should be deduped against the
   // previously stored one even though that previous one is no longer in the DB.
-  EXPECT_EQ(AttributionTrigger::Result::kDeduplicated,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kDeduplicated,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder()
                     .SetConversionDestination(net::SchemefulSite(
                         url::Origin::Create(GURL("https://a.example"))))
@@ -1437,9 +1447,9 @@
 
 TEST_F(AttributionStorageTest, GetAttributionReports_SetsPriority) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetPriority(13).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetPriority(13).Build()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -1465,8 +1475,8 @@
 
 TEST_F(AttributionStorageTest, NoIDReuse_Conversion) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   auto reports = storage()->GetAttributionReports(base::Time::Max());
   EXPECT_THAT(reports,
               ElementsAre(Property(&AttributionReport::ReportId, IsTrue())));
@@ -1477,8 +1487,8 @@
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()), IsEmpty());
 
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   reports = storage()->GetAttributionReports(base::Time::Max());
   EXPECT_THAT(reports,
               ElementsAre(Property(&AttributionReport::ReportId, IsTrue())));
@@ -1489,8 +1499,8 @@
 
 TEST_F(AttributionStorageTest, UpdateReportForSendFailure) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -1523,9 +1533,9 @@
   task_environment_.FastForwardBy(kReportDelay);
 
   // Set a dedup key to ensure that the return deactivated source contains it.
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetDedupKey(13).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetDedupKey(13).Build()));
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(1));
 
   SourceBuilder builder2;
@@ -1556,10 +1566,10 @@
 
   task_environment_.FastForwardBy(kReportDelay);
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()), SizeIs(2));
 
   // 2 sources are deactivated, but only 1 should be returned.
@@ -1577,7 +1587,7 @@
 }
 
 TEST_F(AttributionStorageTest,
-       MaybeCreateAndStoreReport_ReturnsDeactivatedSources) {
+       MaybeCreateAndStoreEventLevelReport_ReturnsDeactivatedSources) {
   SourceBuilder builder;
   builder.SetSourceEventId(7);
   EXPECT_THAT(storage()->StoreSource(builder.Build()).deactivated_sources,
@@ -1586,8 +1596,8 @@
 
   // Store the maximum number of reports for the source.
   for (size_t i = 1; i <= kMaxConversions; i++) {
-    EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-              MaybeCreateAndStoreReport(DefaultTrigger()));
+    EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+              MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
   }
 
   task_environment_.FastForwardBy(kReportDelay);
@@ -1602,7 +1612,8 @@
   // conversions per source.
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(DefaultTrigger()),
-      AllOf(CreateReportStatusIs(AttributionTrigger::Result::kPriorityTooLow),
+      AllOf(CreateReportStatusIs(
+                AttributionTrigger::EventLevelResult::kPriorityTooLow),
             DroppedReportIs(Optional(ReportSourceIs(builder.BuildStored()))),
             DeactivatedSourceIs(DeactivatedSource(
                 builder.BuildStored(),
@@ -1611,8 +1622,8 @@
 
 TEST_F(AttributionStorageTest, ReportID_RoundTrips) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   task_environment_.FastForwardBy(kReportDelay);
 
@@ -1631,8 +1642,8 @@
   EXPECT_EQ(storage()->AdjustOfflineReportTimes(), absl::nullopt);
 
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   const base::Time original_report_time = base::Time::Now() + kReportDelay;
 
@@ -1680,8 +1691,8 @@
           .min = base::Hours(1), .max = base::Hours(3)});
 
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   const base::Time original_report_time = base::Time::Now() + kReportDelay;
 
@@ -1719,8 +1730,8 @@
   EXPECT_EQ(storage()->GetNextReportTime(base::Time::Min()), absl::nullopt);
 
   storage()->StoreSource(SourceBuilder().SetReportingOrigin(origin_a).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetReportingOrigin(origin_a).Build()));
 
   const base::Time report_time_a = base::Time::Now() + kReportDelay;
@@ -1730,8 +1741,8 @@
 
   task_environment_.FastForwardBy(base::Milliseconds(1));
   storage()->StoreSource(SourceBuilder().SetReportingOrigin(origin_b).Build());
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
                 TriggerBuilder().SetReportingOrigin(origin_b).Build()));
 
   const base::Time report_time_b = base::Time::Now() + kReportDelay;
@@ -1743,15 +1754,15 @@
 
 TEST_F(AttributionStorageTest, GetAttributionReports_Shuffles) {
   storage()->StoreSource(SourceBuilder().Build());
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetTriggerData(3).Build()));
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetTriggerData(1).Build()));
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetTriggerData(2).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetTriggerData(3).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetTriggerData(1).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetTriggerData(2).Build()));
 
   EXPECT_THAT(storage()->GetAttributionReports(
                   /*max_report_time=*/base::Time::Max(), /*limit=*/-1),
@@ -1777,9 +1788,9 @@
 TEST_F(AttributionStorageTest, TriggerDebugKey_RoundTrips) {
   storage()->StoreSource(
       SourceBuilder(base::Time::Now()).SetDebugKey(22).Build());
-  EXPECT_EQ(
-      AttributionTrigger::Result::kSuccess,
-      MaybeCreateAndStoreReport(TriggerBuilder().SetDebugKey(33).Build()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(
+                TriggerBuilder().SetDebugKey(33).Build()));
 
   task_environment_.FastForwardBy(kReportDelay);
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Now()),
@@ -1809,8 +1820,9 @@
   EXPECT_THAT(
       storage()->MaybeCreateAndStoreReport(
           TriggerBuilder().SetTriggerData(123).Build()),
-      AllOf(CreateReportStatusIs(AttributionTrigger::Result::kSuccess),
-            NewReportIs(Optional(EventLevelDataIs(TriggerDataIs(123))))));
+      AllOf(
+          CreateReportStatusIs(AttributionTrigger::EventLevelResult::kSuccess),
+          NewReportIs(Optional(EventLevelDataIs(TriggerDataIs(123))))));
 }
 
 // This is tested more thoroughly by the `RateLimitTable` unit tests. Here just
@@ -1869,19 +1881,19 @@
   ASSERT_THAT(storage()->GetActiveSources(), SizeIs(3));
 
   ASSERT_EQ(
-      MaybeCreateAndStoreReport(
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder().SetReportingOrigin(origin1).SetDebugKey(1).Build()),
-      AttributionTrigger::Result::kSuccess);
+      AttributionTrigger::EventLevelResult::kSuccess);
 
   ASSERT_EQ(
-      MaybeCreateAndStoreReport(
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder().SetReportingOrigin(origin2).SetDebugKey(2).Build()),
-      AttributionTrigger::Result::kSuccess);
+      AttributionTrigger::EventLevelResult::kSuccess);
 
   ASSERT_EQ(
-      MaybeCreateAndStoreReport(
+      MaybeCreateAndStoreEventLevelReport(
           TriggerBuilder().SetReportingOrigin(origin3).SetDebugKey(3).Build()),
-      AttributionTrigger::Result::kExcessiveReportingOrigins);
+      AttributionTrigger::EventLevelResult::kExcessiveReportingOrigins);
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()),
               ElementsAre(TriggerDebugKeyIs(1), TriggerDebugKeyIs(2)));
@@ -2012,7 +2024,7 @@
           .SetReportingOrigin(origin1)
           .SetSourceType(CommonSourceInfo::SourceType::kNavigation)
           .Build());
-  MaybeCreateAndStoreReport(
+  MaybeCreateAndStoreEventLevelReport(
       TriggerBuilder().SetReportingOrigin(origin1).Build());
 
   storage()->StoreSource(
@@ -2020,7 +2032,7 @@
           .SetReportingOrigin(origin2)
           .SetSourceType(CommonSourceInfo::SourceType::kEvent)
           .Build());
-  MaybeCreateAndStoreReport(
+  MaybeCreateAndStoreEventLevelReport(
       TriggerBuilder().SetReportingOrigin(origin2).Build());
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()),
@@ -2106,8 +2118,8 @@
 
   storage()->StoreSource(SourceBuilder().Build());
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(DefaultTrigger()));
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(DefaultTrigger()));
 
   base::Time now = base::Time::Now();
 
@@ -2151,7 +2163,7 @@
           .SetReportingOrigin(origin1)
           .SetSourceType(CommonSourceInfo::SourceType::kNavigation)
           .Build());
-  MaybeCreateAndStoreReport(
+  MaybeCreateAndStoreEventLevelReport(
       TriggerBuilder().SetReportingOrigin(origin1).SetTriggerData(6).Build());
 
   storage()->StoreSource(
@@ -2159,10 +2171,10 @@
           .SetReportingOrigin(origin2)
           .SetSourceType(CommonSourceInfo::SourceType::kEvent)
           .Build());
-  MaybeCreateAndStoreReport(TriggerBuilder()
-                                .SetReportingOrigin(origin2)
-                                .SetEventSourceTriggerData(4)
-                                .Build());
+  MaybeCreateAndStoreEventLevelReport(TriggerBuilder()
+                                          .SetReportingOrigin(origin2)
+                                          .SetEventSourceTriggerData(4)
+                                          .Build());
 
   EXPECT_THAT(storage()->GetAttributionReports(base::Time::Max()),
               UnorderedElementsAre(
@@ -2199,8 +2211,8 @@
           .SetReportingOrigin(origin)
           .Build());
 
-  EXPECT_EQ(AttributionTrigger::Result::kNoMatchingEventTriggers,
-            MaybeCreateAndStoreReport(AttributionTrigger(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kNoMatchingEventTriggers,
+            MaybeCreateAndStoreEventLevelReport(AttributionTrigger(
                 net::SchemefulSite(origin), origin,
                 /*debug_key=*/absl::nullopt,
                 {AttributionTrigger::EventTriggerData(
@@ -2209,8 +2221,8 @@
                     /*dedup_key=*/absl::nullopt,
                     CommonSourceInfo::SourceType::kEvent)})));
 
-  EXPECT_EQ(AttributionTrigger::Result::kSuccess,
-            MaybeCreateAndStoreReport(AttributionTrigger(
+  EXPECT_EQ(AttributionTrigger::EventLevelResult::kSuccess,
+            MaybeCreateAndStoreEventLevelReport(AttributionTrigger(
                 net::SchemefulSite(origin), origin,
                 /*debug_key=*/absl::nullopt,
                 {AttributionTrigger::EventTriggerData(
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index 7e05c73d..627f9f4 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -797,39 +797,41 @@
   return tie(a) == tie(b);
 }
 
-std::ostream& operator<<(std::ostream& out, AttributionTrigger::Result status) {
+std::ostream& operator<<(std::ostream& out,
+                         AttributionTrigger::EventLevelResult status) {
   switch (status) {
-    case AttributionTrigger::Result::kSuccess:
+    case AttributionTrigger::EventLevelResult::kSuccess:
       out << "success";
       break;
-    case AttributionTrigger::Result::kSuccessDroppedLowerPriority:
+    case AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority:
       out << "successDroppedLowerPriority";
       break;
-    case AttributionTrigger::Result::kInternalError:
+    case AttributionTrigger::EventLevelResult::kInternalError:
       out << "internalError";
       break;
-    case AttributionTrigger::Result::kNoCapacityForConversionDestination:
+    case AttributionTrigger::EventLevelResult::
+        kNoCapacityForConversionDestination:
       out << "insufficientDestinationCapacity";
       break;
-    case AttributionTrigger::Result::kNoMatchingImpressions:
+    case AttributionTrigger::EventLevelResult::kNoMatchingImpressions:
       out << "noMatchingSources";
       break;
-    case AttributionTrigger::Result::kDeduplicated:
+    case AttributionTrigger::EventLevelResult::kDeduplicated:
       out << "deduplicated";
       break;
-    case AttributionTrigger::Result::kExcessiveAttributions:
+    case AttributionTrigger::EventLevelResult::kExcessiveAttributions:
       out << "excessiveAttributions";
       break;
-    case AttributionTrigger::Result::kPriorityTooLow:
+    case AttributionTrigger::EventLevelResult::kPriorityTooLow:
       out << "priorityTooLow";
       break;
-    case AttributionTrigger::Result::kDroppedForNoise:
+    case AttributionTrigger::EventLevelResult::kDroppedForNoise:
       out << "noised";
       break;
-    case AttributionTrigger::Result::kExcessiveReportingOrigins:
+    case AttributionTrigger::EventLevelResult::kExcessiveReportingOrigins:
       out << "excessiveReportingOrigins";
       break;
-    case AttributionTrigger::Result::kNoMatchingEventTriggers:
+    case AttributionTrigger::EventLevelResult::kNoMatchingEventTriggers:
       out << "noMatchingEventTriggers";
       break;
   }
diff --git a/content/browser/attribution_reporting/attribution_test_utils.h b/content/browser/attribution_reporting/attribution_test_utils.h
index eed06001..598dd2a 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.h
+++ b/content/browser/attribution_reporting/attribution_test_utils.h
@@ -571,7 +571,8 @@
 
 bool operator==(const DeactivatedSource& a, const DeactivatedSource& b);
 
-std::ostream& operator<<(std::ostream& out, AttributionTrigger::Result status);
+std::ostream& operator<<(std::ostream& out,
+                         AttributionTrigger::EventLevelResult status);
 
 std::ostream& operator<<(std::ostream& out, DeactivatedSource::Reason reason);
 
diff --git a/content/browser/attribution_reporting/attribution_trigger.h b/content/browser/attribution_reporting/attribution_trigger.h
index a75e9c1..f2cd37a 100644
--- a/content/browser/attribution_reporting/attribution_trigger.h
+++ b/content/browser/attribution_reporting/attribution_trigger.h
@@ -21,11 +21,12 @@
 // the renderer and is now being used by the browser process.
 class CONTENT_EXPORT AttributionTrigger {
  public:
-  // Represents the potential outcomes from attempting to register a trigger.
+  // Represents the potential event-level outcomes from attempting to register
+  // a trigger.
   //
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
-  enum class Result {
+  enum class EventLevelResult {
     kSuccess = 0,
     // The report was stored successfully, but it replaced an existing report
     // with a lower priority.
diff --git a/content/browser/compositor/image_transport_factory_browsertest.cc b/content/browser/compositor/image_transport_factory_browsertest.cc
index 71843f4..4b72a455 100644
--- a/content/browser/compositor/image_transport_factory_browsertest.cc
+++ b/content/browser/compositor/image_transport_factory_browsertest.cc
@@ -9,7 +9,6 @@
 #include "build/chromeos_buildflags.h"
 #include "components/viz/common/gpu/context_lost_observer.h"
 #include "components/viz/common/gpu/context_provider.h"
-#include "content/browser/gpu/browser_gpu_channel_host_factory.h"
 #include "content/browser/gpu/gpu_data_manager_impl.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_browser_test.h"
@@ -64,12 +63,6 @@
   run_loop.Run();
 
   context_provider->RemoveObserver(&observer);
-
-  // Close the channel to the GPU process. This is needed because the GPU
-  // channel is down by the time that the network service is flushed, but
-  // flushing the network service tries to bring it back up again and there are
-  // pending requests causing a DCHECK to hit.
-  BrowserGpuChannelHostFactory::instance()->CloseChannel();
 }
 
 }  // anonymous namespace
diff --git a/content/browser/gpu/browser_gpu_channel_host_factory.h b/content/browser/gpu/browser_gpu_channel_host_factory.h
index 04106e1..cfca6f4 100644
--- a/content/browser/gpu/browser_gpu_channel_host_factory.h
+++ b/content/browser/gpu/browser_gpu_channel_host_factory.h
@@ -16,7 +16,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
-#include "content/common/content_export.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "ipc/message_filter.h"
 
@@ -30,9 +29,7 @@
  public:
   static void Initialize(bool establish_gpu_channel);
   static void Terminate();
-  CONTENT_EXPORT static BrowserGpuChannelHostFactory* instance() {
-    return instance_;
-  }
+  static BrowserGpuChannelHostFactory* instance() { return instance_; }
 
   BrowserGpuChannelHostFactory(const BrowserGpuChannelHostFactory&) = delete;
   BrowserGpuChannelHostFactory& operator=(const BrowserGpuChannelHostFactory&) =
@@ -48,7 +45,7 @@
 
   // Closes the channel to the GPU process. This should be called before the IO
   // thread stops.
-  CONTENT_EXPORT void CloseChannel();
+  void CloseChannel();
 
   // Notify the BrowserGpuChannelHostFactory of visibility, used to prevent
   // timeouts while backgrounded.
@@ -87,9 +84,7 @@
 
   base::OneShotTimer timeout_;
 
-  // instance() might be inlined at a call site so instance_ must also be
-  // exported.
-  CONTENT_EXPORT static BrowserGpuChannelHostFactory* instance_;
+  static BrowserGpuChannelHostFactory* instance_;
 };
 
 }  // namespace content
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index d24e7a2..ba07d9ae 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -25,6 +25,7 @@
 #include "build/build_config.h"
 #include "content/browser/browser_url_handler_impl.h"
 #include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/network_service_instance_impl.h"
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/content_navigation_policy.h"
@@ -4520,6 +4521,14 @@
     EXPECT_EQ(target_frame->GetSiteInstance(),
               initiator_frame->GetSiteInstance());
 
+    // Start monitoring NetworkService for crashes.
+    //
+    // TODO(https://crbug.com/1169431): This should be part of BrowserTestBase.
+    // (with optional opt-out for things like NetworkServiceRestartBrowserTest).
+    bool did_network_service_crash = false;
+    base::CallbackListSubscription crash_monitoring_subscription =
+        RegisterNetworkServiceCrashHandler(base::BindLambdaForTesting(
+            [&]() { did_network_service_crash = true; }));
     // Ask for cookies in the `target_frame`.  One implicit verification here
     // is whether this step will hit any `cookie_url`-related NOTREACHED or DwoC
     // in RestrictedCookieManager::ValidateAccessToCookiesAt.  This verification
@@ -4528,6 +4537,21 @@
     // ignores possible Blink-side caching, but this is the first time the
     // renderer needs the cookies and so this is okay for this test).
     EXPECT_EQ("", EvalJs(target_frame, "document.cookie"));
+    // |network_context| might receive an error notification, but it's not
+    // guaranteed to have arrived at this point. Flush the remote to make sure
+    // the notification has been received.
+    //
+    // We flush via `initiator_frame`, because in the current set of tests, the
+    // `initiator_frame` always has a mojo connection to the NetworkService via
+    // the `network_service_disconnect_handler_holder_mojo` field of
+    // RenderFrameHostImpl.  (This is not true for the `target_frame` in tests
+    // where that frame uses the process-wide URLLoaderFactory fallback rather
+    // than creating a URLLoaderFactory via RenderFrameHostImpl.)
+    //
+    // TODO(https://crbug.com/1169431): This should be part of BrowserTestBase.
+    if (!IsInProcessNetworkService())
+      initiator_frame->FlushNetworkAndNavigationInterfacesForTesting();
+    EXPECT_FALSE(did_network_service_crash);
 
     // Verify that the "about:blank" frame is able to load an image.
     VerifyImageSubresourceLoads(target_frame);
diff --git a/content/browser/network_service_browsertest.cc b/content/browser/network_service_browsertest.cc
index d36f59d..6517a2a 100644
--- a/content/browser/network_service_browsertest.cc
+++ b/content/browser/network_service_browsertest.cc
@@ -12,7 +12,6 @@
 #include "base/json/values_util.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
@@ -468,15 +467,19 @@
   if (IsInProcessNetworkService())
     return;
 
+  mojo::PendingRemote<network::mojom::NetworkServiceTest>
+      pending_network_service_test;
+  GetNetworkService()->BindTestInterface(
+      pending_network_service_test.InitWithNewPipeAndPassReceiver());
+
   net::EmbeddedTestServer http_server;
   http_server.AddDefaultHandlers(GetTestDataFilePath());
   http_server.RegisterRequestMonitor(base::BindLambdaForTesting(
       [&](const net::test_server::HttpRequest& request) {
         if (request.relative_url == "/hung") {
-          base::PostTask(
-              FROM_HERE, {BrowserThread::UI},
-              base::BindOnce(&BrowserTestBase::SimulateNetworkServiceCrash,
-                             base::Unretained(this)));
+          mojo::Remote<network::mojom::NetworkServiceTest> network_service_test(
+              std::move(pending_network_service_test));
+          network_service_test->SimulateCrash();
         }
       }));
   EXPECT_TRUE(http_server.Start());
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 8ff5cd9..065dae6 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -1114,7 +1114,6 @@
       network_service_test.BindNewPipeAndPassReceiver());
 
   // Crash the network service, but do not wait for full startup.
-  IgnoreNetworkServiceCrashes();
   network_service_test.set_disconnect_handler(run_loop.QuitClosure());
   network_service_test->SimulateCrash();
   run_loop.Run();
diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS
index 05fb123..594e3f0 100644
--- a/content/browser/renderer_host/OWNERS
+++ b/content/browser/renderer_host/OWNERS
@@ -42,7 +42,6 @@
 per-file *clipboard_host_impl*=dcheng@chromium.org
 
 # AgentSchedulingGroup (MBI)
-per-file agent_scheduling_group*=talp@chromium.org
 per-file agent_scheduling_group*=dom@chromium.org
 per-file agent_scheduling_group*=kouhei@chromium.org
 per-file agent_scheduling_group*=haraken@chromium.org
diff --git a/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc b/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc
index fe12ec9..8eb2316 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc
+++ b/content/browser/renderer_host/back_forward_cache_metrics_browsertest.cc
@@ -110,7 +110,7 @@
             base::BindLambdaForTesting(
                 [&run_loop, feature](
                     blink::scheduler::WebSchedulerTrackedFeatures features) {
-                  if (features.Has(feature) && run_loop.running())
+                  if (features.Has(feature))
                     run_loop.Quit();
                 }));
     EXPECT_TRUE(NavigateToURL(shell(), url));
@@ -120,10 +120,6 @@
                   current_frame_host()->GetBackForwardCacheDisablingFeatures(),
                   kFeaturesToIgnore),
               blink::scheduler::WebSchedulerTrackedFeatures(feature));
-
-    // Close the web contents to ensure that no new notifications arrive to the
-    // function local callback above after this function has returned.
-    web_contents()->Close();
   }
 
   std::vector<int64_t> navigation_ids_;
diff --git a/content/public/renderer/content_renderer_client.cc b/content/public/renderer/content_renderer_client.cc
index fac0b275..5bd1b99 100644
--- a/content/public/renderer/content_renderer_client.cc
+++ b/content/public/renderer/content_renderer_client.cc
@@ -177,7 +177,15 @@
 
 bool ContentRendererClient::IsSupportedBitstreamAudioCodec(
     media::AudioCodec codec) {
-  return false;
+  switch (codec) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS) && BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
+    case media::AudioCodec::kDTS:
+    case media::AudioCodec::kDTSXP2:
+      return true;
+#endif
+    default:
+      return false;
+  }
 }
 
 bool ContentRendererClient::ShouldReportDetailedMessageForSource(
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 9bc49c1..5e9059a6 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -739,14 +739,6 @@
 void BrowserTestBase::SimulateNetworkServiceCrash() {
   CHECK(!IsInProcessNetworkService())
       << "Can't crash the network service if it's running in-process!";
-
-  // Check if any unexpected crashes have occurred *before* the expected crash
-  // that we will trigger/simulate below.
-  AssertThatNetworkServiceDidNotCrash();
-
-  // `network_service_test_` field might not be ready yet - some tests call
-  // SimulateNetworkServiceCrash from SetUpOnMainThread, before
-  // InitializeNetworkProcess has been called.
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
       network_service_test.BindNewPipeAndPassReceiver());
@@ -766,10 +758,6 @@
   InitializeNetworkProcess();
 }
 
-void BrowserTestBase::IgnoreNetworkServiceCrashes() {
-  network_service_test_.reset();
-}
-
 #if BUILDFLAG(IS_ANDROID)
 void BrowserTestBase::WaitUntilJavaIsReady(
     base::OnceClosure quit_closure,
@@ -892,7 +880,6 @@
       RunTestOnMainThread();
     }
     TearDownOnMainThread();
-    AssertThatNetworkServiceDidNotCrash();
   }
 
   PostRunTestOnMainThread();
@@ -969,26 +956,6 @@
   initial_web_contents_ = web_contents;
 }
 
-void BrowserTestBase::AssertThatNetworkServiceDidNotCrash() {
-  if (!IsOutOfProcessNetworkService()) {
-    return;
-  }
-
-  // TODO(https://crbug.com/1169431#c2): Enable NetworkService crash detection
-  // on Fuchsia.
-#if !BUILDFLAG(IS_FUCHSIA)
-  if (network_service_test_.is_bound()) {
-    // If there was a crash, then |network_service_test_| will receive an error
-    // notification, but it's not guaranteed to have arrived at this point.
-    // Flush the remote to make sure the notification has been received.
-    network_service_test_.FlushForTesting();
-
-    EXPECT_TRUE(network_service_test_.is_connected())
-        << "Expecting no NetworkService crashes";
-  }
-#endif
-}
-
 void BrowserTestBase::InitializeNetworkProcess() {
   if (initialized_network_process_)
     return;
@@ -1013,26 +980,26 @@
     return;
   }
 
-  network_service_test_.reset();
+  mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
-      network_service_test_.BindNewPipeAndPassReceiver());
+      network_service_test.BindNewPipeAndPassReceiver());
 
   // Do not set up host resolver rules if we allow the test to access
   // the network.
   if (allow_network_access_to_host_resolutions_) {
     mojo::ScopedAllowSyncCallForTesting allow_sync_call;
-    network_service_test_->SetAllowNetworkAccessToHostResolutions();
+    network_service_test->SetAllowNetworkAccessToHostResolutions();
     return;
   }
 
   if (replace_system_dns_config_) {
     mojo::ScopedAllowSyncCallForTesting allow_sync_call;
-    network_service_test_->ReplaceSystemDnsConfig();
+    network_service_test->ReplaceSystemDnsConfig();
   }
 
   if (test_doh_config_.has_value()) {
     mojo::ScopedAllowSyncCallForTesting allow_sync_call;
-    network_service_test_->SetTestDohConfig(*test_doh_config_);
+    network_service_test->SetTestDohConfig(*test_doh_config_);
   }
 
   std::vector<network::mojom::RulePtr> mojo_rules;
@@ -1099,7 +1066,7 @@
   // to dispatch a Java callback that makes network process to enter native
   // code.
   base::RunLoop loop{base::RunLoop::Type::kNestableTasksAllowed};
-  network_service_test_->AddRules(std::move(mojo_rules), loop.QuitClosure());
+  network_service_test->AddRules(std::move(mojo_rules), loop.QuitClosure());
   loop.Run();
 }
 
diff --git a/content/public/test/browser_test_base.h b/content/public/test/browser_test_base.h
index 36b9b67..27b0d47 100644
--- a/content/public/test/browser_test_base.h
+++ b/content/public/test/browser_test_base.h
@@ -16,10 +16,8 @@
 #include "build/chromeos_buildflags.h"
 #include "content/public/test/no_renderer_crashes_assertion.h"
 #include "content/public/test/test_host_resolver.h"
-#include "mojo/public/cpp/bindings/remote.h"
 #include "net/dns/public/dns_over_https_config.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/network/public/mojom/network_service_test.mojom.h"
 #include "storage/browser/quota/quota_settings.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -77,14 +75,6 @@
   // racily start before the rules have been re-applied.
   void SimulateNetworkServiceCrash();
 
-  // Ignores all future NetworkService crashes that would be otherwise detected
-  // and flagged by the AssertThatNetworkServiceDidNotCrash method.
-  //
-  // The IgnoreNetworkServiceCrashes method is useful in a test that plans to
-  // trigger crashes. Note that calling IgnoreNetworkServiceCrashes is *not*
-  // needed when triggering the crash via SimulateNetworkServiceCrash method.
-  void IgnoreNetworkServiceCrashes();
-
   // Returns the host resolver being used for the tests. Subclasses might want
   // to configure it inside tests.
   net::RuleBasedHostResolverProc* host_resolver() {
@@ -108,10 +98,6 @@
   // PreEarlyInitialization() has been called.
   virtual void CreatedBrowserMainParts(BrowserMainParts* browser_main_parts) {}
 
-  // GTest assertions that the connection to `network_service_test_` did not get
-  // dropped unexpectedly.
-  void AssertThatNetworkServiceDidNotCrash();
-
   // Sets flag to allow host resolutions to reach the network. Must be called
   // before Setup() to take effect.
   void SetAllowNetworkAccessToHostResolutions();
@@ -257,8 +243,6 @@
 
   std::unique_ptr<NoRendererCrashesAssertion> no_renderer_crashes_assertion_;
 
-  mojo::Remote<network::mojom::NetworkServiceTest> network_service_test_;
-
   bool initialized_network_process_ = false;
 
   bool allow_network_access_to_host_resolutions_ = false;
diff --git a/content/renderer/OWNERS b/content/renderer/OWNERS
index d525eb2..7fbf60b 100644
--- a/content/renderer/OWNERS
+++ b/content/renderer/OWNERS
@@ -27,6 +27,5 @@
 per-file browser_exposed_renderer_interfaces.cc=file://ipc/SECURITY_OWNERS
 
 # AgentSchedulingGroup (MBI)
-per-file agent_scheduling_group*=talp@chromium.org
 per-file agent_scheduling_group*=dom@chromium.org
 per-file agent_scheduling_group*=kouhei@chromium.org
diff --git a/content/test/attribution_simulator_impl.cc b/content/test/attribution_simulator_impl.cc
index 72432d95..74cfbcd 100644
--- a/content/test/attribution_simulator_impl.cc
+++ b/content/test/attribution_simulator_impl.cc
@@ -224,20 +224,21 @@
 
     std::stringstream reason;
     switch (result.status()) {
-      case AttributionTrigger::Result::kSuccess:
-      case AttributionTrigger::Result::kSuccessDroppedLowerPriority:
+      case AttributionTrigger::EventLevelResult::kSuccess:
+      case AttributionTrigger::EventLevelResult::kSuccessDroppedLowerPriority:
         // TODO(apaseltiner): Consider surfacing reports dropped due to
         // prioritization.
         return;
-      case AttributionTrigger::Result::kInternalError:
-      case AttributionTrigger::Result::kNoCapacityForConversionDestination:
-      case AttributionTrigger::Result::kNoMatchingImpressions:
-      case AttributionTrigger::Result::kDeduplicated:
-      case AttributionTrigger::Result::kExcessiveAttributions:
-      case AttributionTrigger::Result::kPriorityTooLow:
-      case AttributionTrigger::Result::kDroppedForNoise:
-      case AttributionTrigger::Result::kExcessiveReportingOrigins:
-      case AttributionTrigger::Result::kNoMatchingEventTriggers:
+      case AttributionTrigger::EventLevelResult::kInternalError:
+      case AttributionTrigger::EventLevelResult::
+          kNoCapacityForConversionDestination:
+      case AttributionTrigger::EventLevelResult::kNoMatchingImpressions:
+      case AttributionTrigger::EventLevelResult::kDeduplicated:
+      case AttributionTrigger::EventLevelResult::kExcessiveAttributions:
+      case AttributionTrigger::EventLevelResult::kPriorityTooLow:
+      case AttributionTrigger::EventLevelResult::kDroppedForNoise:
+      case AttributionTrigger::EventLevelResult::kExcessiveReportingOrigins:
+      case AttributionTrigger::EventLevelResult::kNoMatchingEventTriggers:
         reason << result.status();
         break;
     }
diff --git a/dbus/bus.h b/dbus/bus.h
index 6095ccef..f172016 100644
--- a/dbus/bus.h
+++ b/dbus/bus.h
@@ -36,7 +36,7 @@
 //
 // For asynchronous operations such as an asynchronous method call, the
 // bus object will use a task runner to monitor the underlying file
-// descriptor used for D-Bus communication. By default, the bus will usegi
+// descriptor used for D-Bus communication. By default, the bus will use
 // the current thread's task runner. If |dbus_task_runner| option is
 // specified, the bus will use that task runner instead.
 //
@@ -346,7 +346,7 @@
                                           const ObjectPath& object_path);
 
   // Unregisters the object manager for the given remote object path
-  // |object_path| exported by the srevice |service_name|.
+  // |object_path| exported by the service |service_name|.
   //
   // Getting an object manager for the same remote object after this call
   // will return a new object, method calls on any remaining copies of the
@@ -602,7 +602,7 @@
       const std::string& service_name,
       const ServiceOwnerChangeCallback& callback);
 
-  // Return the unique name of the bus connnection if it is connected to
+  // Return the unique name of the bus connection if it is connected to
   // D-BUS. Otherwise, return an empty string.
   std::string GetConnectionName();
 
@@ -780,7 +780,7 @@
   bool shutdown_completed_;
 
   // Counters to make sure that OnAddWatch()/OnRemoveWatch() and
-  // OnAddTimeout()/OnRemoveTimeou() are balanced.
+  // OnAddTimeout()/OnRemoveTimeout() are balanced.
   int num_pending_watches_;
   int num_pending_timeouts_;
 
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 49780a30..6439731 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -197,10 +197,7 @@
       "bluetooth_remote_gatt_service_android.cc",
       "bluetooth_remote_gatt_service_android.h",
     ]
-    deps += [
-      ":jni_headers",
-      "//services/device/public/cpp:device_feature_list",
-    ]
+    deps += [ ":jni_headers" ]
   }
 
   if (is_chromeos_ash || is_chromeos_lacros) {
@@ -623,7 +620,6 @@
     deps = [
       "//base:base_java",
       "//components/location/android:location_java",
-      "//services/device/public/java:device_feature_list_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
     ]
     annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java
index 3cd422c..89aa2bc 100644
--- a/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java
+++ b/device/bluetooth/android/java/src/org/chromium/device/bluetooth/ChromeBluetoothDevice.java
@@ -15,7 +15,6 @@
 import org.chromium.base.annotations.JNIAdditionalImport;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.device.DeviceFeatureList;
 
 import java.util.HashMap;
 
@@ -135,9 +134,7 @@
                     if (newState == android.bluetooth.BluetoothProfile.STATE_CONNECTED) {
                         // Try requesting for a larger ATT MTU so that more information can be
                         // exchanged per transmission.
-                        if (!DeviceFeatureList.isEnabled(
-                                    DeviceFeatureList.WEB_BLUETOOTH_REQUEST_LARGER_MTU)
-                                || !mBluetoothGatt.requestMtu(517)) {
+                        if (!mBluetoothGatt.requestMtu(517)) {
                             mBluetoothGatt.discoverServices();
                         }
                     } else if (newState == android.bluetooth.BluetoothProfile.STATE_DISCONNECTED) {
diff --git a/docs/webui_explainer.md b/docs/webui_explainer.md
index ddbed2ef..7487560e 100644
--- a/docs/webui_explainer.md
+++ b/docs/webui_explainer.md
@@ -379,7 +379,7 @@
 <div class="note">
 Data sources are not recreated on refresh, and therefore values that are dynamic
 (i.e. that can change while Chrome is running) may easily become stale. It may
-be preferable to use <code>cr.sendWithPromise()</code> to initialize dynamic
+be preferable to use <code>sendWithPromise()</code> to initialize dynamic
 values and call <code>FireWebUIListener()</code> to update them.
 
 If you really want or need to use <code>AddBoolean()</code> for a dynamic value,
@@ -513,7 +513,7 @@
 * [`FireWebUIListener()`](#FireWebUIListener) allows easily notifying the page
   when an event occurs in C++ and is more loosely coupled (nothing blows up if
   the event dispatch is ignored). JS subscribes to notifications via
-  [`cr.addWebUIListener`](#cr_addWebUIListener).
+  [`addWebUIListener`](#addWebUIListener).
 * [`ResolveJavascriptCallback`](#ResolveJavascriptCallback) and
   [`RejectJavascriptCallback`](#RejectJavascriptCallback) are useful
   when Javascript requires a response to an inquiry about C++-canonical state
@@ -529,7 +529,7 @@
 Here's some example to detect a change to Chrome's theme:
 
 ```js
-cr.addWebUIListener("theme-changed", refreshThemeStyles);
+addWebUIListener("theme-changed", refreshThemeStyles);
 ```
 
 This Javascript event listener can be triggered in C++ via:
@@ -544,7 +544,7 @@
 theme they'll choose, this is a good candidate for an event listener.
 
 If you simply need to get a response in Javascript from C++, consider using
-[`cr.sendWithPromise()`](#cr_sendWithPromise) and
+[`sendWithPromise()`](#sendWithPromise) and
 [`ResolveJavascriptCallback`](#ResolveJavascriptCallback).
 
 ### WebUIMessageHandler::OnJavascriptAllowed()
@@ -626,7 +626,7 @@
 ### WebUIMessageHandler::RejectJavascriptCallback()
 
 This method is called in response to
-[`cr.sendWithPromise()`](#cr_sendWithPromise) to reject the issued Promise. This
+[`sendWithPromise()`](#sendWithPromise) to reject the issued Promise. This
 runs the rejection (second) callback in the [Promise's
 executor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
 and any
@@ -657,16 +657,16 @@
 ### WebUIMessageHandler::ResolveJavascriptCallback()
 
 This method is called in response to
-[`cr.sendWithPromise()`](#cr_sendWithPromise) to fulfill an issued Promise,
+[`sendWithPromise()`](#sendWithPromise) to fulfill an issued Promise,
 often with a value. This results in runnings any fulfillment (first) callbacks
 in the associate Promise executor and any registered
 [`then()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then)
 callbacks.
 
-So, given this JS code:
+So, given this TypeScript code:
 
 ```js
-cr.sendWithPromise('bakeDonuts').then(function(numDonutsBaked) {
+sendWithPromise('bakeDonuts').then(function(numDonutsBaked: number) {
   shop.donuts += numDonutsBaked;
 });
 ```
@@ -734,7 +734,7 @@
 message_callbacks_.find(message)->second.Run(&args);
 ```
 
-### cr.addWebUIListener()
+### addWebUIListener()
 
 WebUI listeners are a convenient way for C++ to inform JavaScript of events.
 
@@ -745,7 +745,9 @@
 code.
 
 Adding WebUI listeners creates and inserts a unique ID into a map in JavaScript,
-just like [cr.sendWithPromise()](#cr_sendWithPromise).
+just like [sendWithPromise()](#sendWithPromise).
+
+addWebUIListener can be imported from 'chrome://resources/js/cr.m.js'.
 
 ```js
 // addWebUIListener():
@@ -774,18 +776,18 @@
 }
 ```
 
-JavaScript can listen for WebUI events via:
+TypeScript can listen for WebUI events via:
 
 ```js
-var donutsReady = 0;
-cr.addWebUIListener('donuts-baked', function(numFreshlyBakedDonuts) {
+let donutsReady: number = 0;
+addWebUIListener('donuts-baked', function(numFreshlyBakedDonuts: number) {
   donutsReady += numFreshlyBakedDonuts;
 });
 ```
 
-### cr.sendWithPromise()
+### sendWithPromise()
 
-`cr.sendWithPromise()` is a wrapper around `chrome.send()`. It's used when
+`sendWithPromise()` is a wrapper around `chrome.send()`. It's used when
 triggering a message requires a response:
 
 ```js
@@ -799,16 +801,18 @@
 In newer WebUI pages, you see code like this:
 
 ```js
-cr.sendWithPromise('getNumberOfDonuts').then(function(numDonuts) {
+sendWithPromise('getNumberOfDonuts').then(function(numDonuts: number) {
   alert('Yay, there are ' + numDonuts + ' delicious donuts left!');
 });
 ```
 
+Note that sendWithPromise can be imported from 'chrome://resources/js/cr.m.js';
+
 On the C++ side, the message registration is similar to
 [`chrome.send()`](#chrome_send) except that the first argument in the
 message handler's list is a callback ID. That ID is passed to
 `ResolveJavascriptCallback()`, which ends up resolving the `Promise` in
-JavaScript and calling the `then()` function.
+JavaScript/TypeScript and calling the `then()` function.
 
 ```c++
 void DonutHandler::HandleGetNumberOfDonuts(const base::ListValue* args) {
@@ -826,7 +830,7 @@
 insert a `Promise` into the JS-side map when created.
 
 ```js
-// cr.sendWithPromise():
+// sendWithPromise():
 var id = methodName + '_' + uidCounter++;
 chromeSendResolverMap[id] = new PromiseResolver;
 chrome.send(methodName, [id].concat(args));
diff --git a/docs/webui_in_chrome.md b/docs/webui_in_chrome.md
index 336f59c5..b65e8868 100644
--- a/docs/webui_in_chrome.md
+++ b/docs/webui_in_chrome.md
@@ -42,21 +42,21 @@
 <div id="example-div">[[message_]]</div>
 ```
 
-`chrome/browser/resources/hello_world/hello_world.js`
+`chrome/browser/resources/hello_world/hello_world.ts`
 ```js
 import './strings.m.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {getTemplate} from './hello_world.html.js';
 
-/** @polymer */
 export class HelloWorldElement extends PolymerElement {
   static get is() {
     return 'hello-world';
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
@@ -67,70 +67,91 @@
       },
     };
   }
+
+  private message_: string;
 }
 
 customElements.define(HelloWorldElement.is, HelloWorldElement);
 ```
 
-Add a `BUILD.gn` file to get Javascript type checking and Polymer compilation:
+Add a 'tsconfig_base.json' file to configure TypeScript options. Typical options
+needed by Polymer UIs include noUncheckedIndexAccess, noUnusedLocals, and
+strictPropertyInitialization, all set to false.
+
+`chrome/browser/resources/hello_world/tsconfig_base.gn`
+```
+{
+  "extends": "../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "noUncheckedIndexedAccess": false,
+    "noUnusedLocals": false,
+    "strictPropertyInitialization": false
+  }
+}
+```
+
+Add a `BUILD.gn` file to get TypeScript compilation and to generate the JS file
+from which the template will be imported.
 
 `chrome/browser/resources/hello_world/BUILD.gn`
 ```
-import("//third_party/closure_compiler/compile_js.gni")
-import("//tools/polymer/html_to_js.gni")
+import("//tools/polymer/html_to_wrapper.gni")
+import("//tools/grit/preprocess_if_expr.gni")
+import("//tools/typescript/ts_library.gni")
 
-html_to_js("web_components") {
-  js_files = [ "hello_world.js" ]
+html_to_wrapper("html_wrapper_files") {
+  in_files = [ "hello_world.html" ]
 }
 
-js_library("hello_world") {
+# Move everything to one folder using preprocess_if_expr.
+preprocess_folder = "preprocessed"
+
+preprocess_if_expr("preprocess_generated") {
+  # This file is generated by html_to_wrapper().
+  in_files = [ "hello_world.html.ts" ]
+  in_folder = target_gen_dir
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  deps = [ ":html_wrapper_files" ]
+}
+
+preprocess_if_expr("preprocess") {
+  in_files = [ "hello_world.ts" ]
+  in_folder = "."
+  out_folder = "$target_gen_dir/$preprocess_folder"
+}
+
+ts_library("build_ts") {
+  root_dir = "$target_gen_dir/$preprocess_folder"
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  in_files = [
+    "hello_world.ts",
+    "hello_world.html.ts"
+  ]
   deps = [
-    "//ui/webui/resources/js:load_time_data.m",
-    "//ui/webui/resources/js:util.m",
+    "//third_party/polymer/v3_0:library,"
+    "//ui/webui/resources:library",
+  ]
+  extra_deps = [
+    ":preprocess",
+    ":preprocess_generated",
   ]
 }
-
-js_type_check("closure_compile") {
-  deps = [ ":hello_world" ]
-}
-```
-
-Add the new `:closure_compile` target to `chrome/browser/resources/BUILD.gn` to
-include it in coverage:
-
-```
-group("closure_compile) {
-  deps = [
-    ...
-    "hello_world:closure_compile"
-    ...
-  ]
 ```
 
 Finally, create an `OWNERS` file for the new folder.
 
 ## Adding the resources
 Resources for the browser are stored in `grd` files.  Current best practice is to autogenerate a grd file for your
-component in the `BUILD` file we created earlier
+component in the `BUILD` file we created earlier. See new content below:
 
-`chrome/browser/resources/hello_world/BUILD.gn`
+`chrome/browser/resources/hello_world/BUILD.gn new additions`
 ```
 import("//tools/grit/grit_rule.gni")
-import("//tools/grit/preprocess_if_expr.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
 
-preprocess_folder = "preprocessed"
-preprocess_gen_manifest = "preprocessed_gen_manifest.json"
 resources_grd_file = "$target_gen_dir/resources.grd"
 
-preprocess_if_expr("preprocess_generated") {
-  deps = [ ":web_components" ]
-  in_folder = target_gen_dir
-  out_folder = "$target_gen_dir/$preprocess_folder"
-  out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
-  in_files = [ "hello_world.js" ]
-}
-
 generate_grd("build_grd") {
   grd_prefix = "hello_world"
   out_grd = resources_grd_file
@@ -139,8 +160,8 @@
     "hello_world_container.html",
   ]
   input_files_base_dir = rebase_path(".", "//")
-  deps = [ ":preprocess_generated" ]
-  manifest_files = [ "$target_gen_dir/$preprocess_gen_manifest" ]
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/$tsconfig.manifest" ]
 }
 
 grit("resources") {
diff --git a/ios/chrome/browser/download/ar_quick_look_tab_helper.mm b/ios/chrome/browser/download/ar_quick_look_tab_helper.mm
index 93c6280..eb73336 100644
--- a/ios/chrome/browser/download/ar_quick_look_tab_helper.mm
+++ b/ios/chrome/browser/download/ar_quick_look_tab_helper.mm
@@ -140,11 +140,9 @@
 
   // Sets fragment component as |search_url|'s query so QueryIterator can check
   // if content scaling is not allowed (allowsContentScaling = 0)
-  url::Replacements<char> replacement;
+  GURL::Replacements replacement;
   GURL search_url = download_task_->GetOriginalUrl();
-  std::string ref = search_url.ref();
-  url::Component query_component(0, ref.length());
-  replacement.SetQuery(ref.c_str(), query_component);
+  replacement.SetQueryStr(search_url.ref_piece());
   search_url = search_url.ReplaceComponents(replacement);
 
   bool allow_content_scaling = true;
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index 98569e6..4445cbff3 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -56,6 +56,7 @@
     "//ios/chrome/browser/screen_time:buildflags",
     "//ios/chrome/browser/sessions:features",
     "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/autofill:features",
     "//ios/chrome/browser/ui/content_suggestions:feature_flags",
     "//ios/chrome/browser/ui/default_promo:utils",
     "//ios/chrome/browser/ui/download:features",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 21ac9ec3..06ab8154 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -67,6 +67,7 @@
 #include "ios/chrome/browser/screen_time/screen_time_buildflags.h"
 #import "ios/chrome/browser/sessions/session_features.h"
 #include "ios/chrome/browser/system_flags.h"
+#import "ios/chrome/browser/ui/autofill/features.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
 #import "ios/chrome/browser/ui/download/features.h"
@@ -823,6 +824,10 @@
      flag_descriptions::kContentSuggestionsUIViewControllerMigrationDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kContentSuggestionsUIViewControllerMigration)},
+    {"autofill-password-rich-iph",
+     flag_descriptions::kAutofillPasswordRichIPHName,
+     flag_descriptions::kAutofillPasswordRichIPHDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kAutofillPasswordRichIPH)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 2efaafe7..0c466f6 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -50,6 +50,11 @@
     "When enabled, Autofill will attempt to find merchant promo/coupon/gift "
     "code fields when parsing forms.";
 
+const char kAutofillPasswordRichIPHName[] = "Autofill password rich IPH";
+const char kAutofillPasswordRichIPHDescription[] =
+    "When enabled, display rich in-product help for autofill password "
+    "suggestions.";
+
 const char kAutofillPruneSuggestionsName[] = "Autofill Prune Suggestions";
 const char kAutofillPruneSuggestionsDescription[] =
     "Further limits the number of suggestions in the Autofill dropdown.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 7b9c212..75e8e6b 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -42,6 +42,10 @@
 extern const char kAutofillParseMerchantPromoCodeFieldsName[];
 extern const char kAutofillParseMerchantPromoCodeFieldsDescription[];
 
+// Title and description for the flag to enable autofill password rich iph.
+extern const char kAutofillPasswordRichIPHName[];
+extern const char kAutofillPasswordRichIPHDescription[];
+
 // Title and description for the flag that controls whether the maximum number
 // of Autofill suggestions shown is pruned.
 extern const char kAutofillPruneSuggestionsName[];
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index 4b042da..238a7d1 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -237,3 +237,12 @@
     "//ios/third_party/earl_grey2:test_lib",
   ]
 }
+
+source_set("features") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "features.cc",
+    "features.h",
+  ]
+  public_deps = [ "//base" ]
+}
diff --git a/ios/chrome/browser/ui/autofill/features.cc b/ios/chrome/browser/ui/autofill/features.cc
new file mode 100644
index 0000000..2699105406
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/features.cc
@@ -0,0 +1,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.
+
+#include "ios/chrome/browser/ui/autofill/features.h"
+
+const base::Feature kAutofillPasswordRichIPH{"AutofillPasswordRichIPH",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/autofill/features.h b/ios/chrome/browser/ui/autofill/features.h
new file mode 100644
index 0000000..00e8088d
--- /dev/null
+++ b/ios/chrome/browser/ui/autofill/features.h
@@ -0,0 +1,13 @@
+// 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 IOS_CHROME_BROWSER_UI_AUTOFILL_FEATURES_H_
+#define IOS_CHROME_BROWSER_UI_AUTOFILL_FEATURES_H_
+
+#include "base/feature_list.h"
+
+// Feature flag to enable rich IPH for password autofill.
+extern const base::Feature kAutofillPasswordRichIPH;
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_FEATURES_H_
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 8e5f3cb..4503bfe 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 @@
-b3e36dfdc633c84de811c8b52a80804434e9641c
\ No newline at end of file
+341e5015ff7d17438b18c99cf1a72bd2efb032c8
\ 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 f0d6ad0..ac340554 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 @@
-bd1ce18ddc986f634c9338b3df34d40d87851cc0
\ No newline at end of file
+aa80884c35fff15bc5929603885fd9afc8a3ef1d
\ 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 38c9f8d3..4e1e89f 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 @@
-1250549cdfeb7fd0aed816e1cb01382edddc9e4c
\ No newline at end of file
+10ef91fefaf35ceaf1c865ee2e52ff87b8139b75
\ 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 586d0a7..345e6681 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 @@
-797c5aba63091142558028dda478bff58608db66
\ No newline at end of file
+51b6c26b7fe200572afd39226a85b807ed541f30
\ 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 9cb4713..0602fbd 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 @@
-1b67ea5f3aaf48cb303bb701939f5ecd04dce9ec
\ No newline at end of file
+7dde7d663494c765066ad006806a3ba32738c772
\ 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 0efc42d..b7484bcd 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 @@
-0bdc212e782e0e576c8b824840e0c485d5946472
\ No newline at end of file
+705f4a8ad80c15719431032ed1a0a30f877c9031
\ 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 be1235f8..499685c 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 @@
-17007128dee5bb1b369c15688aa88fd2e8e19e50
\ No newline at end of file
+763c3266f6e604a70129230ebc08145a48a2db72
\ 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 1199d78..46b65050c 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 @@
-2a6f3dfb6b807e008225e4e6d4d475857dad7584
\ No newline at end of file
+d439cfe26b302d40b7a35f8f50f5a771855fad69
\ 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 ab5271d8..3f5d5319 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 @@
-d9e734feb04c8bc90acc3fef1cb87285639279b8
\ No newline at end of file
+783bb3c320b6f9a4aa803acc944b4b06617fb0f2
\ 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 6d6d55af..9e12122 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 @@
-07eb34ea17cbf553d71120eaece9b83f8017a5aa
\ No newline at end of file
+fe2ce659531f213a492bff9d59b93b2f0847ffb9
\ No newline at end of file
diff --git a/ios/web/navigation/history_state_operations_inttest.mm b/ios/web/navigation/history_state_operations_inttest.mm
index 3b18796..4d1d5cc 100644
--- a/ios/web/navigation/history_state_operations_inttest.mm
+++ b/ios/web/navigation/history_state_operations_inttest.mm
@@ -207,9 +207,8 @@
   std::string empty_state;
   std::string empty_title;
   std::string new_port_string = base::NumberToString(test_server_->port() + 1);
-  url::Replacements<char> port_replacement;
-  port_replacement.SetPort(new_port_string.c_str(),
-                           url::Component(0, new_port_string.length()));
+  GURL::Replacements port_replacement;
+  port_replacement.SetPortStr(new_port_string);
   GURL different_origin_url =
       state_operations_url().ReplaceComponents(port_replacement);
   ASSERT_TRUE(IsOnLoadTextVisible());
@@ -228,9 +227,8 @@
   std::string empty_state;
   std::string empty_title;
   std::string new_port_string = base::NumberToString(test_server_->port() + 1);
-  url::Replacements<char> port_replacement;
-  port_replacement.SetPort(new_port_string.c_str(),
-                           url::Component(0, new_port_string.length()));
+  GURL::Replacements port_replacement;
+  port_replacement.SetPortStr(new_port_string);
   GURL different_origin_url =
       state_operations_url().ReplaceComponents(port_replacement);
   ASSERT_TRUE(IsOnLoadTextVisible());
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc
index 04adf9b3..1547f62d 100644
--- a/media/audio/android/audio_manager_android.cc
+++ b/media/audio/android/audio_manager_android.cc
@@ -62,12 +62,6 @@
       base::android::AttachCurrentThread());
 }
 
-// Return a bit mask of AudioParameters::Format enum values sink device supports
-int GetSinkAudioEncodingFormats() {
-  JNIEnv* env = AttachCurrentThread();
-  return Java_AudioManagerAndroid_getAudioEncodingFormatsSupported(env);
-}
-
 }  // namespace
 
 static bool InitAAudio() {
@@ -477,6 +471,13 @@
                       sample_rate, channels));
 }
 
+// Returns a bit mask of AudioParameters::Format enum values sink device
+// supports.
+int AudioManagerAndroid::GetSinkAudioEncodingFormats() {
+  JNIEnv* env = AttachCurrentThread();
+  return Java_AudioManagerAndroid_getAudioEncodingFormatsSupported(env);
+}
+
 // Returns encoding bitstream formats supported by Sink device. Returns
 // AudioParameters structure.
 AudioParameters AudioManagerAndroid::GetAudioFormatsSupportedBySinkDevice(
diff --git a/media/audio/android/audio_manager_android.h b/media/audio/android/audio_manager_android.h
index f261b64..5c97aff 100644
--- a/media/audio/android/audio_manager_android.h
+++ b/media/audio/android/audio_manager_android.h
@@ -93,6 +93,8 @@
 
   bool IsUsingAAudioForTesting() { return UseAAudio(); }
 
+  static int GetSinkAudioEncodingFormats();
+
  protected:
   void ShutdownOnAudioThread() override;
   AudioParameters GetPreferredOutputStreamParameters(
diff --git a/media/base/android/media_codec_bridge_impl.cc b/media/base/android/media_codec_bridge_impl.cc
index be63bd36..9da9a2c 100644
--- a/media/base/android/media_codec_bridge_impl.cc
+++ b/media/base/android/media_codec_bridge_impl.cc
@@ -183,8 +183,9 @@
   DVLOG(2) << __func__ << ": " << config.AsHumanReadableString()
            << " media_crypto:" << media_crypto.obj();
 
-  const std::string mime =
-      MediaCodecUtil::CodecToAndroidMimeType(config.codec());
+  const std::string mime = MediaCodecUtil::CodecToAndroidMimeType(
+      config.codec(), config.target_output_sample_format());
+
   if (mime.empty())
     return nullptr;
 
diff --git a/media/base/android/media_codec_util.cc b/media/base/android/media_codec_util.cc
index 34bcdbd3..3b53e01 100644
--- a/media/base/android/media_codec_util.cc
+++ b/media/base/android/media_codec_util.cc
@@ -118,7 +118,20 @@
 
 // static
 std::string MediaCodecUtil::CodecToAndroidMimeType(AudioCodec codec) {
-  if (IsPassthroughAudioFormat(codec))
+  return CodecToAndroidMimeType(codec, kUnknownSampleFormat);
+}
+
+// static
+std::string MediaCodecUtil::CodecToAndroidMimeType(AudioCodec codec,
+                                                   SampleFormat sample_format) {
+  // Passthrough is possible for some bitstream formats.
+  const bool is_passthrough = sample_format == kSampleFormatDts ||
+                              sample_format == kSampleFormatDtsxP2 ||
+                              sample_format == kSampleFormatAc3 ||
+                              sample_format == kSampleFormatEac3 ||
+                              sample_format == kSampleFormatMpegHAudio;
+
+  if (IsPassthroughAudioFormat(codec) || is_passthrough)
     return kBitstreamAudioMimeType;
 
   switch (codec) {
@@ -278,8 +291,6 @@
   switch (codec) {
     case AudioCodec::kAC3:
     case AudioCodec::kEAC3:
-    case AudioCodec::kDTS:
-    case AudioCodec::kDTSXP2:
     case AudioCodec::kMpegHAudio:
       return true;
     default:
diff --git a/media/base/android/media_codec_util.h b/media/base/android/media_codec_util.h
index 2825a7c..1933e9c1 100644
--- a/media/base/android/media_codec_util.h
+++ b/media/base/android/media_codec_util.h
@@ -14,6 +14,7 @@
 #include "media/base/android/media_codec_direction.h"
 #include "media/base/audio_codecs.h"
 #include "media/base/media_export.h"
+#include "media/base/sample_format.h"
 #include "media/base/video_codecs.h"
 
 namespace media {
@@ -26,6 +27,8 @@
 class MEDIA_EXPORT MediaCodecUtil {
  public:
   static std::string CodecToAndroidMimeType(AudioCodec codec);
+  static std::string CodecToAndroidMimeType(AudioCodec codec,
+                                            SampleFormat sample_format);
   static std::string CodecToAndroidMimeType(VideoCodec codec);
 
   // Returns true if MediaCodec supports CBCS Encryption.
diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc
index 5bbd6f7a..1285f09 100644
--- a/media/base/audio_decoder_config.cc
+++ b/media/base/audio_decoder_config.cc
@@ -65,21 +65,23 @@
 }
 
 bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const {
-  return ((codec() == config.codec()) &&
-          (bytes_per_channel() == config.bytes_per_channel()) &&
-          (channel_layout() == config.channel_layout()) &&
-          (samples_per_second() == config.samples_per_second()) &&
-          (extra_data() == config.extra_data()) &&
-          (encryption_scheme() == config.encryption_scheme()) &&
-          (sample_format() == config.sample_format()) &&
-          (seek_preroll() == config.seek_preroll()) &&
-          (codec_delay() == config.codec_delay()) &&
-          (profile() == config.profile()) &&
-          (should_discard_decoder_delay() ==
-           config.should_discard_decoder_delay()) &&
-          (target_output_channel_layout() ==
-           config.target_output_channel_layout()) &&
-          (aac_extra_data() == config.aac_extra_data()));
+  return (
+      (codec() == config.codec()) &&
+      (bytes_per_channel() == config.bytes_per_channel()) &&
+      (channel_layout() == config.channel_layout()) &&
+      (samples_per_second() == config.samples_per_second()) &&
+      (extra_data() == config.extra_data()) &&
+      (encryption_scheme() == config.encryption_scheme()) &&
+      (sample_format() == config.sample_format()) &&
+      (seek_preroll() == config.seek_preroll()) &&
+      (codec_delay() == config.codec_delay()) &&
+      (profile() == config.profile()) &&
+      (should_discard_decoder_delay() ==
+       config.should_discard_decoder_delay()) &&
+      (target_output_channel_layout() ==
+       config.target_output_channel_layout()) &&
+      (target_output_sample_format() == config.target_output_sample_format()) &&
+      (aac_extra_data() == config.aac_extra_data()));
 }
 
 std::string AudioDecoderConfig::AsHumanReadableString() const {
@@ -100,6 +102,8 @@
     << (should_discard_decoder_delay() ? "true" : "false")
     << ", target_output_channel_layout: "
     << ChannelLayoutToString(target_output_channel_layout())
+    << ", target_output_sample_format: "
+    << SampleFormatToString(target_output_sample_format())
     << ", has aac extra data: "
     << (aac_extra_data().empty() ? "false" : "true");
   return s.str();
diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h
index 6e1077d..63379a2 100644
--- a/media/base/audio_decoder_config.h
+++ b/media/base/audio_decoder_config.h
@@ -115,6 +115,14 @@
     return target_output_channel_layout_;
   }
 
+  // Optionally set by renderer to signal desired bitstream-passthru format.
+  void set_target_output_sample_format(SampleFormat sample_format) {
+    target_output_sample_format_ = sample_format;
+  }
+  SampleFormat target_output_sample_format() const {
+    return target_output_sample_format_;
+  }
+
   void set_aac_extra_data(std::vector<uint8_t> aac_extra_data) {
     aac_extra_data_ = std::move(aac_extra_data);
   }
@@ -152,6 +160,9 @@
   // Layout of the output hardware. Optionally set. See setter comments.
   ChannelLayout target_output_channel_layout_ = CHANNEL_LAYOUT_NONE;
 
+  // Desired output format of bitstream. Optionally set. See setter comments.
+  SampleFormat target_output_sample_format_ = kUnknownSampleFormat;
+
   // This is a hack for backward compatibility. For AAC, to preserve existing
   // behavior, we set `aac_extra_data_` on all platforms but only set
   // `extra_data` on Android.
diff --git a/media/base/audio_parameters.cc b/media/base/audio_parameters.cc
index f60b3a2..f6895a1 100644
--- a/media/base/audio_parameters.cc
+++ b/media/base/audio_parameters.cc
@@ -206,6 +206,11 @@
   }
 }
 
+bool AudioParameters::IsFormatSupportedByHardware(Format format) const {
+  return hardware_capabilities_.has_value() &&
+         (hardware_capabilities_->bitstream_formats & format);
+}
+
 // static
 AudioParameters AudioParameters::UnavailableDeviceParams() {
   // Using 10 ms buffer since WebAudioMediaStreamSource::DeliverRebufferedAudio
diff --git a/media/base/audio_parameters.h b/media/base/audio_parameters.h
index 2b8c5f8f..d588f6e 100644
--- a/media/base/audio_parameters.h
+++ b/media/base/audio_parameters.h
@@ -13,6 +13,7 @@
 #include "base/numerics/checked_math.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "media/base/audio_bus.h"
 #include "media/base/audio_latency.h"
 #include "media/base/audio_point.h"
 #include "media/base/channel_layout.h"
@@ -170,7 +171,7 @@
         : min_frames_per_buffer(min_frames_per_buffer),
           max_frames_per_buffer(max_frames_per_buffer),
           bitstream_formats(0) {}
-    HardwareCapabilities(int bitstream_formats)
+    explicit HardwareCapabilities(int bitstream_formats)
         : min_frames_per_buffer(0),
           max_frames_per_buffer(0),
           bitstream_formats(bitstream_formats) {}
@@ -239,6 +240,8 @@
   // Return true if |format_| is compressed bitstream.
   bool IsBitstreamFormat() const;
 
+  bool IsFormatSupportedByHardware(Format format) const;
+
   void set_format(Format format) { format_ = format; }
   Format format() const { return format_; }
 
diff --git a/media/base/supported_types.cc b/media/base/supported_types.cc
index f467827..3e174b93 100644
--- a/media/base/supported_types.cc
+++ b/media/base/supported_types.cc
@@ -359,10 +359,15 @@
     case AudioCodec::kALAC:
     case AudioCodec::kAC3:
     case AudioCodec::kMpegHAudio:
-    case AudioCodec::kDTS:
-    case AudioCodec::kDTSXP2:
     case AudioCodec::kUnknown:
       return false;
+    case AudioCodec::kDTS:
+    case AudioCodec::kDTSXP2:
+#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
+      return true;
+#else
+      return false;
+#endif
   }
 }
 
diff --git a/media/filters/android/media_codec_audio_decoder.cc b/media/filters/android/media_codec_audio_decoder.cc
index 852dca1..02df6850 100644
--- a/media/filters/android/media_codec_audio_decoder.cc
+++ b/media/filters/android/media_codec_audio_decoder.cc
@@ -70,20 +70,6 @@
   DCHECK(input_queue_.empty());
   ClearInputQueue(DecoderStatus::Codes::kAborted);
 
-  is_passthrough_ = MediaCodecUtil::IsPassthroughAudioFormat(config.codec());
-  sample_format_ = kSampleFormatS16;
-
-  if (config.codec() == AudioCodec::kAC3)
-    sample_format_ = kSampleFormatAc3;
-  else if (config.codec() == AudioCodec::kEAC3)
-    sample_format_ = kSampleFormatEac3;
-  else if (config.codec() == AudioCodec::kMpegHAudio)
-    sample_format_ = kSampleFormatMpegHAudio;
-  else if (config.codec() == AudioCodec::kDTS)
-    sample_format_ = kSampleFormatDts;
-  else if (config.codec() == AudioCodec::kDTSXP2)
-    sample_format_ = kSampleFormatDtsxP2;
-
   if (state_ == STATE_ERROR) {
     DVLOG(1) << "Decoder is in error state.";
     BindToCurrentLoop(std::move(init_cb)).Run(DecoderStatus::Codes::kFailed);
@@ -93,13 +79,51 @@
   // We can support only the codecs that MediaCodecBridge can decode.
   // TODO(xhwang): Get this list from MediaCodecBridge or just rely on
   // attempting to create one to determine whether the codec is supported.
-  const bool is_codec_supported = config.codec() == AudioCodec::kVorbis ||
-                                  config.codec() == AudioCodec::kFLAC ||
-                                  config.codec() == AudioCodec::kAAC ||
-                                  config.codec() == AudioCodec::kOpus ||
-                                  is_passthrough_;
+
+  bool platform_codec_supported = false;
+  is_passthrough_ = false;
+  sample_format_ = config.target_output_sample_format();
+  switch (config.codec()) {
+    case AudioCodec::kVorbis:
+    case AudioCodec::kFLAC:
+    case AudioCodec::kAAC:
+    case AudioCodec::kOpus:
+      platform_codec_supported = true;
+      break;
+    case AudioCodec::kUnknown:
+    case AudioCodec::kMP3:
+    case AudioCodec::kPCM:
+    case AudioCodec::kAMR_NB:
+    case AudioCodec::kAMR_WB:
+    case AudioCodec::kPCM_MULAW:
+    case AudioCodec::kGSM_MS:
+    case AudioCodec::kPCM_S16BE:
+    case AudioCodec::kPCM_S24BE:
+    case AudioCodec::kPCM_ALAW:
+    case AudioCodec::kALAC:
+      platform_codec_supported = false;
+      break;
+    case AudioCodec::kAC3:
+    case AudioCodec::kEAC3:
+    case AudioCodec::kDTS:
+    case AudioCodec::kDTSXP2:
+    case AudioCodec::kMpegHAudio:
+      is_passthrough_ = sample_format_ != kUnknownSampleFormat;
+      // Check if MediaCodec Library supports decoding of the sample format.
+      platform_codec_supported = MediaCodecUtil::CanDecode(config.codec());
+      break;
+  }
+
+  // sample_format_ is stream type for pass-through. Otherwise sample_format_
+  // should be set to kSampleFormatS16, which is what Android MediaCodec
+  // supports for PCM decode.
+  if (!is_passthrough_)
+    sample_format_ = kSampleFormatS16;
+
+  const bool is_codec_supported = platform_codec_supported || is_passthrough_;
+
   if (!is_codec_supported) {
-    DVLOG(1) << "Unsuported codec " << GetCodecName(config.codec());
+    DVLOG(1) << "Unsupported codec " << GetCodecName(config.codec());
     BindToCurrentLoop(std::move(init_cb))
         .Run(DecoderStatus::Codes::kUnsupportedCodec);
     return;
diff --git a/media/filters/audio_decoder_stream_unittest.cc b/media/filters/audio_decoder_stream_unittest.cc
index 7040c14..55a38c3 100644
--- a/media/filters/audio_decoder_stream_unittest.cc
+++ b/media/filters/audio_decoder_stream_unittest.cc
@@ -49,7 +49,8 @@
       : audio_decoder_stream_(
             std::make_unique<AudioDecoderStream::StreamTraits>(
                 &media_log_,
-                CHANNEL_LAYOUT_STEREO),
+                CHANNEL_LAYOUT_STEREO,
+                kSampleFormatPlanarF32),
             task_environment_.GetMainThreadTaskRunner(),
             base::BindRepeating(&AudioDecoderStreamTest::CreateMockAudioDecoder,
                                 base::Unretained(this)),
diff --git a/media/filters/decoder_selector_unittest.cc b/media/filters/decoder_selector_unittest.cc
index 07f287c1..4951e02a 100644
--- a/media/filters/decoder_selector_unittest.cc
+++ b/media/filters/decoder_selector_unittest.cc
@@ -105,7 +105,8 @@
 
   // StreamTraits() takes different parameters depending on the type.
   static std::unique_ptr<StreamTraits> CreateStreamTraits(MediaLog* media_log) {
-    return std::make_unique<StreamTraits>(media_log, CHANNEL_LAYOUT_STEREO);
+    return std::make_unique<StreamTraits>(media_log, CHANNEL_LAYOUT_STEREO,
+                                          kSampleFormatPlanarF32);
   }
 
   static const base::Feature& ForceHardwareDecodersFeature() {
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc
index 322134fa..f7f8b83 100644
--- a/media/filters/decoder_stream_traits.cc
+++ b/media/filters/decoder_stream_traits.cc
@@ -53,8 +53,11 @@
 
 DecoderStreamTraits<DemuxerStream::AUDIO>::DecoderStreamTraits(
     MediaLog* media_log,
-    ChannelLayout initial_hw_layout)
-    : media_log_(media_log), initial_hw_layout_(initial_hw_layout) {
+    ChannelLayout initial_hw_layout,
+    SampleFormat initial_hw_sample_format)
+    : media_log_(media_log),
+      initial_hw_layout_(initial_hw_layout),
+      initial_hw_sample_format_(initial_hw_sample_format) {
   weak_this_ = weak_factory_.GetWeakPtr();
 }
 
@@ -64,6 +67,7 @@
   auto config = stream->audio_decoder_config();
   // Demuxer is not aware of hw layout, so we set it here.
   config.set_target_output_channel_layout(initial_hw_layout_);
+  config.set_target_output_sample_format(initial_hw_sample_format_);
   return config;
 }
 
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h
index d3d3afba..839c9ad 100644
--- a/media/filters/decoder_stream_traits.h
+++ b/media/filters/decoder_stream_traits.h
@@ -18,6 +18,7 @@
 #include "media/base/media_log_properties.h"
 #include "media/base/moving_average.h"
 #include "media/base/pipeline_status.h"
+#include "media/base/sample_format.h"
 #include "media/base/video_decoder.h"
 #include "media/filters/audio_timestamp_validator.h"
 
@@ -54,7 +55,9 @@
   static bool NeedsBitstreamConversion(DecoderType* decoder);
   static scoped_refptr<OutputType> CreateEOSOutput();
 
-  DecoderStreamTraits(MediaLog* media_log, ChannelLayout initial_hw_layout);
+  DecoderStreamTraits(MediaLog* media_log,
+                      ChannelLayout initial_hw_layout,
+                      SampleFormat initial_hw_sample_format);
 
   void ReportStatistics(const StatisticsCB& statistics_cb, int bytes_decoded);
   void SetIsPlatformDecoder(bool is_platform_decoder);
@@ -87,6 +90,9 @@
   // HW layout at the time pipeline was started. Will not reflect possible
   // device changes.
   ChannelLayout initial_hw_layout_;
+  // HW sample format at the time pipeline was started. Will not reflect
+  // possible device changes.
+  SampleFormat initial_hw_sample_format_;
   PipelineStatistics stats_;
   AudioDecoderConfig config_;
 
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index f5402c2..de7b899 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -449,7 +449,14 @@
           sample_per_second = entry.samplerate;
         } else if (audio_type == kDTSX) {
           codec = AudioCodec::kDTSXP2;
-          channel_layout = GuessChannelLayout(entry.channelcount);
+          // TODO(crbug.com/1302884): Adding a new 5.1.4 channel layout enum
+          // and modify GuessChannelLayout function.
+          // HDMI versions pre HDMI 2.0 can only transmit 8 raw PCM channels.
+          // In the case of a 5_1_4 stream we downmix to 5_1.
+          if (entry.channelcount == 10)
+            channel_layout = CHANNEL_LAYOUT_5_1;
+          else
+            channel_layout = GuessChannelLayout(entry.channelcount);
           sample_per_second = entry.samplerate;
 #endif
         } else {
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index adb0061..74862bc 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -359,7 +359,8 @@
     return false;
   }
 
-  if (!ActivateAsyncEncoder(pp_activate, encoder_count)) {
+  if (!ActivateAsyncEncoder(pp_activate, encoder_count,
+                            config.is_constrained_h264)) {
     DLOG(ERROR) << "Failed activating an async hardware encoder MFT.";
 
     if (pp_activate) {
@@ -625,7 +626,8 @@
 
 bool MediaFoundationVideoEncodeAccelerator::ActivateAsyncEncoder(
     IMFActivate** pp_activate,
-    uint32_t encoder_count) {
+    uint32_t encoder_count,
+    bool is_constrained_h264) {
   DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -640,6 +642,25 @@
         DCHECK(SUCCEEDED(hr));
         auto vendor = GetDriverVendor(pp_activate[i]);
 
+        // Skip NVIDIA GPU due to https://crbug.com/1088650 for constrained
+        // baseline profile H.264 encoding, and go to the next instance
+        // according to merit value.
+        if (codec_ == VideoCodec::kH264 && is_constrained_h264) {
+          // Get the vendor id.
+          base::win::ScopedCoMem<WCHAR> vendor_id;
+          UINT32 id_length;
+          pp_activate[i]->GetAllocatedString(
+              MFT_ENUM_HARDWARE_VENDOR_ID_Attribute, &vendor_id, &id_length);
+          if (!_wcsnicmp(vendor_id, L"VEN_10DE", id_length)) {
+            DLOG(WARNING)
+                << "Skipped NVIDIA GPU due to https://crbug.com/1088650";
+            pp_activate[i]->ShutdownObject();
+            encoder_.Reset();
+            hr = E_FAIL;
+            continue;
+          }
+        }
+
         activate_ = pp_activate[i];
         vendor_ = vendor;
         pp_activate[i] = nullptr;
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
index 2136435..14a8990 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
@@ -87,7 +87,9 @@
 
   // Activates the asynchronous encoder instance |encoder_| according to codec
   // merit.
-  bool ActivateAsyncEncoder(IMFActivate** pp_activate, uint32_t activate_count);
+  bool ActivateAsyncEncoder(IMFActivate** pp_activate,
+                            uint32_t activate_count,
+                            bool is_constrained_h264);
 
   // Initializes and allocates memory for input and output parameters.
   bool InitializeInputOutputParameters(VideoCodecProfile output_profile,
diff --git a/media/mojo/mojom/audio_decoder_config_mojom_traits.cc b/media/mojo/mojom/audio_decoder_config_mojom_traits.cc
index b7cf06f..ef02dd1 100644
--- a/media/mojo/mojom/audio_decoder_config_mojom_traits.cc
+++ b/media/mojo/mojom/audio_decoder_config_mojom_traits.cc
@@ -45,6 +45,10 @@
   if (!input.ReadTargetOutputChannelLayout(&target_output_channel_layout))
     return false;
 
+  media::SampleFormat target_output_sample_format;
+  if (!input.ReadTargetOutputSampleFormat(&target_output_sample_format))
+    return false;
+
   std::vector<uint8_t> aac_extra_data;
   if (!input.ReadAacExtraData(&aac_extra_data))
     return false;
@@ -54,6 +58,7 @@
                      encryption_scheme, seek_preroll, input.codec_delay());
   output->set_profile(profile);
   output->set_target_output_channel_layout(target_output_channel_layout);
+  output->set_target_output_sample_format(target_output_sample_format);
   output->set_aac_extra_data(std::move(aac_extra_data));
 
   if (!input.should_discard_decoder_delay())
diff --git a/media/mojo/mojom/audio_decoder_config_mojom_traits.h b/media/mojo/mojom/audio_decoder_config_mojom_traits.h
index cc65744..bc50cbf 100644
--- a/media/mojo/mojom/audio_decoder_config_mojom_traits.h
+++ b/media/mojo/mojom/audio_decoder_config_mojom_traits.h
@@ -60,6 +60,11 @@
     return input.target_output_channel_layout();
   }
 
+  static media::SampleFormat target_output_sample_format(
+      const media::AudioDecoderConfig& input) {
+    return input.target_output_sample_format();
+  }
+
   static bool should_discard_decoder_delay(
       const media::AudioDecoderConfig& input) {
     return input.should_discard_decoder_delay();
diff --git a/media/mojo/mojom/audio_decoder_config_mojom_traits_unittest.cc b/media/mojo/mojom/audio_decoder_config_mojom_traits_unittest.cc
index aa510d63..a72207eb 100644
--- a/media/mojo/mojom/audio_decoder_config_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/audio_decoder_config_mojom_traits_unittest.cc
@@ -81,11 +81,13 @@
                    48000, EmptyExtraData(), EncryptionScheme::kUnencrypted,
                    base::TimeDelta(), 0);
   input.set_target_output_channel_layout(CHANNEL_LAYOUT_5_1);
+  input.set_target_output_sample_format(kSampleFormatDts);
   std::vector<uint8_t> data = mojom::AudioDecoderConfig::Serialize(&input);
   AudioDecoderConfig output;
   EXPECT_TRUE(mojom::AudioDecoderConfig::Deserialize(std::move(data), &output));
   EXPECT_TRUE(output.Matches(input));
   EXPECT_EQ(output.target_output_channel_layout(), CHANNEL_LAYOUT_5_1);
+  EXPECT_EQ(output.target_output_sample_format(), kSampleFormatDts);
 }
 
 TEST(AudioDecoderConfigStructTraitsTest, AacExtraData) {
diff --git a/media/mojo/mojom/media_types.mojom b/media/mojo/mojom/media_types.mojom
index 4885879..8223958f 100644
--- a/media/mojo/mojom/media_types.mojom
+++ b/media/mojo/mojom/media_types.mojom
@@ -176,6 +176,7 @@
   int32 codec_delay;
   AudioCodecProfile profile;
   ChannelLayout target_output_channel_layout;
+  SampleFormat target_output_sample_format;
   bool should_discard_decoder_delay;
   array<uint8> aac_extra_data;
 };
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index 1d0b3b05f..729e3a74 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -40,6 +40,26 @@
 
 namespace media {
 
+namespace {
+
+AudioParameters::Format ConvertCodecToBitstreamFormat(AudioCodec codec) {
+  switch (codec) {
+    case AudioCodec::kAC3:
+      return AudioParameters::Format::AUDIO_BITSTREAM_AC3;
+    case AudioCodec::kEAC3:
+      return AudioParameters::Format::AUDIO_BITSTREAM_EAC3;
+    case AudioCodec::kDTS:
+      return AudioParameters::Format::AUDIO_BITSTREAM_DTS;
+      // No support for DTS_HD yet as this section is related to the incoming
+      // stream type. DTS_HD support is only added for audio track output to
+      // support audiosink reporting DTS_HD support.
+    default:
+      return AudioParameters::Format::AUDIO_FAKE;
+  }
+}
+
+}  // namespace
+
 AudioRendererImpl::AudioRendererImpl(
     const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
     AudioRendererSink* sink,
@@ -433,19 +453,16 @@
   ChannelLayout hw_channel_layout =
       hw_params.IsValid() ? hw_params.channel_layout() : CHANNEL_LAYOUT_NONE;
 
-  audio_decoder_stream_ = std::make_unique<AudioDecoderStream>(
-      std::make_unique<AudioDecoderStream::StreamTraits>(media_log_,
-                                                         hw_channel_layout),
-      task_runner_, create_audio_decoders_cb_, media_log_);
-
-  audio_decoder_stream_->set_config_change_observer(base::BindRepeating(
-      &AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
+  DVLOG(1) << __func__ << ": " << hw_params.AsHumanReadableString();
 
   AudioCodec codec = stream->audio_decoder_config().codec();
-  if (auto* mc = GetMediaClient())
-    is_passthrough_ = mc->IsSupportedBitstreamAudioCodec(codec);
-  else
+  if (auto* mc = GetMediaClient()) {
+    const auto format = ConvertCodecToBitstreamFormat(codec);
+    is_passthrough_ = mc->IsSupportedBitstreamAudioCodec(codec) &&
+                      hw_params.IsFormatSupportedByHardware(format);
+  } else {
     is_passthrough_ = false;
+  }
   expecting_config_changes_ = stream->SupportsConfigChanges();
 
   bool use_stream_params = !expecting_config_changes_ || !hw_params.IsValid() ||
@@ -470,14 +487,21 @@
       std::max(2 * stream->audio_decoder_config().samples_per_second() / 100,
                hw_params.IsValid() ? hw_params.frames_per_buffer() : 0);
 
+  SampleFormat target_output_sample_format = kUnknownSampleFormat;
   if (is_passthrough_) {
     AudioParameters::Format format = AudioParameters::AUDIO_FAKE;
+    // For DTS and Dolby formats, set target_output_sample_format to the
+    // respective bit-stream format so that passthrough decoder will be selected
+    // by MediaCodecAudioRenderer if this is running on Android.
     if (codec == AudioCodec::kAC3) {
       format = AudioParameters::AUDIO_BITSTREAM_AC3;
+      target_output_sample_format = kSampleFormatAc3;
     } else if (codec == AudioCodec::kEAC3) {
       format = AudioParameters::AUDIO_BITSTREAM_EAC3;
+      target_output_sample_format = kSampleFormatEac3;
     } else if (codec == AudioCodec::kDTS) {
       format = AudioParameters::AUDIO_BITSTREAM_DTS;
+      target_output_sample_format = kSampleFormatDts;
     } else {
       NOTREACHED();
     }
@@ -572,6 +596,19 @@
 
   audio_parameters_.set_latency_tag(AudioLatency::LATENCY_PLAYBACK);
 
+  audio_decoder_stream_ = std::make_unique<AudioDecoderStream>(
+      std::make_unique<AudioDecoderStream::StreamTraits>(
+          media_log_, hw_channel_layout, target_output_sample_format),
+      task_runner_, create_audio_decoders_cb_, media_log_);
+
+  audio_decoder_stream_->set_config_change_observer(base::BindRepeating(
+      &AudioRendererImpl::OnConfigChange, weak_factory_.GetWeakPtr()));
+
+  DVLOG(1) << __func__ << ": is_passthrough_=" << is_passthrough_
+           << " codec=" << codec
+           << " stream->audio_decoder_config().sample_format="
+           << stream->audio_decoder_config().sample_format();
+
   if (!client_->IsVideoStreamAvailable()) {
     // When video is not available, audio prefetch can be enabled.  See
     // crbug/988535.
@@ -1412,4 +1449,5 @@
     speech_recognition_client_->AddAudio(std::move(buffer));
 #endif
 }
+
 }  // namespace media
diff --git a/net/BUILD.gn b/net/BUILD.gn
index fbc895c..4e2cd19 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -781,8 +781,6 @@
     "quic/platform/impl/quic_hostname_utils_impl.cc",
     "quic/platform/impl/quic_hostname_utils_impl.h",
     "quic/platform/impl/quic_iovec_impl.h",
-    "quic/platform/impl/quic_mem_slice_impl.cc",
-    "quic/platform/impl/quic_mem_slice_impl.h",
     "quic/platform/impl/quic_prefetch_impl.h",
     "quic/platform/impl/quic_reference_counted_impl.h",
     "quic/platform/impl/quic_server_stats_impl.h",
diff --git a/net/quic/platform/impl/quic_mem_slice_impl.cc b/net/quic/platform/impl/quic_mem_slice_impl.cc
deleted file mode 100644
index 02635d1..0000000
--- a/net/quic/platform/impl/quic_mem_slice_impl.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/quic/platform/impl/quic_mem_slice_impl.h"
-
-#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
-
-namespace quic {
-
-namespace {
-
-template <typename UniqueBufferPtr>
-class QuicIOBuffer : public net::IOBuffer {
- public:
-  QuicIOBuffer(UniqueBufferPtr buffer, size_t size)
-      : buffer_(std::move(buffer)) {
-    AssertValidBufferSize(size);
-    data_ = buffer_.get();
-  }
-
- private:
-  ~QuicIOBuffer() override { data_ = nullptr; }
-
-  UniqueBufferPtr buffer_;
-};
-
-}  // namespace
-
-QuicMemSliceImpl::QuicMemSliceImpl() = default;
-
-QuicMemSliceImpl::QuicMemSliceImpl(QuicUniqueBufferPtr buffer, size_t length) {
-  io_buffer_ = base::MakeRefCounted<QuicIOBuffer<QuicUniqueBufferPtr>>(
-      std::move(buffer), length);
-  length_ = length;
-}
-
-QuicMemSliceImpl::QuicMemSliceImpl(std::unique_ptr<char[]> buffer,
-                                   size_t length) {
-  io_buffer_ = base::MakeRefCounted<QuicIOBuffer<std::unique_ptr<char[]>>>(
-      std::move(buffer), length);
-  length_ = length;
-}
-
-QuicMemSliceImpl::QuicMemSliceImpl(scoped_refptr<net::IOBuffer> io_buffer,
-                                   size_t length)
-    : io_buffer_(std::move(io_buffer)), length_(length) {}
-
-QuicMemSliceImpl::QuicMemSliceImpl(QuicMemSliceImpl&& other)
-    : io_buffer_(std::move(other.io_buffer_)), length_(other.length_) {
-  other.length_ = 0;
-}
-
-QuicMemSliceImpl& QuicMemSliceImpl::operator=(QuicMemSliceImpl&& other) {
-  io_buffer_ = std::move(other.io_buffer_);
-  length_ = other.length_;
-  other.length_ = 0;
-  return *this;
-}
-
-QuicMemSliceImpl::~QuicMemSliceImpl() = default;
-
-void QuicMemSliceImpl::Reset() {
-  io_buffer_ = nullptr;
-  length_ = 0;
-}
-
-const char* QuicMemSliceImpl::data() const {
-  if (io_buffer_ == nullptr) {
-    return nullptr;
-  }
-  return io_buffer_->data();
-}
-
-}  // namespace quic
diff --git a/net/quic/platform/impl/quic_mem_slice_impl.h b/net/quic/platform/impl/quic_mem_slice_impl.h
deleted file mode 100644
index 830d01b..0000000
--- a/net/quic/platform/impl/quic_mem_slice_impl.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUIC_PLATFORM_IMPL_QUIC_MEM_SLICE_IMPL_H_
-#define NET_QUIC_PLATFORM_IMPL_QUIC_MEM_SLICE_IMPL_H_
-
-#include <memory>
-
-#include "base/memory/ref_counted.h"
-#include "net/base/io_buffer.h"
-#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
-
-namespace quic {
-
-// QuicMemSliceImpl TODO(fayang)
-class QUIC_EXPORT_PRIVATE QuicMemSliceImpl {
- public:
-  // Constructs an empty QuicMemSliceImpl.
-  QuicMemSliceImpl();
-  // Constructs a QuicMemSliceImp by let |allocator| allocate a data buffer of
-  // |length|.
-  QuicMemSliceImpl(QuicUniqueBufferPtr buffer, size_t length);
-  QuicMemSliceImpl(std::unique_ptr<char[]> buffer, size_t length);
-
-  QuicMemSliceImpl(scoped_refptr<net::IOBuffer> io_buffer, size_t length);
-
-  QuicMemSliceImpl(const QuicMemSliceImpl& other) = delete;
-  QuicMemSliceImpl& operator=(const QuicMemSliceImpl& other) = delete;
-
-  // Move constructors. |other| will not hold a reference to the data buffer
-  // after this call completes.
-  QuicMemSliceImpl(QuicMemSliceImpl&& other);
-  QuicMemSliceImpl& operator=(QuicMemSliceImpl&& other);
-
-  ~QuicMemSliceImpl();
-
-  // Release the underlying reference. Further access the memory will result in
-  // undefined behavior.
-  void Reset();
-
-  // Returns a char pointer to underlying data buffer.
-  const char* data() const;
-  // Returns the length of underlying data buffer.
-  size_t length() const { return length_; }
-
-  bool empty() const { return length_ == 0; }
-
-  scoped_refptr<net::IOBuffer>* impl() { return &io_buffer_; }
-
-  size_t* impl_length() { return &length_; }
-
- private:
-  scoped_refptr<net::IOBuffer> io_buffer_;
-  // Length of io_buffer_.
-  size_t length_ = 0;
-};
-
-}  // namespace quic
-
-#endif  // NET_QUIC_PLATFORM_IMPL_QUIC_MEM_SLICE_IMPL_H_
diff --git a/net/quic/quic_chromium_client_session_test.cc b/net/quic/quic_chromium_client_session_test.cc
index 84e99519d..9627963 100644
--- a/net/quic/quic_chromium_client_session_test.cc
+++ b/net/quic/quic_chromium_client_session_test.cc
@@ -247,7 +247,6 @@
 
   void SetIetfConnectionMigrationFlagsAndConnectionOptions() {
     FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = true;
-    FLAGS_quic_reloadable_flag_quic_send_path_response2 = true;
     FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3 = true;
     FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2 = true;
     config_.SetConnectionOptionsToSend({quic::kRVCM});
diff --git a/net/quic/quic_http3_logger.cc b/net/quic/quic_http3_logger.cc
index 4410a3737..ab7fec8 100644
--- a/net/quic/quic_http3_logger.cc
+++ b/net/quic/quic_http3_logger.cc
@@ -290,14 +290,6 @@
                                  "stream_id", stream_id);
 }
 
-void QuicHttp3Logger::OnMaxPushIdFrameSent(const quic::MaxPushIdFrame& frame) {
-  if (!net_log_.IsCapturing()) {
-    return;
-  }
-  net_log_.AddEventWithIntParams(NetLogEventType::HTTP3_MAX_PUSH_ID_SENT,
-                                 "push_id", frame.push_id);
-}
-
 void QuicHttp3Logger::OnPriorityUpdateFrameSent(
     const quic::PriorityUpdateFrame& frame) {
   if (!net_log_.IsCapturing()) {
diff --git a/net/quic/quic_http3_logger.h b/net/quic/quic_http3_logger.h
index b616283..c9840701 100644
--- a/net/quic/quic_http3_logger.h
+++ b/net/quic/quic_http3_logger.h
@@ -53,7 +53,6 @@
 
   void OnSettingsFrameSent(const quic::SettingsFrame& frame) override;
   void OnGoAwayFrameSent(quic::QuicStreamId stream_id) override;
-  void OnMaxPushIdFrameSent(const quic::MaxPushIdFrame& frame) override;
   void OnPriorityUpdateFrameSent(
       const quic::PriorityUpdateFrame& frame) override;
 
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index d59b51a..b776475 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -3286,7 +3286,7 @@
                        client_maker_->MakeConnectionClosePacket(
                            packet_num++, true, quic::QUIC_NETWORK_IDLE_TIMEOUT,
                            "No recent network activity after 4s. Timeout:4s"));
-  } else if (version_.UsesTls()) {
+  } else if (version_.UsesTls() || GetQuicRestartFlag(quic_default_on_pto2)) {
     // Settings were sent in the request packet so there is only 1 packet to
     // retransmit.
     // QuicConnection::OnRetransmissionTimeout skips a packet number when
@@ -3509,7 +3509,7 @@
                        client_maker_->MakeConnectionClosePacket(
                            packet_num++, true, quic::QUIC_NETWORK_IDLE_TIMEOUT,
                            "No recent network activity after 4s. Timeout:4s"));
-  } else if (version_.UsesTls()) {
+  } else if (version_.UsesTls() || GetQuicRestartFlag(quic_default_on_pto2)) {
     // Settings were sent in the request packet so there is only 1 packet to
     // retransmit.
     // QuicConnection::OnRetransmissionTimeout skips a packet number when
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 1ebb2a3..a3dee949 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -263,7 +263,6 @@
 
   void SetIetfConnectionMigrationFlagsAndConnectionOptions() {
     FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = true;
-    FLAGS_quic_reloadable_flag_quic_send_path_response2 = true;
     FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path3 = true;
     FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid_v2 = true;
     quic_params_->connection_options.push_back(quic::kRVCM);
@@ -4808,7 +4807,6 @@
   }
   quic_params_->allow_port_migration = true;
   FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = false;
-  FLAGS_quic_reloadable_flag_quic_send_path_response2 = false;
   socket_factory_ = std::make_unique<TestPortMigrationSocketFactory>();
   Initialize();
 
@@ -4824,7 +4822,6 @@
   }
   quic_params_->allow_port_migration = true;
   FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = false;
-  FLAGS_quic_reloadable_flag_quic_send_path_response2 = false;
   socket_factory_ = std::make_unique<TestPortMigrationSocketFactory>();
   Initialize();
   ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails();
@@ -5118,7 +5115,6 @@
   mock_ncn->SetConnectedNetworksList({kDefaultNetworkForTests});
   quic_params_->allow_port_migration = true;
   FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = false;
-  FLAGS_quic_reloadable_flag_quic_send_path_response2 = false;
   socket_factory_ = std::make_unique<TestPortMigrationSocketFactory>();
   Initialize();
 
@@ -5148,7 +5144,6 @@
   quic_params_->migrate_sessions_on_network_change_v2 = true;
   quic_params_->allow_port_migration = true;
   FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = false;
-  FLAGS_quic_reloadable_flag_quic_send_path_response2 = false;
   socket_factory_ = std::make_unique<TestPortMigrationSocketFactory>();
   Initialize();
 
@@ -5392,10 +5387,9 @@
 }
 
 // Regression test for https://crbug.com/1014092.
-TEST_P(QuicStreamFactoryTest, MultiplePortMigrationsExceedsMaxLimit) {
+TEST_P(QuicStreamFactoryTest, DISABLED_MultiplePortMigrationsExceedsMaxLimit) {
   quic_params_->allow_port_migration = true;
   FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator = false;
-  FLAGS_quic_reloadable_flag_quic_send_path_response2 = false;
   socket_factory_ = std::make_unique<TestPortMigrationSocketFactory>();
   Initialize();
 
@@ -14939,7 +14933,7 @@
   // which is 2 * 400ms with crypto frames and 1.5 * 400ms otherwise.
   base::TimeDelta handshake_timeout =
       QuicVersionUsesCryptoFrames(version_.transport_version)
-          ? 2 * kInitialRtt
+          ? (GetQuicRestartFlag(quic_default_on_pto2) ? 3 : 2) * kInitialRtt
           : 1.5 * kInitialRtt;
   EXPECT_EQ(handshake_timeout, task_runner->NextPendingTaskDelay());
 
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index da24956..1cb59d8 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -469,9 +469,6 @@
     AddQuicStopSendingFrame(stream_id, error_code);
   }
   AddQuicRstStreamFrame(stream_id, error_code);
-  if (!FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2) {
-    AddQuicAckFrame(largest_received, smallest_received);
-  }
   AddQuicConnectionCloseFrame(quic_error, quic_error_details);
 
   return BuildPacket();
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index 179e315c..f267624 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -48,6 +48,7 @@
     "src/common/platform/api/quiche_flag_utils.h",
     "src/common/platform/api/quiche_flags.h",
     "src/common/platform/api/quiche_logging.h",
+    "src/common/platform/api/quiche_mem_slice.h",
     "src/common/platform/api/quiche_prefetch.h",
     "src/common/platform/api/quiche_thread_local.h",
     "src/common/platform/api/quiche_time_utils.h",
@@ -61,6 +62,8 @@
     "src/common/quiche_data_writer.h",
     "src/common/quiche_endian.h",
     "src/common/quiche_linked_hash_map.h",
+    "src/common/quiche_mem_slice_storage.cc",
+    "src/common/quiche_mem_slice_storage.h",
     "src/common/quiche_text_utils.cc",
     "src/common/quiche_text_utils.h",
     "src/http2/adapter/data_source.h",
@@ -253,8 +256,6 @@
     "src/quic/core/crypto/channel_id.h",
     "src/quic/core/crypto/client_proof_source.cc",
     "src/quic/core/crypto/client_proof_source.h",
-    "src/quic/core/crypto/common_cert_set.cc",
-    "src/quic/core/crypto/common_cert_set.h",
     "src/quic/core/crypto/crypto_framer.cc",
     "src/quic/core/crypto/crypto_framer.h",
     "src/quic/core/crypto/crypto_handshake.cc",
@@ -602,9 +603,6 @@
     "src/quic/platform/api/quic_ip_address.h",
     "src/quic/platform/api/quic_ip_address_family.h",
     "src/quic/platform/api/quic_logging.h",
-    "src/quic/platform/api/quic_mem_slice.h",
-    "src/quic/platform/api/quic_mem_slice_storage.cc",
-    "src/quic/platform/api/quic_mem_slice_storage.h",
     "src/quic/platform/api/quic_mutex.cc",
     "src/quic/platform/api/quic_mutex.h",
     "src/quic/platform/api/quic_reference_counted.h",
@@ -1234,6 +1232,7 @@
   sources = [
     # TODO(bnc): Include in tests after test data files are added to QUICHE.
     # "src/common/platform/api/quiche_file_utils_test.cc",
+    "src/common/platform/api/quiche_mem_slice_test.cc",
     "src/common/platform/api/quiche_time_utils_test.cc",
     "src/common/platform/api/quiche_url_utils_test.cc",
     "src/common/quiche_circular_deque_test.cc",
@@ -1241,6 +1240,7 @@
     "src/common/quiche_data_writer_test.cc",
     "src/common/quiche_endian_test.cc",
     "src/common/quiche_linked_hash_map_test.cc",
+    "src/common/quiche_mem_slice_storage_test.cc",
     "src/common/quiche_text_utils_test.cc",
     "src/http2/adapter/event_forwarder_test.cc",
     "src/http2/adapter/header_validator_test.cc",
@@ -1352,7 +1352,6 @@
     "src/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc",
     "src/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc",
     "src/quic/core/crypto/channel_id_test.cc",
-    "src/quic/core/crypto/common_cert_set_test.cc",
     "src/quic/core/crypto/crypto_framer_test.cc",
     "src/quic/core/crypto/crypto_handshake_message_test.cc",
     "src/quic/core/crypto/crypto_secret_boxer_test.cc",
@@ -1469,8 +1468,6 @@
     "src/quic/core/uber_received_packet_manager_test.cc",
     "src/quic/platform/api/quic_hostname_utils_test.cc",
     "src/quic/platform/api/quic_ip_address_test.cc",
-    "src/quic/platform/api/quic_mem_slice_storage_test.cc",
-    "src/quic/platform/api/quic_mem_slice_test.cc",
     "src/quic/platform/api/quic_reference_counted_test.cc",
     "src/quic/platform/api/quic_socket_address_test.cc",
     "src/quic/quic_transport/quic_transport_client_session_test.cc",
diff --git a/ppapi/proxy/tcp_socket_resource_base.cc b/ppapi/proxy/tcp_socket_resource_base.cc
index 19935c2..b0b200c 100644
--- a/ppapi/proxy/tcp_socket_resource_base.cc
+++ b/ppapi/proxy/tcp_socket_resource_base.cc
@@ -5,10 +5,10 @@
 #include "ppapi/proxy/tcp_socket_resource_base.h"
 
 #include <cstring>
+#include <iterator>
 
 #include "base/bind.h"
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
 #include "base/notreached.h"
 #include "ppapi/c/pp_bool.h"
 #include "ppapi/c/pp_errors.h"
@@ -37,10 +37,10 @@
       version_(version) {
   local_addr_.size = 0;
   memset(local_addr_.data, 0,
-         base::size(local_addr_.data) * sizeof(*local_addr_.data));
+         std::size(local_addr_.data) * sizeof(*local_addr_.data));
   remote_addr_.size = 0;
   memset(remote_addr_.data, 0,
-         base::size(remote_addr_.data) * sizeof(*remote_addr_.data));
+         std::size(remote_addr_.data) * sizeof(*remote_addr_.data));
 }
 
 TCPSocketResourceBase::TCPSocketResourceBase(
diff --git a/printing/backend/win_helper.cc b/printing/backend/win_helper.cc
index 36883ee..7f21c24 100644
--- a/printing/backend/win_helper.cc
+++ b/printing/backend/win_helper.cc
@@ -11,16 +11,20 @@
 #include <memory>
 
 #include "base/check_op.h"
+#include "base/containers/fixed_flat_set.h"
 #include "base/debug/alias.h"
 #include "base/file_version_info.h"
 #include "base/files/file_path.h"
 #include "base/memory/free_deleter.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/scoped_blocking_call.h"
+#include "base/win/registry.h"
+#include "base/win/windows_types.h"
 #include "base/win/windows_version.h"
 #include "printing/backend/print_backend.h"
 #include "printing/backend/print_backend_consts.h"
@@ -129,6 +133,36 @@
 const char kXpsTicketColor[] = "Color";
 const char kXpsTicketMonochrome[] = "Monochrome";
 
+// Registry path prefix for the printer drivers' keys.
+constexpr wchar_t kDriversRegistryKeyPath[] =
+    L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers\\";
+
+// Registry value name for a port.
+constexpr wchar_t kPortRegistryValue[] = L"Port";
+
+// List of printer ports which are known to cause a UI dialog to be displayed
+// when printing.
+constexpr wchar_t kPrinterDriverPortFile[] = L"FILE:";
+constexpr wchar_t kPrinterDriverPortPrompt[] = L"PORTPROMPT:";
+constexpr wchar_t kPrinterDriverPortFax[] = L"SHRFAX:";
+
+// Gets the port used for a particular printer driver.  This can be found in
+// the Windows registry entry for the driver.
+std::wstring GetPrinterDriverPort(const std::string& printer_name) {
+  base::win::RegKey reg_key;
+  std::wstring root_key(std::wstring(kDriversRegistryKeyPath) +
+                        base::UTF8ToWide(printer_name));
+  LONG result =
+      reg_key.Open(HKEY_LOCAL_MACHINE, root_key.c_str(), KEY_QUERY_VALUE);
+  if (result != ERROR_SUCCESS)
+    return std::wstring();
+  std::wstring port_value;
+  result = reg_key.ReadValue(kPortRegistryValue, &port_value);
+  if (result != ERROR_SUCCESS)
+    return std::wstring();
+  return port_value;
+}
+
 }  // namespace
 
 namespace printing {
@@ -423,6 +457,13 @@
   return driver_info;
 }
 
+bool DoesDriverDisplayFileDialogForPrinting(const std::string& printer_name) {
+  static constexpr auto kPortNames = base::MakeFixedFlatSet<base::WStringPiece>(
+      {kPrinterDriverPortFile, kPrinterDriverPortPrompt,
+       kPrinterDriverPortFax});
+  return kPortNames.contains(GetPrinterDriverPort(printer_name));
+}
+
 std::unique_ptr<DEVMODE, base::FreeDeleter> XpsTicketToDevMode(
     const std::wstring& printer_name,
     const std::string& print_ticket) {
diff --git a/printing/backend/win_helper.h b/printing/backend/win_helper.h
index 09aff42..577efc8 100644
--- a/printing/backend/win_helper.h
+++ b/printing/backend/win_helper.h
@@ -162,6 +162,11 @@
 
 COMPONENT_EXPORT(PRINT_BACKEND) std::string GetDriverInfo(HANDLE printer);
 
+// Determines if the specified printer driver is known to cause a file save
+// UI dialog to be displayed when printing a document.
+COMPONENT_EXPORT(PRINT_BACKEND)
+bool DoesDriverDisplayFileDialogForPrinting(const std::string& printer_name);
+
 COMPONENT_EXPORT(PRINT_BACKEND)
 std::unique_ptr<DEVMODE, base::FreeDeleter> XpsTicketToDevMode(
     const std::wstring& printer_name,
diff --git a/printing/buildflags/buildflags.gni b/printing/buildflags/buildflags.gni
index 422abea..f96a406 100644
--- a/printing/buildflags/buildflags.gni
+++ b/printing/buildflags/buildflags.gni
@@ -7,15 +7,18 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/features.gni")
 import("//build/config/sanitizers/sanitizers.gni")
+import("//pdf/features.gni")
 
 declare_args() {
-  # Enable basic printing support and UI.
+  # Enables basic printing support and UI.
   enable_basic_printing = !is_chromecast && !is_ios && !is_fuchsia
 }
 
 declare_args() {
-  # Enable printing with print preview.
-  enable_print_preview = enable_basic_printing && !is_android
+  # Enables printing with print preview.
+  # Print preview requires PDF per //printing/BUILD.gn.
+  # The is_android condition is currently redundant but left for clarity.
+  enable_print_preview = enable_basic_printing && enable_pdf && !is_android
 
   if (use_fuzzing_engine && (is_linux || is_chromeos)) {
     # For fuzzing, just restrict to chromeos and linux.
@@ -25,7 +28,7 @@
                 is_mac) && !is_chromecast && !is_fuchsia
   }
 
-  # Enable out-of-process printing.  While this definition matches
+  # Enables out-of-process printing. While this definition matches
   # `enable_print_preview`, do not base this definition upon that.  This
   # feature could still be appropriate for some build configurations which
   # explicitly disable print preview.
@@ -33,12 +36,12 @@
 }
 
 declare_args() {
-  # Enable exporting to tagged PDF.
+  # Enables exporting to tagged PDF.
   enable_tagged_pdf = enable_print_preview
 }
 
 declare_args() {
-  # Enable the CUPS IPP printing backend.
+  # Enables the CUPS IPP printing backend.
   # TODO(crbug.com/226176): Remove this after CUPS PPD API calls are removed.
   use_cups_ipp = use_cups && !is_linux
 }
diff --git a/services/device/public/cpp/device_feature_list.cc b/services/device/public/cpp/device_feature_list.cc
index 09f7fd0..05d1ca3 100644
--- a/services/device/public/cpp/device_feature_list.cc
+++ b/services/device/public/cpp/device_feature_list.cc
@@ -20,7 +20,6 @@
 // in other locations in the code base (e.g. content_features.h).
 const base::Feature* const kFeaturesExposedToJava[] = {
     &kGenericSensorExtraClasses,
-    &kWebBluetoothRequestLargerMtu,
 };
 
 // TODO(crbug.com/1060097): Removethis once a generalized FeatureList exists.
diff --git a/services/device/public/cpp/device_features.cc b/services/device/public/cpp/device_features.cc
index 8357fad..17ce17d 100644
--- a/services/device/public/cpp/device_features.cc
+++ b/services/device/public/cpp/device_features.cc
@@ -18,9 +18,5 @@
 // NetworkLocationProvider for macOS.
 const base::Feature kMacCoreLocationBackend{"MacCoreLocationBackend",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
-// Controls whether Web Bluetooth should request for a larger ATT MTU so that
-// more information can be exchanged per transmission.
-const base::Feature kWebBluetoothRequestLargerMtu{
-    "WebBluetoothRequestLargerMtu", base::FEATURE_ENABLED_BY_DEFAULT};
 
 }  // namespace features
diff --git a/services/device/public/cpp/device_features.h b/services/device/public/cpp/device_features.h
index d9f20a9..04719255 100644
--- a/services/device/public/cpp/device_features.h
+++ b/services/device/public/cpp/device_features.h
@@ -22,7 +22,6 @@
 DEVICE_FEATURES_EXPORT extern const base::Feature
     kMacCoreLocationImplementation;
 DEVICE_FEATURES_EXPORT extern const base::Feature kMacCoreLocationBackend;
-DEVICE_FEATURES_EXPORT extern const base::Feature kWebBluetoothRequestLargerMtu;
 
 }  // namespace features
 
diff --git a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
index a9cf126..18c616c 100644
--- a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
+++ b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
@@ -18,8 +18,6 @@
 public class DeviceFeatureList {
     public static final String GENERIC_SENSOR_EXTRA_CLASSES = "GenericSensorExtraClasses";
 
-    public static final String WEB_BLUETOOTH_REQUEST_LARGER_MTU = "WebBluetoothRequestLargerMtu";
-
     private DeviceFeatureList() {}
 
     /**
diff --git a/services/network/web_transport.cc b/services/network/web_transport.cc
index 53b1095..4e2e5ee6 100644
--- a/services/network/web_transport.cc
+++ b/services/network/web_transport.cc
@@ -12,11 +12,10 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "net/base/io_buffer.h"
-#include "net/quic/platform/impl/quic_mem_slice_impl.h"
+#include "net/third_party/quiche/src/common/platform/api/quiche_mem_slice.h"
 #include "net/third_party/quiche/src/quic/core/quic_session.h"
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
 #include "services/network/network_context.h"
 #include "services/network/public/mojom/web_transport.mojom.h"
 
@@ -408,10 +407,9 @@
 
   datagram_callbacks_.emplace(std::move(callback));
 
-  auto buffer = base::MakeRefCounted<net::IOBuffer>(data.size());
-  memcpy(buffer->data(), data.data(), data.size());
-  quic::QuicMemSlice slice(
-      quic::QuicMemSliceImpl(std::move(buffer), data.size()));
+  quic::QuicBuffer buffer(quic::SimpleBufferAllocator::Get(), data.size());
+  memcpy(buffer.data(), data.data(), data.size());
+  quiche::QuicheMemSlice slice(std::move(buffer));
   transport_->session()->SendOrQueueDatagram(std::move(slice));
 }
 
diff --git a/styleguide/web/web.md b/styleguide/web/web.md
index b648c923..23bfdd87 100644
--- a/styleguide/web/web.md
+++ b/styleguide/web/web.md
@@ -290,7 +290,10 @@
 For properties that don't have an RTL-friendly alternatives, use
 `html[dir='rtl']` as a prefix in your selectors.
 
-## JavaScript
+## JavaScript/TypeScript
+
+New WebUI code (except for ChromeOS specific code) should be written in
+TypeScript.
 
 ### Style
 
@@ -308,7 +311,7 @@
     * Use `@type` (instead of `@return` or `@param`) for JSDoc annotations on
       getters/setters
 
-* See [Annotating JavaScript for the Closure
+* For legacy code using closure, see [Annotating JavaScript for the Closure
   Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler)
   for @ directives
 
@@ -320,6 +323,9 @@
 
 ### Closure compiler
 
+* Closure compiler should only be used by legacy code that has not yet been
+  converted to use TypeScript.
+
 * Use the [closure
   compiler](https://chromium.googlesource.com/chromium/src/+/main/docs/closure_compilation.md)
   to identify JS type errors and enforce correct JSDoc annotations.
@@ -361,20 +367,20 @@
 
 Also see the [Google Polymer Style Guide](http://go/polymer-style).
 
-* Elements with UI should have their HTML in a .html file and logic in a JS file
-  with the same name. The HTML should be copied into the final JS file at build
-  time, replacing the special `{__html_template__}` sequence, using the
-  html_to_js BUILD.gn rule. For example the following will paste the contents
-  of my_app.html into the final generated JS file:
+* Elements with UI should have their HTML in a .html file and logic in a TS file
+  with the same name. The HTML template can be imported into the final JS file
+  at runtime from a generated JS wrapper file via the getTemplate() function.
+  THe wrapper file is generated using the html_to_wrapper gn rule:
 ```
-  html_to_js('web_components') {
-    js_files = [ 'my_app.js' ]
+  html_to_wrapper('html_wrapper_files') {
+    in_files = [ 'my_app.html' ]
   }
 ```
 
 * In new code, use class based syntax for custom elements. Example:
 ```js
-import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {getTemplate} from './my_app.html.js';
 
 class MyAppElement extends PolymerElement {
   static get is() {
@@ -382,7 +388,7 @@
   }
 
   static get template() {
-    return html`{__html_template__}`;
+    return getTemplate();
   }
 
   static get properties() {
@@ -390,6 +396,8 @@
       foo: String,
     };
   }
+
+  foo: string;
 }
 
 customElements.define(MyAppElement.is, MyAppElement);
@@ -409,6 +417,17 @@
 * Use camelCase for element IDs to simplify local DOM accessors (i.e.
   `this.$.camelCase` instead of `this.$['dash-case']`).
 
+* Note: In TypeScript, the `this.$.camelCase` accessor requires adding an
+  interface:
+
+```js
+interface MyAppElement {
+  $: {
+    camelCase: HTMLElement,
+  };
+}
+```
+
 * Use `this.foo` instead of `newFoo` arguments in observers when possible.
   This makes changing the type of `this.foo` easier (as the `@type` is
   duplicated in less places, i.e. `@param`).
@@ -473,8 +492,9 @@
 ### Example
 
 The following BUILD.gn example code uses preprocess_if_expr to preprocess any
-`<if expr>` in the final my_app.js file that is generated by the earlier
-html_to_js example. It then uses the manifest from this operation and the
+`<if expr>` in my_app.ts and in the my_app.html.ts file that is generated by
+the earlier html_to_wrapper example. It then runs the TypeScript compiler on
+the outputs of this operation and uses the manifest from this operation and the
 in_files option to place both the final, preprocessed file and a separate (not
 preprocessed) icon into a generated grd file using generate_grd:
 
@@ -482,21 +502,45 @@
 preprocess_folder = "preprocessed"
 preprocess_manifest = "preprocessed_manifest.json"
 
-# Read file from target_gen_dir, where it will be pasted by html_to_js.
 preprocess_if_expr("preprocess") {
-  deps = [ ":web_components" ]
-  in_folder = target_gen_dir
-  in_files = [ "my_app.js" ]
+  in_folder = "."
+  in_files = [ "my_app.ts" ]
   out_folder = "$target_gen_dir/$preprocess_folder"
-  out_manifest = "$target_gen_dir/$preprocess_manifest"
 }
 
-# Put the preprocessed file as well as a separate my_icon.svg file in the grd:
+# Read file from target_gen_dir, where it will be pasted by html_to_wrapper.
+preprocess_if_expr("preprocess_generated") {
+  in_folder = target_gen_dir
+  in_files = [ "my_app.html.ts" ]
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  deps = [ ":html_wrapper_files" ]
+}
+
+# Run TS compiler on the two files:
+ts_library("build_ts") {
+  root_dir = "$target_gen_dir/$preprocess_folder"
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  in_files = [
+    "my_app.html.ts",
+    "my_app.ts",
+  ]
+  deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+  ]
+  extra_deps = [
+    ":preprocess",
+    ":preprocess_generated",
+  ]
+}
+
+# Put the compiled files as well as a separate my_icon.svg file in the grd:
 generate_grd("build_grd") {
   input_files = [ "my_icon.svg" ]
   input_files_base_dir = rebase_path(".", "//")
-  deps = [ ":preprocess" ]
-  manifest_files = [ "$target_gen_dir/$preprocess_manifest" ]
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
   grd_prefix = [ "foo" ]
   out_grd = "$target_gen_dir/resources.grd"
 }
@@ -510,7 +554,7 @@
 
 Note #2:
 These preprocessor statements can live in places that surprise linters or
-formatters (for example: running clang-format on a .js file with an `<if>` in
+formatters (for example: running clang-format on a .ts file with an `<if>` in
 it).  Generally, putting these language-invalid features inside of comments
 helps alleviate problems with unexpected input.
 ***
@@ -521,7 +565,7 @@
 
 Example:
 ```js
-function isWindows() {
+function isWindows(): boolean {
   // <if expr="win">
   return true;
   // </if>
diff --git a/testing/android/native_test/native_test_launcher.cc b/testing/android/native_test/native_test_launcher.cc
index 6a5ac16..8881e174 100644
--- a/testing/android/native_test/native_test_launcher.cc
+++ b/testing/android/native_test/native_test_launcher.cc
@@ -14,13 +14,14 @@
 #include <signal.h>
 #include <string.h>
 
+#include <iterator>
+
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/at_exit.h"
 #include "base/base_switches.h"
 #include "base/clang_profiling_buildflags.h"
 #include "base/command_line.h"
-#include "base/cxx17_backports.h"
 #include "base/debug/debugger.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -86,7 +87,7 @@
 
   // Command line initialized basically, will be fully initialized later.
   static const char* const kInitialArgv[] = { "ChromeTestActivity" };
-  base::CommandLine::Init(base::size(kInitialArgv), kInitialArgv);
+  base::CommandLine::Init(std::size(kInitialArgv), kInitialArgv);
 
   std::vector<std::string> args;
 
diff --git a/testing/buildbot/chromium.mac.json b/testing/buildbot/chromium.mac.json
index 1d556f9..7e508f1 100644
--- a/testing/buildbot/chromium.mac.json
+++ b/testing/buildbot/chromium.mac.json
@@ -1899,7 +1899,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 20
         },
         "test": "browser_tests",
         "test_id_prefix": "ninja://chrome/test:browser_tests/"
@@ -9034,7 +9034,7 @@
           ],
           "quickrun_shards": 10,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
+          "shards": 12
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 7e2468c..3819cd5 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -626,6 +626,12 @@
           '--test-launcher-print-timestamps',
         ],
       },
+      # crbug.com/1300822
+      'Mac10.12 Tests': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
       'Mac10.15 Tests': {
         # crbug.com/1042757
         'swarming': {
@@ -1238,6 +1244,7 @@
       },
       'Mac11 Tests': {
         'swarming': {
+          'shards': 12,
           'quickrun_shards': 10,
         },
       },
diff --git a/third_party/blink/common/OWNERS b/third_party/blink/common/OWNERS
index 0cb6ff53..6b7ae867 100644
--- a/third_party/blink/common/OWNERS
+++ b/third_party/blink/common/OWNERS
@@ -4,6 +4,7 @@
 haraken@chromium.org
 jam@chromium.org
 jbroman@chromium.org
+kinuko@chromium.org
 pfeldman@chromium.org
 
 # Any core owner can approve blink finch flags.
diff --git a/third_party/blink/public/OWNERS b/third_party/blink/public/OWNERS
index 29908ca..869406c 100644
--- a/third_party/blink/public/OWNERS
+++ b/third_party/blink/public/OWNERS
@@ -10,6 +10,7 @@
 japhet@chromium.org
 jbroman@chromium.org
 jochen@chromium.org
+kinuko@chromium.org
 mkwst@chromium.org
 pdr@chromium.org
 pfeldman@chromium.org
diff --git a/third_party/blink/renderer/core/OWNERS b/third_party/blink/renderer/core/OWNERS
index a13affc..be1e4c4 100644
--- a/third_party/blink/renderer/core/OWNERS
+++ b/third_party/blink/renderer/core/OWNERS
@@ -43,6 +43,7 @@
 junov@chromium.org
 kbr@chromium.org
 keishi@chromium.org
+kinuko@chromium.org
 kojii@chromium.org
 kouhei@chromium.org
 masonf@chromium.org
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js b/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
index adc6d86..ac3aa4b 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendar_picker.js
@@ -2488,101 +2488,99 @@
   return DateRangeManager;
 }
 
-/**
- * @constructor
- * @extends ListCell
- * @param {!Array} shortMonthLabels
- */
-function YearListCell(shortMonthLabels) {
-  ListCell.call(this);
-  this.element.classList.add(YearListCell.ClassNameYearListCell);
-  this.element.style.height = YearListCell.GetHeight() + 'px';
+// ----------------------------------------------------------------
 
+class YearListCell extends ListCell {
   /**
-   * @type {!Element}
-   * @const
+   * @param {!Array} shortMonthLabels
    */
-  this.label = createElement('div', YearListCell.ClassNameLabel, '----');
-  this.element.appendChild(this.label);
-  this.label.style.height =
-      (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
-  this.label.style.lineHeight =
-      (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
+  constructor(shortMonthLabels) {
+    super();
+    this.element.classList.add(YearListCell.ClassNameYearListCell);
+    this.element.style.height = YearListCell.GetHeight() + 'px';
 
-  /**
-   * @type {!Array} Array of the 12 month button elements.
-   * @const
-   */
-  this.monthButtons = [];
-  var monthChooserElement =
-      createElement('div', YearListCell.ClassNameMonthChooser);
-  for (var r = 0; r < YearListCell.ButtonRows; ++r) {
-    var buttonsRow =
-        createElement('div', YearListCell.ClassNameMonthButtonsRow);
-    buttonsRow.setAttribute('role', 'row');
-    for (var c = 0; c < YearListCell.ButtonColumns; ++c) {
-      var month = c + r * YearListCell.ButtonColumns;
-      var button = createElement(
-          'div', YearListCell.ClassNameMonthButton, shortMonthLabels[month]);
-      button.setAttribute('role', 'gridcell');
-      button.dataset.month = month;
-      buttonsRow.appendChild(button);
-      this.monthButtons.push(button);
+    /**
+     * @type {!Element}
+     * @const
+     */
+    this.label = createElement('div', YearListCell.ClassNameLabel, '----');
+    this.element.appendChild(this.label);
+    this.label.style.height =
+        (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
+    this.label.style.lineHeight =
+        (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
+
+    /**
+     * @type {!Array} Array of the 12 month button elements.
+     * @const
+     */
+    this.monthButtons = [];
+    var monthChooserElement =
+        createElement('div', YearListCell.ClassNameMonthChooser);
+    for (var r = 0; r < YearListCell.ButtonRows; ++r) {
+      var buttonsRow =
+          createElement('div', YearListCell.ClassNameMonthButtonsRow);
+      buttonsRow.setAttribute('role', 'row');
+      for (var c = 0; c < YearListCell.ButtonColumns; ++c) {
+        var month = c + r * YearListCell.ButtonColumns;
+        var button = createElement(
+            'div', YearListCell.ClassNameMonthButton, shortMonthLabels[month]);
+        button.setAttribute('role', 'gridcell');
+        button.dataset.month = month;
+        buttonsRow.appendChild(button);
+        this.monthButtons.push(button);
+      }
+      monthChooserElement.appendChild(buttonsRow);
     }
-    monthChooserElement.appendChild(buttonsRow);
+    this.element.appendChild(monthChooserElement);
+
+    /**
+     * @type {!boolean}
+     * @private
+     */
+    this._selected = false;
+    /**
+     * @type {!number}
+     * @private
+     */
+    this._height = 0;
   }
-  this.element.appendChild(monthChooserElement);
 
-  /**
-   * @type {!boolean}
-   * @private
-   */
-  this._selected = false;
-  /**
-   * @type {!number}
-   * @private
-   */
-  this._height = 0;
-}
-
-{
-  YearListCell.prototype = Object.create(ListCell.prototype);
-
-  YearListCell._Height = hasInaccuratePointingDevice() ? 31 : 25;
-  YearListCell._Height = 25;
-  YearListCell.GetHeight = function() {
+  static _Height = hasInaccuratePointingDevice() ? 31 : 25;
+  static _Height = 25;
+  static GetHeight() {
     return YearListCell._Height;
-  };
-  YearListCell.BorderBottomWidth = 1;
-  YearListCell.ButtonRows = 3;
-  YearListCell.ButtonColumns = 4;
-  YearListCell._SelectedHeight = 128;
-  YearListCell.GetSelectedHeight = function() {
+  }
+  static BorderBottomWidth = 1;
+  static ButtonRows = 3;
+  static ButtonColumns = 4;
+  static _SelectedHeight = 128;
+  static GetSelectedHeight() {
     return YearListCell._SelectedHeight;
-  };
-  YearListCell.ClassNameYearListCell = 'year-list-cell';
-  YearListCell.ClassNameLabel = 'label';
-  YearListCell.ClassNameMonthChooser = 'month-chooser';
-  YearListCell.ClassNameMonthButtonsRow = 'month-buttons-row';
-  YearListCell.ClassNameMonthButton = 'month-button';
-  YearListCell.ClassNameHighlighted = 'highlighted';
-  YearListCell.ClassNameSelected = 'selected';
-  YearListCell.ClassNameToday = 'today';
+  }
+  static ClassNameYearListCell = 'year-list-cell';
+  static ClassNameLabel = 'label';
+  static ClassNameMonthChooser = 'month-chooser';
+  static ClassNameMonthButtonsRow = 'month-buttons-row';
+  static ClassNameMonthButton = 'month-button';
+  static ClassNameHighlighted = 'highlighted';
+  static ClassNameSelected = 'selected';
+  static ClassNameToday = 'today';
 
-  YearListCell._recycleBin = [];
+  static _recycleBin = [];
 
   /**
    * @return {!Array}
    * @override
    */
-  YearListCell.prototype._recycleBin = function() {
+  _recycleBin() {
     return YearListCell._recycleBin;
-  };
+  }
 
   /**
    * @param {!number} row
    */
-  YearListCell.prototype.reset = function(row) {
+  reset(row) {
     this.row = row;
     this.label.textContent = row + 1;
     for (var i = 0; i < this.monthButtons.length; ++i) {
@@ -2591,26 +2589,28 @@
       this.monthButtons[i].classList.remove(YearListCell.ClassNameToday);
     }
     this.show();
-  };
+  }
 
   /**
    * @return {!number} The height in pixels.
    */
-  YearListCell.prototype.height = function() {
+  height() {
     return this._height;
-  };
+  }
 
   /**
    * @param {!number} height Height in pixels.
    */
-  YearListCell.prototype.setHeight = function(height) {
+  setHeight(height) {
     if (this._height === height)
       return;
     this._height = height;
     this.element.style.height = this._height + 'px';
-  };
+  }
 }
 
+// ----------------------------------------------------------------
+
 // clang-format off
 class YearListView extends dateRangeManagerMixin(ListView) {
   // clang-format on
@@ -3665,68 +3665,64 @@
   };
 }
 
-/**
- * @constructor
- * @extends ListCell
- */
-function DayCell() {
-  ListCell.call(this);
-  this.element.classList.add(DayCell.ClassNameDayCell);
-  this.element.style.width = DayCell.GetWidth() + 'px';
-  this.element.style.height = DayCell.GetHeight() + 'px';
-  this.element.style.lineHeight =
-      (DayCell.GetHeight() - DayCell.PaddingSize * 2) + 'px';
-  this.element.setAttribute('role', 'gridcell');
-  /**
-   * @type {?Day}
-   */
-  this.day = null;
-};
+// ----------------------------------------------------------------
 
-{
-  DayCell.prototype = Object.create(ListCell.prototype);
+class DayCell extends ListCell {
+  constructor() {
+    super();
+    this.element.classList.add(DayCell.ClassNameDayCell);
+    this.element.style.width = DayCell.GetWidth() + 'px';
+    this.element.style.height = DayCell.GetHeight() + 'px';
+    this.element.style.lineHeight =
+        (DayCell.GetHeight() - DayCell.PaddingSize * 2) + 'px';
+    this.element.setAttribute('role', 'gridcell');
+    /**
+     * @type {?Day}
+     */
+    this.day = null;
+  }
 
-  DayCell._Width = 28;
-  DayCell.GetWidth = function() {
+  static _Width = 28;
+  static GetWidth() {
     return DayCell._Width;
-  };
-  DayCell._Height = 28;
-  DayCell.GetHeight = function() {
+  }
+  static _Height = 28;
+  static GetHeight() {
     return DayCell._Height;
-  };
-  DayCell.PaddingSize = 1;
-  DayCell.ClassNameDayCell = 'day-cell';
-  DayCell.ClassNameHighlighted = 'highlighted';
-  DayCell.ClassNameDisabled = 'disabled';
-  DayCell.ClassNameCurrentMonth = 'current-month';
-  DayCell.ClassNameToday = 'today';
+  }
+  static PaddingSize = 1;
+  static ClassNameDayCell = 'day-cell';
+  static ClassNameHighlighted = 'highlighted';
+  static ClassNameDisabled = 'disabled';
+  static ClassNameCurrentMonth = 'current-month';
+  static ClassNameToday = 'today';
 
-  DayCell._recycleBin = [];
+  static _recycleBin = [];
 
-  DayCell.recycleOrCreate = function() {
+  static recycleOrCreate() {
     return DayCell._recycleBin.pop() || new DayCell();
-  };
+  }
 
   /**
    * @return {!Array}
    * @override
    */
-  DayCell.prototype._recycleBin = function() {
+  _recycleBin() {
     return DayCell._recycleBin;
-  };
+  }
 
   /**
    * @override
    */
-  DayCell.prototype.throwAway = function() {
-    ListCell.prototype.throwAway.call(this);
+  throwAway() {
+    super.throwAway();
     this.day = null;
-  };
+  }
 
   /**
    * @param {!boolean} highlighted
    */
-  DayCell.prototype.setHighlighted = function(highlighted) {
+  setHighlighted(highlighted) {
     if (highlighted) {
       this.element.classList.add(DayCell.ClassNameHighlighted);
       this.element.setAttribute('aria-selected', 'true');
@@ -3734,48 +3730,48 @@
       this.element.classList.remove(DayCell.ClassNameHighlighted);
       this.element.setAttribute('aria-selected', 'false');
     }
-  };
+  }
 
   /**
    * @param {!boolean} disabled
    */
-  DayCell.prototype.setDisabled = function(disabled) {
+  setDisabled(disabled) {
     if (disabled)
       this.element.classList.add(DayCell.ClassNameDisabled);
     else
       this.element.classList.remove(DayCell.ClassNameDisabled);
-  };
+  }
 
   /**
    * @param {!boolean} selected
    */
-  DayCell.prototype.setIsInCurrentMonth = function(selected) {
+  setIsInCurrentMonth(selected) {
     if (selected)
       this.element.classList.add(DayCell.ClassNameCurrentMonth);
     else
       this.element.classList.remove(DayCell.ClassNameCurrentMonth);
-  };
+  }
 
   /**
    * @param {!boolean} selected
    */
-  DayCell.prototype.setIsToday = function(selected) {
+  setIsToday(selected) {
     if (selected)
       this.element.classList.add(DayCell.ClassNameToday);
     else
       this.element.classList.remove(DayCell.ClassNameToday);
-  };
+  }
 
   /**
    * @param {!Day} day
    */
-  DayCell.prototype.reset = function(day) {
+  reset(day) {
     this.day = day;
     this.element.textContent = localizeNumber(this.day.date.toString());
     this.element.setAttribute('aria-label', this.day.format());
     this.element.id = this.day.toString();
     this.show();
-  };
+  }
 }
 
 // ----------------------------------------------------------------
@@ -5023,6 +5019,7 @@
 // Necessary for some web tests.
 window.CalendarPicker = CalendarPicker;
 window.Day = Day;
+window.DayCell = DayCell;
 window.Month = Month;
 window.Week = Week;
 window.WeekNumberCell = WeekNumberCell;
diff --git a/third_party/blink/renderer/core/inspector/inspect_tools.cc b/third_party/blink/renderer/core/inspector/inspect_tools.cc
index b3c08bd..6fe1580 100644
--- a/third_party/blink/renderer/core/inspector/inspect_tools.cc
+++ b/third_party/blink/renderer/core/inspector/inspect_tools.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/inspector/node_content_visibility_state.h"
 #include "third_party/blink/renderer/core/layout/hit_test_location.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/cursors.h"
@@ -347,6 +348,9 @@
   std::tie(node_, content_visibility_state_) =
       DetermineContentVisibilityState(node);
   contrast_info_ = FetchContrast(node_);
+  if (auto* flexbox = DynamicTo<LayoutNGFlexibleBox>(node->GetLayoutObject())) {
+    flexbox->SetNeedsLayoutForDevtools();
+  }
 }
 
 String NodeHighlightTool::GetOverlayName() {
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index 6e72d67..a241ec7 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
@@ -1040,12 +1041,18 @@
          layout_flex->StyleRef().ResolvedIsColumnFlexDirection();
 }
 
-Vector<Vector<std::pair<PhysicalRect, float>>> GetFlexLinesAndItems(
-    LayoutBox* layout_box,
-    bool is_horizontal,
-    bool is_reverse) {
-  Vector<Vector<std::pair<PhysicalRect, float>>> flex_lines;
+DevtoolsFlexInfo GetFlexLinesAndItems(LayoutBox* layout_box,
+                                      bool is_horizontal,
+                                      bool is_reverse) {
+  if (auto* layout_ng_flex = DynamicTo<LayoutNGFlexibleBox>(layout_box)) {
+    const DevtoolsFlexInfo* flex_info_from_layout =
+        layout_ng_flex->FlexLayoutData();
+    DCHECK(flex_info_from_layout);
+    return *flex_info_from_layout;
+  }
 
+  DevtoolsFlexInfo flex_info;
+  Vector<DevtoolsFlexInfo::Line>& flex_lines = flex_info.lines;
   // Flex containers can't get fragmented yet, but this may change in the
   // future.
   for (const auto& fragment : layout_box->PhysicalFragments()) {
@@ -1084,13 +1091,14 @@
         flex_lines.emplace_back();
       }
 
-      flex_lines.back().push_back(std::make_pair(item_rect, adjusted_baseline));
+      flex_lines.back().items.push_back(
+          DevtoolsFlexInfo::Item(item_rect, LayoutUnit(adjusted_baseline)));
 
       progression = is_reverse ? item_start : item_end;
     }
   }
 
-  return flex_lines;
+  return flex_info;
 }
 
 std::unique_ptr<protocol::DictionaryValue> BuildFlexContainerInfo(
@@ -1120,28 +1128,28 @@
   container_builder.AppendPath(QuadToPath(content_quad), scale);
 
   // Gather all flex items, sorted by flex line.
-  Vector<Vector<std::pair<PhysicalRect, float>>> flex_lines =
+  DevtoolsFlexInfo flex_lines =
       GetFlexLinesAndItems(layout_box, is_horizontal, is_reverse);
 
   // We send a list of flex lines, each containing a list of flex items, with
   // their baselines, to the frontend.
   std::unique_ptr<protocol::ListValue> lines_info =
       protocol::ListValue::create();
-  for (auto line : flex_lines) {
+  for (auto line : flex_lines.lines) {
     std::unique_ptr<protocol::ListValue> items_info =
         protocol::ListValue::create();
-    for (auto item_data : line) {
+    for (auto item_data : line.items) {
       std::unique_ptr<protocol::DictionaryValue> item_info =
           protocol::DictionaryValue::create();
 
       gfx::QuadF item_margin_quad =
-          layout_object->LocalRectToAbsoluteQuad(item_data.first);
+          layout_object->LocalRectToAbsoluteQuad(item_data.rect);
       FrameQuadToViewport(containing_view, item_margin_quad);
       PathBuilder item_builder;
       item_builder.AppendPath(QuadToPath(item_margin_quad), scale);
 
       item_info->setValue("itemBorder", item_builder.Release());
-      item_info->setDouble("baseline", item_data.second);
+      item_info->setDouble("baseline", item_data.baseline);
 
       items_info->pushValue(std::move(item_info));
     }
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 7a2c292a..1f80dd0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -935,11 +935,10 @@
   } else {
     config->color_format = ColorFormat::kHex;
   }
-
-  node->GetDocument().EnsurePaintLocationDataValidForNode(
-      node, DocumentUpdateReason::kInspector);
   NodeHighlightTool tool(this, GetFrontend(), node, "" /* selector_list */,
                          std::move(config));
+  node->GetDocument().EnsurePaintLocationDataValidForNode(
+      node, DocumentUpdateReason::kInspector);
   *result = tool.GetNodeInspectorHighlightAsJson(
       true /* append_element_info */, include_distance.fromMaybe(false));
   return Response::Success();
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 196d758..f2416f9 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -758,6 +758,7 @@
 const char kScrollbarChanged[] = "Scrollbar changed";
 const char kDisplayLock[] = "Display lock";
 const char kCanvasFormattedTextRunChange[] = "CanvasFormattedText runs changed";
+const char kDevtools[] = "Inspected by devtools";
 }  // namespace layout_invalidation_reason
 
 void inspector_layout_invalidation_tracking_event::Data(
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
index 4ce22e6..61072710 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -298,6 +298,7 @@
 extern const char kScrollbarChanged[];
 extern const char kDisplayLock[];
 extern CORE_EXPORT const char kCanvasFormattedTextRunChange[];
+extern const char kDevtools[];
 }  // namespace layout_invalidation_reason
 
 // LayoutInvalidationReasonForTracing is strictly for tracing. Blink logic must
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index 658f98e7..9d3c565 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -312,6 +312,7 @@
   "ng/flex/ng_flex_break_token_data.h",
   "ng/flex/ng_flex_child_iterator.cc",
   "ng/flex/ng_flex_child_iterator.h",
+  "ng/flex/ng_flex_data.h",
   "ng/flex/ng_flex_item_iterator.cc",
   "ng/flex/ng_flex_item_iterator.h",
   "ng/flex/ng_flex_layout_algorithm.cc",
@@ -477,6 +478,8 @@
   "ng/mathml/ng_math_scripts_layout_algorithm.h",
   "ng/mathml/ng_math_space_layout_algorithm.cc",
   "ng/mathml/ng_math_space_layout_algorithm.h",
+  "ng/mathml/ng_math_token_layout_algorithm.cc",
+  "ng/mathml/ng_math_token_layout_algorithm.h",
   "ng/mathml/ng_math_under_over_layout_algorithm.cc",
   "ng/mathml/ng_math_under_over_layout_algorithm.h",
   "ng/mathml/ng_mathml_paint_info.h",
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 762b5ffb..ad42a4f 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -2190,6 +2190,14 @@
     NOT_DESTROYED();
     return bitfields_.WhitespaceChildrenMayChange();
   }
+  void SetNeedsDevtoolsInfo(bool b) {
+    NOT_DESTROYED();
+    bitfields_.SetNeedsDevtoolsInfo(b);
+  }
+  bool NeedsDevtoolsInfo() const {
+    NOT_DESTROYED();
+    return bitfields_.NeedsDevtoolsInfo();
+  }
 
   virtual void Paint(const PaintInfo&) const;
 
@@ -3960,6 +3968,7 @@
           might_traverse_physical_fragments_(false),
           is_anonymous_ng_multicol_inline_wrapper_(false),
           whitespace_children_may_change_(false),
+          needs_devtools_info_(false),
           positioned_state_(kIsStaticallyPositioned),
           selection_state_(static_cast<unsigned>(SelectionState::kNone)),
           subtree_paint_property_update_reasons_(
@@ -4309,6 +4318,8 @@
     ADD_BOOLEAN_BITFIELD(whitespace_children_may_change_,
                          WhitespaceChildrenMayChange);
 
+    ADD_BOOLEAN_BITFIELD(needs_devtools_info_, NeedsDevtoolsInfo);
+
    private:
     // This is the cached 'position' value of this object
     // (see ComputedStyle::position).
diff --git a/third_party/blink/renderer/core/layout/layout_object_factory.cc b/third_party/blink/renderer/core/layout/layout_object_factory.cc
index 5bcbfb9..e921275 100644
--- a/third_party/blink/renderer/core/layout/layout_object_factory.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_factory.cc
@@ -65,6 +65,7 @@
 #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
 #include "third_party/blink/renderer/core/mathml/mathml_element.h"
+#include "third_party/blink/renderer/core/mathml/mathml_token_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
@@ -162,7 +163,7 @@
                                              LegacyLayout legacy) {
   DCHECK(IsA<MathMLElement>(node));
   bool disable_ng_for_type = !RuntimeEnabledFeatures::MathMLCoreEnabled();
-  if (To<MathMLElement>(node).IsTokenElement()) {
+  if (IsA<MathMLTokenElement>(node)) {
     return CreateObject<LayoutBlockFlow, LayoutNGMathMLBlockFlow,
                         LayoutBlockFlow>(node, legacy, disable_ng_for_type);
   }
diff --git a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc
index 0ae1e244..b751a764 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 
 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
@@ -76,44 +77,16 @@
   return LayoutNGMixin<LayoutBlock>::IsChildAllowed(object, style);
 }
 
-// This is devtools' entry point into layout. This function is intended to have
-// no side effects on the NGFragment tree or the LayoutObject tree.
-//
-// Execution time of this function is not critical, but the flex item layouts
-// will hit their caches, so it's probably fast anyway.
-DevtoolsFlexInfo LayoutNGFlexibleBox::LayoutForDevtools() {
-  DCHECK(!NeedsLayout());
-  DevtoolsReadonlyLayoutScope fragments_and_tree_are_now_readonly;
-  const NGLayoutResult* old_layout_result = GetCachedLayoutResult();
-#if DCHECK_IS_ON()
-  MinMaxSizes old_min_max_sizes = IntrinsicLogicalWidths();
-  String old_string = old_layout_result->PhysicalFragment().DumpFragmentTree(
-      NGPhysicalLineBoxFragment::DumpAll);
-#endif
+void LayoutNGFlexibleBox::SetNeedsLayoutForDevtools() {
+  SetNeedsLayout(layout_invalidation_reason::kDevtools);
+  SetNeedsDevtoolsInfo(true);
+}
 
-  DCHECK(old_layout_result);
-  const NGConstraintSpace& constraint_space =
-      old_layout_result->GetConstraintSpaceForCaching();
-
-  NGBlockNode node(this);
-  NGFragmentGeometry fragment_geometry =
-      CalculateInitialFragmentGeometry(constraint_space, node);
-  NGLayoutAlgorithmParams params(node, fragment_geometry, constraint_space);
-  DevtoolsFlexInfo flex_info;
-  NGFlexLayoutAlgorithm flex_algorithm(params, &flex_info);
-  [[maybe_unused]] auto* new_result = flex_algorithm.Layout();
-
-#if DCHECK_IS_ON()
-  MinMaxSizes new_min_max_sizes = IntrinsicLogicalWidths();
-  DCHECK_EQ(old_min_max_sizes, new_min_max_sizes)
-      << "Legacy min_max_sizes changed!";
-
-  String new_string = new_result->PhysicalFragment().DumpFragmentTree(
-      NGPhysicalLineBoxFragment::DumpAll);
-  DCHECK_EQ(old_string, new_string) << "Fragment tree changed!";
-#endif
-
-  return flex_info;
+const DevtoolsFlexInfo* LayoutNGFlexibleBox::FlexLayoutData() const {
+  const wtf_size_t fragment_count = PhysicalFragmentCount();
+  DCHECK_GE(fragment_count, 1u);
+  // Currently, devtools data is on the first fragment of a fragmented flexbox.
+  return GetLayoutResult(0)->FlexLayoutData();
 }
 
 void LayoutNGFlexibleBox::RemoveChild(LayoutObject* child) {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h
index 13d9bcf..73be37e 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_FLEX_LAYOUT_NG_FLEXIBLE_BOX_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h"
 #include "third_party/blink/renderer/core/layout/ng/layout_ng_block.h"
 
 namespace blink {
@@ -15,16 +16,6 @@
 // Layout doesn't store this flex line -> flex items hierarchy there, or
 // anywhere, because neither paint nor ancestor layout needs it. So the NG flex
 // layout algorithm will fill one of these in when devtools requests it.
-struct DevtoolsFlexInfo {
-  struct Item {
-    PhysicalRect rect;
-    LayoutUnit baseline;
-  };
-  struct Line {
-    Vector<Item> items;
-  };
-  Vector<Line> lines;
-};
 
 class CORE_EXPORT LayoutNGFlexibleBox : public LayoutNGBlock {
  public:
@@ -48,7 +39,11 @@
     return "LayoutNGFlexibleBox";
   }
 
-  DevtoolsFlexInfo LayoutForDevtools();
+  const DevtoolsFlexInfo* FlexLayoutData() const;
+  // Once this is set to true it is never set back to false. This is maybe okay,
+  // but could make devtools use too much memory after a lot of flexboxes have
+  // been inspected.
+  void SetNeedsLayoutForDevtools();
 
  protected:
   bool IsChildAllowed(LayoutObject* object,
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h
new file mode 100644
index 0000000..8728993
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h
@@ -0,0 +1,28 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_FLEX_NG_FLEX_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_FLEX_NG_FLEX_DATA_H_
+
+#include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/platform/geometry/layout_unit.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+struct DevtoolsFlexInfo {
+  struct Item {
+    Item(PhysicalRect rect, LayoutUnit baseline)
+        : rect(rect), baseline(baseline) {}
+    PhysicalRect rect;
+    LayoutUnit baseline;
+  };
+  struct Line {
+    Vector<Item> items;
+  };
+  Vector<Line> lines;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_FLEX_NG_FLEX_DATA_H_
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 da66dac3..a72fc2b 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
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_break_token_data.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_child_iterator.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_item_iterator.h"
 #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_line.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
@@ -67,8 +68,10 @@
       algorithm_(&Style(),
                  MainAxisContentExtent(LayoutUnit::Max()),
                  child_percentage_size_,
-                 &Node().GetDocument()),
-      layout_info_for_devtools_(layout_info_for_devtools) {
+                 &Node().GetDocument()) {
+  if (Node().GetLayoutBox()->NeedsDevtoolsInfo())
+    layout_info_for_devtools_ = std::make_unique<DevtoolsFlexInfo>();
+
   // TODO(almaher): Support multi-line column fragmentation.
   involved_in_block_fragmentation_ =
       InvolvedInBlockFragmentation(container_builder_) &&
@@ -815,7 +818,6 @@
 NGFlexLayoutAlgorithm::RelayoutIgnoringChildScrollbarChanges() {
   DCHECK(!ignore_child_scrollbar_changes_);
   DCHECK(!layout_info_for_devtools_);
-  DCHECK(!DevtoolsReadonlyLayoutScope::InDevtoolsLayout());
   NGLayoutAlgorithmParams params(
       Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(),
       BreakToken(), /* early_break */ nullptr);
@@ -911,7 +913,10 @@
 
   if (has_column_percent_flex_basis_)
     container_builder_.SetHasDescendantThatDependsOnPercentageBlockSize(true);
-
+  if (UNLIKELY(layout_info_for_devtools_)) {
+    container_builder_.TransferFlexLayoutData(
+        std::move(layout_info_for_devtools_));
+  }
   if (UNLIKELY(InvolvedInBlockFragmentation(container_builder_))) {
     NGBreakStatus break_status = FinishFragmentation(
         Node(), ConstraintSpace(), BorderPadding().block_end,
@@ -1480,9 +1485,7 @@
     // devtools uses margin box.
     item_rect.Expand(flex_item->physical_margins_);
     DCHECK_GE(layout_info_for_devtools_->lines.size(), 1u);
-    DevtoolsFlexInfo::Item item;
-    item.rect = item_rect;
-    item.baseline = flex_item->MarginBoxAscent();
+    DevtoolsFlexInfo::Item item(item_rect, flex_item->MarginBoxAscent());
     layout_info_for_devtools_->lines[flex_line_idx].items.push_back(item);
   }
 
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 c7c3cbbe..5ed95e9 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
@@ -159,7 +159,7 @@
   bool has_processed_first_line_ = false;
 
   FlexLayoutAlgorithm algorithm_;
-  DevtoolsFlexInfo* layout_info_for_devtools_;
+  std::unique_ptr<DevtoolsFlexInfo> layout_info_for_devtools_;
 
   // The block size of the entire flex container (ignoring any fragmentation).
   LayoutUnit total_block_size_;
diff --git a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm_test.cc
index cdca35d..dd65d743 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/ng_flex_layout_algorithm_test.cc
@@ -17,11 +17,12 @@
  protected:
   DevtoolsFlexInfo LayoutForDevtools(const String& body_content) {
     SetBodyInnerHTML(body_content);
-    UpdateAllLifecyclePhasesForTest();
     LayoutNGFlexibleBox* flex =
         To<LayoutNGFlexibleBox>(GetLayoutObjectByElementId("flexbox"));
     EXPECT_NE(flex, nullptr);
-    return flex->LayoutForDevtools();
+    flex->SetNeedsLayoutForDevtools();
+    UpdateAllLifecyclePhasesForTest();
+    return *flex->FlexLayoutData();
   }
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
index 834e1de9..7a36a93b 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/mathml/mathml_operator_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_radical_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_scripts_element.h"
+#include "third_party/blink/renderer/core/mathml/mathml_token_element.h"
 
 namespace blink {
 
@@ -232,12 +233,20 @@
   return base_properties && base_properties->has_movablelimits;
 }
 
-bool IsOperatorWithSpecialShaping(const NGBlockNode& node) {
+bool IsTextOnlyToken(const NGBlockNode& node) {
   if (!node.IsBlock() || !node.IsMathML() || !node.FirstChild().IsInline())
     return false;
+  if (auto* element = DynamicTo<MathMLTokenElement>(node.GetDOMNode()))
+    return !element->GetTokenContent().characters.IsNull();
+  return false;
+}
+
+bool IsOperatorWithSpecialShaping(const NGBlockNode& node) {
+  if (!IsTextOnlyToken(node))
+    return false;
   // https://w3c.github.io/mathml-core/#layout-of-operators
   if (auto* element = DynamicTo<MathMLOperatorElement>(node.GetDOMNode())) {
-    UChar32 base_code_point = element->GetOperatorContent().code_point;
+    UChar32 base_code_point = element->GetTokenContent().code_point;
     if (base_code_point == kNonCharacter ||
         !node.Style().GetFont().PrimaryFont() ||
         !node.Style().GetFont().PrimaryFont()->GlyphForCharacter(
@@ -396,7 +405,7 @@
   properties.is_large_op =
       core_operator->HasBooleanProperty(MathMLOperatorElement::kLargeOp);
 
-  properties.is_vertical = core_operator->GetOperatorContent().is_vertical;
+  properties.is_vertical = core_operator->IsVertical();
 
   LayoutUnit leading_space(core_operator->DefaultLeadingSpace() *
                            core_operator_style.FontSize());
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
index 7592c30..59364d4 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h
@@ -97,6 +97,7 @@
                                                       UChar character);
 
 bool IsUnderOverLaidOutAsSubSup(const NGBlockNode& node);
+bool IsTextOnlyToken(const NGBlockNode& node);
 bool IsOperatorWithSpecialShaping(const NGBlockNode& node);
 
 LayoutUnit MathTableBaseline(const ComputedStyle&, LayoutUnit block_offset);
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_operator_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_operator_layout_algorithm.cc
index 82d53d3..87455e8 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_operator_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_operator_layout_algorithm.cc
@@ -59,7 +59,7 @@
   auto* element = DynamicTo<MathMLOperatorElement>(Node().GetDOMNode());
   if (element->HasBooleanProperty(MathMLOperatorElement::kStretchy)) {
     // "If the operator has the stretchy property:"
-    if (!element->GetOperatorContent().is_vertical) {
+    if (!element->IsVertical()) {
       // "If the stretch axis of the operator is inline."
       if (ConstraintSpace().HasTargetStretchInlineSize())
         operator_target_size = ConstraintSpace().TargetStretchInlineSize();
@@ -130,9 +130,8 @@
 
   StretchyOperatorShaper shaper(
       GetBaseCodePoint(),
-      element->GetOperatorContent().is_vertical
-          ? OpenTypeMathStretchData::StretchAxis::Vertical
-          : OpenTypeMathStretchData::StretchAxis::Horizontal);
+      element->IsVertical() ? OpenTypeMathStretchData::StretchAxis::Vertical
+                            : OpenTypeMathStretchData::StretchAxis::Horizontal);
   StretchyOperatorShaper::Metrics metrics;
   scoped_refptr<ShapeResult> shape_result =
       shaper.Shape(&Style().GetFont(), operator_target_size, &metrics);
@@ -159,7 +158,7 @@
   LayoutUnit ascent = BorderScrollbarPadding().block_start + operator_ascent;
   LayoutUnit descent = operator_descent + BorderScrollbarPadding().block_end;
   if (element->HasBooleanProperty(MathMLOperatorElement::kStretchy) &&
-      element->GetOperatorContent().is_vertical) {
+      element->IsVertical()) {
     // "The stretchy glyph is shifted towards the line-under by a value Δ so
     // that its center aligns with the center of the target"
     LayoutUnit delta = ((operator_ascent - operator_descent) -
@@ -187,7 +186,7 @@
   auto* element = DynamicTo<MathMLOperatorElement>(Node().GetDOMNode());
   if (element->HasBooleanProperty(MathMLOperatorElement::kStretchy)) {
     // "If the operator has the stretchy property:"
-    if (!element->GetOperatorContent().is_vertical) {
+    if (!element->IsVertical()) {
       // "If the stretch axis of the operator is inline."
       // The spec current says we should rely on the layout algorithm of
       // § 3.2.1.1 Layout of <mtext>. Instead, we perform horizontal stretching
@@ -219,7 +218,7 @@
 
 UChar32 NGMathOperatorLayoutAlgorithm::GetBaseCodePoint() const {
   return DynamicTo<MathMLOperatorElement>(Node().GetDOMNode())
-      ->GetOperatorContent()
+      ->GetTokenContent()
       .code_point;
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.cc
new file mode 100644
index 0000000..8691cad
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.cc
@@ -0,0 +1,84 @@
+// 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 "third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.h"
+
+#include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_layout_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
+#include "third_party/blink/renderer/core/mathml/mathml_token_element.h"
+
+namespace blink {
+
+NGMathTokenLayoutAlgorithm::NGMathTokenLayoutAlgorithm(
+    const NGLayoutAlgorithmParams& params)
+    : NGLayoutAlgorithm(params) {
+  DCHECK(params.space.IsNewFormattingContext());
+  container_builder_.SetIsInlineFormattingContext(
+      Node().IsInlineFormattingContextRoot());
+}
+
+const NGLayoutResult* NGMathTokenLayoutAlgorithm::Layout() {
+  DCHECK(!IsResumingLayout(BreakToken()));
+
+  NGLayoutInputNode child = Node().FirstChild();
+  DCHECK(child && child.IsInline());
+  DCHECK(!child.NextSibling());
+  DCHECK(!child.IsOutOfFlowPositioned());
+
+  TextMetrics metrics(Style().GetFont(), Style().Direction(),
+                      kAlphabeticTextBaseline, kStartTextAlign,
+                      DynamicTo<MathMLTokenElement>(Node().GetDOMNode())
+                          ->GetTokenContent()
+                          .characters);
+  LayoutUnit ink_ascent(metrics.actualBoundingBoxAscent());
+  LayoutUnit ink_descent(metrics.actualBoundingBoxDescent());
+  LayoutUnit ascent = BorderScrollbarPadding().block_start + ink_ascent;
+  LayoutUnit descent = ink_descent + BorderScrollbarPadding().block_end;
+
+  NGInlineChildLayoutContext context;
+  NGFragmentItemsBuilder items_builder(
+      To<NGInlineNode>(child), container_builder_.GetWritingDirection());
+  container_builder_.SetItemsBuilder(&items_builder);
+  context.SetItemsBuilder(&items_builder);
+  const NGLayoutResult* child_layout_result =
+      To<NGInlineNode>(child).Layout(ConstraintSpace(), nullptr, &context);
+
+  const auto& line_box =
+      To<NGPhysicalLineBoxFragment>(child_layout_result->PhysicalFragment());
+  const FontHeight line_metrics = line_box.Metrics();
+  container_builder_.AddResult(
+      *child_layout_result,
+      {BorderScrollbarPadding().inline_start, ascent - line_metrics.ascent});
+
+  LayoutUnit intrinsic_block_size = ascent + descent;
+  LayoutUnit block_size = ComputeBlockSizeForFragment(
+      ConstraintSpace(), Style(), BorderPadding(), intrinsic_block_size,
+      container_builder_.InitialBorderBoxSize().inline_size);
+  container_builder_.SetBaseline(ascent);
+  container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
+  container_builder_.SetFragmentsTotalBlockSize(block_size);
+
+  return container_builder_.ToBoxFragment();
+}
+
+MinMaxSizesResult NGMathTokenLayoutAlgorithm::ComputeMinMaxSizes(
+    const MinMaxSizesFloatInput& input) {
+  NGLayoutInputNode child = Node().FirstChild();
+  DCHECK(child && child.IsInline());
+  DCHECK(!child.NextSibling());
+  DCHECK(!child.IsOutOfFlowPositioned());
+
+  MinMaxSizes sizes;
+  sizes += BorderScrollbarPadding().InlineSum();
+
+  const auto child_result = To<NGInlineNode>(child).ComputeMinMaxSizes(
+      Style().GetWritingMode(), ConstraintSpace(), MinMaxSizesFloatInput());
+  sizes += child_result.sizes;
+
+  return MinMaxSizesResult(sizes, /* depends_on_block_constraints */ false);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.h
new file mode 100644
index 0000000..1769417
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.h
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_TOKEN_LAYOUT_ALGORITHM_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_TOKEN_LAYOUT_ALGORITHM_H_
+
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_algorithm.h"
+
+namespace blink {
+
+class CORE_EXPORT NGMathTokenLayoutAlgorithm
+    : public NGLayoutAlgorithm<NGBlockNode,
+                               NGBoxFragmentBuilder,
+                               NGBlockBreakToken> {
+ public:
+  explicit NGMathTokenLayoutAlgorithm(const NGLayoutAlgorithmParams& params);
+
+ private:
+  const NGLayoutResult* Layout() final;
+  MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesFloatInput&) final;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_MATHML_NG_MATH_TOKEN_LAYOUT_ALGORITHM_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 821d1a8..83e824ce 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_row_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_scripts_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_space_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_token_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/mathml/ng_math_under_over_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h"
@@ -76,6 +77,7 @@
 #include "third_party/blink/renderer/core/mathml/mathml_radical_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_scripts_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_space_element.h"
+#include "third_party/blink/renderer/core/mathml/mathml_token_element.h"
 #include "third_party/blink/renderer/core/mathml/mathml_under_over_element.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
@@ -143,10 +145,11 @@
     } else if (IsA<MathMLPaddedElement>(element)) {
       CreateAlgorithmAndRun<NGMathPaddedLayoutAlgorithm>(params, callback);
       return;
-    } else if (IsA<MathMLElement>(element) &&
-               To<MathMLElement>(*element).IsTokenElement()) {
+    } else if (IsA<MathMLTokenElement>(element)) {
       if (IsOperatorWithSpecialShaping(params.node))
         CreateAlgorithmAndRun<NGMathOperatorLayoutAlgorithm>(params, callback);
+      else if (IsTextOnlyToken(params.node))
+        CreateAlgorithmAndRun<NGMathTokenLayoutAlgorithm>(params, callback);
       else
         CreateAlgorithmAndRun<NGBlockLayoutAlgorithm>(params, callback);
       return;
@@ -417,11 +420,6 @@
   const NGLayoutResult* layout_result =
       box_->CachedLayoutResult(constraint_space, break_token, early_break,
                                &fragment_geometry, &cache_status);
-  if (UNLIKELY(DevtoolsReadonlyLayoutScope::InDevtoolsLayout())) {
-    DCHECK_EQ(cache_status, NGLayoutCacheStatus::kHit);
-    DCHECK(!box_->NeedsLayoutOverflowRecalc());
-    return layout_result;
-  }
   if (cache_status == NGLayoutCacheStatus::kHit) {
     DCHECK(layout_result);
 
@@ -1762,13 +1760,6 @@
   if (constraint_space.CacheSlot() == NGCacheSlot::kLayout && !layout_result)
     layout_result = old_measure_result;
 
-  if (UNLIKELY(DevtoolsReadonlyLayoutScope::InDevtoolsLayout())) {
-    DCHECK(layout_result);
-    DCHECK(!box_->NeedsLayout());
-    DCHECK(MaySkipLegacyLayout(*this, *layout_result, constraint_space));
-    return layout_result;
-  }
-
   // We need to force a layout on the child if the constraint space given will
   // change the layout.
   bool needs_force_relayout =
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 b0c3193..7516289 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
@@ -9,6 +9,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/layout/geometry/box_sides.h"
 #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h"
@@ -588,6 +589,10 @@
       std::unique_ptr<NGGridLayoutData> grid_layout_data) {
     grid_layout_data_ = std::move(grid_layout_data);
   }
+  void TransferFlexLayoutData(
+      std::unique_ptr<DevtoolsFlexInfo> flex_layout_data) {
+    flex_layout_data_ = std::move(flex_layout_data);
+  }
 
   const NGGridLayoutData& GridLayoutData() const {
     DCHECK(grid_layout_data_);
@@ -736,6 +741,8 @@
   // Grid specific types.
   std::unique_ptr<NGGridLayoutData> grid_layout_data_;
 
+  std::unique_ptr<DevtoolsFlexInfo> flex_layout_data_;
+
   LogicalBoxSides sides_to_include_;
 
   scoped_refptr<SerializedScriptValue> custom_layout_data_;
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 09d432d24..1dbe869 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
@@ -107,6 +107,8 @@
     EnsureRareData()->math_layout_data_ = builder->math_data_;
   if (builder->grid_layout_data_)
     EnsureRareData()->grid_layout_data_ = std::move(builder->grid_layout_data_);
+  if (builder->flex_layout_data_)
+    EnsureRareData()->flex_layout_data_ = std::move(builder->flex_layout_data_);
 }
 
 NGLayoutResult::NGLayoutResult(NGLineBoxFragmentBuilderPassKey passkey,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index 0b1f802b..caf2fec07 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_exclusion_space.h"
+#include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_bfc_offset.h"
 #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h"
 #include "third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h"
@@ -309,6 +310,9 @@
   const NGGridLayoutData* GridLayoutData() const {
     return HasRareData() ? rare_data_->grid_layout_data_.get() : nullptr;
   }
+  const DevtoolsFlexInfo* FlexLayoutData() const {
+    return HasRareData() ? rare_data_->flex_layout_data_.get() : nullptr;
+  }
 
   LayoutUnit MathItalicCorrection() const {
     return HasRareData() && rare_data_->math_layout_data_
@@ -549,6 +553,7 @@
     int lines_until_clamp = 0;
     wtf_size_t table_column_count_ = 0;
     std::unique_ptr<const NGGridLayoutData> grid_layout_data_;
+    std::unique_ptr<const DevtoolsFlexInfo> flex_layout_data_;
     absl::optional<MathData> math_layout_data_;
   };
   bool HasRareData() const { return rare_data_; }
diff --git a/third_party/blink/renderer/core/mathml/build.gni b/third_party/blink/renderer/core/mathml/build.gni
index c8e730f5..493e2f5 100644
--- a/third_party/blink/renderer/core/mathml/build.gni
+++ b/third_party/blink/renderer/core/mathml/build.gni
@@ -19,6 +19,8 @@
   "mathml_scripts_element.h",
   "mathml_space_element.cc",
   "mathml_space_element.h",
+  "mathml_token_element.cc",
+  "mathml_token_element.h",
   "mathml_under_over_element.cc",
   "mathml_under_over_element.h",
 ]
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.cc b/third_party/blink/renderer/core/mathml/mathml_element.cc
index 97fe6207..4f011986 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_element.cc
@@ -177,11 +177,4 @@
   return parsed_value->ConvertToLength(conversion_data);
 }
 
-bool MathMLElement::IsTokenElement() const {
-  return HasTagName(mathml_names::kMiTag) || HasTagName(mathml_names::kMoTag) ||
-         HasTagName(mathml_names::kMnTag) ||
-         HasTagName(mathml_names::kMtextTag) ||
-         HasTagName(mathml_names::kMsTag);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.h b/third_party/blink/renderer/core/mathml/mathml_element.h
index c28fe2dc..b927336 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.h
+++ b/third_party/blink/renderer/core/mathml/mathml_element.h
@@ -33,8 +33,6 @@
   bool IsMathMLElement() const =
       delete;  // This will catch anyone doing an unnecessary check.
 
-  bool IsTokenElement() const;
-
   virtual bool IsGroupingElement() const { return false; }
 
  protected:
diff --git a/third_party/blink/renderer/core/mathml/mathml_operator_element.cc b/third_party/blink/renderer/core/mathml/mathml_operator_element.cc
index bc3d8c8..94e7e483 100644
--- a/third_party/blink/renderer/core/mathml/mathml_operator_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_operator_element.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/renderer/core/mathml/mathml_operator_element.h"
 
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
-#include "third_party/blink/renderer/core/dom/character_data.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
@@ -20,22 +19,6 @@
     MathMLOperatorElement::kLargeOp | MathMLOperatorElement::kMovableLimits;
 static const uint32_t kOperatorPropertyFlagsNone = 0;
 
-UChar32 OperatorCodepoint(const String& text_content) {
-  DCHECK(!text_content.Is8Bit());
-  auto content_length = text_content.length();
-  // Reject malformed UTF-16 and operator strings consisting of more than one
-  // codepoint.
-  if ((content_length > 2) || (content_length == 0) ||
-      (content_length == 1 && !U16_IS_SINGLE(text_content[0])) ||
-      (content_length == 2 && !U16_IS_LEAD(text_content[0])))
-    return kNonCharacter;
-
-  UChar32 character;
-  unsigned offset = 0;
-  U16_NEXT(text_content, offset, content_length, character);
-  return character;
-}
-
 // https://w3c.github.io/mathml-core/#operator-dictionary-categories-values
 // Leading and trailing spaces are respresented in math units, i.e. 1/18em.
 struct MathMLOperatorDictionaryProperties {
@@ -83,48 +66,18 @@
 }  // namespace
 
 MathMLOperatorElement::MathMLOperatorElement(Document& doc)
-    : MathMLElement(mathml_names::kMoTag, doc) {
-  operator_content_ = absl::nullopt;
+    : MathMLTokenElement(mathml_names::kMoTag, doc) {
   properties_.dictionary_category =
       MathMLOperatorDictionaryCategory::kUndefined;
   properties_.dirty_flags = kOperatorPropertyFlagsAll;
 }
 
-MathMLOperatorElement::OperatorContent
-MathMLOperatorElement::ParseOperatorContent() {
-  MathMLOperatorElement::OperatorContent operator_content;
-  // Build the text content of the <mo> element. If it contains something other
-  // than character data, exit early since no special handling is required.
-  StringBuilder text_content;
-  for (auto* child = firstChild(); child; child = child->nextSibling()) {
-    auto* character_data = DynamicTo<CharacterData>(child);
-    if (!character_data)
-      return operator_content;
-    if (child->getNodeType() == kTextNode) {
-      text_content.Append(character_data->data());
-      // TODO(crbug.com/6606): Is it worth checking the length of text_content
-      // and exiting early here? For example the MathML Operator dictionary only
-      // contains content which (as a UTF-16 string) is of length 1 or 2, and
-      // other things rely on the assumption that OperatorContent::code_point
-      // is not kNonCharacter.
-    }
-  }
-  // Parse the operator content.
-  operator_content.characters = text_content.ToString();
-  operator_content.characters.Ensure16Bit();
-  operator_content.code_point = OperatorCodepoint(operator_content.characters);
-  operator_content.is_vertical =
-      Character::IsVerticalMathCharacter(operator_content.code_point);
-  return operator_content;
-}
-
 void MathMLOperatorElement::ChildrenChanged(
     const ChildrenChange& children_change) {
-  operator_content_ = absl::nullopt;
   properties_.dictionary_category =
       MathMLOperatorDictionaryCategory::kUndefined;
   properties_.dirty_flags = kOperatorPropertyFlagsAll;
-  MathMLElement::ChildrenChanged(children_change);
+  MathMLTokenElement::ChildrenChanged(children_change);
 }
 
 void MathMLOperatorElement::SetOperatorPropertyDirtyFlagIfNeeded(
@@ -175,7 +128,7 @@
         ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
             layout_invalidation_reason::kAttributeChanged);
   }
-  MathMLElement::ParseAttribute(param);
+  MathMLTokenElement::ParseAttribute(param);
 }
 
 // https://w3c.github.io/mathml-core/#dfn-algorithm-for-determining-the-properties-of-an-embellished-operator
@@ -183,7 +136,7 @@
   if (properties_.dictionary_category !=
       MathMLOperatorDictionaryCategory::kUndefined)
     return;
-  if (GetOperatorContent().characters.IsEmpty()) {
+  if (GetTokenContent().characters.IsEmpty()) {
     properties_.dictionary_category = MathMLOperatorDictionaryCategory::kNone;
     return;
   }
@@ -218,7 +171,7 @@
   // We then try and find an entry in the operator dictionary to override the
   // default values.
   // https://w3c.github.io/mathml-core/#dfn-algorithm-for-determining-the-properties-of-an-embellished-operator
-  auto category = FindCategory(GetOperatorContent().characters, form);
+  auto category = FindCategory(GetTokenContent().characters, form);
   if (category != MathMLOperatorDictionaryCategory::kNone) {
     // Step 2.
     properties_.dictionary_category = category;
@@ -231,7 +184,7 @@
         if (fallback_form == form)
           continue;
         category = FindCategory(
-            GetOperatorContent().characters,
+            GetTokenContent().characters,
             static_cast<MathMLOperatorDictionaryForm>(fallback_form));
         if (category != MathMLOperatorDictionaryCategory::kNone) {
           properties_.dictionary_category = category;
@@ -272,11 +225,12 @@
   }
 }
 
-const MathMLOperatorElement::OperatorContent&
-MathMLOperatorElement::GetOperatorContent() {
-  if (!operator_content_)
-    operator_content_ = ParseOperatorContent();
-  return operator_content_.value();
+bool MathMLOperatorElement::IsVertical() {
+  if (!is_vertical_.has_value()) {
+    is_vertical_ =
+        Character::IsVerticalMathCharacter(GetTokenContent().code_point);
+  }
+  return is_vertical_.value();
 }
 
 bool MathMLOperatorElement::HasBooleanProperty(OperatorPropertyFlag flag) {
diff --git a/third_party/blink/renderer/core/mathml/mathml_operator_element.h b/third_party/blink/renderer/core/mathml/mathml_operator_element.h
index 535ced2..77b65e4 100644
--- a/third_party/blink/renderer/core/mathml/mathml_operator_element.h
+++ b/third_party/blink/renderer/core/mathml/mathml_operator_element.h
@@ -6,8 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_OPERATOR_ELEMENT_H_
 
 #include "third_party/abseil-cpp/absl/types/optional.h"
-#include "third_party/blink/renderer/core/mathml/mathml_element.h"
-#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+#include "third_party/blink/renderer/core/mathml/mathml_token_element.h"
 
 namespace blink {
 
@@ -20,15 +19,10 @@
 // Math units are 1/18em.
 constexpr double kMathUnitFraction = 1.0 / 18.0;
 
-class CORE_EXPORT MathMLOperatorElement final : public MathMLElement {
+class CORE_EXPORT MathMLOperatorElement final : public MathMLTokenElement {
  public:
   explicit MathMLOperatorElement(Document&);
 
-  struct OperatorContent {
-    String characters;
-    UChar32 code_point = kNonCharacter;
-    bool is_vertical = true;
-  };
   enum OperatorPropertyFlag {
     kStretchy = 0x1,
     kSymmetric = 0x2,
@@ -42,7 +36,7 @@
   void AddMathRSpaceIfNeeded(ComputedStyle&, const CSSToLengthConversionData&);
   void AddMathMinSizeIfNeeded(ComputedStyle&, const CSSToLengthConversionData&);
   void AddMathMaxSizeIfNeeded(ComputedStyle&, const CSSToLengthConversionData&);
-  const OperatorContent& GetOperatorContent();
+  bool IsVertical();
 
   double DefaultLeadingSpace();
   double DefaultTrailingSpace();
@@ -50,9 +44,12 @@
   void CheckFormAfterSiblingChange();
 
  private:
-  absl::optional<OperatorContent> operator_content_;
+  // Whether the operator stretches along the block or inline axis.
+  // https://w3c.github.io/mathml-core/#dfn-stretch-axis
+  absl::optional<bool> is_vertical_;
   // Operator properties calculated from dictionary and attributes.
   // It contains dirty flags to allow efficient dictionary updating.
+  // https://w3c.github.io/mathml-core/#dictionary-based-attributes
   struct Properties {
     MathMLOperatorDictionaryCategory dictionary_category;
     unsigned flags : 4;
@@ -66,7 +63,6 @@
   void SetOperatorPropertyDirtyFlagIfNeeded(const AttributeModificationParams&,
                                             const OperatorPropertyFlag&,
                                             bool& needs_layout);
-  OperatorContent ParseOperatorContent();
   void ChildrenChanged(const ChildrenChange&) final;
 };
 
diff --git a/third_party/blink/renderer/core/mathml/mathml_tag_names.json5 b/third_party/blink/renderer/core/mathml/mathml_tag_names.json5
index 5fa80b6..1274e5d 100644
--- a/third_party/blink/renderer/core/mathml/mathml_tag_names.json5
+++ b/third_party/blink/renderer/core/mathml/mathml_tag_names.json5
@@ -69,11 +69,11 @@
     },
     {
       name: "mi",
-      interfaceName: "MathMLElement",
+      interfaceName: "MathMLTokenElement",
     },
     {
       name: "mn",
-      interfaceName: "MathMLElement",
+      interfaceName: "MathMLTokenElement",
     },
     {
       name: "mo",
@@ -93,7 +93,7 @@
     },
     {
       name: "ms",
-      interfaceName: "MathMLElement",
+      interfaceName: "MathMLTokenElement",
     },
     {
       name: "mspace",
@@ -117,7 +117,7 @@
     },
     {
       name: "mtext",
-      interfaceName: "MathMLElement",
+      interfaceName: "MathMLTokenElement",
     },
     {
       name: "mover",
diff --git a/third_party/blink/renderer/core/mathml/mathml_token_element.cc b/third_party/blink/renderer/core/mathml/mathml_token_element.cc
new file mode 100644
index 0000000..0c6c51d
--- /dev/null
+++ b/third_party/blink/renderer/core/mathml/mathml_token_element.cc
@@ -0,0 +1,78 @@
+// 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 "third_party/blink/renderer/core/mathml/mathml_token_element.h"
+
+#include "third_party/blink/renderer/core/dom/character_data.h"
+#include "third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block_flow.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+MathMLTokenElement::MathMLTokenElement(const QualifiedName& tagName,
+                                       Document& document)
+    : MathMLElement(tagName, document) {
+  token_content_ = absl::nullopt;
+}
+
+namespace {
+
+UChar32 TokenCodePoint(const String& text_content) {
+  DCHECK(!text_content.Is8Bit());
+  auto content_length = text_content.length();
+  // Reject malformed UTF-16 and operator strings consisting of more than one
+  // codepoint.
+  if ((content_length > 2) || (content_length == 0) ||
+      (content_length == 1 && !U16_IS_SINGLE(text_content[0])) ||
+      (content_length == 2 && !U16_IS_LEAD(text_content[0])))
+    return kNonCharacter;
+
+  UChar32 character;
+  unsigned offset = 0;
+  U16_NEXT(text_content, offset, content_length, character);
+  return character;
+}
+
+}  // namespace
+
+MathMLTokenElement::TokenContent MathMLTokenElement::ParseTokenContent() {
+  MathMLTokenElement::TokenContent token_content;
+  // Build the text content of the token element. If it contains something other
+  // than character data, exit early since no special handling is required.
+  StringBuilder text_content;
+  for (auto* child = firstChild(); child; child = child->nextSibling()) {
+    auto* character_data = DynamicTo<CharacterData>(child);
+    if (!character_data)
+      return token_content;
+    if (child->getNodeType() == kTextNode)
+      text_content.Append(character_data->data());
+  }
+  // Parse the token content.
+  token_content.characters = text_content.ToString();
+  token_content.characters.Ensure16Bit();
+  token_content.code_point = TokenCodePoint(token_content.characters);
+  return token_content;
+}
+
+const MathMLTokenElement::TokenContent& MathMLTokenElement::GetTokenContent() {
+  if (!token_content_)
+    token_content_ = ParseTokenContent();
+  return token_content_.value();
+}
+
+void MathMLTokenElement::ChildrenChanged(
+    const ChildrenChange& children_change) {
+  token_content_ = absl::nullopt;
+  MathMLElement::ChildrenChanged(children_change);
+}
+
+LayoutObject* MathMLTokenElement::CreateLayoutObject(const ComputedStyle& style,
+                                                     LegacyLayout legacy) {
+  if (!RuntimeEnabledFeatures::MathMLCoreEnabled() ||
+      !style.IsDisplayMathType() || legacy == LegacyLayout::kForce)
+    return MathMLElement::CreateLayoutObject(style, legacy);
+  return MakeGarbageCollected<LayoutNGMathMLBlockFlow>(this);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/mathml/mathml_token_element.h b/third_party/blink/renderer/core/mathml/mathml_token_element.h
new file mode 100644
index 0000000..717034c
--- /dev/null
+++ b/third_party/blink/renderer/core/mathml/mathml_token_element.h
@@ -0,0 +1,57 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_TOKEN_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_TOKEN_ELEMENT_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/core/mathml/mathml_element.h"
+#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
+
+namespace blink {
+
+class Document;
+
+class CORE_EXPORT MathMLTokenElement : public MathMLElement {
+ public:
+  explicit MathMLTokenElement(const QualifiedName&, Document&);
+
+  struct TokenContent {
+    String characters;
+    UChar32 code_point = kNonCharacter;
+  };
+  const TokenContent& GetTokenContent();
+
+ protected:
+  void ChildrenChanged(const ChildrenChange&) override;
+
+ private:
+  TokenContent ParseTokenContent();
+  absl::optional<TokenContent> token_content_;
+  LayoutObject* CreateLayoutObject(const ComputedStyle&,
+                                   LegacyLayout legacy) final;
+};
+
+template <>
+inline bool IsElementOfType<const MathMLTokenElement>(const Node& node) {
+  return IsA<MathMLTokenElement>(node);
+}
+template <>
+struct DowncastTraits<MathMLTokenElement> {
+  static bool AllowFrom(const Node& node) {
+    auto* mathml_element = DynamicTo<MathMLElement>(node);
+    return mathml_element && AllowFrom(*mathml_element);
+  }
+  static bool AllowFrom(const MathMLElement& mathml_element) {
+    return mathml_element.HasTagName(mathml_names::kMiTag) ||
+           mathml_element.HasTagName(mathml_names::kMoTag) ||
+           mathml_element.HasTagName(mathml_names::kMnTag) ||
+           mathml_element.HasTagName(mathml_names::kMtextTag) ||
+           mathml_element.HasTagName(mathml_names::kMsTag);
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_MATHML_MATHML_TOKEN_ELEMENT_H_
diff --git a/third_party/blink/renderer/modules/OWNERS b/third_party/blink/renderer/modules/OWNERS
index 475e6ec7..4e16111 100644
--- a/third_party/blink/renderer/modules/OWNERS
+++ b/third_party/blink/renderer/modules/OWNERS
@@ -1,6 +1,7 @@
 dcheng@chromium.org
 haraken@chromium.org
 jbroman@chromium.org
+kinuko@chromium.org
 mkwst@chromium.org
 
 # Reviewers for inspector-related code:
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc b/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
index 914fe6f..4ebe0871 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_selector.cc
@@ -10,6 +10,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "media/base/channel_layout.h"
 #include "media/base/demuxer_stream.h"
+#include "media/base/sample_format.h"
 #include "media/filters/decrypting_demuxer_stream.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -116,7 +117,8 @@
 DecoderSelector<media::DemuxerStream::AUDIO>::CreateStreamTraits() {
   // TODO(chcunningham): Consider plumbing real hw channel layout.
   return std::make_unique<DecoderSelector::StreamTraits>(
-      &null_media_log_, media::CHANNEL_LAYOUT_NONE);
+      &null_media_log_, media::CHANNEL_LAYOUT_NONE,
+      media::kUnknownSampleFormat);
 }
 
 template <>
diff --git a/third_party/blink/renderer/platform/OWNERS b/third_party/blink/renderer/platform/OWNERS
index 14f21ebd..61e8b6c 100644
--- a/third_party/blink/renderer/platform/OWNERS
+++ b/third_party/blink/renderer/platform/OWNERS
@@ -10,6 +10,7 @@
 jochen@chromium.org
 junov@chromium.org
 kbr@chromium.org
+kinuko@chromium.org
 kojii@chromium.org
 mkwst@chromium.org
 noel@chromium.org
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc
index ca6d21f7..3397f713 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_factory.cc
@@ -170,6 +170,16 @@
       supported_formats.profiles.push_back(profile.profile);
       supported_formats.scalability_modes.push_back(profile.scalability_modes);
       supported_formats.sdp_formats.push_back(std::move(*format));
+
+#if BUILDFLAG(IS_WIN)
+      if (profile.profile == media::VideoCodecProfile::H264PROFILE_BASELINE) {
+        supported_formats.profiles.push_back(profile.profile);
+        supported_formats.scalability_modes.push_back(
+            profile.scalability_modes);
+        cricket::AddH264ConstrainedBaselineProfileToSupportedFormats(
+            &supported_formats.sdp_formats);
+      }
+#endif
     }
   }
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index 4218e2c6..fe0453f 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -13,6 +13,7 @@
 # Tests that fail in legacy but pass in NG
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy-attribute.https.sub.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy.https.sub.html [ Timeout ]
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
index 3f64f143..72cba213 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
+++ b/third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials
@@ -47,6 +47,7 @@
 crbug.com/1209223 external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy-attribute-redirect-on-load.https.sub.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy-attribute.https.sub.html [ Timeout ]
 crbug.com/626703 external/wpt/geolocation-API/enabled-by-feature-policy.https.sub.html [ Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index a21c9e1..0b4f647 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1740,16 +1740,9 @@
 
 # ====== MathMLCore-only tests from here ======
 
-# These tests fail because we don't support MathML ink metrics.
-crbug.com/1125137 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-001.html [ Failure ]
-crbug.com/1125137 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-002.html [ Failure ]
-crbug.com/1125137 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-003.html [ Failure ]
-crbug.com/1125137 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-004.html [ Failure ]
-crbug.com/1125137 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-005.html [ Failure ]
-crbug.com/1125137 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-006.html [ Failure ]
-
 # This test fail because we don't properly center elements in MathML tables.
 crbug.com/1125111 external/wpt/mathml/presentation-markup/tables/table-003.html [ Failure ]
+crbug.com/1125111 external/wpt/mathml/presentation-markup/direction/direction-006.html [ Failure ]
 
 # These tests fail because some CSS properties affect MathML layout.
 crbug.com/1227601 external/wpt/mathml/relations/css-styling/ignored-properties-001.html [ Failure Timeout ]
@@ -1854,6 +1847,8 @@
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-002.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-003.html [ Failure ]
 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-004.html [ Failure ]
+crbug.com/1295264 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-currentcolor-001.html [ Failure ]
+crbug.com/1295264 crbug.com/1147859 external/wpt/css/css-pseudo/highlight-painting-currentcolor-002.html [ Failure ]
 crbug.com/995106 external/wpt/css/css-pseudo/first-letter-exclude-inline-marker.html [ Failure ]
 crbug.com/995106 external/wpt/css/css-pseudo/first-letter-exclude-inline-child-marker.html [ Failure ]
 crbug.com/1172333 external/wpt/css/css-pseudo/first-letter-hi-001.html [ Failure ]
@@ -3335,6 +3330,7 @@
 crbug.com/626703 [ Win ] virtual/prerender/external/wpt/speculation-rules/prerender/storage-foundation.https.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-overflow/webkit-line-clamp-036.html [ Failure ]
 crbug.com/626703 [ Mac ] virtual/prerender/external/wpt/speculation-rules/prerender/media-autoplay.html [ Skip Timeout ]
 crbug.com/626703 [ Win ] virtual/prerender/external/wpt/speculation-rules/prerender/media-autoplay.html [ Skip Timeout ]
@@ -7622,3 +7618,6 @@
 
 # Sheriff 2022-03-03
 crbug.com/1302571 virtual/threaded/http/tests/devtools/isolated-code-cache/cross-origin-test.js [ Skip ]
+
+# Sheriff 2022-03-04
+crbug.com/1302856 [ Mac11-arm64 ] virtual/exotic-color-space/images/rgb-png-with-cmyk-color-profile.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 87aed0b..4ea7cc1 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: a723fd8ceb7615a4fd30b984c9928116200b1de6
+Version: 206491af1ac7c2400c0f45cca4ea133dd2845762
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 8a725ba..7706602 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -263,6 +263,15 @@
      }
     },
     "css-animations": {
+     "crashtests": {
+      "replace-keyframes-animating-filter-001.html": [
+       "e6355376f722062c4adb257ff627c5756b185517",
+       [
+        null,
+        {}
+       ]
+      ]
+     },
      "keyframes-remove-documentElement-crash.html": [
       "9573ce1b0f8d1a413c0fb609207c5d98350053d6",
       [
@@ -1382,6 +1391,13 @@
         {}
        ]
       ],
+      "nested-with-percentage-size-and-oof.html": [
+       "bf53cc17f3f9f773f54fd39f4182db37469acd21",
+       [
+        null,
+        {}
+       ]
+      ],
       "nested-with-tall-padding-and-oof.html": [
        "a055124257e2e284ad4c7d88702fe9ddc02ef7f5",
        [
@@ -230334,7 +230350,7 @@
      []
     ],
     "OWNERS": [
-     "81033345d3849abd37e17c0848e9dfa71f1af15d",
+     "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
      []
     ],
     "README.md": [
@@ -234203,7 +234219,7 @@
       ]
      },
      "test-case.sub.js": [
-      "6cb262be0f5aaf30f9445835b827ed5a58e2f993",
+      "81d9e1c8e3ec8f2150e88a000cdc379b2c197444",
       []
      ],
      "unreached.js": [
@@ -234254,11 +234270,11 @@
        []
       ],
       "message-opener-and-navigate-back.html": [
-       "6e0d3f5579fb7b37cf2a0c2cbe0bb3596f3581c6",
+       "75ee5bee7cc8c226e899fd587b03743ebbc81c5c",
        []
       ],
       "message-top-and-navigate-back.html": [
-       "6fccbaaaf39326e56bb5d83c637b2ec9bafcdc98",
+       "53d5a18cb36b7b3108ef8e2fa596a6b9d528d9bd",
        []
       ],
       "navigate-parent-to-blob.html": [
@@ -234278,11 +234294,11 @@
        []
       ],
       "postmessage-opener.html": [
-       "4278c0e6732fc5b614487c6ff0cc83f41f6e1c27",
+       "7ee11bc78de53414cb7dde5b3ce59272bf823911",
        []
       ],
       "postmessage-top.html": [
-       "4f83a58dd4a80b5a6171266d2944e628d204e434",
+       "242063a80e2980388f9881c097a9b387929f4396",
        []
       ],
       "srcdoc-child-frame.html": [
@@ -290917,6 +290933,14 @@
          "9a20aa07710744a5a7a28e58734026de63fb07af",
          []
         ],
+        "layers-several-complex-expected.html": [
+         "60fffec0c0b545ad0d2c4c0d90b96266ff004401",
+         []
+        ],
+        "layers-several-complex.html": [
+         "586e1f07e764dd62bdf18a5d27064e37bec83400",
+         []
+        ],
         "layers-shadow-expected.html": [
          "c58bc5063e385dbd7626590342aadae2aab7b4e2",
          []
@@ -352270,7 +352294,7 @@
       ]
      ],
      "change-csp-attribute-and-history-navigation.html": [
-      "b69160c70da29a76ddf08b188bc8f31b23bb4b77",
+      "64b52061772f636d472d73cda5fbb06df7bb4ab0",
       [
        null,
        {}
@@ -355514,14 +355538,14 @@
       ]
      ],
      "history-iframe.sub.html": [
-      "112e18487398d0fc49f1226fbbe4d62809f53fc5",
+      "412b3ac3464d44822d21af142f3702b3eae7505f",
       [
        null,
        {}
       ]
      ],
      "history.sub.html": [
-      "672def174374f2361fdaa7a0fbadc8b954507e30",
+      "6ebd679a28076f4d61b0a5f629df54d3d190d26d",
       [
        null,
        {}
@@ -355612,7 +355636,7 @@
       ]
      ],
      "window-open-local-after-network-scheme.sub.html": [
-      "dfae9a3020bc442a007d39d96605da10eb291dbc",
+      "0cdc03ce9213db075dcd1e8375517f9661f92c6c",
       [
        null,
        {}
@@ -357300,7 +357324,7 @@
       ]
      ],
      "injected-inline-style-blocked.sub.html": [
-      "978671223e85996112fd5429969eb35e7eda09d5",
+      "9477a95978e8df26b75085497b6fead01ccfdeca",
       [
        null,
        {}
@@ -360224,7 +360248,7 @@
      ]
     ],
     "fedcm-network-requests.sub.https.html": [
-     "ec58a11f5af1fce7bf44e9649f8614a2c32dcdee",
+     "09841893ae385f4c04e864db8f1a1d4fa0987d3e",
      [
       null,
       {}
@@ -360238,7 +360262,7 @@
      ]
     ],
     "fedcm.https.html": [
-     "688212e2ed2a306a0d588de197d94b46520c4a4c",
+     "9b21187a399eeaf6fb7b803881f277e7bb965c9c",
      [
       null,
       {}
@@ -423622,14 +423646,16 @@
      ]
     ],
     "getCurrentPosition_permission_allow.https.html": [
-     "95171099159486cdbea0d17d4bf1ea066d77ccbf",
+     "4ecb64324e9baa721b649193e00f6f4d74a6e269",
      [
       null,
-      {}
+      {
+       "testdriver": true
+      }
      ]
     ],
     "getCurrentPosition_permission_deny.https.html": [
-     "297f9a2629737907b66743dfa8fd43c3b97f3020",
+     "54bba9aeabf7e1c2ddccdbcfc76c522b44c2d2c5",
      [
       null,
       {
@@ -486642,7 +486668,7 @@
    },
    "priority-hints": {
     "fetch-api-request.tentative.any.js": [
-     "0c10a3e2f96ecc16f5e96298e7855457c4bd51c2",
+     "668bdb78cd6fd91adc95cf136220c1089335fc04",
      [
       "priority-hints/fetch-api-request.tentative.any.html",
       {}
@@ -486653,14 +486679,14 @@
      ]
     ],
     "img-attr-named-constructor.tentative.html": [
-     "ce677ccc177b6c1e002939d174339fd25c8f3303",
+     "6460cd9cd861af45f1dea4d9291a926df84b92a4",
      [
       null,
       {}
      ]
     ],
     "link-attr.tentative.html": [
-     "1c5aeb530eb2d56cde31c434b43bf3c18efeebe6",
+     "1d9838309f0e3cdf36c1334a615952784001adfd",
      [
       null,
       {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-001-ref.html
new file mode 100644
index 0000000..979e54a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-001-ref.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8" />
+<title>CSS Pseudo-Elements Test: Reference</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="stylesheet" href="support/highlights.css">
+<style>
+div {
+  color: lime;
+  background: green;
+  margin: 10px;
+}
+#empty > span {
+}
+#color-currentcolor > span {
+  color: currentcolor;
+}
+#backgroundcolor-currentcolor > span {
+  background-color: currentcolor;
+}
+#textdecorationcolor-currentcolor > span {
+  text-decoration-line: underline;
+  text-decoration-color: currentcolor;
+}
+#textshadow-currentcolor > span {
+  text-shadow: currentcolor 2px 2px;
+}
+#textfillcolor-currentcolor > span {
+  -webkit-text-fill-color: currentcolor;
+}
+#textstrokecolor-currentcolor > span {
+  -webkit-text-stroke-width: 1px;
+  -webkit-text-stroke-color: currentcolor;
+}
+#color-currentcolor-backgroundcolor-currentcolor > span {
+  color: currentcolor;
+  background-color: currentcolor;
+}
+#color-currentcolor-backgroundcolor-blue > span {
+  color: currentcolor;
+  background-color: blue;
+}
+#color-blue-backgroundcolor-currentcolor > span {
+  color: blue;
+  background-color: currentcolor;
+}
+</style>
+
+<div id="empty" class="highlight_reftest"><span>example</span> - empty</div>
+<div id="color-currentcolor" class="highlight_reftest"><span>example</span> - color-currentcolor</div>
+<div id="backgroundcolor-currentcolor" class="highlight_reftest"><span>example</span> - backgroundcolor-currentcolor</div>
+<div id="textdecorationcolor-currentcolor" class="highlight_reftest"><span>example</span> - textdecorationcolor-currentcolor</div>
+<div id="textshadow-currentcolor" class="highlight_reftest"><span>example</span> - textshadow-currentcolor</div>
+<div id="textfillcolor-currentcolor" class="highlight_reftest"><span>example</span> - textfillcolor-currentcolor</div>
+<div id="textstrokecolor-currentcolor" class="highlight_reftest"><span>example</span> - textstrokecolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-currentcolor" class="highlight_reftest"><span>example</span> - color-currentcolor-backgroundcolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-blue" class="highlight_reftest"><span>example</span> - color-currentcolor-backgroundcolor-blue</div>
+<div id="color-blue-backgroundcolor-currentcolor" class="highlight_reftest"><span>example</span> - color-blue-backgroundcolor-currentcolor</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-001.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-001.html
new file mode 100644
index 0000000..da79d2d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-001.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>CSS Pseudo-Elements Test: Custom Highlights currentcolor painting single layer</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo/#highlight-text">
+<meta name="assert" content="Checks that currentcolor is properly resolved for the different color properites allowed for highlight pseudos (using custom highlights in this test), when we have only one highlight.">
+<link rel="match" href="highlight-painting-currentcolor-001-ref.html">
+<link rel="stylesheet" href="support/highlights.css">
+<style>
+div {
+  color: lime;
+  background: green;
+  margin: 10px;
+}
+::highlight(empty) {
+}
+::highlight(color-currentcolor) {
+  color: currentcolor;
+}
+::highlight(backgroundcolor-currentcolor) {
+  background-color: currentcolor;
+}
+::highlight(textdecorationcolor-currentcolor) {
+  text-decoration-line: underline;
+  text-decoration-color: currentcolor;
+}
+::highlight(textshadow-currentcolor) {
+  text-shadow: currentcolor 2px 2px;
+}
+::highlight(textfillcolor-currentcolor) {
+  -webkit-text-fill-color: currentcolor;
+}
+::highlight(textstrokecolor-currentcolor) {
+  -webkit-text-stroke-width: 1px;
+  -webkit-text-stroke-color: currentcolor;
+}
+::highlight(color-currentcolor-backgroundcolor-currentcolor) {
+  color: currentcolor;
+  background-color: currentcolor;
+}
+::highlight(color-currentcolor-backgroundcolor-blue) {
+  color: currentcolor;
+  background-color: blue;
+}
+::highlight(color-blue-backgroundcolor-currentcolor) {
+  color: blue;
+  background-color: currentcolor;
+}
+</style>
+
+<div id="empty" class="highlight_reftest">example - empty</div>
+<div id="color-currentcolor" class="highlight_reftest">example - color-currentcolor</div>
+<div id="backgroundcolor-currentcolor" class="highlight_reftest">example - backgroundcolor-currentcolor</div>
+<div id="textdecorationcolor-currentcolor" class="highlight_reftest">example - textdecorationcolor-currentcolor</div>
+<div id="textshadow-currentcolor" class="highlight_reftest">example - textshadow-currentcolor</div>
+<div id="textfillcolor-currentcolor" class="highlight_reftest">example - textfillcolor-currentcolor</div>
+<div id="textstrokecolor-currentcolor" class="highlight_reftest">example - textstrokecolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-currentcolor" class="highlight_reftest">example - color-currentcolor-backgroundcolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-blue" class="highlight_reftest">example - color-currentcolor-backgroundcolor-blue</div>
+<div id="color-blue-backgroundcolor-currentcolor" class="highlight_reftest">example - color-blue-backgroundcolor-currentcolor</div>
+
+<script>
+  function range(node, start, end) {
+    let range = new Range();
+    range.setStart(node, start);
+    range.setEnd(node, end);
+    return range;
+  }
+
+  const divs = document.getElementsByTagName("div");
+  for (let div of divs) {
+    CSS.highlights.set(div.id, new Highlight(range(div.firstChild, 0, 7)));
+  }
+</script>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-002-ref.html
new file mode 100644
index 0000000..8a61e9c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-002-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8" />
+<title>CSS Pseudo-Elements Test: Reference</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="stylesheet" href="support/highlights.css">
+<style>
+div {
+  color: lime;
+  background: green;
+  margin: 10px;
+}
+div > span {
+  color: yellow;
+  background: maroon;
+}
+#empty > span > span {
+}
+#color-currentcolor > span > span {
+  color: currentcolor;
+}
+#backgroundcolor-currentcolor > span > span {
+  background-color: currentcolor;
+}
+#textdecorationcolor-currentcolor > span > span {
+  text-decoration-line: underline;
+  text-decoration-color: currentcolor;
+}
+#textshadow-currentcolor > span > span {
+  text-shadow: currentcolor 2px 2px;
+}
+#textfillcolor-currentcolor > span > span {
+  -webkit-text-fill-color: currentcolor;
+}
+#textstrokecolor-currentcolor > span > span {
+  -webkit-text-stroke-width: 1px;
+  -webkit-text-stroke-color: currentcolor;
+}
+#color-currentcolor-backgroundcolor-currentcolor > span > span {
+  color: currentcolor;
+  background-color: currentcolor;
+}
+#color-currentcolor-backgroundcolor-blue > span > span {
+  color: currentcolor;
+  background-color: blue;
+}
+#color-blue-backgroundcolor-currentcolor > span > span {
+  color: blue;
+  background-color: currentcolor;
+}
+</style>
+
+<div id="empty" class="highlight_reftest"><span><span>ex</span>ample</span> - empty</div>
+<div id="color-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - color-currentcolor</div>
+<div id="backgroundcolor-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - backgroundcolor-currentcolor</div>
+<div id="textdecorationcolor-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - textdecorationcolor-currentcolor</div>
+<div id="textshadow-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - textshadow-currentcolor</div>
+<div id="textfillcolor-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - textfillcolor-currentcolor</div>
+<div id="textstrokecolor-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - textstrokecolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - color-currentcolor-backgroundcolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-blue" class="highlight_reftest"><span><span>ex</span>ample</span> - color-currentcolor-backgroundcolor-blue</div>
+<div id="color-blue-backgroundcolor-currentcolor" class="highlight_reftest"><span><span>ex</span>ample</span> - color-blue-backgroundcolor-currentcolor</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-002.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-002.html
new file mode 100644
index 0000000..b3a27b7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-painting-currentcolor-002.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>CSS Pseudo-Elements Test: Custom Highlights currentcolor painting two layers</title>
+<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo/#highlight-text">
+<meta name="assert" content="Checks that currentcolor is properly resolved for the different color properites allowed for highlight pseudos (using custom highlights in this test), when we have two highlights one of top of the other.">
+<link rel="match" href="highlight-painting-currentcolor-002-ref.html">
+<link rel="stylesheet" href="support/highlights.css">
+<style>
+div {
+  color: lime;
+  background: green;
+  margin: 10px;
+}
+::highlight(below) {
+  color: yellow;
+  background: maroon;
+}
+::highlight(empty) {
+}
+::highlight(color-currentcolor) {
+  color: currentcolor;
+}
+::highlight(backgroundcolor-currentcolor) {
+  background-color: currentcolor;
+}
+::highlight(textdecorationcolor-currentcolor) {
+  text-decoration-line: underline;
+  text-decoration-color: currentcolor;
+}
+::highlight(textshadow-currentcolor) {
+  text-shadow: currentcolor 2px 2px;
+}
+::highlight(textfillcolor-currentcolor) {
+  -webkit-text-fill-color: currentcolor;
+}
+::highlight(textstrokecolor-currentcolor) {
+  -webkit-text-stroke-width: 1px;
+  -webkit-text-stroke-color: currentcolor;
+}
+::highlight(color-currentcolor-backgroundcolor-currentcolor) {
+  color: currentcolor;
+  background-color: currentcolor;
+}
+::highlight(color-currentcolor-backgroundcolor-blue) {
+  color: currentcolor;
+  background-color: blue;
+}
+::highlight(color-blue-backgroundcolor-currentcolor) {
+  color: blue;
+  background-color: currentcolor;
+}
+</style>
+
+<div id="empty" class="highlight_reftest">example - empty</div>
+<div id="color-currentcolor" class="highlight_reftest">example - color-currentcolor</div>
+<div id="backgroundcolor-currentcolor" class="highlight_reftest">example - backgroundcolor-currentcolor</div>
+<div id="textdecorationcolor-currentcolor" class="highlight_reftest">example - textdecorationcolor-currentcolor</div>
+<div id="textshadow-currentcolor" class="highlight_reftest">example - textshadow-currentcolor</div>
+<div id="textfillcolor-currentcolor" class="highlight_reftest">example - textfillcolor-currentcolor</div>
+<div id="textstrokecolor-currentcolor" class="highlight_reftest">example - textstrokecolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-currentcolor" class="highlight_reftest">example - color-currentcolor-backgroundcolor-currentcolor</div>
+<div id="color-currentcolor-backgroundcolor-blue" class="highlight_reftest">example - color-currentcolor-backgroundcolor-blue</div>
+<div id="color-blue-backgroundcolor-currentcolor" class="highlight_reftest">example - color-blue-backgroundcolor-currentcolor</div>
+
+<script>
+  function range(node, start, end) {
+    let range = new Range();
+    range.setStart(node, start);
+    range.setEnd(node, end);
+    return range;
+  }
+
+  const divs = document.getElementsByTagName("div");
+  let ranges = [];
+  for (let div of divs) {
+    ranges.push(range(div.firstChild, 0, 7));
+  }
+  CSS.highlights.set("below", new Highlight(...ranges));
+
+  for (let div of divs) {
+    CSS.highlights.set(div.id, new Highlight(range(div.firstChild, 0, 2)));
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html b/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html
index 9517109..4ecb643 100644
--- a/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html
+++ b/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_allow.https.html
@@ -1,34 +1,34 @@
-<!DOCTYPE HTML>
-<meta charset='utf-8'>
-<title>Geolocation Test: getCurrentPosition location access allowed</title>
-<link rel='help' href='http://www.w3.org/TR/geolocation-API/#get-current-position'>
-<script src='/resources/testharness.js'></script>
-<script src='/resources/testharnessreport.js'></script>
-<script src='support.js'></script>
-
-<p>Clear all Geolocation permissions before running this test. If prompted for permission, please allow.</p>
-<div id='log'></div>
-
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Geolocation Test: getCurrentPosition() location access</title>
+<link
+  rel="help"
+  href="https://www.w3.org/TR/geolocation/#getcurrentposition-method"
+/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
 <script>
-// Rewrite http://dev.w3.org/geo/api/test-suite/t.html?00002
-var t = async_test('User allows access, check that success callback is called or error callback is called with correct code.'),
-    onSuccess, onError, hasMethodReturned = false;
-
-t.step(function() {
-  onSuccess = t.step_func_done(function(pos) {
-    // Rewrite http://dev.w3.org/geo/api/test-suite/t.html?00031
-    assert_true(hasMethodReturned);
-  });
-
-  onError = t.step_func_done(function(err) {
-    // Rewrite http://dev.w3.org/geo/api/test-suite/t.html?00031
-    assert_true(hasMethodReturned);
-    assert_false(isUsingPreemptivePermission);
-    assert_equals(err.code, err.POSITION_UNAVAILABLE, errorToString(err));
-  });
-
-  geo.getCurrentPosition(onSuccess, onError);
-  hasMethodReturned = true;
-});
-
+  promise_test(async (t) => {
+    t.add_cleanup(() => {
+      return test_driver.set_permission({ name: "geolocation" }, "prompt");
+    });
+    await test_driver.set_permission({ name: "geolocation" }, "granted");
+    const position = await new Promise((resolve, reject) => {
+      let calledAsync = false;
+      navigator.geolocation.getCurrentPosition(
+        t.step_func((position) => {
+          assert_true(calledAsync, "Expected callback to be called asynchronously")
+          resolve(position);
+        }),
+        reject
+      );
+      calledAsync = true;
+    });
+    assert_true(
+      position instanceof GeolocationPosition,
+      "Expected GeolocationPosition"
+    );
+  }, "User allows access, check that success callback is called.");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_deny.https.html b/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_deny.https.html
index 297f9a2..54bba9a 100644
--- a/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_deny.https.html
+++ b/third_party/blink/web_tests/external/wpt/geolocation-API/getCurrentPosition_permission_deny.https.html
@@ -12,13 +12,18 @@
       return test_driver.set_permission({ name: "geolocation" }, "prompt");
     });
     await test_driver.set_permission({ name: "geolocation" }, "denied");
-    const result = await new Promise((resolve, reject) => {
-      navigator.geolocation.getCurrentPosition(reject, resolve);
+    const error = await new Promise((resolve, reject) => {
+      let calledAsync = false;
+      navigator.geolocation.getCurrentPosition(reject, t.step_func((error) => {
+        assert_true(calledAsync, "Expected callback to be called asynchronously");
+        resolve(error);
+      }));
+      calledAsync = true;
     });
     assert_true(
-      result instanceof GeolocationPositionError,
+      error instanceof GeolocationPositionError,
       "should be a GeolocationPositionError"
     );
-    assert_equals(result.code, result.PERMISSION_DENIED);
+    assert_equals(error.code, error.PERMISSION_DENIED);
   }, "User denies access, check that error callback is called with correct code");
 </script>
diff --git a/third_party/harfbuzz-ng/fuzz/hb_shape_fuzzer.cc b/third_party/harfbuzz-ng/fuzz/hb_shape_fuzzer.cc
index 0e0e673..280baea5 100644
--- a/third_party/harfbuzz-ng/fuzz/hb_shape_fuzzer.cc
+++ b/third_party/harfbuzz-ng/fuzz/hb_shape_fuzzer.cc
@@ -11,7 +11,8 @@
 #include <hb-ot.h>
 // clang-format on
 
-#include "base/cxx17_backports.h"
+#include <iterator>
+
 #include "third_party/harfbuzz-ng/utils/hb_scoped.h"
 
 constexpr size_t kMaxInputLength = 16800;
@@ -40,7 +41,7 @@
   if (size > sizeof(text32)) {
     memcpy(text32, data + size - sizeof(text32), sizeof(text32));
     HbScoped<hb_buffer_t> buffer(hb_buffer_create());
-    hb_buffer_add_utf32(buffer.get(), text32, base::size(text32), 0, -1);
+    hb_buffer_add_utf32(buffer.get(), text32, std::size(text32), 0, -1);
     hb_buffer_guess_segment_properties(buffer.get());
     hb_shape(font.get(), buffer.get(), nullptr, 0);
   }
diff --git a/third_party/harfbuzz-ng/fuzz/hb_subset_fuzzer.cc b/third_party/harfbuzz-ng/fuzz/hb_subset_fuzzer.cc
index d0df2657..7ae0437 100644
--- a/third_party/harfbuzz-ng/fuzz/hb_subset_fuzzer.cc
+++ b/third_party/harfbuzz-ng/fuzz/hb_subset_fuzzer.cc
@@ -11,8 +11,9 @@
 #include <hb-subset.h>
 // clang-format on
 
+#include <iterator>
+
 #include "base/check.h"
-#include "base/cxx17_backports.h"
 #include "third_party/harfbuzz-ng/utils/hb_scoped.h"
 
 namespace {
@@ -88,14 +89,14 @@
                                  'Z', '1', '2', '3', '@', '_', '%',
                                  '&', ')', '*', '$', '!'};
 
-  TrySubset(face.get(), text, base::size(text), subset_flags);
+  TrySubset(face.get(), text, std::size(text), subset_flags);
 
   hb_codepoint_t text_from_data[16];
   if (size > sizeof(text_from_data) + 1) {
     memcpy(text_from_data, data + size - sizeof(text_from_data),
            sizeof(text_from_data));
     subset_flags = data[size - sizeof(text_from_data) - 1];
-    size_t text_size = base::size(text_from_data);
+    size_t text_size = std::size(text_from_data);
     TrySubset(face.get(), text_from_data, text_size, subset_flags);
   }
 
diff --git a/third_party/leveldatabase/env_chromium.cc b/third_party/leveldatabase/env_chromium.cc
index a7a81a9d..6c128c7 100644
--- a/third_party/leveldatabase/env_chromium.cc
+++ b/third_party/leveldatabase/env_chromium.cc
@@ -5,12 +5,12 @@
 #include "third_party/leveldatabase/env_chromium.h"
 
 #include <atomic>
+#include <iterator>
 #include <limits>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/check_op.h"
-#include "base/cxx17_backports.h"
 #include "base/files/file_error_or.h"
 #include "base/files/file_util.h"
 #include "base/format_macros.h"
@@ -624,7 +624,7 @@
   const int kOtherError = 0;
   int error = kOtherError;
   const std::string& str_error = status.ToString();
-  const size_t kNumPatterns = base::size(patterns);
+  const size_t kNumPatterns = std::size(patterns);
   for (size_t i = 0; i < kNumPatterns; ++i) {
     if (str_error.find(patterns[i]) != std::string::npos) {
       error = i + 1;
@@ -637,7 +637,7 @@
 int GetNumCorruptionCodes() {
   // + 1 for the "other" error that is returned when a corruption message
   // doesn't match any of the patterns.
-  return base::size(patterns) + 1;
+  return std::size(patterns) + 1;
 }
 
 std::string GetCorruptionMessage(const leveldb::Status& status) {
diff --git a/third_party/qcms/qcms_color_space_fuzzer.cc b/third_party/qcms/qcms_color_space_fuzzer.cc
index 814f8f0..c8eacdb 100644
--- a/third_party/qcms/qcms_color_space_fuzzer.cc
+++ b/third_party/qcms/qcms_color_space_fuzzer.cc
@@ -4,9 +4,9 @@
 
 #include <cstddef>
 #include <cstdint>
+#include <iterator>
 #include <random>
 
-#include "base/cxx17_backports.h"
 #include "testing/libfuzzer/fuzzers/color_space_data.h"
 #include "third_party/qcms/src/qcms.h"
 
@@ -18,7 +18,7 @@
   static std::uniform_int_distribution<uint32_t> uniform(0u, ~0u);
 
   std::mt19937_64 random(hash);
-  for (size_t i = 0; i < base::size(pixels); ++i)
+  for (size_t i = 0; i < std::size(pixels); ++i)
     pixels[i] = uniform(random);
 }
 
@@ -46,9 +46,9 @@
 
 static qcms_profile* SelectProfile(size_t hash) {
   static qcms_profile* profiles[4] = {
-      qcms_profile_from_memory(kSRGBData, base::size(kSRGBData)),
-      qcms_profile_from_memory(kSRGBPara, base::size(kSRGBPara)),
-      qcms_profile_from_memory(kAdobeData, base::size(kAdobeData)),
+      qcms_profile_from_memory(kSRGBData, std::size(kSRGBData)),
+      qcms_profile_from_memory(kSRGBPara, std::size(kSRGBPara)),
+      qcms_profile_from_memory(kAdobeData, std::size(kAdobeData)),
       qcms_profile_sRGB(),
   };
 
diff --git a/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc b/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc
index 8ea1f3b..fa95168 100644
--- a/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc
+++ b/third_party/sudden_motion_sensor/sudden_motion_sensor_mac.cc
@@ -53,9 +53,9 @@
 #include <math.h>
 #include <sys/sysctl.h>
 
+#include <iterator>
 #include <memory>
 
-#include "base/cxx17_backports.h"
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
 
@@ -339,7 +339,7 @@
 
   // Look for the current model in the supported sensor list.
   base::ScopedCFTypeRef<CFDataRef> board_id_data;
-  const int kNumSensors = base::size(kSupportedSensors);
+  const int kNumSensors = std::size(kSupportedSensors);
 
   for (int i = 0; i < kNumSensors; ++i) {
     // Check if the supported sensor model name is a prefix
diff --git a/third_party/zlib/google/compression_utils_unittest.cc b/third_party/zlib/google/compression_utils_unittest.cc
index 415b9ab..76572e5 100644
--- a/third_party/zlib/google/compression_utils_unittest.cc
+++ b/third_party/zlib/google/compression_utils_unittest.cc
@@ -7,9 +7,9 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <iterator>
 #include <string>
 
-#include "base/cxx17_backports.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace compression {
@@ -33,24 +33,24 @@
 }  // namespace
 
 TEST(CompressionUtilsTest, GzipCompression) {
-  std::string data(reinterpret_cast<const char*>(kData), base::size(kData));
+  std::string data(reinterpret_cast<const char*>(kData), std::size(kData));
   std::string compressed_data;
   EXPECT_TRUE(GzipCompress(data, &compressed_data));
   std::string golden_compressed_data(
       reinterpret_cast<const char*>(kCompressedData),
-      base::size(kCompressedData));
+      std::size(kCompressedData));
   EXPECT_EQ(golden_compressed_data, compressed_data);
 }
 
 TEST(CompressionUtilsTest, GzipUncompression) {
   std::string compressed_data(reinterpret_cast<const char*>(kCompressedData),
-                              base::size(kCompressedData));
+                              std::size(kCompressedData));
 
   std::string uncompressed_data;
   EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
 
   std::string golden_data(reinterpret_cast<const char*>(kData),
-                          base::size(kData));
+                          std::size(kData));
   EXPECT_EQ(golden_data, uncompressed_data);
 }
 
@@ -59,7 +59,7 @@
   EXPECT_TRUE(GzipUncompress(kCompressedData, &uncompressed_data));
 
   std::string golden_data(reinterpret_cast<const char*>(kData),
-                          base::size(kData));
+                          std::size(kData));
   EXPECT_EQ(golden_data, uncompressed_data);
 }
 
@@ -84,10 +84,10 @@
 
 TEST(CompressionUtilsTest, InPlace) {
   const std::string original_data(reinterpret_cast<const char*>(kData),
-                                  base::size(kData));
+                                  std::size(kData));
   const std::string golden_compressed_data(
       reinterpret_cast<const char*>(kCompressedData),
-      base::size(kCompressedData));
+      std::size(kCompressedData));
 
   std::string data(original_data);
   EXPECT_TRUE(GzipCompress(data, &data));
diff --git a/third_party/zlib/google/zip_reader_unittest.cc b/third_party/zlib/google/zip_reader_unittest.cc
index cb887a9..363e302 100644
--- a/third_party/zlib/google/zip_reader_unittest.cc
+++ b/third_party/zlib/google/zip_reader_unittest.cc
@@ -8,12 +8,12 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <iterator>
 #include <string>
 #include <vector>
 
 #include "base/bind.h"
 #include "base/check.h"
-#include "base/cxx17_backports.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -516,7 +516,7 @@
       "\x50\x75\x78\x0b\x00\x01\x04\x8e\xf0\x00\x00\x04\x88\x13\x00\x00"
       "\x50\x4b\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00\x4e\x00\x00\x00"
       "\x52\x00\x00\x00\x00\x00";
-  std::string data(kTestData, base::size(kTestData));
+  std::string data(kTestData, std::size(kTestData));
   ZipReader reader;
   ASSERT_TRUE(reader.OpenFromString(data));
   base::FilePath target_path(FILE_PATH_LITERAL("test.txt"));
diff --git a/tools/android/common/adb_connection.cc b/tools/android/common/adb_connection.cc
index 4295172..4ed8b830 100644
--- a/tools/android/common/adb_connection.cc
+++ b/tools/android/common/adb_connection.cc
@@ -14,7 +14,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "base/cxx17_backports.h"
+#include <iterator>
+
 #include "base/logging.h"
 #include "base/posix/eintr_wrapper.h"
 #include "tools/android/common/net.h"
@@ -39,19 +40,19 @@
   const size_t kLengthOfLength = 4;
 
   const char kAddressPrefix[] = { 't', 'c', 'p', ':' };
-  size_t address_length = base::size(kAddressPrefix) + strlen(forward_to);
+  size_t address_length = std::size(kAddressPrefix) + strlen(forward_to);
   if (address_length > kBufferMaxLength - kLengthOfLength) {
     LOG(ERROR) << "Forward to address is too long: " << forward_to;
     return -1;
   }
 
   char request[kBufferMaxLength];
-  memcpy(request + kLengthOfLength, kAddressPrefix, base::size(kAddressPrefix));
-  memcpy(request + kLengthOfLength + base::size(kAddressPrefix), forward_to,
+  memcpy(request + kLengthOfLength, kAddressPrefix, std::size(kAddressPrefix));
+  memcpy(request + kLengthOfLength + std::size(kAddressPrefix), forward_to,
          strlen(forward_to));
 
   char length_buffer[kLengthOfLength + 1];
-  snprintf(length_buffer, base::size(length_buffer), "%04X",
+  snprintf(length_buffer, std::size(length_buffer), "%04X",
            static_cast<int>(address_length));
   memcpy(request, length_buffer, kLengthOfLength);
 
diff --git a/tools/android/forwarder2/forwarders_manager.cc b/tools/android/forwarder2/forwarders_manager.cc
index dc25b14..5cd993a 100644
--- a/tools/android/forwarder2/forwarders_manager.cc
+++ b/tools/android/forwarder2/forwarders_manager.cc
@@ -7,12 +7,13 @@
 #include <stddef.h>
 #include <sys/select.h>
 #include <unistd.h>
+
 #include <algorithm>
+#include <iterator>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
-#include "base/cxx17_backports.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -81,7 +82,7 @@
     deletion_notifier_.receiver_fd(),
   };
 
-  for (size_t i = 0; i < base::size(notifier_fds); ++i) {
+  for (size_t i = 0; i < std::size(notifier_fds); ++i) {
     const int notifier_fd = notifier_fds[i];
     DCHECK_GT(notifier_fd, -1);
     FD_SET(notifier_fd, &read_fds);
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
index 16694cb..28def0a 100644
--- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc
+++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <iostream>
+#include <iterator>
 #include <set>
 #include <string>
 #include <tuple>
@@ -11,7 +12,6 @@
 
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
-#include "base/cxx17_backports.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "base/types/id_type.h"
@@ -921,10 +921,10 @@
 struct FuzzTraits<gfx::Transform> {
   static bool Fuzz(gfx::Transform* p, Fuzzer* fuzzer) {
     SkScalar matrix[16];
-    for (size_t i = 0; i < base::size(matrix); i++) {
+    for (size_t i = 0; i < std::size(matrix); i++) {
       matrix[i] = p->matrix().rc(i / 4, i % 4);
     }
-    if (!FuzzParamArray(&matrix[0], base::size(matrix), fuzzer))
+    if (!FuzzParamArray(&matrix[0], std::size(matrix), fuzzer))
       return false;
     *p = gfx::Transform(matrix[0], matrix[1], matrix[2], matrix[3], matrix[4],
                         matrix[5], matrix[6], matrix[7], matrix[8], matrix[9],
diff --git a/tools/json_schema_compiler/test/arrays_unittest.cc b/tools/json_schema_compiler/test/arrays_unittest.cc
index 5d533c09..e1aa7287 100644
--- a/tools/json_schema_compiler/test/arrays_unittest.cc
+++ b/tools/json_schema_compiler/test/arrays_unittest.cc
@@ -6,10 +6,10 @@
 
 #include <stddef.h>
 
+#include <iterator>
 #include <memory>
 #include <utility>
 
-#include "base/cxx17_backports.h"
 #include "base/values.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/json_schema_compiler/test/enums.h"
@@ -78,7 +78,7 @@
                                           arrays::ENUMERATION_TWO,
                                           arrays::ENUMERATION_THREE};
   EXPECT_EQ(std::vector<arrays::Enumeration>(
-                expected_types, expected_types + base::size(expected_types)),
+                expected_types, expected_types + std::size(expected_types)),
             enum_array_reference.types);
 
   // Test ToValue.
@@ -112,7 +112,7 @@
                                                  arrays::ENUMERATION_THREE};
   EXPECT_EQ(std::vector<arrays::Enumeration>(
                 expected_infile_types,
-                expected_infile_types + base::size(expected_infile_types)),
+                expected_infile_types + std::size(expected_infile_types)),
             enum_array_mixed.infile_enums);
 
   test::api::enums::Enumeration expected_external_types[] = {
@@ -120,7 +120,7 @@
       test::api::enums::ENUMERATION_THREE};
   EXPECT_EQ(std::vector<test::api::enums::Enumeration>(
                 expected_external_types,
-                expected_external_types + base::size(expected_external_types)),
+                expected_external_types + std::size(expected_external_types)),
             enum_array_mixed.external_enums);
 
   // Test ToValue.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b6b322a..9d12b3b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -52863,7 +52863,6 @@
   <int value="-1159154050" label="FilesSinglePartitionFormat:enabled"/>
   <int value="-1159151875" label="RecordWebAppDebugInfo:disabled"/>
   <int value="-1158993534" label="PrintScaling:enabled"/>
-  <int value="-1158291698" label="VariationsFakeCrashAfterStartup:enabled"/>
   <int value="-1156950420"
       label="CryptAuthV2DeviceActivityStatusUseConnectivity:enabled"/>
   <int value="-1156179600" label="OmniboxRichEntitySuggestions:enabled"/>
@@ -54617,7 +54616,6 @@
   <int value="83422372"
       label="ChromeHomePersonalizedOmniboxSuggestions:enabled"/>
   <int value="84911198" label="ScanCardsInWebPayments:disabled"/>
-  <int value="87306743" label="VariationsFakeCrashAfterStartup:disabled"/>
   <int value="87886288" label="DiagnosticsApp:enabled"/>
   <int value="88249612" label="CalendarView:enabled"/>
   <int value="88437020" label="FeaturePolicy:enabled"/>
@@ -56325,7 +56323,6 @@
   <int value="1287625114" label="EnableIncognitoShortcutOnDesktop:disabled"/>
   <int value="1287947083" label="WebAssemblyCodeProtectionPku:enabled"/>
   <int value="1288612648" label="PluginVmFullscreen:enabled"/>
-  <int value="1288713607" label="EcheSWAResizing:disabled"/>
   <int value="1289433604" label="RecoverFromNeverSaveAndroid:enabled"/>
   <int value="1289495171" label="AssistantBetterOnboarding:disabled"/>
   <int value="1289670142" label="ArcAccountRestrictions:disabled"/>
@@ -56591,7 +56588,6 @@
   <int value="1467583815" label="AVIF:disabled"/>
   <int value="1469407485" label="disable-accelerated-2d-canvas"/>
   <int value="1472938299" label="WellKnownChangePassword:disabled"/>
-  <int value="1473157145" label="EcheSWAResizing:enabled"/>
   <int value="1473379096" label="NearbySharingReceiveWifiCredentials:disabled"/>
   <int value="1473838479" label="EnableVirtualKeyboardMdUi:disabled"/>
   <int value="1473967338" label="OmniboxShortBookmarkSuggestions:enabled"/>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 031e148d..030f83de 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -10091,6 +10091,7 @@
 <histogram_suffixes name="SoftwareReporterEngine" separator="_">
   <suffix name="ESET" label=""/>
   <suffix name="URZA" label=""/>
+  <affected-histogram name="SoftwareReporter.CreateJobResult"/>
   <affected-histogram name="SoftwareReporter.FoundUwSReadError"/>
   <affected-histogram name="SoftwareReporter.LogsUploadEnabled"/>
   <affected-histogram name="SoftwareReporter.LogsUploadResult"/>
@@ -10099,9 +10100,9 @@
   <affected-histogram name="SoftwareReporter.MemoryUsed"/>
   <affected-histogram name="SoftwareReporter.MinorVersion"/>
   <affected-histogram name="SoftwareReporter.RunningTime"/>
-  <affected-histogram name="SoftwareReporter.RunningTimeAccordingToChrome"/>
+  <affected-histogram name="SoftwareReporter.RunningTimeAccordingToChrome2"/>
   <affected-histogram name="SoftwareReporter.RunningTimeRegistryError"/>
-  <affected-histogram name="SoftwareReporter.RunningTimeWithoutSleep"/>
+  <affected-histogram name="SoftwareReporter.RunningTimeWithoutSleep2"/>
   <affected-histogram name="SoftwareReporter.Step"/>
 </histogram_suffixes>
 
diff --git a/tools/metrics/histograms/metadata/software/histograms.xml b/tools/metrics/histograms/metadata/software/histograms.xml
index 77d3045..71e0054 100644
--- a/tools/metrics/histograms/metadata/software/histograms.xml
+++ b/tools/metrics/histograms/metadata/software/histograms.xml
@@ -125,6 +125,19 @@
   </summary>
 </histogram>
 
+<histogram name="SoftwareReporter.CreateJobResult" enum="WinGetLastError"
+    expires_after="2022-08-21">
+  <owner>joenotcharles@google.com</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    The Windows error code from CreateJobObject, which is called just before
+    launching the Software Reporter. If this is not ERROR_SUCCESS, the reporter
+    process is not attached to a job and may outlive the browser. Not logged on
+    Windows 7, where CreateJobObject is not called because nested job objects
+    aren't supported.
+  </summary>
+</histogram>
+
 <histogram name="SoftwareReporter.FoundUwSReadError" enum="BooleanError"
     expires_after="2022-07-03">
   <owner>drubery@chromium.org</owner>
@@ -283,27 +296,29 @@
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.RunningTimeAccordingToChrome" units="ms"
-    expires_after="2022-07-31">
+<histogram name="SoftwareReporter.RunningTimeAccordingToChrome2" units="ms"
+    expires_after="2022-08-21">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The amount of time from the Software Reporter process launch to the time it
     exits, in milliseconds. This includes time the computer was asleep or
-    hibernating. SoftwareReporter.RunningTimeWithoutSleep excludes those
-    periods.
+    hibernating. SoftwareReporter.RunningTimeWithoutSleep2 excludes those
+    periods. This replaces SoftwareReporter.RunningTimeAccordingToChrome which
+    also included queuing time of tasks before and after launch.
   </summary>
 </histogram>
 
-<histogram name="SoftwareReporter.RunningTimeWithoutSleep" units="ms"
+<histogram name="SoftwareReporter.RunningTimeWithoutSleep2" units="ms"
     expires_after="2022-08-21">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The amount of time from the Software Reporter process launch to the time it
     exits, in milliseconds. This does not include time the computer was asleep
-    or hibernating. SoftwareReporter.RunningTimeAccordingToChrome includes those
-    periods.
+    or hibernating. SoftwareReporter.RunningTimeAccordingToChrome2 includes
+    those periods. This replaces SoftwareReporter.RunningTimeWithoutSleep which
+    also included queuing time of tasks before and after launch.
   </summary>
 </histogram>
 
diff --git a/tools/style_variable_generator/blend_colors_test_expected.css b/tools/style_variable_generator/blend_colors_test_expected.css
index fa9b779..147eecb 100644
--- a/tools/style_variable_generator/blend_colors_test_expected.css
+++ b/tools/style_variable_generator/blend_colors_test_expected.css
@@ -6,6 +6,9 @@
  *  blend_colors_test.json5
  *  colors_test_palette.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/colors_test_custom_dark_toggle_expected.css b/tools/style_variable_generator/colors_test_custom_dark_toggle_expected.css
index 30dcdd3..85744da0 100644
--- a/tools/style_variable_generator/colors_test_custom_dark_toggle_expected.css
+++ b/tools/style_variable_generator/colors_test_custom_dark_toggle_expected.css
@@ -6,6 +6,9 @@
  *  colors_test.json5
  *  colors_test_palette.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/colors_test_dark_only_expected.css b/tools/style_variable_generator/colors_test_dark_only_expected.css
index 54111256..60da93b 100644
--- a/tools/style_variable_generator/colors_test_dark_only_expected.css
+++ b/tools/style_variable_generator/colors_test_dark_only_expected.css
@@ -6,6 +6,9 @@
  *  colors_test.json5
  *  colors_test_palette.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/colors_test_debug_placeholder_expected.css b/tools/style_variable_generator/colors_test_debug_placeholder_expected.css
index 333ec0f..0132427b 100644
--- a/tools/style_variable_generator/colors_test_debug_placeholder_expected.css
+++ b/tools/style_variable_generator/colors_test_debug_placeholder_expected.css
@@ -6,6 +6,9 @@
  *  colors_test.json5
  *  colors_test_palette.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/colors_test_expected.css b/tools/style_variable_generator/colors_test_expected.css
index afc7e75..1bdd64f 100644
--- a/tools/style_variable_generator/colors_test_expected.css
+++ b/tools/style_variable_generator/colors_test_expected.css
@@ -6,6 +6,9 @@
  *  colors_test.json5
  *  colors_test_palette.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/css_generator.tmpl b/tools/style_variable_generator/css_generator.tmpl
index 06f28ca..c60b096f 100644
--- a/tools/style_variable_generator/css_generator.tmpl
+++ b/tools/style_variable_generator/css_generator.tmpl
@@ -9,6 +9,9 @@
 {%- endfor %}
  */
 {%- endif %}
+:root {
+  color-scheme: light dark;
+}
 {% macro render_variables_for_mode(mode) -%}
 {%- for model_name, color in colors[mode].items() %}
   {{model_name | to_css_var_name}}-rgb: {{color | css_color_rgb}};
diff --git a/tools/style_variable_generator/inverted_colors_test_expected.css b/tools/style_variable_generator/inverted_colors_test_expected.css
index 9a7ecc6e..1acc8d9 100644
--- a/tools/style_variable_generator/inverted_colors_test_expected.css
+++ b/tools/style_variable_generator/inverted_colors_test_expected.css
@@ -6,6 +6,9 @@
  *  colors_test_palette.json5
  *  inverted_colors_test.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/suppress_sources_comment_test_expected.css b/tools/style_variable_generator/suppress_sources_comment_test_expected.css
index 377e115..8033ae3 100644
--- a/tools/style_variable_generator/suppress_sources_comment_test_expected.css
+++ b/tools/style_variable_generator/suppress_sources_comment_test_expected.css
@@ -1,6 +1,9 @@
 /* Copyright 2020 The Chromium Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {
   --google-grey-900-rgb: 32, 33, 36;
diff --git a/tools/style_variable_generator/typography_test_expected.css b/tools/style_variable_generator/typography_test_expected.css
index 4ba2772d..dc6d7f2 100644
--- a/tools/style_variable_generator/typography_test_expected.css
+++ b/tools/style_variable_generator/typography_test_expected.css
@@ -5,6 +5,9 @@
 /* This file is generated from:
  *  typography_test.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {}
 
diff --git a/tools/style_variable_generator/untyped_css_test_expected.css b/tools/style_variable_generator/untyped_css_test_expected.css
index 7ca19df..c665deef 100644
--- a/tools/style_variable_generator/untyped_css_test_expected.css
+++ b/tools/style_variable_generator/untyped_css_test_expected.css
@@ -5,6 +5,9 @@
 /* This file is generated from:
  *  untyped_css_test.json5
  */
+:root {
+  color-scheme: light dark;
+}
 
 html:not(body) {}
 
diff --git a/ui/chromeos/strings/network_element_localized_strings_provider.cc b/ui/chromeos/strings/network_element_localized_strings_provider.cc
index 4fbf889..21506be 100644
--- a/ui/chromeos/strings/network_element_localized_strings_provider.cc
+++ b/ui/chromeos/strings/network_element_localized_strings_provider.cc
@@ -234,8 +234,13 @@
       {"OncVPN-IPsec-AuthType", IDS_ONC_VPN_AUTH_TYPE},
       {"OncVPN-IPsec-AuthType_PSK", IDS_ONC_VPN_IPSEC_PSK},
       {"OncVPN-IPsec-AuthType_Cert", IDS_ONC_EAP_USER_CERT},
+      {"OncVPN-IPsec-AuthType_EAP", IDS_ONC_VPN_AUTH_TYPE_USERNAME},
       {"OncVPN-IPsec-Group", IDS_ONC_VPN_IPSEC_GROUP},
+      {"OncVPN-IPsec-LocalIdentity", IDS_ONC_VPN_IPSEC_LOCAL_IDENTITY},
+      {"OncVPN-IPsec-Password", IDS_ONC_VPN_PASSWORD},
       {"OncVPN-IPsec-PSK", IDS_ONC_VPN_IPSEC_PSK},
+      {"OncVPN-IPsec-RemoteIdentity", IDS_ONC_VPN_IPSEC_REMOTE_IDENTITY},
+      {"OncVPN-IPsec-Username", IDS_ONC_VPN_USERNAME},
       {"OncVPN-L2TP-Password", IDS_ONC_VPN_PASSWORD},
       {"OncVPN-L2TP-Username", IDS_ONC_VPN_USERNAME},
       {"OncVPN-OpenVPN-Auth", IDS_ONC_VPN_OPENVPN_AUTH},
diff --git a/ui/file_manager/file_manager/common/js/files_app_entry_types.js b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
index 8dad588c..5c0d148 100644
--- a/ui/file_manager/file_manager/common/js/files_app_entry_types.js
+++ b/ui/file_manager/file_manager/common/js/files_app_entry_types.js
@@ -727,4 +727,9 @@
   get iconName() {
     return /** @type{string} */ ('crostini');
   }
+
+  /** @override */
+  toURL() {
+    return `fake-entry://guest-os/${this.guest_id}`;
+  }
 }
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index b9909c38..049999f6 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -53,6 +53,7 @@
     ":file_watcher",
     ":folder_shortcuts_data_model",
     ":gear_menu_controller",
+    ":guest_os_controller",
     ":holding_space_util",
     ":import_controller",
     ":last_modified_controller",
@@ -129,6 +130,7 @@
     ":file_watcher",
     ":folder_shortcuts_data_model",
     ":gear_menu_controller",
+    ":guest_os_controller",
     ":holding_space_util",
     ":import_controller",
     ":last_modified_controller",
@@ -854,6 +856,17 @@
   ]
 }
 
+js_library("guest_os_controller") {
+  deps = [
+    ":directory_model",
+    ":navigation_list_model",
+    "ui:directory_tree",
+    "//ui/file_manager/file_manager/common/js:api",
+    "//ui/file_manager/file_manager/common/js:files_app_entry_types",
+    "//ui/file_manager/file_manager/common/js:util",
+  ]
+}
+
 js_library("holding_space_util") {
   deps = [
     "//ui/file_manager/file_manager/common/js:metrics",
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 08a7b639c..7c2bcfe 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -113,6 +113,9 @@
     /** @private {?CrostiniController} */
     this.crostiniController_ = null;
 
+    /** @private {?GuestOsController} */
+    this.guestOsController_ = null;
+
     /**
      * ImportHistory. Non-null only once history observer is added in
      * {@code addHistoryObserver}.
@@ -1339,9 +1342,9 @@
         maybeShowToast, this.ui_.toast);
 
     if (util.isGuestOsEnabled()) {
-      const guestOsController = new GuestOsController(
+      this.guestOsController_ = new GuestOsController(
           this.directoryModel_, assert(this.directoryTree));
-      await guestOsController.refresh();
+      await this.guestOsController_.refresh();
     }
   }
 
diff --git a/ui/file_manager/file_manager/foreground/js/guest_os_controller.js b/ui/file_manager/file_manager/foreground/js/guest_os_controller.js
index 9ea915b..03dbd76 100644
--- a/ui/file_manager/file_manager/foreground/js/guest_os_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/guest_os_controller.js
@@ -27,14 +27,27 @@
 
     /** @private @const */
     this.directoryTree_ = directoryTree;
+
+    chrome.fileManagerPrivate.onMountableGuestsChanged.addListener(
+        this.onMountableGuestsChanged.bind(this));
   }
 
   /**
-   * Refresh the Guest OS files items by fetching an updated list of guests,
+   * Refresh the Guest OS placeholders by fetching an updated list of guests,
    * adding them to the directory tree and triggering a redraw.
    */
   async refresh() {
     const guests = await listMountableGuests();
+    this.onMountableGuestsChanged(guests);
+  }
+
+  /**
+   * Updates the list of Guest OSs when we receive an event for the list of
+   * registered guests changing, by adding them to the directory tree and
+   * triggering a redraw.
+   * @param {!Array<!chrome.fileManagerPrivate.MountableGuest>} guests
+   */
+  async onMountableGuestsChanged(guests) {
     this.directoryTree_.dataModel.guestOsPlaceholders = guests.map(guest => {
       return new NavigationModelFakeItem(
           guest.displayName, NavigationModelItemType.GUEST_OS,
diff --git a/ui/file_manager/integration_tests/file_manager/guest_os.js b/ui/file_manager/integration_tests/file_manager/guest_os.js
index 1fc09761..cd75a93 100644
--- a/ui/file_manager/integration_tests/file_manager/guest_os.js
+++ b/ui/file_manager/integration_tests/file_manager/guest_os.js
@@ -42,8 +42,10 @@
       await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.hello], []);
 
   // Check that our guests are listed.
-  const query = '#directory-tree [root-type-icon=guest_os]';
-  await remoteCall.waitForElementsCount(appId, [query], names.length);
+  for (const name of names) {
+    await remoteCall.waitForElement(
+        appId, `#directory-tree [entry-label="${name}"]`);
+  }
 };
 
 /**
@@ -74,3 +76,46 @@
   // We expect there to be an error, from the mount failure.
   return IGNORE_APP_ERRORS;
 };
+
+/**
+ * Tests that the list of guests is updated when new guests are added or
+ * removed.
+ */
+testcase.listUpdatedWhenGuestsChanged = async () => {
+  // Open the files app.
+  const appId =
+      await setupAndWaitUntilReady(RootPath.DOWNLOADS, [ENTRIES.hello], []);
+
+  // We'll use this query a lot to check how many guests we have.
+  const query = '#directory-tree [root-type-icon=guest_os]';
+
+  const names = ['Etcetera', 'Electra'];
+  const ids = [];
+
+  for (const name of names) {
+    // Add a guest...
+    ids.push(await sendTestMessage(
+        {name: 'registerMountableGuest', displayName: name}));
+
+    // ...and it should show up
+    await remoteCall.waitForElement(
+        appId, `#directory-tree [entry-label="${name}"]`);
+  }
+
+  // Check that we have the right number of entries.
+  await remoteCall.waitForElementsCount(appId, [query], ids.length);
+
+  // Remove the guests...
+  for (const guestId of ids) {
+    await sendTestMessage({name: 'unregisterMountableGuest', guestId: guestId});
+  }
+
+  // ...and they should all be gone.
+  await remoteCall.waitForElementsCount(appId, [query], 0);
+
+  // Then add them back for good measure.
+  for (const name of names) {
+    await sendTestMessage({name: 'registerMountableGuest', displayName: name});
+  }
+  await remoteCall.waitForElementsCount(appId, [query], names.length);
+};
diff --git a/ui/ozone/platform/wayland/test/test_gtk_primary_selection.cc b/ui/ozone/platform/wayland/test/test_gtk_primary_selection.cc
index a027fda..7d9e2290 100644
--- a/ui/ozone/platform/wayland/test/test_gtk_primary_selection.cc
+++ b/ui/ozone/platform/wayland/test/test_gtk_primary_selection.cc
@@ -37,7 +37,7 @@
 
 struct GtkPrimarySelectionDevice final : public TestSelectionDevice::Delegate {
   TestSelectionOffer* CreateAndSendOffer() override {
-    const struct gtk_primary_selection_offer_interface kOfferImpl = {
+    static const struct gtk_primary_selection_offer_interface kOfferImpl = {
         &TestSelectionOffer::Receive, &Destroy};
     wl_resource* device_resource = device->resource();
     const int version = wl_resource_get_version(device_resource);
@@ -98,7 +98,7 @@
   ~GtkPrimarySelectionDeviceManager() override = default;
 
   TestSelectionDevice* CreateDevice(wl_client* client, uint32_t id) override {
-    const struct gtk_primary_selection_device_interface
+    static const struct gtk_primary_selection_device_interface
         kTestSelectionDeviceImpl = {&TestSelectionDevice::SetSelection,
                                     &Destroy};
     auto* delegate = new GtkPrimarySelectionDevice;
@@ -110,7 +110,7 @@
   }
 
   TestSelectionSource* CreateSource(wl_client* client, uint32_t id) override {
-    const struct gtk_primary_selection_source_interface
+    static const struct gtk_primary_selection_source_interface
         kTestSelectionSourceImpl = {&TestSelectionSource::Offer, &Destroy};
     auto* delegate = new GtkPrimarySelectionSource;
     wl_resource* resource = CreateResourceWithImpl<TestSelectionSource>(
@@ -130,7 +130,7 @@
 
 TestSelectionDeviceManager* CreateTestSelectionManagerGtk() {
   constexpr uint32_t kVersion = 1;
-  const struct gtk_primary_selection_device_manager_interface
+  static const struct gtk_primary_selection_device_manager_interface
       kTestSelectionManagerImpl = {&TestSelectionDeviceManager::CreateSource,
                                    &TestSelectionDeviceManager::GetDevice,
                                    &Destroy};
diff --git a/ui/ozone/platform/wayland/test/test_zwp_primary_selection.cc b/ui/ozone/platform/wayland/test/test_zwp_primary_selection.cc
index 6e22a3a..447fa6e 100644
--- a/ui/ozone/platform/wayland/test/test_zwp_primary_selection.cc
+++ b/ui/ozone/platform/wayland/test/test_zwp_primary_selection.cc
@@ -37,7 +37,7 @@
 
 struct ZwpPrimarySelectionDevice final : public TestSelectionDevice::Delegate {
   TestSelectionOffer* CreateAndSendOffer() override {
-    const struct zwp_primary_selection_offer_v1_interface kOfferImpl = {
+    static const struct zwp_primary_selection_offer_v1_interface kOfferImpl = {
         &TestSelectionOffer::Receive, &Destroy};
     wl_resource* device_resource = device->resource();
     const int version = wl_resource_get_version(device_resource);
@@ -98,7 +98,7 @@
   ~ZwpPrimarySelectionDeviceManager() override = default;
 
   TestSelectionDevice* CreateDevice(wl_client* client, uint32_t id) override {
-    const struct zwp_primary_selection_device_v1_interface
+    static const struct zwp_primary_selection_device_v1_interface
         kTestSelectionDeviceImpl = {&TestSelectionDevice::SetSelection,
                                     &Destroy};
     auto* delegate = new ZwpPrimarySelectionDevice;
@@ -110,7 +110,7 @@
   }
 
   TestSelectionSource* CreateSource(wl_client* client, uint32_t id) override {
-    const struct zwp_primary_selection_source_v1_interface
+    static const struct zwp_primary_selection_source_v1_interface
         kTestSelectionSourceImpl = {&TestSelectionSource::Offer, &Destroy};
     auto* delegate = new ZwpPrimarySelectionSource;
     wl_resource* resource = CreateResourceWithImpl<TestSelectionSource>(
@@ -130,7 +130,7 @@
 
 TestSelectionDeviceManager* CreateTestSelectionManagerZwp() {
   constexpr uint32_t kVersion = 1;
-  const struct zwp_primary_selection_device_manager_v1_interface
+  static const struct zwp_primary_selection_device_manager_v1_interface
       kTestSelectionManagerImpl = {&TestSelectionDeviceManager::CreateSource,
                                    &TestSelectionDeviceManager::GetDevice,
                                    &Destroy};
diff --git a/ui/webui/resources/cr_components/app_management/BUILD.gn b/ui/webui/resources/cr_components/app_management/BUILD.gn
index d80739bd..40621102 100644
--- a/ui/webui/resources/cr_components/app_management/BUILD.gn
+++ b/ui/webui/resources/cr_components/app_management/BUILD.gn
@@ -48,6 +48,25 @@
     "//mojo/public/mojom/base",
     "//url/mojom:url_mojom_gurl",
   ]
+
+  cpp_typemaps = [
+    {
+      types = [
+        {
+          mojom = "app_management.mojom.Permission"
+          cpp = "::apps::PermissionPtr"
+          move_only = true
+        },
+      ]
+      traits_headers = [
+        "//ui/webui/resources/cr_components/app_management/app_management_mojom_traits.h",
+        "//components/services/app_service/public/cpp/permission.h",
+      ]
+      traits_sources = [ "//ui/webui/resources/cr_components/app_management/app_management_mojom_traits.cc" ]
+      traits_public_deps =
+          [ "//components/services/app_service/public/cpp:app_types" ]
+    },
+  ]
 }
 
 preprocess_if_expr("preprocess_src") {
@@ -107,3 +126,16 @@
   manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
   resource_path_prefix = "cr_components/app_management"
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "app_management_mojom_traits_unittests.cc" ]
+
+  deps = [
+    ":mojo_bindings",
+    "//base/test:test_support",
+    "//chrome/test:test_support",
+    "//mojo/public/cpp/test_support:test_utils",
+    "//testing/gtest",
+  ]
+}
diff --git a/ui/webui/resources/cr_components/app_management/DEPS b/ui/webui/resources/cr_components/app_management/DEPS
new file mode 100644
index 0000000..908b1607
--- /dev/null
+++ b/ui/webui/resources/cr_components/app_management/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "app_management_mojom_traits\.*": [
+    "+components/services/app_service/public",
+  ],
+}
diff --git a/ui/webui/resources/cr_components/app_management/OWNERS b/ui/webui/resources/cr_components/app_management/OWNERS
index dc73ed8..9396e267 100644
--- a/ui/webui/resources/cr_components/app_management/OWNERS
+++ b/ui/webui/resources/cr_components/app_management/OWNERS
@@ -5,3 +5,6 @@
 
 per-file *.mojom=set noparent
 per-file *.mojom=file://ipc/SECURITY_OWNERS
+
+per-file *_mojom_traits*.*=set noparent
+per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS
diff --git a/ui/webui/resources/cr_components/app_management/app_management.mojom b/ui/webui/resources/cr_components/app_management/app_management.mojom
index 905d960..b1d194f 100644
--- a/ui/webui/resources/cr_components/app_management/app_management.mojom
+++ b/ui/webui/resources/cr_components/app_management/app_management.mojom
@@ -7,6 +7,36 @@
 import "components/services/app_service/public/mojom/types.mojom";
 import "url/mojom/url.mojom";
 
+// The types of permissions in App Service.
+enum PermissionType {
+  kUnknown         = 0,
+  kCamera          = 1,
+  kLocation        = 2,
+  kMicrophone      = 3,
+  kNotifications   = 4,
+  kContacts        = 5,
+  kStorage         = 6,
+  kPrinting        = 7,
+};
+
+enum TriState {
+  kAllow,
+  kBlock,
+  kAsk,
+};
+
+union PermissionValue {
+  bool bool_value;
+  TriState tristate_value;
+};
+
+struct Permission {
+  PermissionType permission_type;
+  PermissionValue value;
+  // If the permission is managed by an enterprise policy.
+  bool is_managed;
+};
+
 struct App {
   string id;
 
@@ -21,7 +51,7 @@
   apps.mojom.OptionalBool is_policy_pinned;
   string? version;
   string? size;
-  map<apps.mojom.PermissionType, apps.mojom.Permission> permissions;
+  map<PermissionType, Permission> permissions;
   apps.mojom.InstallReason install_reason;
   apps.mojom.InstallSource install_source;
   bool hide_more_settings;
@@ -55,9 +85,12 @@
   GetApp(string app_id) => (App? app);
   GetExtensionAppPermissionMessages(string app_id) =>
       (array<ExtensionAppPermissionMessage> messages);
+  // Pins or unpins for an app identified with `app_id`.
   SetPinned(string app_id, apps.mojom.OptionalBool pinned);
+  // Sets `permission` for an app identified with `app_id`.
   SetPermission(string app_id,
-                apps.mojom.Permission permission);
+                Permission permission);
+  // Enables resize lock mode for the app identified by `app_id`.
   SetResizeLocked(string app_id, bool locked);
   Uninstall(string app_id);
   OpenNativeSettings(string app_id);
diff --git a/ui/webui/resources/cr_components/app_management/app_management_mojom_traits.cc b/ui/webui/resources/cr_components/app_management/app_management_mojom_traits.cc
new file mode 100644
index 0000000..7234b82
--- /dev/null
+++ b/ui/webui/resources/cr_components/app_management/app_management_mojom_traits.cc
@@ -0,0 +1,138 @@
+// 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 "ui/webui/resources/cr_components/app_management/app_management_mojom_traits.h"
+
+#include <utility>
+
+namespace mojo {
+
+bool StructTraits<PermissionDataView, apps::PermissionPtr>::Read(
+    PermissionDataView data,
+    apps::PermissionPtr* out) {
+  apps::PermissionType permission_type;
+  if (!data.ReadPermissionType(&permission_type))
+    return false;
+
+  apps::PermissionValuePtr value;
+  if (!data.ReadValue(&value))
+    return false;
+
+  *out = std::make_unique<apps::Permission>(permission_type, std::move(value),
+                                            data.is_managed());
+  return true;
+}
+
+PermissionType EnumTraits<PermissionType, apps::PermissionType>::ToMojom(
+    apps::PermissionType input) {
+  switch (input) {
+    case apps::PermissionType::kUnknown:
+      return PermissionType::kUnknown;
+    case apps::PermissionType::kCamera:
+      return PermissionType::kCamera;
+    case apps::PermissionType::kLocation:
+      return PermissionType::kLocation;
+    case apps::PermissionType::kMicrophone:
+      return PermissionType::kMicrophone;
+    case apps::PermissionType::kNotifications:
+      return PermissionType::kNotifications;
+    case apps::PermissionType::kContacts:
+      return PermissionType::kContacts;
+    case apps::PermissionType::kStorage:
+      return PermissionType::kStorage;
+    case apps::PermissionType::kPrinting:
+      return PermissionType::kPrinting;
+  }
+}
+
+bool EnumTraits<PermissionType, apps::PermissionType>::FromMojom(
+    PermissionType input,
+    apps::PermissionType* output) {
+  switch (input) {
+    case PermissionType::kUnknown:
+      *output = apps::PermissionType::kUnknown;
+      return true;
+    case PermissionType::kCamera:
+      *output = apps::PermissionType::kCamera;
+      return true;
+    case PermissionType::kLocation:
+      *output = apps::PermissionType::kLocation;
+      return true;
+    case PermissionType::kMicrophone:
+      *output = apps::PermissionType::kMicrophone;
+      return true;
+    case PermissionType::kNotifications:
+      *output = apps::PermissionType::kNotifications;
+      return true;
+    case PermissionType::kContacts:
+      *output = apps::PermissionType::kContacts;
+      return true;
+    case PermissionType::kStorage:
+      *output = apps::PermissionType::kStorage;
+      return true;
+    case PermissionType::kPrinting:
+      *output = apps::PermissionType::kPrinting;
+      return true;
+  }
+}
+
+TriState EnumTraits<TriState, apps::TriState>::ToMojom(apps::TriState input) {
+  switch (input) {
+    case apps::TriState::kAllow:
+      return TriState::kAllow;
+    case apps::TriState::kBlock:
+      return TriState::kBlock;
+    case apps::TriState::kAsk:
+      return TriState::kAsk;
+  }
+}
+
+bool EnumTraits<TriState, apps::TriState>::FromMojom(TriState input,
+                                                     apps::TriState* output) {
+  switch (input) {
+    case TriState::kAllow:
+      *output = apps::TriState::kAllow;
+      return true;
+    case TriState::kBlock:
+      *output = apps::TriState::kBlock;
+      return true;
+    case TriState::kAsk:
+      *output = apps::TriState::kAsk;
+      return true;
+  }
+}
+
+PermissionValueDataView::Tag
+UnionTraits<PermissionValueDataView, apps::PermissionValuePtr>::GetTag(
+    const apps::PermissionValuePtr& r) {
+  if (r->bool_value.has_value()) {
+    return PermissionValueDataView::Tag::BOOL_VALUE;
+  } else if (r->tristate_value.has_value()) {
+    return PermissionValueDataView::Tag::TRISTATE_VALUE;
+  }
+  NOTREACHED();
+  return PermissionValueDataView::Tag::BOOL_VALUE;
+}
+
+bool UnionTraits<PermissionValueDataView, apps::PermissionValuePtr>::Read(
+    PermissionValueDataView data,
+    apps::PermissionValuePtr* out) {
+  switch (data.tag()) {
+    case PermissionValueDataView::Tag::BOOL_VALUE: {
+      *out = std::make_unique<apps::PermissionValue>(data.bool_value());
+      return true;
+    }
+    case PermissionValueDataView::Tag::TRISTATE_VALUE: {
+      apps::TriState tristate_value;
+      if (!data.ReadTristateValue(&tristate_value))
+        return false;
+      *out = std::make_unique<apps::PermissionValue>(tristate_value);
+      return true;
+    }
+  }
+  NOTREACHED();
+  return false;
+}
+
+}  // namespace mojo
diff --git a/ui/webui/resources/cr_components/app_management/app_management_mojom_traits.h b/ui/webui/resources/cr_components/app_management/app_management_mojom_traits.h
new file mode 100644
index 0000000..8823626
--- /dev/null
+++ b/ui/webui/resources/cr_components/app_management/app_management_mojom_traits.h
@@ -0,0 +1,72 @@
+// 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 UI_WEBUI_RESOURCES_CR_COMPONENTS_APP_MANAGEMENT_APP_MANAGEMENT_MOJOM_TRAITS_H_
+#define UI_WEBUI_RESOURCES_CR_COMPONENTS_APP_MANAGEMENT_APP_MANAGEMENT_MOJOM_TRAITS_H_
+
+#include "components/services/app_service/public/cpp/permission.h"
+#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h"
+
+namespace mojo {
+
+namespace {
+
+using PermissionDataView = app_management::mojom::PermissionDataView;
+using PermissionType = app_management::mojom::PermissionType;
+using TriState = app_management::mojom::TriState;
+using PermissionValueDataView = app_management::mojom::PermissionValueDataView;
+
+}  // namespace
+
+template <>
+struct StructTraits<PermissionDataView, apps::PermissionPtr> {
+  static apps::PermissionType permission_type(const apps::PermissionPtr& r) {
+    return r->permission_type;
+  }
+
+  static const apps::PermissionValuePtr& value(const apps::PermissionPtr& r) {
+    return r->value;
+  }
+
+  static bool is_managed(const apps::PermissionPtr& r) { return r->is_managed; }
+
+  static bool Read(PermissionDataView, apps::PermissionPtr* out);
+};
+
+template <>
+struct EnumTraits<PermissionType, apps::PermissionType> {
+  static PermissionType ToMojom(apps::PermissionType input);
+  static bool FromMojom(PermissionType input, apps::PermissionType* output);
+};
+
+template <>
+struct EnumTraits<TriState, apps::TriState> {
+  static TriState ToMojom(apps::TriState input);
+  static bool FromMojom(TriState input, apps::TriState* output);
+};
+
+template <>
+struct UnionTraits<PermissionValueDataView, apps::PermissionValuePtr> {
+  static PermissionValueDataView::Tag GetTag(const apps::PermissionValuePtr& r);
+
+  static bool IsNull(const apps::PermissionValuePtr& r) {
+    return !r->bool_value.has_value() && !r->tristate_value.has_value();
+  }
+
+  static void SetToNull(apps::PermissionValuePtr* out) { out->reset(); }
+
+  static bool bool_value(const apps::PermissionValuePtr& r) {
+    return r->bool_value.value();
+  }
+
+  static apps::TriState tristate_value(const apps::PermissionValuePtr& r) {
+    return r->tristate_value.value();
+  }
+
+  static bool Read(PermissionValueDataView data, apps::PermissionValuePtr* out);
+};
+
+}  // namespace mojo
+
+#endif  // UI_WEBUI_RESOURCES_CR_COMPONENTS_APP_MANAGEMENT_APP_MANAGEMENT_MOJOM_TRAITS_H_
diff --git a/ui/webui/resources/cr_components/app_management/app_management_mojom_traits_unittests.cc b/ui/webui/resources/cr_components/app_management/app_management_mojom_traits_unittests.cc
new file mode 100644
index 0000000..bfe3d36a
--- /dev/null
+++ b/ui/webui/resources/cr_components/app_management/app_management_mojom_traits_unittests.cc
@@ -0,0 +1,102 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "components/services/app_service/public/cpp/permission.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/webui/resources/cr_components/app_management/app_management.mojom.h"
+#include "ui/webui/resources/cr_components/app_management/app_management_mojom_traits.h"
+
+TEST(AppManagementMojomTraitsTest, RoundTripPermissions) {
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kUnknown,
+        std::make_unique<apps::PermissionValue>(true),
+        /*is_managed=*/false);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kCamera,
+        std::make_unique<apps::PermissionValue>(true),
+        /*is_managed=*/true);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kLocation,
+        std::make_unique<apps::PermissionValue>(apps::TriState::kAllow),
+        /*is_managed=*/false);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kMicrophone,
+        std::make_unique<apps::PermissionValue>(apps::TriState::kBlock),
+        /*is_managed=*/true);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kNotifications,
+        std::make_unique<apps::PermissionValue>(apps::TriState::kAsk),
+        /*is_managed=*/false);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kContacts,
+        std::make_unique<apps::PermissionValue>(apps::TriState::kAllow),
+        /*is_managed=*/true);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kStorage,
+        std::make_unique<apps::PermissionValue>(apps::TriState::kBlock),
+        /*is_managed=*/false);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+  {
+    auto permission = std::make_unique<apps::Permission>(
+        apps::PermissionType::kPrinting,
+        std::make_unique<apps::PermissionValue>(apps::TriState::kBlock),
+        /*is_managed=*/false);
+    apps::PermissionPtr output;
+    ASSERT_TRUE(
+        mojo::test::SerializeAndDeserialize<app_management::mojom::Permission>(
+            permission, output));
+    EXPECT_EQ(*permission, *output);
+  }
+}
diff --git a/ui/webui/resources/cr_components/app_management/permission_constants.ts b/ui/webui/resources/cr_components/app_management/permission_constants.ts
index 65d47c8..8e167aa 100644
--- a/ui/webui/resources/cr_components/app_management/permission_constants.ts
+++ b/ui/webui/resources/cr_components/app_management/permission_constants.ts
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {PermissionType} from './types.mojom-webui.js';
+import {PermissionType} from './app_management.mojom-webui.js';
 
-export {PermissionType, PermissionValue, TriState} from './types.mojom-webui.js';
+export {Permission, PermissionType, PermissionValue, TriState} from './app_management.mojom-webui.js';
 
 export type PermissionTypeIndex = keyof typeof PermissionType;
diff --git a/ui/webui/resources/cr_components/app_management/permission_item.ts b/ui/webui/resources/cr_components/app_management/permission_item.ts
index 1099009..79da479 100644
--- a/ui/webui/resources/cr_components/app_management/permission_item.ts
+++ b/ui/webui/resources/cr_components/app_management/permission_item.ts
@@ -8,12 +8,12 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {App} from './app_management.mojom-webui.js';
+import {Permission} from './app_management.mojom-webui.js';
 import {BrowserProxy} from './browser_proxy.js';
 import {AppManagementUserAction} from './constants.js';
 import {PermissionType, PermissionTypeIndex, TriState} from './permission_constants.js';
 import {createBoolPermission, createTriStatePermission, getBoolPermissionValue, getTriStatePermissionValue, isBoolValue, isTriStateValue} from './permission_util.js';
 import {AppManagementToggleRowElement} from './toggle_row.js';
-import {Permission} from './types.mojom-webui.js';
 import {getPermission, getPermissionValueBool, recordAppManagementUserAction} from './util.js';
 
 export class AppManagementPermissionItemElement extends PolymerElement {
@@ -34,7 +34,7 @@
 
       /**
        * A string version of the permission type. Must be a value of the
-       * permission type enum in apps.mojom.PermissionType.
+       * permission type enum in appManagement.mojom.PermissionType.
        */
       permissionType: String,
 
diff --git a/ui/webui/resources/cr_components/app_management/permission_util.ts b/ui/webui/resources/cr_components/app_management/permission_util.ts
index 38d6b6a..79f7273be 100644
--- a/ui/webui/resources/cr_components/app_management/permission_util.ts
+++ b/ui/webui/resources/cr_components/app_management/permission_util.ts
@@ -4,8 +4,8 @@
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 
+import {Permission} from './app_management.mojom-webui.js';
 import {PermissionType, PermissionValue, TriState} from './permission_constants.js';
-import {Permission} from './types.mojom-webui.js';
 
 export function createPermission(
     permissionType: PermissionType, value: PermissionValue,
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js
index 8c7af90..d2fdb61 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js
@@ -7,7 +7,7 @@
 // clang-format on
 
 /**
- * @implements {chromeos.multideviceSetup.mojom.MultiDeviceSetupInterface}
+ * @implements {ash.multideviceSetup.mojom.MultiDeviceSetupInterface}
  */
 /* #export */ class FakeMojoService {
   constructor() {
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
index f1d36cf..297c909 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js
@@ -15,7 +15,7 @@
   /** @interface */
   /* #export */ class MojoInterfaceProvider {
     /**
-     * @return {!chromeos.multideviceSetup.mojom.MultiDeviceSetupRemote}
+     * @return {!ash.multideviceSetup.mojom.MultiDeviceSetupRemote}
      */
     getMojoServiceRemote() {}
   }
@@ -23,15 +23,14 @@
   /** @implements {multidevice_setup.MojoInterfaceProvider} */
   /* #export */ class MojoInterfaceProviderImpl {
     constructor() {
-      /** @private {?chromeos.multideviceSetup.mojom.MultiDeviceSetupRemote} */
+      /** @private {?ash.multideviceSetup.mojom.MultiDeviceSetupRemote} */
       this.remote_ = null;
     }
 
     /** @override */
     getMojoServiceRemote() {
       if (!this.remote_) {
-        this.remote_ =
-            chromeos.multideviceSetup.mojom.MultiDeviceSetup.getRemote();
+        this.remote_ = ash.multideviceSetup.mojom.MultiDeviceSetup.getRemote();
       }
 
       return this.remote_;
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
index 241167e..87e201e 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js
@@ -91,7 +91,7 @@
       /**
        * Array of objects representing all potential MultiDevice hosts.
        *
-       * @private {!Array<!chromeos.multideviceSetup.mojom.HostDevice>}
+       * @private {!Array<!ash.multideviceSetup.mojom.HostDevice>}
        */
       devices_: Array,
 
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
index 6b0b3413..a5fe08da 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js
@@ -33,7 +33,7 @@
     /**
      * Array of objects representing all potential MultiDevice hosts.
      *
-     * @type {!Array<!chromeos.multideviceSetup.mojom.HostDevice>}
+     * @type {!Array<!ash.multideviceSetup.mojom.HostDevice>}
      */
     devices: {
       type: Array,
@@ -137,7 +137,7 @@
   },
 
   /**
-   * @param {!Array<!chromeos.multideviceSetup.mojom.HostDevice>} devices
+   * @param {!Array<!ash.multideviceSetup.mojom.HostDevice>} devices
    * @return {string} Label for devices selection content.
    * @private
    */
@@ -153,7 +153,7 @@
   },
 
   /**
-   * @param {!Array<!chromeos.multideviceSetup.mojom.HostDevice>} devices
+   * @param {!Array<!ash.multideviceSetup.mojom.HostDevice>} devices
    * @return {boolean} True if there are more than one potential host devices.
    * @private
    */
@@ -162,7 +162,7 @@
   },
 
   /**
-   * @param {!Array<!chromeos.multideviceSetup.mojom.HostDevice>} devices
+   * @param {!Array<!ash.multideviceSetup.mojom.HostDevice>} devices
    * @return {boolean} True if there is exactly one potential host device.
    * @private
    */
@@ -171,7 +171,7 @@
   },
 
   /**
-   * @param {!Array<!chromeos.multideviceSetup.mojom.HostDevice>} devices
+   * @param {!Array<!ash.multideviceSetup.mojom.HostDevice>} devices
    * @return {string} Name of the first device in device list if there are any.
    *     Returns an empty string otherwise.
    * @private
@@ -193,7 +193,7 @@
   },
 
   /**
-   * @param {!chromeos.multideviceSetup.mojom.HostDevice} device
+   * @param {!ash.multideviceSetup.mojom.HostDevice} device
    * @return {string} Name of the device, with connectivity status information.
    * @private
    */
@@ -207,7 +207,7 @@
   },
 
   /**
-   * @param {!chromeos.multideviceSetup.mojom.HostDevice} device
+   * @param {!ash.multideviceSetup.mojom.HostDevice} device
    * @return {string} Returns a unique identifier for the input device, using
    *     the device's Instance ID if it is available; otherwise, the device's
    *     legacy device ID is used.
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.html b/ui/webui/resources/cr_components/chromeos/network/network_config.html
index 32d42da..4193f93 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.html
@@ -102,6 +102,18 @@
               items="[[ipsecAuthTypeItems_]]" onc-prefix="VPN.IPsec.AuthType"
               disabled="[[hasGuid_(guid)]]">
           </network-config-select>
+          <template is="dom-if" if="[[showVpn_.IPsecEAP]]" restamp>
+            <network-config-input label="[[i18n('OncVPN-IPsec-Username')]]"
+                id="ipsec-eap-username-input"
+                value="{{eapProperties_.identity}}"
+                property="[[managedEapProperties_.identity]]">
+            </network-config-input>
+            <network-password-input label="[[i18n('OncVPN-IPsec-Password')]]"
+                id="ipsec-eap-password-input"
+                value="{{eapProperties_.password}}"
+                property="[[managedEapProperties_.password]]">
+            </network-password-input>
+          </template>
           <template is="dom-if" if="[[!showVpn_.IKEv2]]" restamp>
             <network-config-input label="[[i18n('OncVPN-L2TP-Username')]]"
                 id="l2tp-username-input"
@@ -124,6 +136,18 @@
                 property="[[managedProperties_.typeProperties.vpn.ipSec.psk]]">
             </network-password-input>
           </template>
+          <template is="dom-if" if="[[showVpn_.IKEv2]]" restamp>
+            <network-config-input label="[[i18n('OncVPN-IPsec-LocalIdentity')]]"
+                id="ipsec-local-id-input"
+                value="{{configProperties_.typeConfig.vpn.ipSec.localIdentity}}"
+                property="[[managedProperties_.typeProperties.vpn.ipSec.localIdentity]]">
+            </network-config-input>
+            <network-config-input label="[[i18n('OncVPN-IPsec-RemoteIdentity')]]"
+                id="ipsec-remote-id-input"
+                value="{{configProperties_.typeConfig.vpn.ipSec.remoteIdentity}}"
+                property="[[managedProperties_.typeProperties.vpn.ipSec.remoteIdentity]]">
+            </network-config-input>
+          </template>
         </template>
         <template is="dom-if" if="[[showVpn_.OpenVPN]]" restamp>
           <network-config-input label="[[i18n('OncVPN-OpenVPN-Username')]]"
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 775713e7..b997c60 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -26,6 +26,7 @@
 const IpsecAuthType = {
   PSK: 'PSK',
   CERT: 'Cert',
+  EAP: 'EAP',
 };
 
 /**
@@ -287,6 +288,7 @@
      * @private {?{
      *   IPsec: (boolean|undefined),
      *   IPsecPSK: (boolean|undefined),
+     *   IPsecEAP: (boolean|undefined),
      *   IKEv2: (boolean|undefined),
      *   OpenVPN: (boolean|undefined),
      *   WireGuard: (boolean|undefined),
@@ -362,11 +364,7 @@
      */
     ipsecAuthTypeItems_: {
       type: Array,
-      readOnly: true,
-      value: [
-        IpsecAuthType.PSK,
-        IpsecAuthType.CERT,
-      ],
+      value: [],
     },
 
     /**
@@ -422,6 +420,7 @@
     'updateShowEap_(configProperties_.*, eapProperties_.*, securityType_)',
     'updateCertItems_(cachedServerCaCerts_, cachedUserCerts_, vpnType_)',
     'updateVpnType_(configProperties_, vpnType_, ipsecAuthType_)',
+    'updateVpnIPsecAuthTypeItems_(vpnType_)',
     'updateVpnIPsecCerts_(vpnType_, ipsecAuthType_,' +
         'configProperties_.typeConfig.vpn.ipSec.*, serverCaCerts_, userCerts_)',
     'updateOpenVPNCerts_(vpnType_,' +
@@ -869,9 +868,12 @@
           OncMojo.getActiveString(ipSec.authenticationType) || 'PSK',
       clientCertPkcs11Id: OncMojo.getActiveString(ipSec.clientCertPkcs11Id),
       clientCertType: OncMojo.getActiveString(ipSec.clientCertType),
+      eap: ipSec.eap ? this.getEAPConfigProperties_(ipSec.eap) : null,
       group: OncMojo.getActiveString(ipSec.group),
       ikeVersion: this.getActiveInt32_(ipSec.ikeVersion),
+      localIdentity: OncMojo.getActiveString(ipSec.localIdentity),
       psk: OncMojo.getActiveString(ipSec.psk),
+      remoteIdentity: OncMojo.getActiveString(ipSec.remoteIdentity),
       saveCredentials: this.getActiveBoolean_(ipSec.saveCredentials),
       serverCaPems: this.getActiveStringList_(ipSec.serverCaPems),
       serverCaRefs: this.getActiveStringList_(ipSec.serverCaRefs),
@@ -1154,6 +1156,8 @@
       eap = properties.typeConfig.wifi.eap;
     } else if (properties.typeConfig.ethernet) {
       eap = properties.typeConfig.ethernet.eap;
+    } else if (properties.typeConfig.vpn && properties.typeConfig.vpn.ipSec) {
+      eap = properties.typeConfig.vpn.ipSec.eap;
     }
     if (opt_create) {
       return eap || {
@@ -1196,6 +1200,11 @@
       case mojom.NetworkType.kEthernet:
         managedEap = managedProperties.typeProperties.ethernet.eap;
         break;
+      case mojom.NetworkType.kVPN:
+        if (managedProperties.typeProperties.vpn.ipSec) {
+          managedEap = managedProperties.typeProperties.vpn.ipSec.eap;
+        }
+        break;
     }
     return managedEap || null;
   },
@@ -1233,8 +1242,14 @@
       // Initiate it to "PSK" for simplicity.
       return IpsecAuthType.PSK;
     }
-    return vpn.ipSec.authenticationType === 'Cert' ? IpsecAuthType.CERT :
-                                                     IpsecAuthType.PSK;
+    if (vpn.ipSec.authenticationType === IpsecAuthType.PSK) {
+      return IpsecAuthType.PSK;
+    } else if (vpn.ipSec.authenticationType === IpsecAuthType.CERT) {
+      return IpsecAuthType.CERT;
+    } else if (vpn.ipSec.authenticationType === IpsecAuthType.EAP) {
+      return IpsecAuthType.EAP;
+    }
+    assertNotReached();
   },
 
   /** @private */
@@ -1315,15 +1330,34 @@
       case VPNConfigType.IKEV2:
         vpn.type = {value: mojom.VpnType.kIKEv2};
         if (!vpn.ipSec) {
+          this.ipsecAuthType_ = IpsecAuthType.EAP;
           vpn.ipSec = {
             authenticationType: this.ipsecAuthType_,
             ikeVersion: 2,
             saveCredentials: false,
           };
         }
+        if (this.ipsecAuthType_ === IpsecAuthType.EAP && !vpn.ipSec.eap) {
+          vpn.ipSec.eap = {
+            domainSuffixMatch: [],
+            outer: 'MSCHAPv2',
+            saveCredentials: false,
+            subjectAltNameMatch: [],
+            useSystemCas: false,
+          };
+          this.eapProperties_ = vpn.ipSec.eap;
+        }
         break;
       case VPNConfigType.L2TP_IPSEC:
         vpn.type = {value: mojom.VpnType.kL2TPIPsec};
+        if (this.ipsecAuthType_ !== IpsecAuthType.PSK &&
+            this.ipsecAuthType_ !== IpsecAuthType.CERT) {
+          // This will happen if user changes the VPN type to IKEv2 where the
+          // default value of auth type is EAP, and then changes the VPN type to
+          // L2TP/IPsec.
+          this.ipsecAuthType_ = IpsecAuthType.PSK;
+        }
+
         if (!vpn.ipSec) {
           vpn.ipSec = {
             authenticationType: this.ipsecAuthType_,
@@ -1347,11 +1381,13 @@
     const isIpsec = this.vpnType_ === VPNConfigType.L2TP_IPSEC ||
         this.vpnType_ === VPNConfigType.IKEV2;
     const ipsecAuthIsPsk = this.ipsecAuthType_ === IpsecAuthType.PSK;
+    const ipsecAuthIsEap = this.ipsecAuthType_ === IpsecAuthType.EAP;
     const ipsecAuthIsCert = this.ipsecAuthType_ === IpsecAuthType.CERT;
     const isOpenvpn = this.vpnType_ === VPNConfigType.OPEN_VPN;
     this.showVpn_ = {
       IPsec: isIpsec,
       IPsecPSK: isIpsec && ipsecAuthIsPsk,
+      IPsecEAP: isIpsec && ipsecAuthIsEap,
       IKEv2: this.vpnType_ === VPNConfigType.IKEV2,
       OpenVPN: isOpenvpn,
       WireGuard: this.vpnType_ === VPNConfigType.WIREGUARD,
@@ -1384,12 +1420,23 @@
   },
 
   /** @private */
+  updateVpnIPsecAuthTypeItems_() {
+    this.ipsecAuthTypeItems_ = [
+      IpsecAuthType.PSK,
+      IpsecAuthType.CERT,
+    ];
+    if (this.vpnType_ === VPNConfigType.IKEV2) {
+      this.ipsecAuthTypeItems_.push(IpsecAuthType.EAP);
+    }
+  },
+
+  /** @private */
   updateVpnIPsecCerts_() {
     if (this.vpnType_ !== VPNConfigType.L2TP_IPSEC &&
         this.vpnType_ !== VPNConfigType.IKEV2) {
       return;
     }
-    if (this.ipsecAuthType_ !== IpsecAuthType.CERT) {
+    if (this.ipsecAuthType_ === IpsecAuthType.PSK) {
       return;
     }
     const ipSec = this.configProperties_.typeConfig.vpn.ipSec;
@@ -1782,6 +1829,9 @@
         // is invalid.
         return this.selectedServerCaHashIsValid_() &&
             this.selectedUserCertHashIsValid_();
+      case IpsecAuthType.EAP:
+        return this.selectedServerCaHashIsValid_() &&
+            !!this.eapProperties_.identity;
       default:
         assertNotReached();
     }
@@ -1993,6 +2043,23 @@
       ipsec.clientCertPkcs11Id = this.getUserCertPkcs11Id_();
     } else {
       delete ipsec.clientCertType;
+      delete ipsec.clientCertPkcs11Id;
+    }
+
+    if (ipsec.authenticationType === IpsecAuthType.EAP) {
+      // Not all fields in eap are used by IKEv2, so create a new object here.
+      const eap = ipsec.eap;
+      ipsec.eap = {
+        domainSuffixMatch: [],
+        identity: eap.identity,
+        outer: 'MSCHAPv2',
+        password: eap.password,
+        saveCredentials: this.vpnSaveCredentials_,
+        subjectAltNameMatch: [],
+        useSystemCas: false,
+      };
+    } else {
+      delete ipsec.eap;
     }
 
     ipsec.ikeVersion = 2;
diff --git a/url/gurl.cc b/url/gurl.cc
index d7ede359..8fe812cf 100644
--- a/url/gurl.cc
+++ b/url/gurl.cc
@@ -223,8 +223,7 @@
 }
 
 // Note: code duplicated below (it's inconvenient to use a template here).
-GURL GURL::ReplaceComponents(
-    const url::Replacements<char>& replacements) const {
+GURL GURL::ReplaceComponents(const Replacements& replacements) const {
   GURL result;
 
   // Not allowed for invalid URLs.
@@ -243,8 +242,7 @@
 }
 
 // Note: code duplicated above (it's inconvenient to use a template here).
-GURL GURL::ReplaceComponents(
-    const url::Replacements<char16_t>& replacements) const {
+GURL GURL::ReplaceComponents(const ReplacementsW& replacements) const {
   GURL result;
 
   // Not allowed for invalid URLs.
@@ -281,7 +279,7 @@
   if (SchemeIsFileSystem())
     return inner_url_->DeprecatedGetOriginAsURL();
 
-  url::Replacements<char> replacements;
+  Replacements replacements;
   replacements.ClearUsername();
   replacements.ClearPassword();
   replacements.ClearPath();
@@ -298,7 +296,7 @@
   if (!has_ref() && !has_username() && !has_password())
     return GURL(*this);
 
-  url::Replacements<char> replacements;
+  Replacements replacements;
   replacements.ClearRef();
   replacements.ClearUsername();
   replacements.ClearPassword();
diff --git a/url/gurl.h b/url/gurl.h
index 8ad47d6..af401ef 100644
--- a/url/gurl.h
+++ b/url/gurl.h
@@ -164,10 +164,10 @@
   // It is an error to replace components of an invalid URL. The result will
   // be the empty URL.
   //
-  // Note that we use the more general url::Replacements type to give
-  // callers extra flexibility rather than our override.
-  GURL ReplaceComponents(const url::Replacements<char>& replacements) const;
-  GURL ReplaceComponents(const url::Replacements<char16_t>& replacements) const;
+  // Note that this intentionally disallows direct use of url::Replacements,
+  // which is harder to use correctly.
+  GURL ReplaceComponents(const Replacements& replacements) const;
+  GURL ReplaceComponents(const ReplacementsW& replacements) const;
 
   // A helper function that is equivalent to replacing the path with a slash
   // and clearing out everything after that. We sometimes need to know just the
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
index 692f475..e63257f 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java
@@ -31,6 +31,8 @@
 import org.chromium.components.content_capture.ContentCaptureConsumer;
 import org.chromium.components.content_capture.OnscreenContentProvider;
 import org.chromium.components.embedder_support.view.ContentView;
+import org.chromium.components.webapps.bottomsheet.PwaBottomSheetController;
+import org.chromium.components.webapps.bottomsheet.PwaBottomSheetControllerFactory;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
@@ -87,6 +89,8 @@
     private final ManagedBottomSheetController mBottomSheetController;
     private final BottomSheetObserver mBottomSheetObserver;
 
+    private PwaBottomSheetController mPwaBottomSheetController;
+
     private TabImpl mTab;
 
     private WebContentsGestureStateTracker mGestureStateTracker;
@@ -189,6 +193,9 @@
                 KeyboardVisibilityDelegate.getInstance(), () -> mBottomSheetContainer);
         BottomSheetControllerFactory.attach(mWindowAndroid, mBottomSheetController);
 
+        mPwaBottomSheetController = PwaBottomSheetControllerFactory.createPwaBottomSheetController(
+                mWindowAndroid.getContext().get());
+        PwaBottomSheetControllerFactory.attach(mWindowAndroid, mPwaBottomSheetController);
         mBottomSheetObserver = new EmptyBottomSheetObserver() {
             /** A token for suppressing app modal dialogs. */
             private int mAppModalToken = TokenHolder.INVALID_TOKEN;
@@ -229,6 +236,7 @@
     public void destroy() {
         BottomSheetControllerFactory.detach(mBottomSheetController);
         mBottomSheetController.removeObserver(mBottomSheetObserver);
+        PwaBottomSheetControllerFactory.detach(mPwaBottomSheetController);
         mWindowAndroid.setModalDialogManager(null);
         setActiveTab(null);
         if (mOnscreenContentProvider != null) mOnscreenContentProvider.destroy();
diff --git a/weblayer/browser/navigation_browsertest.cc b/weblayer/browser/navigation_browsertest.cc
index 3f00c67b..ee207e6 100644
--- a/weblayer/browser/navigation_browsertest.cc
+++ b/weblayer/browser/navigation_browsertest.cc
@@ -218,14 +218,7 @@
   EXPECT_EQ(observer.navigation_state(), NavigationState::kFailed);
 }
 
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-// https://crbug.com/1296643
-#define MAYBE_Download DISABLED_Download
-#else
-#define MAYBE_Download Download
-#endif
-
-IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, MAYBE_Download) {
+IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, Download) {
   EXPECT_TRUE(embedded_test_server()->Start());
   GURL url(embedded_test_server()->GetURL("/content-disposition.html"));