diff --git a/DEPS b/DEPS
index 6b9cec0..c88536a 100644
--- a/DEPS
+++ b/DEPS
@@ -253,23 +253,23 @@
   # 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': 'f379b259a55e61e4ca173bf392c43ec20ca9e0e4',
+  'skia_revision': 'e4d4c9362b9878588e57fc9054cd4bb67a816dd6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '9e5ad1e99a34694669f2dee0f2fb8901f00760ba',
+  'v8_revision': '7efc6f755cf1bffe738b25f110ea7a345716c488',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '9d486a85e8888ad22a39bf1bb5d15fba90c944cc',
+  'angle_revision': 'ba04fcfd10c7c967b7efb64a31d133c85f1da08c',
   # 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': '626c44cc5cf29a877205150149cb66cc074d93b7',
+  'swiftshader_revision': 'd09282e5c609528f81ebd3452f80e841ed3bf88b',
   # 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': '82fcfc52eb65d7b2495a3d077f4789943f83877f',
+  'pdfium_revision': '268f21c9d342b6bfd66621eb3940f9a1902898f9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # 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': '393a4054354f1a9c6bdf55b4c44e99ef14467527',
+  'devtools_frontend_revision': '5a8baef0f1e60dfaf393743dfcfa51b1e17e3630',
   # 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.
@@ -364,7 +364,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'b2e4a961f6c9585f3f9c8a66538a2cfe15507404',
+  'dawn_revision': 'b07c120772613a4f85dd2052dd9d09d60a90fa0f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -392,7 +392,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': 'f4b80ccb59fb7d7a7c45e73e53da61158f985b07',
+  'nearby_revision': '6b3402ccd0753a60cd190d3ec70e508d3b6880ff',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -431,7 +431,7 @@
   'libcxx_revision':       '79a2e924d96e2fc1e4b937c42efd08898fa472d7',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:ab9104586734cb45aa77c70ca5042edbcc9f6aa5',
+  'gn_version': 'git_revision:04a2891d554d2325f04631bac356c29ffcdebf2a',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -735,11 +735,11 @@
   },
 
   'src/ios/third_party/earl_grey2/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + 'd1d9015293e9414cef561f3d28725a8400d0743f',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '2ef1035e02a18b24ee4bf31e689c503b3c420ce9',
       'condition': 'checkout_ios',
   },
 
-    'src/ios/third_party/edo/src': {
+  'src/ios/third_party/edo/src': {
       'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '727e556705278598fce683522beedbb9946bfda0',
       'condition': 'checkout_ios',
   },
@@ -1132,7 +1132,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ba94bbeaa857d50798437275242ec7cb72cca7b1',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0a6b544e206b3adada64b01fb51d4a63c0faa580',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1343,7 +1343,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + '3b8ee157a8f3536bbf5ad2448e9e3370463c1e40',
 
   'src/third_party/libaom/source/libaom':
-    Var('aomedia_git') + '/aom.git' + '@' +  'cc31d8c4b722cf7ecd68ac351bf3d288de07bc68',
+    Var('aomedia_git') + '/aom.git' + '@' +  'd48a17ce9848b793ecd774ce41a44bd19359385e',
 
   'src/third_party/libavif/src':
     Var('chromium_git') + '/external/github.com/AOMediaCodec/libavif.git' + '@' + Var('libavif_revision'),
@@ -1593,7 +1593,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/android/aemu/release/linux-amd64',
-              'version': 'ivTaBdipsKUzxzx1sQZV1PtNRvV5aFqjiIrZXEXFJyYC'
+              'version': 'lbYV0rO8V4GxeqmRrKZeRgQmbFxh2BwafFgd9cjYmWYC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1667,6 +1667,17 @@
       'dep_type': 'cipd',
   },
 
+  'src/third_party/swift-format': {
+      'packages': [
+          {
+              'package': 'infra/3pp/tools/swift-format/${{platform}}',
+              'version': 'version:2@505.chromium.1',
+          },
+      ],
+      'condition': 'host_os == mac',
+      'dep_type': 'cipd',
+  },
+
   'src/third_party/swiftshader':
     Var('swiftshader_git') + '/SwiftShader.git' + '@' +  Var('swiftshader_revision'),
 
@@ -1733,7 +1744,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'aa5ba0a0f5e55a8715a629bd1d3110a671107a1e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '00112748e187c16693363ec157e42a7738312128',
+    Var('webrtc_git') + '/src.git' + '@' + '7cbbcc96ee3f768d7d8928cdfe4d79c937b16074',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1803,7 +1814,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ce66871b870909680af5f991bfffee8cf5ac1686',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e4ef0564e2148c22600de2e647d670166d19bc48',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java
index d1d2e26..7f86e9c 100644
--- a/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java
+++ b/android_webview/support_library/java/src/org/chromium/support_lib_glue/SupportLibServiceWorkerControllerAdapter.java
@@ -35,8 +35,10 @@
     @Override
     public void setServiceWorkerClient(InvocationHandler client) {
         recordApiCall(ApiCall.SET_SERVICE_WORKER_CLIENT);
-        mAwServiceWorkerController.setServiceWorkerClient(new SupportLibServiceWorkerClientAdapter(
-                BoundaryInterfaceReflectionUtil.castToSuppLibClass(
-                        ServiceWorkerClientBoundaryInterface.class, client)));
+        mAwServiceWorkerController.setServiceWorkerClient(client == null
+                        ? null
+                        : new SupportLibServiceWorkerClientAdapter(
+                                BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                                        ServiceWorkerClientBoundaryInterface.class, client)));
     }
 }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index a49ea30..f5374bb 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -172,6 +172,10 @@
     "ambient/model/ambient_slideshow_photo_config.h",
     "ambient/model/ambient_topic_queue.cc",
     "ambient/model/ambient_topic_queue.h",
+    "ambient/model/ambient_topic_queue_animation_delegate.cc",
+    "ambient/model/ambient_topic_queue_animation_delegate.h",
+    "ambient/model/ambient_topic_queue_slideshow_delegate.cc",
+    "ambient/model/ambient_topic_queue_slideshow_delegate.h",
     "ambient/resources/ambient_animation_resource_constants.h",
     "ambient/resources/ambient_animation_static_resources.h",
     "ambient/ui/ambient_animation_player.cc",
@@ -2400,6 +2404,7 @@
     "ambient/model/ambient_animation_photo_config_unittest.cc",
     "ambient/model/ambient_animation_photo_provider_unittest.cc",
     "ambient/model/ambient_backend_model_unittest.cc",
+    "ambient/model/ambient_topic_queue_animation_delegate_unittest.cc",
     "ambient/model/ambient_topic_queue_unittest.cc",
     "ambient/ui/ambient_animation_resizer_unittest.cc",
     "ambient/ui/ambient_animation_shield_controller_unittest.cc",
diff --git a/ash/ambient/model/ambient_topic_queue.h b/ash/ambient/model/ambient_topic_queue.h
index a48aa3c..9b9593e 100644
--- a/ash/ambient/model/ambient_topic_queue.h
+++ b/ash/ambient/model/ambient_topic_queue.h
@@ -41,6 +41,21 @@
 // caller Pop()s everything from it.
 class ASH_EXPORT AmbientTopicQueue {
  public:
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    // Returns a non-empty set of sizes that specify the desired size of the
+    // topic photos returned by IMAX. This is invoked before each topic fetch,
+    // so implementations may change the set of sizes they specify if desired.
+    //
+    // The AmbientTopicQueue will make a best effort to maintain a uniform
+    // distribution of the specified topic sizes in its queue. Note that the
+    // |topic_fetch_limit| is always honored though and is a global limit for
+    // all topics in the queue irrespective of size.
+    virtual std::vector<gfx::Size> GetTopicSizes() = 0;
+  };
+
   // Starts automatically filling the queue on construction. Note this class
   // intentionally does not have a method to clear the queue/reset its state.
   // For this, it's cheap to just destroy and re-create the queue.
diff --git a/ash/ambient/model/ambient_topic_queue_animation_delegate.cc b/ash/ambient/model/ambient_topic_queue_animation_delegate.cc
new file mode 100644
index 0000000..5ad3859
--- /dev/null
+++ b/ash/ambient/model/ambient_topic_queue_animation_delegate.cc
@@ -0,0 +1,151 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/ambient/model/ambient_topic_queue_animation_delegate.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "cc/paint/skottie_resource_metadata.h"
+#include "ui/gfx/geometry/size_f.h"
+
+namespace ash {
+namespace {
+
+bool IsPortrait(const gfx::Size& size) {
+  DCHECK(!size.IsEmpty());
+  return size.height() > size.width();
+}
+
+bool IsSquare(const gfx::Size& size) {
+  DCHECK(!size.IsEmpty());
+  // This is arbitrary. Just a rough estimate that a "square" picture has an
+  // aspect ratio in the range [1 - kAspectRatioDelta, 1 + kAspectRatioDelta].
+  static constexpr float kAspectRatioDelta = 0.05f;
+  static constexpr float kAspectRatioLowerBound = 1.f - kAspectRatioDelta;
+  static constexpr float kAspectRatioUpperBound = 1.f + kAspectRatioDelta;
+  float aspect_ratio = gfx::SizeF(size).AspectRatio();
+  return aspect_ratio > kAspectRatioLowerBound &&
+         aspect_ratio < kAspectRatioUpperBound;
+}
+
+// Determines one size that best represents the group of image assets in the
+// |resource_metadata| whose orientation matches |is_portrait|. The logic is
+// currently as follows:
+// * Compute the average aspect ratio of all assets with matching orientation.
+// * Calculate the smallest size whose a) aspect ratio matches the average
+//   computed above and b) dimensions exceed those of all assets with matching
+//   orientation. This ensures that we ultimately download the largest
+//   possible resolution of photos from IMAX and any resizing that happens
+//   "shrinks" the photo to fit in the animation, which generally has better
+//   quality that "growing" a photo.
+// * Discard any "square" orientations from the aspect ratio calculation. These
+//   are outliers that aren't quite portrait or landscape and bias the average
+//   aspect ratio. Since they are "square", it is a good enough compromise to
+//   use either a portrait or landscape photo and center-crop it to a square
+//   orientation before rendering. If this is not good enough in the future, we
+//   can return a third size in |GetTopicSizes()|, but it is currently not worth
+//   it.
+//
+// Returns an empty gfx::Size instance if there are no assets that match the
+// |is_portrait| orientation.
+gfx::Size SummarizeImageAssetSizes(
+    const cc::SkottieResourceMetadataMap& resource_metadata,
+    bool is_portrait) {
+  constexpr int kDimensionInvalid = -1;
+  int largest_width_observed = kDimensionInvalid;
+  int largest_height_observed = kDimensionInvalid;
+  float aspect_ratio_sum = 0.f;
+  int num_assets_found = 0;
+  for (const auto& [_, asset_metadata] : resource_metadata.asset_storage()) {
+    if (!asset_metadata.size.has_value() ||
+        IsPortrait(*asset_metadata.size) != is_portrait) {
+      continue;
+    }
+
+    largest_width_observed =
+        std::max(asset_metadata.size->width(), largest_width_observed);
+    largest_height_observed =
+        std::max(asset_metadata.size->height(), largest_height_observed);
+    if (!IsSquare(*asset_metadata.size)) {
+      ++num_assets_found;
+      aspect_ratio_sum += gfx::SizeF(*asset_metadata.size).AspectRatio();
+    }
+  }
+
+  if (num_assets_found == 0) {
+    if (largest_width_observed == kDimensionInvalid) {
+      // There were no assets matching the desired orientation.
+      return gfx::Size();
+    } else {
+      // There were assets matching the desired orientation, but all of them
+      // were closer to being "square".
+      int square_length =
+          std::max(largest_width_observed, largest_height_observed);
+      return gfx::Size(square_length, square_length);
+    }
+  }
+
+  float average_aspect_ratio = aspect_ratio_sum / num_assets_found;
+  // There are corner cases here where an asset found above may ultimately have
+  // a dimension larger than the computed size, but it's not worth accounting
+  // for.
+  gfx::Size candidate_a = gfx::Size(
+      largest_width_observed,
+      base::ClampRound<int>(largest_width_observed / average_aspect_ratio));
+  gfx::Size candidate_b = gfx::Size(
+      base::ClampRound<int>(largest_height_observed * average_aspect_ratio),
+      largest_height_observed);
+  // Both candidates should have the same aspect ratio, so comparing one of the
+  // dimensions (width in this case) is sufficient.
+  return candidate_a.width() > candidate_b.width() ? candidate_a : candidate_b;
+}
+
+// The output will always have 1 size for landscape assets and 1 size for
+// portrait assets (or 0 if there are no assets of a particular orientation).
+std::vector<gfx::Size> ComputeTopicSizes(
+    const cc::SkottieResourceMetadataMap& resource_metadata) {
+  static constexpr gfx::Size kDefaultTopicSize = gfx::Size(500, 500);
+
+  gfx::Size landscape_size =
+      SummarizeImageAssetSizes(resource_metadata, /*is_portrait=*/false);
+  gfx::Size portrait_size =
+      SummarizeImageAssetSizes(resource_metadata, /*is_portrait=*/true);
+  std::vector<gfx::Size> output;
+  if (!landscape_size.IsEmpty())
+    output.push_back(std::move(landscape_size));
+  if (!portrait_size.IsEmpty())
+    output.push_back(std::move(portrait_size));
+
+  if (output.empty()) {
+    LOG(DFATAL) << "Failed to compute topic sizes for animation. Animation "
+                   "file is likely invalid.";
+    return {kDefaultTopicSize};
+  }
+  return output;
+}
+
+}  // namespace
+
+AmbientTopicQueueAnimationDelegate::AmbientTopicQueueAnimationDelegate(
+    const cc::SkottieResourceMetadataMap& resource_metadata)
+    : topic_sizes_(ComputeTopicSizes(resource_metadata)) {}
+
+AmbientTopicQueueAnimationDelegate::~AmbientTopicQueueAnimationDelegate() =
+    default;
+
+std::vector<gfx::Size> AmbientTopicQueueAnimationDelegate::GetTopicSizes() {
+  // At the time this was written, UX has agreed that the landscape and portrait
+  // versions of a given animation theme will have the same image asset sizes
+  // (only the animation's layout will be different). Thus, it is sufficient
+  // and simplest to just compute the desired topic sizes once with whichever
+  // version of the animation is loaded initially (either topic or landscape).
+  //
+  // If this changes in the future, this will need to recompute topic sizes with
+  // the new animation orientation.
+  return topic_sizes_;
+}
+
+}  // namespace ash
diff --git a/ash/ambient/model/ambient_topic_queue_animation_delegate.h b/ash/ambient/model/ambient_topic_queue_animation_delegate.h
new file mode 100644
index 0000000..e1d86a6
--- /dev/null
+++ b/ash/ambient/model/ambient_topic_queue_animation_delegate.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_AMBIENT_MODEL_AMBIENT_TOPIC_QUEUE_ANIMATION_DELEGATE_H_
+#define ASH_AMBIENT_MODEL_AMBIENT_TOPIC_QUEUE_ANIMATION_DELEGATE_H_
+
+#include <vector>
+
+#include "ash/ambient/model/ambient_topic_queue.h"
+#include "ash/ash_export.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+class SkottieResourceMetadataMap;
+}  // namespace cc
+
+namespace ash {
+
+// For the UI that renders a Lottie animation file using the Skottie library.
+class ASH_EXPORT AmbientTopicQueueAnimationDelegate
+    : public AmbientTopicQueue::Delegate {
+ public:
+  explicit AmbientTopicQueueAnimationDelegate(
+      const cc::SkottieResourceMetadataMap& resource_metadata);
+  AmbientTopicQueueAnimationDelegate(
+      const AmbientTopicQueueAnimationDelegate&) = delete;
+  AmbientTopicQueueAnimationDelegate& operator=(
+      const AmbientTopicQueueAnimationDelegate&) = delete;
+  ~AmbientTopicQueueAnimationDelegate() override;
+
+  // AmbientTopicQueue::Delegate implementation:
+  std::vector<gfx::Size> GetTopicSizes() override;
+
+ private:
+  const std::vector<gfx::Size> topic_sizes_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_AMBIENT_MODEL_AMBIENT_TOPIC_QUEUE_ANIMATION_DELEGATE_H_
diff --git a/ash/ambient/model/ambient_topic_queue_animation_delegate_unittest.cc b/ash/ambient/model/ambient_topic_queue_animation_delegate_unittest.cc
new file mode 100644
index 0000000..d0deddb
--- /dev/null
+++ b/ash/ambient/model/ambient_topic_queue_animation_delegate_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/ambient/model/ambient_topic_queue_animation_delegate.h"
+
+#include <utility>
+
+#include "base/check.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_piece.h"
+#include "cc/paint/skottie_resource_metadata.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ash {
+namespace {
+
+using ::testing::UnorderedElementsAre;
+
+}  // namespace
+
+class AmbientTopicQueueAnimationDelegateTest : public ::testing::Test {
+ protected:
+  void RegisterAsset(base::StringPiece resource_id,
+                     absl::optional<gfx::Size> size) {
+    CHECK(resource_metadata_.RegisterAsset("test-path", "test-name",
+                                           resource_id, std::move(size)))
+        << "Asset " << resource_id << " already registered";
+  }
+
+  cc::SkottieResourceMetadataMap resource_metadata_;
+};
+
+TEST_F(AmbientTopicQueueAnimationDelegateTest,
+       GetTopicSizesWithPortraitAndLandscape) {
+  RegisterAsset("landscape-1", gfx::Size(100, 50));
+  RegisterAsset("landscape-2", gfx::Size(120, 40));
+  RegisterAsset("portrait-1", gfx::Size(50, 100));
+  RegisterAsset("portrait-2", gfx::Size(60, 80));
+  AmbientTopicQueueAnimationDelegate delegate(resource_metadata_);
+  EXPECT_THAT(
+      delegate.GetTopicSizes(),
+      UnorderedElementsAre(gfx::Size(125, 50),
+                           gfx::Size(base::ClampRound<int>(62.5), 100)));
+}
+
+TEST_F(AmbientTopicQueueAnimationDelegateTest, GetTopicSizesWithOnlyPortrait) {
+  RegisterAsset("portrait-1", gfx::Size(60, 100));
+  RegisterAsset("portrait-2", gfx::Size(100, 125));
+  AmbientTopicQueueAnimationDelegate delegate(resource_metadata_);
+  EXPECT_THAT(
+      delegate.GetTopicSizes(),
+      UnorderedElementsAre(gfx::Size(100, base::ClampRound<int>(100 / .7f))));
+}
+
+TEST_F(AmbientTopicQueueAnimationDelegateTest, GetTopicSizesWithOnlyLandscape) {
+  RegisterAsset("landscape-1", gfx::Size(200, 100));
+  RegisterAsset("landscape-2", gfx::Size(120, 40));
+  AmbientTopicQueueAnimationDelegate delegate(resource_metadata_);
+  EXPECT_THAT(delegate.GetTopicSizes(),
+              UnorderedElementsAre(gfx::Size(250, 100)));
+}
+
+TEST_F(AmbientTopicQueueAnimationDelegateTest, GetTopicSizesWithSquare) {
+  RegisterAsset("landscape-1", gfx::Size(200, 100));
+  RegisterAsset("landscape-2", gfx::Size(120, 40));
+  // Should be ignored when calculating the average aspect ratio.
+  RegisterAsset("landscape-3", gfx::Size(300, 300));
+  AmbientTopicQueueAnimationDelegate delegate(resource_metadata_);
+  EXPECT_THAT(delegate.GetTopicSizes(),
+              UnorderedElementsAre(gfx::Size(750, 300)));
+}
+
+TEST_F(AmbientTopicQueueAnimationDelegateTest, GetTopicSizesWithOnlySquare) {
+  RegisterAsset("square-1", gfx::Size(200, 200));
+  RegisterAsset("square-2", gfx::Size(100, 100));
+  AmbientTopicQueueAnimationDelegate delegate(resource_metadata_);
+  EXPECT_THAT(delegate.GetTopicSizes(),
+              UnorderedElementsAre(gfx::Size(200, 200)));
+}
+
+TEST_F(AmbientTopicQueueAnimationDelegateTest, HandlesMissingAssetSize) {
+  RegisterAsset("landscape-1", gfx::Size(200, 100));
+  RegisterAsset("landscape-2", gfx::Size(120, 40));
+  RegisterAsset("landscape-3", absl::nullopt);
+  AmbientTopicQueueAnimationDelegate delegate(resource_metadata_);
+  EXPECT_THAT(delegate.GetTopicSizes(),
+              UnorderedElementsAre(gfx::Size(250, 100)));
+}
+
+}  // namespace ash
diff --git a/ash/ambient/model/ambient_topic_queue_slideshow_delegate.cc b/ash/ambient/model/ambient_topic_queue_slideshow_delegate.cc
new file mode 100644
index 0000000..a00abef
--- /dev/null
+++ b/ash/ambient/model/ambient_topic_queue_slideshow_delegate.cc
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/ambient/model/ambient_topic_queue_slideshow_delegate.h"
+
+#include <algorithm>
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ash {
+
+AmbientTopicQueueSlideshowDelegate::AmbientTopicQueueSlideshowDelegate() =
+    default;
+
+AmbientTopicQueueSlideshowDelegate::~AmbientTopicQueueSlideshowDelegate() =
+    default;
+
+std::vector<gfx::Size> AmbientTopicQueueSlideshowDelegate::GetTopicSizes() {
+  auto* ambient_container = Shell::GetContainer(
+      Shell::GetPrimaryRootWindow(), kShellWindowId_AmbientModeContainer);
+  gfx::Size display_size_px = display::Screen::GetScreen()
+                                  ->GetDisplayNearestView(ambient_container)
+                                  .GetSizeInPixel();
+
+  // For portrait photos, the server returns image of half requested width.
+  // When the device is in portrait mode, where only shows one portrait photo,
+  // it will cause unnecessary scaling. To reduce this effect, always requesting
+  // the landscape display size.
+  const int width = std::max(display_size_px.width(), display_size_px.height());
+  const int height =
+      std::min(display_size_px.width(), display_size_px.height());
+  return {gfx::Size(width, height)};
+}
+
+}  // namespace ash
diff --git a/ash/ambient/model/ambient_topic_queue_slideshow_delegate.h b/ash/ambient/model/ambient_topic_queue_slideshow_delegate.h
new file mode 100644
index 0000000..d6f47a57
--- /dev/null
+++ b/ash/ambient/model/ambient_topic_queue_slideshow_delegate.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_AMBIENT_MODEL_AMBIENT_TOPIC_QUEUE_SLIDESHOW_DELEGATE_H_
+#define ASH_AMBIENT_MODEL_AMBIENT_TOPIC_QUEUE_SLIDESHOW_DELEGATE_H_
+
+#include "ash/ambient/model/ambient_topic_queue.h"
+#include "ash/ash_export.h"
+
+namespace ash {
+
+// For the UI that iterates through a slideshow of images and displays them at
+// full-screen resolution.
+class ASH_EXPORT AmbientTopicQueueSlideshowDelegate
+    : public AmbientTopicQueue::Delegate {
+ public:
+  AmbientTopicQueueSlideshowDelegate();
+  AmbientTopicQueueSlideshowDelegate(
+      const AmbientTopicQueueSlideshowDelegate&) = delete;
+  AmbientTopicQueueSlideshowDelegate& operator=(
+      const AmbientTopicQueueSlideshowDelegate&) = delete;
+  ~AmbientTopicQueueSlideshowDelegate() override;
+
+  // AmbientTopicQueue::Delegate implementation:
+  std::vector<gfx::Size> GetTopicSizes() override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_AMBIENT_MODEL_AMBIENT_TOPIC_QUEUE_SLIDESHOW_DELEGATE_H_
diff --git a/ash/dbus/user_authentication_service_provider.cc b/ash/dbus/user_authentication_service_provider.cc
index 3be6dd5..1cfbd7b 100644
--- a/ash/dbus/user_authentication_service_provider.cc
+++ b/ash/dbus/user_authentication_service_provider.cc
@@ -4,10 +4,13 @@
 
 #include "ash/dbus/user_authentication_service_provider.h"
 
+#include <string>
+
 #include "ash/public/cpp/in_session_auth_dialog_controller.h"
 #include "ash/public/cpp/webauthn_request_registrar.h"
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
@@ -32,6 +35,14 @@
 
   exported_object->ExportMethod(
       chromeos::kUserAuthenticationServiceInterface,
+      chromeos::kUserAuthenticationServiceShowAuthDialogV2Method,
+      base::BindRepeating(&UserAuthenticationServiceProvider::ShowAuthDialogV2,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&UserAuthenticationServiceProvider::OnExported,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  exported_object->ExportMethod(
+      chromeos::kUserAuthenticationServiceInterface,
       chromeos::kUserAuthenticationServiceCancelMethod,
       base::BindRepeating(&UserAuthenticationServiceProvider::Cancel,
                           weak_ptr_factory_.GetWeakPtr()),
@@ -57,6 +68,46 @@
   }
 }
 
+void UserAuthenticationServiceProvider::ShowAuthDialogV2(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+  std::string origin_name;
+  if (!reader.PopString(&origin_name)) {
+    LOG(ERROR) << "Unable to parse origin name";
+    OnAuthFlowComplete(method_call, std::move(response_sender), false);
+    return;
+  }
+  // TODO(b/156258540): Show RP id in the dialog prompt.
+  int verification_type;
+  if (!reader.PopInt32(&verification_type)) {
+    LOG(ERROR) << "Unable to parse verification_type";
+    OnAuthFlowComplete(method_call, std::move(response_sender), false);
+    return;
+  }
+  std::string request_id;
+  if (!reader.PopString(&request_id)) {
+    LOG(ERROR) << "Unable to parse request id";
+    OnAuthFlowComplete(method_call, std::move(response_sender), false);
+    return;
+  }
+
+  aura::Window* source_window =
+      WebAuthnRequestRegistrar::Get()->GetWindowForRequestId(request_id);
+  if (!source_window) {
+    LOG(ERROR) << "Cannot find window with the given request id";
+    OnAuthFlowComplete(method_call, std::move(response_sender), false);
+    return;
+  }
+
+  auto* auth_dialog_controller = InSessionAuthDialogController::Get();
+  auth_dialog_controller->ShowAuthenticationDialog(
+      source_window, origin_name,
+      base::BindOnce(&UserAuthenticationServiceProvider::OnAuthFlowComplete,
+                     weak_ptr_factory_.GetWeakPtr(), method_call,
+                     std::move(response_sender)));
+}
+
 void UserAuthenticationServiceProvider::ShowAuthDialog(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender) {
@@ -82,7 +133,8 @@
   }
 
   aura::Window* source_window =
-      WebAuthnRequestRegistrar::Get()->GetWindowForRequestId(request_id);
+      WebAuthnRequestRegistrar::Get()->GetWindowForRequestId(
+          base::NumberToString(request_id));
   if (!source_window) {
     LOG(ERROR) << "Cannot find window with the given request id";
     OnAuthFlowComplete(method_call, std::move(response_sender), false);
diff --git a/ash/dbus/user_authentication_service_provider.h b/ash/dbus/user_authentication_service_provider.h
index a662690..b54df87 100644
--- a/ash/dbus/user_authentication_service_provider.h
+++ b/ash/dbus/user_authentication_service_provider.h
@@ -37,7 +37,13 @@
                   const std::string& method_name,
                   bool success);
 
-  // Called on UI thread in response to D-Bus requests.
+  // Called on UI thread in response to D-Bus requests. This expects the
+  // request_id field to be a string.
+  void ShowAuthDialogV2(dbus::MethodCall* method_call,
+                        dbus::ExportedObject::ResponseSender response_sender);
+
+  // [Deprecated] Use ShowAuthDialogV2. Called on UI thread in response to D-Bus
+  // requests. This expects the request_id field to be an integer.
   void ShowAuthDialog(dbus::MethodCall* method_call,
                       dbus::ExportedObject::ResponseSender response_sender);
 
diff --git a/ash/in_session_auth/webauthn_request_registrar_impl.cc b/ash/in_session_auth/webauthn_request_registrar_impl.cc
index df08f9f..214f8b9 100644
--- a/ash/in_session_auth/webauthn_request_registrar_impl.cc
+++ b/ash/in_session_auth/webauthn_request_registrar_impl.cc
@@ -7,6 +7,7 @@
 #include "ash/shell.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "base/bind.h"
+#include "base/strings/string_number_conversions.h"
 #include "ui/aura/window.h"
 #include "ui/base/class_property.h"
 
@@ -63,8 +64,8 @@
 }
 
 aura::Window* WebAuthnRequestRegistrarImpl::GetWindowForRequestId(
-    uint32_t request_id) {
-  if (request_id == kInvalidRequestId) {
+    std::string request_id) {
+  if (request_id == base::NumberToString(kInvalidRequestId)) {
     return nullptr;
   }
 
@@ -72,7 +73,7 @@
       Shell::Get()->mru_window_tracker()->BuildMruWindowList(kAllDesks);
   for (aura::Window* window : windows) {
     uint32_t window_request_id = window->GetProperty(kWebAuthnRequestId);
-    if (window_request_id == request_id) {
+    if (base::NumberToString(window_request_id) == request_id) {
       return window;
     }
   }
diff --git a/ash/in_session_auth/webauthn_request_registrar_impl.h b/ash/in_session_auth/webauthn_request_registrar_impl.h
index 653f365..fca22b5 100644
--- a/ash/in_session_auth/webauthn_request_registrar_impl.h
+++ b/ash/in_session_auth/webauthn_request_registrar_impl.h
@@ -5,6 +5,8 @@
 #ifndef ASH_IN_SESSION_AUTH_WEBAUTHN_REQUEST_REGISTRAR_IMPL_H_
 #define ASH_IN_SESSION_AUTH_WEBAUTHN_REQUEST_REGISTRAR_IMPL_H_
 
+#include <string>
+
 #include "ash/public/cpp/webauthn_request_registrar.h"
 #include "base/callback_forward.h"
 #include "ui/aura/window_tracker.h"
@@ -26,7 +28,7 @@
 
   // WebAuthnRequestRegistrar:
   GenerateRequestIdCallback GetRegisterCallback(aura::Window* window) override;
-  aura::Window* GetWindowForRequestId(uint32_t request_id) override;
+  aura::Window* GetWindowForRequestId(std::string request_id) override;
 
  private:
   uint32_t DoRegister(aura::Window* window);
diff --git a/ash/public/cpp/webauthn_request_registrar.h b/ash/public/cpp/webauthn_request_registrar.h
index d65ef3b..d89927c9 100644
--- a/ash/public/cpp/webauthn_request_registrar.h
+++ b/ash/public/cpp/webauthn_request_registrar.h
@@ -6,6 +6,7 @@
 #define ASH_PUBLIC_CPP_WEBAUTHN_REQUEST_REGISTRAR_H_
 
 #include <stdint.h>
+#include <string>
 
 #include "ash/public/cpp/ash_public_export.h"
 #include "base/callback_forward.h"
@@ -30,7 +31,7 @@
 
   // Returns the window that was registered with |request_id|, or nullptr if no
   // such window.
-  virtual aura::Window* GetWindowForRequestId(uint32_t request_id) = 0;
+  virtual aura::Window* GetWindowForRequestId(std::string request_id) = 0;
 
  protected:
   WebAuthnRequestRegistrar();
diff --git a/ash/webui/media_app_ui/BUILD.gn b/ash/webui/media_app_ui/BUILD.gn
index ec0b3e3..fa7c3ad7 100644
--- a/ash/webui/media_app_ui/BUILD.gn
+++ b/ash/webui/media_app_ui/BUILD.gn
@@ -28,6 +28,8 @@
   deps = [
     ":mojo_bindings",
     ":mojo_bindings_js",
+    "//ash/constants:constants",
+    "//ash/webui/media_app_ui:buildflags",
     "//ash/webui/resources:media_app_bundle_resources",
     "//ash/webui/resources:media_app_resources",
     "//ash/webui/web_applications",
diff --git a/ash/webui/media_app_ui/media_app_ui.cc b/ash/webui/media_app_ui/media_app_ui.cc
index 2700bb5..3ff90c0 100644
--- a/ash/webui/media_app_ui/media_app_ui.cc
+++ b/ash/webui/media_app_ui/media_app_ui.cc
@@ -6,7 +6,9 @@
 
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "ash/webui/grit/ash_media_app_resources.h"
+#include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/media_app_ui/media_app_page_handler.h"
 #include "ash/webui/media_app_ui/url_constants.h"
 #include "ash/webui/web_applications/webui_test_prod_util.h"
@@ -53,25 +55,47 @@
 
   // Redirects "system_assets/*" (from manifest.json) to the icons for the
   // gallery app.
-  // TODO(b/141588875): Switch these to IDR_MEDIA_APP_APP_ICON_*_PNG in the
-  // internal media_app_bundle_resources.grd file (and add more icon
-  // resolutions) when the final icon is ready.
-  source->AddResourcePath("system_assets/app_icon_16.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_16_PNG);
-  source->AddResourcePath("system_assets/app_icon_32.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_32_PNG);
-  source->AddResourcePath("system_assets/app_icon_48.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_48_PNG);
-  source->AddResourcePath("system_assets/app_icon_64.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_64_PNG);
-  source->AddResourcePath("system_assets/app_icon_96.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_96_PNG);
-  source->AddResourcePath("system_assets/app_icon_128.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_128_PNG);
-  source->AddResourcePath("system_assets/app_icon_192.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_192_PNG);
-  source->AddResourcePath("system_assets/app_icon_256.png",
-                          IDR_MEDIA_APP_GALLERY_ICON_256_PNG);
+  bool app_icons_added = false;
+  if (base::FeatureList::IsEnabled(chromeos::features::kMediaAppHandlesPdf)) {
+#if BUILDFLAG(ENABLE_CROS_MEDIA_APP)
+    source->AddResourcePath("system_assets/app_icon_16.png",
+                            IDR_MEDIA_APP_APP_ICON_16_PNG);
+    source->AddResourcePath("system_assets/app_icon_32.png",
+                            IDR_MEDIA_APP_APP_ICON_32_PNG);
+    source->AddResourcePath("system_assets/app_icon_48.png",
+                            IDR_MEDIA_APP_APP_ICON_48_PNG);
+    source->AddResourcePath("system_assets/app_icon_64.png",
+                            IDR_MEDIA_APP_APP_ICON_64_PNG);
+    source->AddResourcePath("system_assets/app_icon_96.png",
+                            IDR_MEDIA_APP_APP_ICON_96_PNG);
+    source->AddResourcePath("system_assets/app_icon_128.png",
+                            IDR_MEDIA_APP_APP_ICON_128_PNG);
+    source->AddResourcePath("system_assets/app_icon_192.png",
+                            IDR_MEDIA_APP_APP_ICON_192_PNG);
+    source->AddResourcePath("system_assets/app_icon_256.png",
+                            IDR_MEDIA_APP_APP_ICON_256_PNG);
+    app_icons_added = true;
+#endif  // BUILDFLAG(ENABLE_CROS_MEDIA_APP)
+  }
+  if (!app_icons_added) {
+    source->AddResourcePath("system_assets/app_icon_16.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_16_PNG);
+    source->AddResourcePath("system_assets/app_icon_32.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_32_PNG);
+    source->AddResourcePath("system_assets/app_icon_48.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_48_PNG);
+    source->AddResourcePath("system_assets/app_icon_64.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_64_PNG);
+    source->AddResourcePath("system_assets/app_icon_96.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_96_PNG);
+    source->AddResourcePath("system_assets/app_icon_128.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_128_PNG);
+    source->AddResourcePath("system_assets/app_icon_192.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_192_PNG);
+    source->AddResourcePath("system_assets/app_icon_256.png",
+                            IDR_MEDIA_APP_GALLERY_ICON_256_PNG);
+  }
+
   // Favicons.
   source->AddResourcePath("system_assets/pdf_icon.svg",
                           IDR_MEDIA_APP_PDF_ICON_SVG);
diff --git a/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts b/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts
index 48be676..ea299cb 100644
--- a/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts
+++ b/ash/webui/personalization_app/resources/trusted/user/user_selectors.ts
@@ -83,9 +83,10 @@
       return state.user.profileImage;
     case 'externalImage':
       return bufferToPngObjectUrl(userImage.externalImage!);
+    default:
+      console.warn('Unknown image type received', key);
+      return null;
   }
-
-  console.warn('Unknown image type received', key);
 }
 
 /**
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
index 6d54ba08..743e967b 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
@@ -152,7 +152,6 @@
         return;
       default:
         assertNotReached();
-        return;
     }
   }
 
diff --git a/base/memory/raw_ptr_unittest.cc b/base/memory/raw_ptr_unittest.cc
index 2811586..4c7b72e 100644
--- a/base/memory/raw_ptr_unittest.cc
+++ b/base/memory/raw_ptr_unittest.cc
@@ -1521,18 +1521,26 @@
 
 TEST(MTECheckedPtrImpl, AdvancedPointerShiftedAppropriately) {
   uint64_t* unwrapped_ptr = new uint64_t[6];
-  raw_ptr<uint64_t> ptr = unwrapped_ptr;
+  CountingRawPtr<uint64_t> ptr = unwrapped_ptr;
 
   // This is unwrapped, but still useful for ensuring that the
   // shift is sized in `uint64_t`s.
   auto original_addr = reinterpret_cast<uintptr_t>(ptr.get());
+  EXPECT_EQ(g_get_for_extraction_cnt, 1);
+  EXPECT_EQ(g_get_for_dereference_cnt, 0);
 
   ptr += 5;
   EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr.get()) - original_addr,
             5 * sizeof(uint64_t));
+  EXPECT_EQ(g_get_for_extraction_cnt, 2);
+  EXPECT_EQ(g_get_for_dereference_cnt, 0);
   delete[] unwrapped_ptr;
 
   EXPECT_DEATH_IF_SUPPORTED(*ptr, "");
+
+  // We assert that no visible extraction actually took place.
+  EXPECT_EQ(g_get_for_extraction_cnt, 2);
+  EXPECT_EQ(g_get_for_dereference_cnt, 0);
 }
 
 #endif  // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) &&
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 05e93df..2e36cc9 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220406.1.1
+7.20220407.0.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 05e93df..b879270 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220406.1.1
+7.20220406.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 05e93df..2e36cc9 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220406.1.1
+7.20220407.0.1
diff --git a/build_overrides/tint.gni b/build_overrides/tint.gni
index 2a31e3b..661bd76 100644
--- a/build_overrides/tint.gni
+++ b/build_overrides/tint.gni
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-tint_root_dir = "//third_party/dawn/third_party/tint"
+tint_root_dir = "//third_party/dawn"
 tint_spirv_tools_dir = "//third_party/vulkan-deps/spirv-tools/src"
 tint_googletest_dir = "//third_party/googletest/src"
 tint_spirv_headers_dir = "//third_party/vulkan-deps/spirv-headers/src"
diff --git a/chrome/VERSION b/chrome/VERSION
index 4a3fd8f..e028978c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=102
 MINOR=0
-BUILD=4989
+BUILD=4990
 PATCH=0
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index a65dc7d..ad4936c 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -108,9 +108,6 @@
 #define IDC_MIGRATE_LOCAL_CREDIT_CARD_FOR_PAGE 35015
 #define IDC_SEND_TAB_TO_SELF            35016
 #define IDC_FOCUS_THIS_TAB              35017
-#define IDC_CONTENT_LINK_SEND_TAB_TO_SELF 35018
-#define IDC_SEND_TAB_TO_SELF_SINGLE_TARGET  35019
-#define IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET  35020
 #define IDC_QRCODE_GENERATOR            35021
 #define IDC_WINDOW_CLOSE_TABS_TO_RIGHT  35022
 #define IDC_WINDOW_CLOSE_OTHER_TABS     35023
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 084d9cb..a321ed8d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8659,9 +8659,6 @@
       <message name="IDS_OMNIBOX_BUBBLE_ITEM_SUBTITLE_DAYS_SEND_TAB_TO_SELF" desc="The label of valid device button inside omnibox bubble, which shows the last activated time of a valid device.">
         Active <ph name="DEVICE_LAST_ACTIVATED_TIME">$1<ex>- 3 days ago</ex></ph> days ago
       </message>
-      <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET" desc="The label for the different context menu items for sharing this tab to the single other valid target device.">
-        Send to <ph name="DEVICE_NAME">$1<ex>Ted's Pixel2</ex></ph>
-      </message>
       <message name="IDS_TOOLBAR_BUTTON_SEND_TAB_TO_SELF_FROM_DEVICE" desc="The label for the device a received tab was sent from. Shown in the toolbar bubble.">
         Shared from <ph name="DEVICE_NAME">$1<ex>Ted's Pixel2</ex></ph>
       </message>
@@ -8677,27 +8674,17 @@
       <message name="IDS_TOOLBAR_BUTTON_SEND_TAB_TO_SELF_BUTTON_HINT_TEXT" desc="The hint text shown on received tab dialog shown from the toolbar button. Where 'THERE' is the device the user selected.">
         Choose a device. Then, to see the page, open Chrome there.
       </message>
-      <if expr="not use_titlecase">
-        <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF" desc="The label of item for share this tab to other devices in different context menus.">
-          Send to your devices
-        </message>
-        <message name="IDS_LINK_MENU_SEND_TAB_TO_SELF" desc="The label of item for share this tab to other devices in content area link right click menu.">
-          Send link to your devices
-        </message>
-        <message name="IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET" desc="The label for the link context menu item for sharing this tab to the single other valid target device">
-          Send link to <ph name="DEVICE_NAME">$1<ex>Ted's Pixel2</ex></ph>
-        </message>
-      </if>
       <if expr="use_titlecase">
-        <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF" desc="In Title Case: The label of item for share this tab to other devices in different context menus.">
-          Send to Your Devices
-        </message>
-        <message name="IDS_LINK_MENU_SEND_TAB_TO_SELF" desc="In Title Case: The label of item for share this tab to other devices in content area link right click menu.">
-          Send Link to Your Devices
-        </message>
-        <message name="IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET" desc="The label for the link context menu item for sharing this tab to the single other valid target device">
-          Send Link to <ph name="DEVICE_NAME">$1<ex>Ted's Pixel2</ex></ph>
-        </message>
+        <then>
+          <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF" desc="In Title Case: The label of item for share this tab to other devices in different context menus.">
+            Send to Your Devices
+          </message>
+        </then>
+        <else>
+          <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF" desc="The label of item for share this tab to other devices in different context menus.">
+            Send to your devices
+          </message>
+        </else>
       </if>
       <if expr="is_android">
       <message name="IDS_SEND_TAB_TO_SELF_MESSAGE" desc="Text for a message indicating that the tab was shared from a syncing device.">
diff --git a/chrome/app/generated_resources_grd/IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET.png.sha1
deleted file mode 100644
index ca4beab..0000000
--- a/chrome/app/generated_resources_grd/IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-146264fee60b43106f0127a3ab8a516e4b59f893
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LINK_MENU_SEND_TAB_TO_SELF.png.sha1 b/chrome/app/generated_resources_grd/IDS_LINK_MENU_SEND_TAB_TO_SELF.png.sha1
deleted file mode 100644
index 1cedebc..0000000
--- a/chrome/app/generated_resources_grd/IDS_LINK_MENU_SEND_TAB_TO_SELF.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f0db35e017cf5ac9341ff80d414b70f8ec4cb674
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET.png.sha1 b/chrome/app/generated_resources_grd/IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET.png.sha1
deleted file mode 100644
index b101808..0000000
--- a/chrome/app/generated_resources_grd/IDS_LINK_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-46d556494c97665f1c50ec56fd6dee2c5a85fb88
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index a9c746a..e776796 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5315,6 +5315,7 @@
     ]
     deps += [
       ":dlp_policy_event_proto",
+      "//chrome/browser/chromeos/extensions/login_screen",
       "//chromeos/crosapi/cpp",
       "//chromeos/crosapi/mojom",
       "//chromeos/dbus/dlp:dlp",
@@ -5326,6 +5327,9 @@
       "//ui/platform_window",
     ]
 
+    allow_circular_includes_from +=
+        [ "//chrome/browser/chromeos/extensions/login_screen" ]
+
     # component builds are not compatible on device.
     assert(!is_chromeos_device || !is_component_build)
   }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3d21ca8..ce51187 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2788,8 +2788,8 @@
     {"Comprehensive", kSnoopingProtectionRecall,
      std::size(kSnoopingProtectionRecall), nullptr}};
 
-const FeatureEntry::FeatureParam kQuickDim6s[] = {
-    {"QuickDim_quick_dim_ms", "6000"},
+const FeatureEntry::FeatureParam kQuickDim10s[] = {
+    {"QuickDim_quick_dim_ms", "10000"},
     {"QuickDim_filter_config_case", "2"},
     {"QuickDim_positive_count_threshold", "1"},
     {"QuickDim_negative_count_threshold", "2"},
@@ -2798,9 +2798,9 @@
     {"QuickDim_negative_score_threshold", "0"},
 };
 
-const FeatureEntry::FeatureParam kQuickDim6sQuickLock66s[] = {
-    {"QuickDim_quick_dim_ms", "6000"},
-    {"QuickDim_quick_lock_ms", "66000"},
+const FeatureEntry::FeatureParam kQuickDim10sQuickLock70s[] = {
+    {"QuickDim_quick_dim_ms", "10000"},
+    {"QuickDim_quick_lock_ms", "70000"},
     {"QuickDim_filter_config_case", "2"},
     {"QuickDim_positive_count_threshold", "1"},
     {"QuickDim_negative_count_threshold", "2"},
@@ -2809,9 +2809,9 @@
     {"QuickDim_negative_score_threshold", "0"},
 };
 
-const FeatureEntry::FeatureParam kQuickDim6sQuickLock126s[] = {
-    {"QuickDim_quick_dim_ms", "6000"},
-    {"QuickDim_quick_lock_ms", "126000"},
+const FeatureEntry::FeatureParam kQuickDim10sQuickLock130s[] = {
+    {"QuickDim_quick_dim_ms", "10000"},
+    {"QuickDim_quick_lock_ms", "130000"},
     {"QuickDim_filter_config_case", "2"},
     {"QuickDim_positive_count_threshold", "1"},
     {"QuickDim_negative_count_threshold", "2"},
@@ -2854,17 +2854,17 @@
 };
 
 const FeatureEntry::FeatureVariation kQuickDimVariations[] = {
-    {"Dim6sLock66s", kQuickDim6sQuickLock66s,
-     std::size(kQuickDim6sQuickLock66s), nullptr},
-    {"Dim6sLock126s", kQuickDim6sQuickLock126s,
-     std::size(kQuickDim6sQuickLock126s), nullptr},
+    {"Dim10sLock70s", kQuickDim10sQuickLock70s,
+     std::size(kQuickDim10sQuickLock70s), nullptr},
+    {"Dim10sLock130s", kQuickDim10sQuickLock130s,
+     std::size(kQuickDim10sQuickLock130s), nullptr},
     {"Dim45sLock105s", kQuickDim45sQuickLock105s,
      std::size(kQuickDim45sQuickLock105s), nullptr},
     {"Dim45sLock165s", kQuickDim45sQuickLock165s,
      std::size(kQuickDim45sQuickLock165s), nullptr},
     {"Dim120sLock240s", kQuickDim120sQuickLock240s,
      std::size(kQuickDim120sQuickLock240s), nullptr},
-    {"Dim6sNoLock", kQuickDim6s, std::size(kQuickDim6s), nullptr},
+    {"Dim10sNoLock", kQuickDim10s, std::size(kQuickDim10s), nullptr},
 };
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
@@ -6078,6 +6078,11 @@
      flag_descriptions::kEnableNeuralPalmAdaptiveHoldDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ui::kEnableNeuralPalmAdaptiveHold)},
 
+    {"enable-neural-palm-rejection-beta-model",
+     flag_descriptions::kEnableNeuralPalmRejectionBetaModelName,
+     flag_descriptions::kEnableNeuralPalmRejectionBetaModelDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(ui::kEnableNeuralPalmRejectionBetaModel)},
+
     {"enable-neural-palm-rejection-model-v2",
      flag_descriptions::kEnableNeuralPalmRejectionModelV2Name,
      flag_descriptions::kEnableNeuralPalmRejectionModelV2Description, kOsCrOS,
diff --git a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
index a912388..ef5c2740 100644
--- a/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/common_apps_navigation_throttle.cc
@@ -115,9 +115,7 @@
 
   // Projector:
   if (app_id == ash::kChromeUITrustedProjectorSwaAppId &&
-      url.DeprecatedGetOriginAsURL() ==
-          GURL(ash::kChromeUIUntrustedProjectorPwaUrl)
-              .DeprecatedGetOriginAsURL()) {
+      url.GetWithEmptyPath() == GURL(ash::kChromeUIUntrustedProjectorPwaUrl)) {
     std::string override_url = ash::kChromeUITrustedProjectorAppUrl;
     if (url.path().length() > 1)
       override_url += url.path().substr(1);
diff --git a/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc b/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc
index 8899411..0cb4d2d 100644
--- a/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc
+++ b/chrome/browser/ash/arc/input_method_manager/arc_input_method_manager_service.cc
@@ -701,7 +701,6 @@
     return;
   }
 
-  DCHECK(!active_connection_);
   active_connection_ = std::make_unique<InputConnectionImpl>(
       proxy_ime_engine_.get(), imm_bridge_.get(), context_id);
   mojo::PendingRemote<mojom::InputConnection> connection_remote;
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
index 9dad80b3..c78cb83f 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action_move.cc
@@ -81,6 +81,7 @@
   }
   void OnBindingToKeyboard() override { NOTIMPLEMENTED(); }
   void OnBindingToMouse(std::string mouse_action) override { NOTIMPLEMENTED(); }
+  void OnMenuEntryPressed() override { NOTIMPLEMENTED(); }
 };
 
 class ActionMove::ActionMoveKeyView : public ActionView {
@@ -129,29 +130,59 @@
       return;
 
     auto keys = binding->keys();
-    for (int i = 0; i < keys.size(); i++) {
-      auto text = GetDisplayText(keys[i]);
-      auto tag = ActionTag::CreateTextActionTag(text);
-      auto tag_size = tag->GetPreferredSize();
-      tag->SetSize(tag_size);
-      int x = kDirection[i][0];
-      int y = kDirection[i][1];
-      auto pos = gfx::Point(
-          radius + x * (radius - kTagOffset) - tag_size.width() / 2,
-          radius + y * (radius - kTagOffset) - tag_size.height() / 2);
-      tag->SetPosition(pos);
-      tags_.emplace_back(AddChildView(std::move(tag)));
+    if (tags_.empty()) {
+      for (int i = 0; i < keys.size(); i++) {
+        auto tag = ActionTag::CreateTextActionTag(GetDisplayText(keys[i]));
+        auto tag_size = tag->GetPreferredSize();
+        tag->SetSize(tag_size);
+        int x = kDirection[i][0];
+        int y = kDirection[i][1];
+        auto pos = gfx::Point(
+            radius + x * (radius - kTagOffset) - tag_size.width() / 2,
+            radius + y * (radius - kTagOffset) - tag_size.height() / 2);
+        tag->SetPosition(pos);
+        tags_.emplace_back(AddChildView(std::move(tag)));
+      }
+    } else {
+      DCHECK(tags_.size() == keys.size());
+      for (int i = 0; i < keys.size(); i++)
+        tags_[i]->SetTextActionTag(std::move(GetDisplayText(keys[i])));
     }
   }
 
   void OnKeyBindingChange(ActionTag* action_tag, ui::DomCode code) override {
-    // TODO(cuicuiruan): Implement the key binding change for key-bound
-    // |ActionMove|.
+    DCHECK(tags_.size() == kActionMoveKeysSize);
+    if (tags_.size() != kActionMoveKeysSize)
+      return;
+    auto it = std::find(tags_.begin(), tags_.end(), action_tag);
+    DCHECK(it != tags_.end());
+    if (it == tags_.end())
+      return;
+
+    if (ShouldShowErrorMsg(code))
+      return;
+
+    auto& binding = action_->GetCurrentDisplayedBinding();
+    DCHECK(binding.keys().size() == kActionMoveKeysSize);
+    const int index = it - tags_.begin();
+    std::vector<ui::DomCode> new_keys = binding.keys();
+    new_keys[index] = code;
+    auto input_element = InputElement::CreateActionMoveKeyElement(new_keys);
+    display_overlay_controller_->OnBindingChange(action_,
+                                                 std::move(input_element));
+  }
+
+  // TODO(cuicuiruan): Remove this for post MVP for editing |ActionMove|.
+  void SetDisplayMode(const DisplayMode mode) override {
+    ActionView::SetDisplayMode(mode);
+    if (menu_entry_)
+      menu_entry_->SetVisible(false);
   }
 
   // TODO(cuicuiruan): implement for post MVP once the design is ready.
   void OnBindingToKeyboard() override { NOTIMPLEMENTED(); }
   void OnBindingToMouse(std::string mouse_action) override { NOTIMPLEMENTED(); }
+  void OnMenuEntryPressed() override { NOTIMPLEMENTED(); }
 };
 
 ActionMove::ActionMove(aura::Window* window) : Action(window) {}
@@ -309,7 +340,8 @@
     view = std::make_unique<ActionMoveKeyView>(this, display_overlay_controller,
                                                content_bounds);
   }
-  view->set_editable(false);
+  action_view_ = view.get();
+  view->set_editable(true);
   auto center_pos = GetUICenterPosition(content_bounds);
   view->SetPositionFromCenterPosition(center_pos);
   return view;
diff --git a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
index 1e309568..2d174b1b 100644
--- a/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
+++ b/chrome/browser/ash/arc/input_overlay/actions/action_tap.cc
@@ -157,6 +157,14 @@
     display_overlay_controller_->OnBindingChange(action_,
                                                  std::move(input_element));
   }
+
+  void OnMenuEntryPressed() override {
+    display_overlay_controller_->AddActionEditMenu(this, ActionType::kTap);
+    DCHECK(menu_entry_);
+    if (!menu_entry_)
+      return;
+    menu_entry_->RequestFocus();
+  }
 };
 
 ActionTap::ActionTap(aura::Window* window) : Action(window) {}
diff --git a/chrome/browser/ash/arc/input_overlay/constants.h b/chrome/browser/ash/arc/input_overlay/constants.h
index 20ec7d82f..1bf47af 100644
--- a/chrome/browser/ash/arc/input_overlay/constants.h
+++ b/chrome/browser/ash/arc/input_overlay/constants.h
@@ -51,6 +51,14 @@
   kPending,
 };
 
+// Action types according to the touch events.
+enum class ActionType {
+  // |kTap| involves touch down and up.
+  kTap,
+  // |kMove| involves touch down, move and up.
+  kMove,
+};
+
 }  // namespace input_overlay
 }  // namespace arc
 
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index a21348b..fb6e86f 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -283,7 +283,8 @@
   return absl::optional<gfx::Rect>(menu_entry_->bounds());
 }
 
-void DisplayOverlayController::AddActionEditMenu(ActionView* anchor) {
+void DisplayOverlayController::AddActionEditMenu(ActionView* anchor,
+                                                 ActionType action_type) {
   auto* overlay_widget = GetOverlayWidget();
   DCHECK(overlay_widget);
   if (!overlay_widget)
@@ -292,7 +293,8 @@
   DCHECK(parent_view);
   if (!parent_view)
     return;
-  auto action_edit_menu = ActionEditMenu::BuildActionTapEditMenu(this, anchor);
+  auto action_edit_menu =
+      ActionEditMenu::BuildActionEditMenu(this, anchor, action_type);
   if (action_edit_menu)
     action_edit_menu_ = parent_view->AddChildView(std::move(action_edit_menu));
 }
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
index 2701f8d5..7cecf881 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -46,7 +46,7 @@
   // Get the bounds of |overlay_menu_entry_| in contents view.
   absl::optional<gfx::Rect> GetOverlayMenuEntryBounds();
 
-  void AddActionEditMenu(ActionView* anchor);
+  void AddActionEditMenu(ActionView* anchor, ActionType action_type);
   void RemoveActionEditMenu();
 
   void AddEditErrorMsg(ActionView* action_view, base::StringPiece error_msg);
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.cc b/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.cc
index c325c7e..2ab870a8 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.cc
@@ -86,16 +86,27 @@
 ActionEditMenu::~ActionEditMenu() = default;
 
 // static
-std::unique_ptr<ActionEditMenu> ActionEditMenu::BuildActionTapEditMenu(
+std::unique_ptr<ActionEditMenu> ActionEditMenu::BuildActionEditMenu(
     DisplayOverlayController* display_overlay_controller,
-    ActionView* anchor) {
+    ActionView* anchor,
+    ActionType action_type) {
   if (!display_overlay_controller)
     return nullptr;
   display_overlay_controller->RemoveActionEditMenu();
 
   auto menu =
       std::make_unique<ActionEditMenu>(display_overlay_controller, anchor);
-  menu->InitActionTapEditMenu();
+
+  switch (action_type) {
+    case ActionType::kTap:
+      menu->InitActionTapEditMenu();
+      break;
+    case ActionType::kMove:
+      menu->InitActionTapEditMenu();
+      break;
+    default:
+      NOTREACHED();
+  }
 
   return menu;
 }
@@ -154,6 +165,11 @@
   }
 }
 
+void ActionEditMenu::InitActionMoveEditMenu() {
+  // TODO(cuicuiruan): Implement after post MVP.
+  NOTIMPLEMENTED();
+}
+
 void ActionEditMenu::OnKeyBoardKeyBindingButtonPressed() {
   DCHECK(anchor_view_);
   if (!anchor_view_)
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.h b/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.h
index 513ff08..012cc56 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_edit_menu.h
@@ -29,14 +29,19 @@
   ActionEditMenu& operator=(const ActionEditMenu&) = delete;
   ~ActionEditMenu() override;
 
-  static std::unique_ptr<ActionEditMenu> BuildActionTapEditMenu(
+  static std::unique_ptr<ActionEditMenu> BuildActionEditMenu(
       DisplayOverlayController* display_overlay_controller,
-      ActionView* anchor);
+      ActionView* anchor,
+      ActionType action_type);
 
  private:
   class BindingButton;
 
+  // Create edit menu for each action types.
   void InitActionTapEditMenu();
+  void InitActionMoveEditMenu();
+
+  // Function calls for each menu item button.
   void OnKeyBoardKeyBindingButtonPressed();
   void OnMouseLeftClickBindingButtonPressed();
   void OnMouseRightClickBindingButtonPressed();
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
index bdcf5efd..c34eb2fb 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
@@ -15,6 +15,8 @@
 // TODO(cuicuiruan): move the strings to chrome/app/generated_resources.grd
 // after UX/UI strings are confirmed.
 constexpr base::StringPiece kEditErrorUnsupportedKey("Unsupported key");
+constexpr base::StringPiece kEditErrorDuplicatedKey(
+    "Duplicated key in the same action");
 }  // namespace
 
 ActionView::ActionView(Action* action,
@@ -51,14 +53,6 @@
   SetPosition(gfx::Point(left, top));
 }
 
-void ActionView::OnMenuEntryPressed() {
-  display_overlay_controller_->AddActionEditMenu(this);
-  DCHECK(menu_entry_);
-  if (!menu_entry_)
-    return;
-  menu_entry_->RequestFocus();
-}
-
 gfx::Point ActionView::GetEditMenuPosition(gfx::Size menu_size) {
   DCHECK(menu_entry_);
   if (!menu_entry_)
@@ -115,6 +109,19 @@
 }
 
 bool ActionView::ShouldShowErrorMsg(ui::DomCode code) {
+  // Check if |code| is duplicated with the keys in its action. For example,
+  // there are four keys involved in the key-bound |ActionMove|.
+  auto& binding = action_->GetCurrentDisplayedBinding();
+  if (IsKeyboardBound(binding)) {
+    for (const auto& key : binding.keys()) {
+      if (key != code)
+        continue;
+      display_overlay_controller_->AddEditErrorMsg(this,
+                                                   kEditErrorDuplicatedKey);
+      return true;
+    }
+  }
+
   if (!action_->support_modifier_key() &&
       ModifierDomCodeToEventFlag(code) != ui::EF_NONE) {
     display_overlay_controller_->AddEditErrorMsg(this,
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.h b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
index 8309fc6..d1b577d5 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
@@ -40,11 +40,15 @@
   virtual void OnKeyBindingChange(ActionTag* action_tag, ui::DomCode code) = 0;
   virtual void OnBindingToKeyboard() = 0;
   virtual void OnBindingToMouse(std::string mouse_action) = 0;
+  // Each type of the actions shows different edit menu.
+  virtual void OnMenuEntryPressed() = 0;
+
+  // TODO(cuicuiruan): Remove virtual for post MVP once edit menu is ready for
+  // |ActionMove|.
+  virtual void SetDisplayMode(const DisplayMode mode);
 
   // Set position from its center position.
   void SetPositionFromCenterPosition(gfx::PointF& center_position);
-  void SetDisplayMode(const DisplayMode mode);
-  void OnMenuEntryPressed();
   // Get edit menu position in parent's bounds.
   gfx::Point GetEditMenuPosition(gfx::Size menu_size);
   void RemoveEditMenu();
diff --git a/chrome/browser/ash/crosapi/login_ash.cc b/chrome/browser/ash/crosapi/login_ash.cc
index c71bc42..cae89c22 100644
--- a/chrome/browser/ash/crosapi/login_ash.cc
+++ b/chrome/browser/ash/crosapi/login_ash.cc
@@ -16,13 +16,15 @@
 #include "chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/common/pref_names.h"
-#include "chromeos/crosapi/mojom/login.mojom.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_type.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/user_activity/user_activity_detector.h"
 
 namespace crosapi {
@@ -230,6 +232,18 @@
   std::move(callback).Run();
 }
 
+void LoginAsh::AddLacrosCleanupTriggeredObserver(
+    mojo::PendingRemote<mojom::LacrosCleanupTriggeredObserver> observer) {
+  mojo::Remote<mojom::LacrosCleanupTriggeredObserver> remote(
+      std::move(observer));
+  lacros_cleanup_triggered_observers_.Add(std::move(remote));
+}
+
+mojo::RemoteSet<mojom::LacrosCleanupTriggeredObserver>&
+LoginAsh::GetCleanupTriggeredObservers() {
+  return lacros_cleanup_triggered_observers_;
+}
+
 void LoginAsh::OnScreenLockerAuthenticate(
     base::OnceCallback<void(const absl::optional<std::string>&)> callback,
     bool success) {
@@ -244,7 +258,7 @@
 
 void LoginAsh::OnOptionalErrorCallbackComplete(
     base::OnceCallback<void(const absl::optional<std::string>&)> callback,
-    absl::optional<std::string> error) {
+    const absl::optional<std::string>& error) {
   std::move(callback).Run(error);
 }
 
diff --git a/chrome/browser/ash/crosapi/login_ash.h b/chrome/browser/ash/crosapi/login_ash.h
index 806149ee..ff5135aee 100644
--- a/chrome/browser/ash/crosapi/login_ash.h
+++ b/chrome/browser/ash/crosapi/login_ash.h
@@ -12,7 +12,9 @@
 #include "chromeos/crosapi/mojom/login.mojom.h"
 #include "components/user_manager/user_type.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace crosapi {
@@ -57,6 +59,12 @@
   void SetDataForNextLoginAttempt(
       const std::string& data_for_next_login_attempt,
       SetDataForNextLoginAttemptCallback callback) override;
+  void AddLacrosCleanupTriggeredObserver(
+      mojo::PendingRemote<mojom::LacrosCleanupTriggeredObserver> observer)
+      override;
+
+  mojo::RemoteSet<mojom::LacrosCleanupTriggeredObserver>&
+  GetCleanupTriggeredObservers();
 
  private:
   void OnScreenLockerAuthenticate(
@@ -64,7 +72,7 @@
       bool success);
   void OnOptionalErrorCallbackComplete(
       base::OnceCallback<void(const absl::optional<std::string>&)> callback,
-      absl::optional<std::string> error);
+      const absl::optional<std::string>& error);
   absl::optional<std::string> CanLaunchSession();
   absl::optional<std::string> LockSession(
       absl::optional<user_manager::UserType> user_type = absl::nullopt);
@@ -75,6 +83,11 @@
       base::OnceCallback<void(const absl::optional<std::string>&)> callback);
 
   mojo::ReceiverSet<mojom::Login> receivers_;
+
+  // Support any number of observers.
+  mojo::RemoteSet<mojom::LacrosCleanupTriggeredObserver>
+      lacros_cleanup_triggered_observers_;
+
   base::WeakPtrFactory<LoginAsh> weak_factory_{this};
 };
 
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 5b802c92..2b120a41 100644
--- a/chrome/browser/ash/dbus/vm/vm_launch_service_provider.cc
+++ b/chrome/browser/ash/dbus/vm/vm_launch_service_provider.cc
@@ -14,8 +14,8 @@
 #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/guest_os/guest_os_launcher.h"
+#include "chrome/browser/ash/guest_os/guest_os_wayland_server.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -69,9 +69,10 @@
                                                ss.str()));
 }
 
-void OnLaunchEnsured(dbus::MethodCall* method_call,
-                     dbus::ExportedObject::ResponseSender response_sender,
-                     guest_os::launcher::ResponseType response) {
+template <typename T>
+void HandleReturn(dbus::MethodCall* method_call,
+                  dbus::ExportedObject::ResponseSender response_sender,
+                  borealis::Expected<T, std::string> response) {
   if (!response) {
     std::move(response_sender)
         .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
@@ -134,52 +135,10 @@
     return;
   }
 
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-  if (!profile ||
-      ProfileHelper::GetUserIdHashFromProfile(profile) != request.owner_id()) {
-    std::move(response_sender)
-        .Run(dbus::ErrorResponse::FromMethodCall(
-            method_call, DBUS_ERROR_INVALID_ARGS, "Invalid owner_id"));
-    return;
-  }
-
-  switch (request.vm_type()) {
-    case vm_tools::launch::VmType::BOREALIS:
-      borealis::BorealisService::GetForProfile(profile)
-          ->WaylandInterface()
-          .GetWaylandServer(
-              base::BindOnce(&VmLaunchServiceProvider::OnWaylandServerStarted,
-                             weak_ptr_factory_.GetWeakPtr(), method_call,
-                             std::move(response_sender)));
-      break;
-    default:
-      LOG(WARNING) << "StartWaylandServer is not implemented for VM type="
-                   << request.vm_type() << " owner=" << request.owner_id();
-      std::move(response_sender)
-          .Run(dbus::ErrorResponse::FromMethodCall(
-              method_call, DBUS_ERROR_NOT_SUPPORTED, "Not implemented"));
-      break;
-  }
-}
-
-void VmLaunchServiceProvider::OnWaylandServerStarted(
-    dbus::MethodCall* method_call,
-    dbus::ExportedObject::ResponseSender response_sender,
-    borealis::BorealisCapabilities* capabilities,
-    const base::FilePath& path) {
-  if (!capabilities || path.empty()) {
-    std::move(response_sender)
-        .Run(dbus::ErrorResponse::FromMethodCall(
-            method_call, DBUS_ERROR_FAILED, "Wayland server creation failed"));
-    return;
-  }
-  std::unique_ptr<dbus::Response> response =
-      dbus::Response::FromMethodCall(method_call);
-  vm_tools::launch::StartWaylandServerResponse response_pb;
-  response_pb.mutable_server()->set_path(path.AsUTF8Unsafe());
-  dbus::MessageWriter writer(response.get());
-  writer.AppendProtoAsArrayOfBytes(response_pb);
-  std::move(response_sender).Run(std::move(response));
+  guest_os::GuestOsWaylandServer::StartServer(
+      request, base::BindOnce(
+                   &HandleReturn<vm_tools::launch::StartWaylandServerResponse>,
+                   method_call, std::move(response_sender)));
 }
 
 void VmLaunchServiceProvider::StopWaylandServer(
@@ -245,8 +204,9 @@
   }
 
   guest_os::launcher::EnsureLaunched(
-      request, base::BindOnce(&OnLaunchEnsured, method_call,
-                              std::move(response_sender)));
+      request,
+      base::BindOnce(&HandleReturn<vm_tools::launch::EnsureVmLaunchedResponse>,
+                     method_call, std::move(response_sender)));
 }
 
 }  // 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 3f9347b..855528f3 100644
--- a/chrome/browser/ash/dbus/vm/vm_launch_service_provider.h
+++ b/chrome/browser/ash/dbus/vm/vm_launch_service_provider.h
@@ -10,10 +10,6 @@
 #include "chromeos/dbus/services/cros_dbus_service.h"
 #include "dbus/exported_object.h"
 
-namespace borealis {
-class BorealisCapabilities;
-}
-
 namespace ash {
 
 class VmLaunchServiceProvider
@@ -33,12 +29,6 @@
   void StartWaylandServer(dbus::MethodCall* method_call,
                           dbus::ExportedObject::ResponseSender response_sender);
 
-  void OnWaylandServerStarted(
-      dbus::MethodCall* method_call,
-      dbus::ExportedObject::ResponseSender response_sender,
-      borealis::BorealisCapabilities* capabilities,
-      const base::FilePath& path);
-
   void StopWaylandServer(dbus::MethodCall* method_call,
                          dbus::ExportedObject::ResponseSender response_sender);
 
diff --git a/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.cc b/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.cc
index e3ca73a..22a9a925 100644
--- a/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.cc
+++ b/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.cc
@@ -5,12 +5,30 @@
 #include "chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.h"
 
 #include "base/files/file_path.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/user_manager/user_manager.h"
 
 namespace ash {
 namespace diagnostics {
 
 base::FilePath DiagnosticsBrowserDelegateImpl::GetActiveUserProfileDir() {
-  return base::FilePath();
+  // Handle no user logged in.
+  if (!user_manager::UserManager::IsInitialized() ||
+      !user_manager::UserManager::Get()->IsUserLoggedIn()) {
+    return base::FilePath();
+  }
+
+  auto* user = user_manager::UserManager::Get()->GetActiveUser();
+  DCHECK(user);
+  auto* profile = ash::ProfileHelper::Get()->GetProfileByUser(user);
+
+  // Profile may be null if called before profile load is complete.
+  if (profile == nullptr) {
+    return base::FilePath();
+  }
+
+  return profile->GetPath();
 }
 
 }  // namespace diagnostics
diff --git a/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc b/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc
index b5e38eaa..8b33480 100644
--- a/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc
+++ b/chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl_unittest.cc
@@ -5,20 +5,145 @@
 #include "chrome/browser/ash/diagnostics/diagnostics_browser_delegate_impl.h"
 
 #include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/login/login_state/login_state.h"
+#include "components/account_id/account_id.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/user_manager/user.h"
+#include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_names.h"
+#include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
 namespace diagnostics {
 
+namespace {
+
+const char kGuestUserDir[] = "Guest Profile";
+const char kSignInUserDir[] = "Default";
+const char kTestUserEmail[] = "test@example.com";
+const char kTestUserEmailDir[] = "u-test@example.com-hash";
+
+}  // namespace
+
 class DiagnosticsBrowserDelegateImplTest : public testing::Test {
  public:
-  DiagnosticsBrowserDelegateImplTest() = default;
+  DiagnosticsBrowserDelegateImplTest() : delegate_() {
+    CHECK(profile_manager_.SetUp());
+  }
+
   ~DiagnosticsBrowserDelegateImplTest() override = default;
+
+  void SetUp() override {
+    user_manager_ = std::make_unique<ash::FakeChromeUserManager>();
+    user_manager_->Initialize();
+    ProfileHelper::Get()->Initialize();
+    LoginState::Initialize();
+
+    task_env_.RunUntilIdle();
+  }
+
+  void TearDown() override {
+    // Clean up user manager.
+    user_manager_->Shutdown();
+    user_manager_->Destroy();
+    user_manager_.reset();
+
+    LoginState::Shutdown();
+    profile_manager_.DeleteAllTestingProfiles();
+
+    // Let any pending tasks complete.
+    task_env_.RunUntilIdle();
+    testing::Test::TearDown();
+  }
+
+  // Get profile path based on user_data_dir path from current profile manager.
+  base::FilePath GetExpectedPath(const std::string& path) {
+    return profile_manager_.profile_manager()->user_data_dir().Append(path);
+  }
+
+  // Creates guest profile and user then sets that account to active.
+  void LoginAsGuest() {
+    auto* profile = profile_manager_.CreateGuestProfile();
+    auto* user = user_manager_->AddGuestUser();
+    ProfileHelper::Get()->SetUserToProfileMappingForTesting(user, profile);
+    LoginAndSetActiveUserInUserManager(user_manager::GuestAccountId());
+  }
+
+  // Creates regular_user profile and user then sets that account to active.
+  void LoginAsRegularTestUser() {
+    const AccountId id = AccountId::FromUserEmail(kTestUserEmail);
+    auto* user = user_manager_->AddUser(id);
+    auto* profile = profile_manager_.CreateTestingProfile(kTestUserEmail);
+    ProfileHelper::Get()->SetUserToProfileMappingForTesting(user, profile);
+    LoginAndSetActiveUserInUserManager(id);
+  }
+
+  // Looks up SignIn profile and creates regular user then sets that account to
+  // active.
+  void LoginAsSignInUser() {
+    const AccountId id = user_manager::SignInAccountId();
+    auto* profile = ProfileHelper::Get()->GetSigninProfile();
+    auto* user = user_manager_->AddUser(id);
+    ProfileHelper::Get()->SetUserToProfileMappingForTesting(user, profile);
+    LoginAndSetActiveUserInUserManager(id);
+  }
+
+  // Helper to set active user in the UserManager.
+  void LoginAndSetActiveUserInUserManager(const AccountId& id) {
+    user_manager_->LoginUser(id);
+    user_manager_->SwitchActiveUser(id);
+    task_env_.RunUntilIdle();
+  }
+
+ protected:
+  ash::diagnostics::DiagnosticsBrowserDelegateImpl delegate_;
+
+ private:
+  content::BrowserTaskEnvironment task_env_{};
+  std::unique_ptr<ash::FakeChromeUserManager> user_manager_;
+  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
+  TestingPrefServiceSimple local_state_;
 };
 
-TEST_F(DiagnosticsBrowserDelegateImplTest, GetActiveUserProfileDir) {
-  DiagnosticsBrowserDelegateImpl delegate;
-  EXPECT_EQ(base::FilePath(), delegate.GetActiveUserProfileDir());
+TEST_F(DiagnosticsBrowserDelegateImplTest, GetActiveUserProfileDirForSignIn) {
+  const base::FilePath expected_path = GetExpectedPath(kSignInUserDir);
+
+  LoginAsSignInUser();
+
+  EXPECT_EQ(expected_path, delegate_.GetActiveUserProfileDir());
+}
+
+TEST_F(DiagnosticsBrowserDelegateImplTest,
+       GetActiveUserProfileDirForOtherUsers) {
+  const base::FilePath expected_path = GetExpectedPath(kGuestUserDir);
+
+  LoginAsGuest();
+
+  EXPECT_EQ(expected_path, delegate_.GetActiveUserProfileDir());
+}
+
+TEST_F(DiagnosticsBrowserDelegateImplTest,
+       GetActiveUserProfileDirForRegularUser) {
+  const base::FilePath expected_path = GetExpectedPath(kTestUserEmailDir);
+
+  LoginAsRegularTestUser();
+
+  EXPECT_EQ(expected_path, delegate_.GetActiveUserProfileDir());
+}
+
+TEST_F(DiagnosticsBrowserDelegateImplTest,
+       GetActiveUserProfileDirForNoUserLoggedIn) {
+  EXPECT_TRUE(user_manager::UserManager::IsInitialized());
+  EXPECT_FALSE(user_manager::UserManager::Get()->IsUserLoggedIn());
+  EXPECT_EQ(base::FilePath(), delegate_.GetActiveUserProfileDir());
 }
 
 }  // namespace diagnostics
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index c84bd7a..f659819d 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -1596,6 +1596,14 @@
         TestCase("recentAudioDownloadsAndDrive")
             .EnableFiltersInRecents()
             .FilesSwa(),
+        TestCase("recentDocumentsDownloads").EnableFiltersInRecents(),
+        TestCase("recentDocumentsDownloads")
+            .EnableFiltersInRecents()
+            .FilesSwa(),
+        TestCase("recentDocumentsDownloadsAndDrive").EnableFiltersInRecents(),
+        TestCase("recentDocumentsDownloadsAndDrive")
+            .EnableFiltersInRecents()
+            .FilesSwa(),
         TestCase("recentImagesDownloads"),
         TestCase("recentImagesDownloads").FilesSwa(),
         TestCase("recentImagesDownloads").EnableFiltersInRecents(),
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest.cc b/chrome/browser/ash/file_manager/file_manager_jstest.cc
index 04c9860..260742d1 100644
--- a/chrome/browser/ash/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_jstest.cc
@@ -168,6 +168,10 @@
   RunTestURL("common/js/file_type_unittest.m_gen.html");
 }
 
+IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FilteredVolumeManagerTest) {
+  RunTestURL("common/js/filtered_volume_manager_unittest.m_gen.html");
+}
+
 IN_PROC_BROWSER_TEST_F(CanvasFileManagerJsTest, ImageOrientation) {
   RunTestURL("foreground/js/metadata/image_orientation_unittest.m_gen.html");
 }
diff --git a/chrome/browser/ash/file_manager/file_manager_string_util.cc b/chrome/browser/ash/file_manager/file_manager_string_util.cc
index 6e7f10b..bf86e29 100644
--- a/chrome/browser/ash/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/ash/file_manager/file_manager_string_util.cc
@@ -65,6 +65,7 @@
   // TODO(crbug.com/438921): Rename string IDs to something like
   // FILE_TYPE_WHATEVER.
   SET_STRING("AUDIO_FILE_TYPE", IDS_FILE_BROWSER_AUDIO_FILE_TYPE);
+  SET_STRING("CSV_TEXT_FILE_TYPE", IDS_FILE_BROWSER_CSV_TEXT_FILE_TYPE);
   SET_STRING("EXCEL_FILE_TYPE", IDS_FILE_BROWSER_EXCEL_FILE_TYPE);
   SET_STRING("FOLDER", IDS_FILE_BROWSER_FOLDER);
   SET_STRING("GDOC_DOCUMENT_FILE_TYPE",
@@ -193,6 +194,8 @@
              IDS_FILE_BROWSER_MEDIA_VIEW_IMAGES_ROOT_LABEL);
   SET_STRING("MEDIA_VIEW_VIDEOS_ROOT_LABEL",
              IDS_FILE_BROWSER_MEDIA_VIEW_VIDEOS_ROOT_LABEL);
+  SET_STRING("MEDIA_VIEW_DOCUMENTS_ROOT_LABEL",
+             IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL);
   SET_STRING("RECENT_VIEW_FILTER_ON", IDS_FILE_BROWSER_RECENT_VIEW_FILTER_ON);
   SET_STRING("RECENT_VIEW_FILTER_OFF", IDS_FILE_BROWSER_RECENT_VIEW_FILTER_OFF);
   SET_STRING("RECENT_VIEW_FILTER_RESET",
diff --git a/chrome/browser/ash/guest_os/guest_os_launcher.cc b/chrome/browser/ash/guest_os/guest_os_launcher.cc
index fb3da6e..98da66e 100644
--- a/chrome/browser/ash/guest_os/guest_os_launcher.cc
+++ b/chrome/browser/ash/guest_os/guest_os_launcher.cc
@@ -4,11 +4,136 @@
 
 #include "chrome/browser/ash/guest_os/guest_os_launcher.h"
 
+#include <sstream>
+
+#include "base/logging.h"
+#include "chrome/browser/ash/borealis/borealis_context.h"
+#include "chrome/browser/ash/borealis/borealis_context_manager.h"
+#include "chrome/browser/ash/borealis/borealis_service.h"
+#include "chrome/browser/ash/crostini/crostini_manager.h"
+#include "chrome/browser/ash/crostini/crostini_simple_types.h"
+#include "chrome/browser/ash/crostini/crostini_util.h"
+#include "chrome/browser/ash/plugin_vm/plugin_vm_manager.h"
+#include "chrome/browser/ash/plugin_vm/plugin_vm_manager_factory.h"
+#include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chromeos/dbus/vm_launch/launch.pb.h"
+
 namespace guest_os::launcher {
 
+namespace {
+
+ResponseType Success(std::string vm_name, std::string container_name) {
+  vm_tools::launch::EnsureVmLaunchedResponse response;
+  response.set_vm_name(std::move(vm_name));
+  response.set_container_name(std::move(container_name));
+  return ResponseType(std::move(response));
+}
+
+void LaunchBorealis(Profile* profile, LaunchCallback callback) {
+  borealis::BorealisService::GetForProfile(profile)
+      ->ContextManager()
+      .StartBorealis(base::BindOnce(
+          [](LaunchCallback callback,
+             borealis::BorealisContextManager::ContextOrFailure
+                 context_or_failure) {
+            if (!context_or_failure) {
+              std::stringstream error_msg;
+              error_msg << "Failed to launch ("
+                        << static_cast<int>(context_or_failure.Error().error())
+                        << "): " << context_or_failure.Error().description();
+              std::move(callback).Run(
+                  ResponseType::Unexpected(error_msg.str()));
+              return;
+            }
+            std::move(callback).Run(Success(
+                context_or_failure.Value()->vm_name(), /*container_name=*/""));
+          },
+          std::move(callback)));
+}
+
+void LaunchCrostini(Profile* profile,
+                    bool just_termina,
+                    LaunchCallback callback) {
+  crostini::CrostiniManager::RestartOptions options;
+  options.start_vm_only = just_termina;
+  auto container_id = crostini::ContainerId::GetDefault();
+  crostini::CrostiniManager::GetForProfile(profile)->RestartCrostiniWithOptions(
+      container_id, std::move(options),
+      base::BindOnce(
+          [](std::string vm_name, std::string container_name,
+             LaunchCallback callback, crostini::CrostiniResult result) {
+            if (result != crostini::CrostiniResult::SUCCESS) {
+              std::stringstream error_msg;
+              error_msg << "Failed to launch: code="
+                        << static_cast<int>(result);
+              std::move(callback).Run(
+                  ResponseType::Unexpected(error_msg.str()));
+              return;
+            }
+            std::move(callback).Run(Success(vm_name, container_name));
+          },
+          container_id.vm_name, just_termina ? "" : container_id.container_name,
+          std::move(callback)));
+}
+
+void LaunchPluginVm(Profile* profile, LaunchCallback callback) {
+  plugin_vm::PluginVmManagerFactory::GetForProfile(profile)->LaunchPluginVm(
+      base::BindOnce(
+          [](LaunchCallback callback, bool success) {
+            if (!success) {
+              std::move(callback).Run(
+                  ResponseType::Unexpected("Failed to launch Plugin VM"));
+              return;
+            }
+            std::move(callback).Run(
+                Success(plugin_vm::kPluginVmName, /*container_name=*/""));
+          },
+          std::move(callback)));
+}
+
+}  // namespace
+
 void EnsureLaunched(const vm_tools::launch::EnsureVmLaunchedRequest& request,
                     LaunchCallback response_callback) {
-  std::move(response_callback).Run(ResponseType::Unexpected("Not Implemented"));
+  if (request.launch_descriptors().empty()) {
+    std::move(response_callback)
+        .Run(ResponseType::Unexpected("No launch_descriptors provided"));
+    return;
+  }
+
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+  if (!profile || ash::ProfileHelper::GetUserIdHashFromProfile(profile) !=
+                      request.owner_id()) {
+    std::move(response_callback)
+        .Run(ResponseType::Unexpected(
+            "Provided owner_id does not match the primary profile"));
+    return;
+  }
+
+  // Descriptors are are an increasingly-specific list of identifiers for what
+  // the user wants chrome to launch.
+  //
+  // E.g. ["crostini"] may refer to the default linux container, whereas
+  // ["crostini", "foo"] could instead refer to a custom linux container called
+  // "foo".
+  const std::string& main_descriptor = request.launch_descriptors()[0];
+  if (main_descriptor == "borealis") {
+    LaunchBorealis(profile, std::move(response_callback));
+  } else if (main_descriptor == "crostini") {
+    LaunchCrostini(profile, /*just_termina=*/false,
+                   std::move(response_callback));
+  } else if (main_descriptor == "plugin_vm") {
+    LaunchPluginVm(profile, std::move(response_callback));
+  } else if (main_descriptor == "termina") {
+    LaunchCrostini(profile, /*just_termina=*/true,
+                   std::move(response_callback));
+  } else {
+    std::move(response_callback)
+        .Run(
+            ResponseType::Unexpected("Unknown descriptor: " + main_descriptor));
+  }
 }
 
 }  // namespace guest_os::launcher
diff --git a/chrome/browser/ash/guest_os/guest_os_wayland_server.cc b/chrome/browser/ash/guest_os/guest_os_wayland_server.cc
new file mode 100644
index 0000000..dae7705
--- /dev/null
+++ b/chrome/browser/ash/guest_os/guest_os_wayland_server.cc
@@ -0,0 +1,63 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/guest_os/guest_os_wayland_server.h"
+
+#include "chrome/browser/ash/borealis/borealis_service.h"
+#include "chrome/browser/ash/borealis/borealis_wayland_interface.h"
+#include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+
+namespace guest_os {
+
+namespace {
+
+using ResponseType =
+    borealis::Expected<vm_tools::launch::StartWaylandServerResponse,
+                       std::string>;
+
+void OnWaylandServerStarted(
+    base::OnceCallback<void(ResponseType)> response_callback,
+    borealis::BorealisCapabilities* capabilities,
+    const base::FilePath& path) {
+  if (!capabilities || path.empty()) {
+    std::move(response_callback)
+        .Run(ResponseType::Unexpected("Wayland server creation failed"));
+    return;
+  }
+  vm_tools::launch::StartWaylandServerResponse response;
+  response.mutable_server()->set_path(path.AsUTF8Unsafe());
+  std::move(response_callback).Run(ResponseType(std::move(response)));
+}
+
+}  // namespace
+
+void GuestOsWaylandServer::StartServer(
+    const vm_tools::launch::StartWaylandServerRequest& request,
+    base::OnceCallback<void(ResponseType)> response_callback) {
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+  if (!profile || ash::ProfileHelper::GetUserIdHashFromProfile(profile) !=
+                      request.owner_id()) {
+    std::move(response_callback)
+        .Run(ResponseType::Unexpected("Invalid owner_id"));
+    return;
+  }
+
+  switch (request.vm_type()) {
+    case vm_tools::launch::VmType::BOREALIS:
+      borealis::BorealisService::GetForProfile(profile)
+          ->WaylandInterface()
+          .GetWaylandServer(base::BindOnce(&OnWaylandServerStarted,
+                                           std::move(response_callback)));
+      break;
+    default:
+      std::move(response_callback)
+          .Run(ResponseType::Unexpected(
+              "Not implemented for the given vm_type"));
+      break;
+  }
+}
+
+}  // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/guest_os_wayland_server.h b/chrome/browser/ash/guest_os/guest_os_wayland_server.h
new file mode 100644
index 0000000..8baf703
--- /dev/null
+++ b/chrome/browser/ash/guest_os/guest_os_wayland_server.h
@@ -0,0 +1,25 @@
+// 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_ASH_GUEST_OS_GUEST_OS_WAYLAND_SERVER_H_
+#define CHROME_BROWSER_ASH_GUEST_OS_GUEST_OS_WAYLAND_SERVER_H_
+
+#include "base/callback_forward.h"
+#include "chrome/browser/ash/borealis/infra/expected.h"
+#include "chromeos/dbus/vm_launch/launch.pb.h"
+
+namespace guest_os {
+
+class GuestOsWaylandServer {
+ public:
+  static void StartServer(
+      const vm_tools::launch::StartWaylandServerRequest& request,
+      base::OnceCallback<
+          void(borealis::Expected<vm_tools::launch::StartWaylandServerResponse,
+                                  std::string>)> response_callback);
+};
+
+}  // namespace guest_os
+
+#endif  // CHROME_BROWSER_ASH_GUEST_OS_GUEST_OS_WAYLAND_SERVER_H_
diff --git a/chrome/browser/ash/input_method/assistive_suggester.cc b/chrome/browser/ash/input_method/assistive_suggester.cc
index 8177bf3..273abc06 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester.cc
@@ -234,20 +234,22 @@
          base::FeatureList::IsEnabled(features::kAssistMultiWordExpanded);
 }
 
-DisabledReason AssistiveSuggester::GetDisabledReasonForPersonalInfo() {
+DisabledReason AssistiveSuggester::GetDisabledReasonForPersonalInfo(
+    const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions) {
   if (!base::FeatureList::IsEnabled(features::kAssistPersonalInfo)) {
     return DisabledReason::kFeatureFlagOff;
   }
   if (!profile_->GetPrefs()->GetBoolean(prefs::kAssistPersonalInfoEnabled)) {
     return DisabledReason::kUserSettingsOff;
   }
-  if (!suggester_switch_->IsPersonalInfoSuggestionAllowed()) {
+  if (!enabled_suggestions.personal_info_suggestions) {
     return DisabledReason::kUrlOrAppNotAllowed;
   }
   return DisabledReason::kNone;
 }
 
-DisabledReason AssistiveSuggester::GetDisabledReasonForEmoji() {
+DisabledReason AssistiveSuggester::GetDisabledReasonForEmoji(
+    const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions) {
   if (!profile_->GetPrefs()->GetBoolean(
           prefs::kEmojiSuggestionEnterpriseAllowed)) {
     return DisabledReason::kEnterpriseSettingsOff;
@@ -255,7 +257,7 @@
   if (!profile_->GetPrefs()->GetBoolean(prefs::kEmojiSuggestionEnabled)) {
     return DisabledReason::kUserSettingsOff;
   }
-  if (!suggester_switch_->IsEmojiSuggestionAllowed()) {
+  if (!enabled_suggestions.emoji_suggestions) {
     return DisabledReason::kUrlOrAppNotAllowed;
   }
   return DisabledReason::kNone;
@@ -321,16 +323,15 @@
 }
 
 bool AssistiveSuggester::IsAssistiveTypeAllowedInBrowserContext(
-    AssistiveType type) {
-  // TODO(b/222218270): Replace all legacy suggester_switch->IsXAllowed() calls
-  // with GetEnabledSuggestions.
+    AssistiveType type,
+    const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions) {
   switch (GetAssistiveFeatureForType(type)) {
     case AssistiveFeature::kPersonalInfoSuggestion:
-      return suggester_switch_->IsPersonalInfoSuggestionAllowed();
+      return enabled_suggestions.personal_info_suggestions;
     case AssistiveFeature::kEmojiSuggestion:
-      return suggester_switch_->IsEmojiSuggestionAllowed();
+      return enabled_suggestions.emoji_suggestions;
     case AssistiveFeature::kMultiWordSuggestion:
-      return suggester_switch_->IsMultiWordSuggestionAllowed();
+      return enabled_suggestions.multi_word_suggestions;
     default:
       LOG(DFATAL) << "Unexpected AssistiveType value: "
                   << static_cast<int>(type);
@@ -425,18 +426,22 @@
 }
 
 void AssistiveSuggester::RecordAssistiveMatchMetricsForAssistiveType(
-    AssistiveType type) {
+    AssistiveType type,
+    const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions) {
   RecordAssistiveMatch(type);
   if (!IsAssistiveTypeEnabled(type)) {
     RecordAssistiveDisabled(type);
-  } else if (!IsAssistiveTypeAllowedInBrowserContext(type)) {
+  } else if (!IsAssistiveTypeAllowedInBrowserContext(type,
+                                                     enabled_suggestions)) {
     RecordAssistiveNotAllowed(type);
   }
 }
 
-void AssistiveSuggester::RecordAssistiveMatchMetrics(const std::u16string& text,
-                                                     int cursor_pos,
-                                                     int anchor_pos) {
+void AssistiveSuggester::RecordAssistiveMatchMetrics(
+    const std::u16string& text,
+    int cursor_pos,
+    int anchor_pos,
+    const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions) {
   int len = static_cast<int>(text.length());
   if (cursor_pos > 0 && cursor_pos <= len && cursor_pos == anchor_pos &&
       (cursor_pos == len || base::IsAsciiWhitespace(text[cursor_pos]))) {
@@ -446,15 +451,17 @@
     // Personal info suggestion match
     AssistiveType type = ProposePersonalInfoAssistiveAction(text_before_cursor);
     if (type != AssistiveType::kGenericAction) {
-      RecordAssistiveMatchMetricsForAssistiveType(type);
+      RecordAssistiveMatchMetricsForAssistiveType(type, enabled_suggestions);
       RecordAssistiveDisabledReasonForPersonalInfo(
-          GetDisabledReasonForPersonalInfo());
+          GetDisabledReasonForPersonalInfo(enabled_suggestions));
       // Emoji suggestion match
     } else if (emoji_suggester_.ShouldShowSuggestion(text_before_cursor)) {
-      RecordAssistiveMatchMetricsForAssistiveType(AssistiveType::kEmoji);
+      RecordAssistiveMatchMetricsForAssistiveType(AssistiveType::kEmoji,
+                                                  enabled_suggestions);
       base::RecordAction(
           base::UserMetricsAction("InputMethod.Assistive.EmojiSuggested"));
-      RecordAssistiveDisabledReasonForEmoji(GetDisabledReasonForEmoji());
+      RecordAssistiveDisabledReasonForEmoji(
+          GetDisabledReasonForEmoji(enabled_suggestions));
     }
   }
 }
@@ -487,9 +494,8 @@
     int cursor_pos,
     int anchor_pos,
     const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions) {
-  // TODO(b/225988020): Make record assistive match metrics take enabled
-  // suggestions.
-  RecordAssistiveMatchMetrics(text, cursor_pos, anchor_pos);
+  RecordAssistiveMatchMetrics(text, cursor_pos, anchor_pos,
+                              enabled_suggestions);
   if (!IsAssistiveFeatureEnabled() || context_id_ == -1)
     return;
 
diff --git a/chrome/browser/ash/input_method/assistive_suggester.h b/chrome/browser/ash/input_method/assistive_suggester.h
index f9554aa4..ab581d6b 100644
--- a/chrome/browser/ash/input_method/assistive_suggester.h
+++ b/chrome/browser/ash/input_method/assistive_suggester.h
@@ -120,17 +120,23 @@
 
   // Checks the text before cursor, emits metric if any assistive prefix is
   // matched.
-  void RecordAssistiveMatchMetrics(const std::u16string& text,
-                                   int cursor_pos,
-                                   int anchor_pos);
+  void RecordAssistiveMatchMetrics(
+      const std::u16string& text,
+      int cursor_pos,
+      int anchor_pos,
+      const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
 
-  void RecordAssistiveMatchMetricsForAssistiveType(AssistiveType type);
+  void RecordAssistiveMatchMetricsForAssistiveType(
+      AssistiveType type,
+      const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
 
   // Only the first applicable reason in DisabledReason enum is returned.
-  DisabledReason GetDisabledReasonForEmoji();
+  DisabledReason GetDisabledReasonForEmoji(
+      const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
 
   // Only the first applicable reason in DisabledReason enum is returned.
-  DisabledReason GetDisabledReasonForPersonalInfo();
+  DisabledReason GetDisabledReasonForPersonalInfo(
+      const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
 
   // Only the first applicable reason in DisabledReason enum is returned.
   DisabledReason GetDisabledReasonForMultiWord(
@@ -140,7 +146,9 @@
 
   bool IsAssistiveTypeEnabled(AssistiveType type);
 
-  bool IsAssistiveTypeAllowedInBrowserContext(AssistiveType type);
+  bool IsAssistiveTypeAllowedInBrowserContext(
+      AssistiveType type,
+      const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
 
   bool WithinGrammarFragment(int cursor_pos, int anchor_pos);
 
diff --git a/chrome/browser/ash/input_method/assistive_suggester_client_filter.cc b/chrome/browser/ash/input_method/assistive_suggester_client_filter.cc
index 96817f1a..a7af3694 100644
--- a/chrome/browser/ash/input_method/assistive_suggester_client_filter.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester_client_filter.cc
@@ -23,25 +23,23 @@
 namespace {
 
 const char* kAllowedDomainsForPersonalInfoSuggester[] = {
-    "discord.com",      "messenger.com",       "web.whatsapp.com",
-    "web.skype.com",    "duo.google.com",      "hangouts.google.com",
-    "chat.google.com",  "messages.google.com", "web.telegram.org",
-    "voice.google.com",
+    "discord.com",         "messenger.com",    "web.whatsapp.com",
+    "web.skype.com",       "duo.google.com",   "hangouts.google.com",
+    "messages.google.com", "web.telegram.org", "voice.google.com",
 };
 
 const char* kAllowedDomainsForEmojiSuggester[] = {
-    "discord.com",      "messenger.com",       "web.whatsapp.com",
-    "web.skype.com",    "duo.google.com",      "hangouts.google.com",
-    "chat.google.com",  "messages.google.com", "web.telegram.org",
-    "voice.google.com",
+    "discord.com",         "messenger.com",    "web.whatsapp.com",
+    "web.skype.com",       "duo.google.com",   "hangouts.google.com",
+    "messages.google.com", "web.telegram.org", "voice.google.com",
 };
 
 // TODO(b/3339115): Add web.skype.com back to the list after compatibility
 //    issues are solved.
 const char* kAllowedDomainsForMultiWordSuggester[] = {
-    "discord.com",         "messenger.com",       "web.whatsapp.com",
-    "duo.google.com",      "hangouts.google.com", "chat.google.com",
-    "messages.google.com", "web.telegram.org",    "voice.google.com",
+    "discord.com",      "messenger.com",       "web.whatsapp.com",
+    "duo.google.com",   "hangouts.google.com", "messages.google.com",
+    "web.telegram.org", "voice.google.com",
 };
 
 const char* kTestUrls[] = {
diff --git a/chrome/browser/ash/input_method/assistive_suggester_unittest.cc b/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
index dd403843..18e096ae 100644
--- a/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
+++ b/chrome/browser/ash/input_method/assistive_suggester_unittest.cc
@@ -654,6 +654,28 @@
                                        GetParam().expected_assistive_type, 1);
 }
 
+TEST_P(AssistiveSuggesterPersonalInfoTest,
+       ShouldRecordDisabledReasonWhenSwitchDisabled) {
+  assistive_suggester_ = std::make_unique<AssistiveSuggester>(
+      suggestion_handler_.get(), profile_.get(),
+      std::make_unique<FakeSuggesterSwitch>(EnabledSuggestions{
+          .personal_info_suggestions = false,
+      }),
+      personal_data_.get());
+  assistive_suggester_->OnActivate(kUsEnglishEngineId);
+  assistive_suggester_->OnFocus(5);
+
+  assistive_suggester_->OnSurroundingTextChanged(
+      GetParam().surrounding_text, GetParam().surrounding_text.length(),
+      GetParam().surrounding_text.length());
+
+  histogram_tester_.ExpectTotalCount(
+      "InputMethod.Assistive.Disabled.PersonalInfo", 1);
+  histogram_tester_.ExpectUniqueSample(
+      "InputMethod.Assistive.Disabled.PersonalInfo",
+      DisabledReason::kUrlOrAppNotAllowed, 1);
+}
+
 TEST_P(AssistiveSuggesterPersonalInfoTest, ShouldReturnPrefixBasedSuggestions) {
   assistive_suggester_->OnActivate(kUsEnglishEngineId);
   assistive_suggester_->OnFocus(5);
@@ -1066,6 +1088,26 @@
                                        AssistiveType::kEmoji, 1);
 }
 
+TEST_F(AssistiveSuggesterEmojiTest,
+       ShouldRecordDisabledReasonWhenSwitchDisabled) {
+  assistive_suggester_ = std::make_unique<AssistiveSuggester>(
+      suggestion_handler_.get(), profile_.get(),
+      std::make_unique<FakeSuggesterSwitch>(EnabledSuggestions{
+          .emoji_suggestions = false,
+      }),
+      nullptr);
+  assistive_suggester_->get_emoji_suggester_for_testing()
+      ->LoadEmojiMapForTesting(kEmojiData);
+  assistive_suggester_->OnActivate(kUsEnglishEngineId);
+  assistive_suggester_->OnFocus(5);
+
+  assistive_suggester_->OnSurroundingTextChanged(u"arrow ", 6, 6);
+
+  histogram_tester_.ExpectTotalCount("InputMethod.Assistive.Disabled.Emoji", 1);
+  histogram_tester_.ExpectUniqueSample("InputMethod.Assistive.Disabled.Emoji",
+                                       DisabledReason::kUrlOrAppNotAllowed, 1);
+}
+
 TEST_F(AssistiveSuggesterEmojiTest, ShouldReturnPrefixBasedEmojiSuggestions) {
   assistive_suggester_->OnActivate(kUsEnglishEngineId);
   assistive_suggester_->OnFocus(5);
diff --git a/chrome/browser/ash/input_method/ime_rules_config.h b/chrome/browser/ash/input_method/ime_rules_config.h
index 2438d9c..759715f 100644
--- a/chrome/browser/ash/input_method/ime_rules_config.h
+++ b/chrome/browser/ash/input_method/ime_rules_config.h
@@ -49,10 +49,19 @@
   std::vector<std::string> rule_auto_correct_domain_denylist_;
   // The default denylist of domains that will turn off auto_correct feature.
   std::vector<std::string> default_auto_correct_domain_denylist_{
-      "amazon.com",           "b.corp.google.com", "buganizer.corp.google.com",
-      "classroom.google.com", "desmos.com",        "docs.google.com",
-      "facebook.com",         "instagram.com",     "outlook.live.com",
-      "outlook.office.com",   "quizlet.com",       "whatsapp.com",
+      "amazon.com",
+      "b.corp.google.com",
+      "buganizer.corp.google.com",
+      "cider.corp.google.com",
+      "classroom.google.com",
+      "desmos.com",
+      "docs.google.com",
+      "facebook.com",
+      "instagram.com",
+      "outlook.live.com",
+      "outlook.office.com",
+      "quizlet.com",
+      "whatsapp.com",
       "youtube.com",
   };
 };
diff --git a/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc b/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc
index efd1a7a..868d901 100644
--- a/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/web_applications/media_app/media_web_app_info.h"
-#include "base/containers/span.h"
 
 #include <memory>
 #include <string>
@@ -11,7 +10,9 @@
 #include "ash/constants/ash_features.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/webui/grit/ash_media_app_resources.h"
+#include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/media_app_ui/url_constants.h"
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
@@ -19,6 +20,7 @@
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chromeos/grit/chromeos_media_app_bundle_resources.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -137,19 +139,41 @@
   info->scope = GURL(ash::kChromeUIMediaAppURL);
 
   info->title = l10n_util::GetStringUTF16(IDS_MEDIA_APP_APP_NAME);
-  web_app::CreateIconInfoForSystemWebApp(
-      info->start_url,
-      {
-          {"app_icon_16.png", 16, IDR_MEDIA_APP_GALLERY_ICON_16_PNG},
-          {"app_icon_32.png", 32, IDR_MEDIA_APP_GALLERY_ICON_32_PNG},
-          {"app_icon_48.png", 48, IDR_MEDIA_APP_GALLERY_ICON_48_PNG},
-          {"app_icon_64.png", 64, IDR_MEDIA_APP_GALLERY_ICON_64_PNG},
-          {"app_icon_96.png", 96, IDR_MEDIA_APP_GALLERY_ICON_96_PNG},
-          {"app_icon_128.png", 128, IDR_MEDIA_APP_GALLERY_ICON_128_PNG},
-          {"app_icon_192.png", 192, IDR_MEDIA_APP_GALLERY_ICON_192_PNG},
-          {"app_icon_256.png", 256, IDR_MEDIA_APP_GALLERY_ICON_256_PNG},
-      },
-      *info);
+
+  bool app_icons_added = false;
+  if (base::FeatureList::IsEnabled(chromeos::features::kMediaAppHandlesPdf)) {
+#if BUILDFLAG(ENABLE_CROS_MEDIA_APP)
+    web_app::CreateIconInfoForSystemWebApp(
+        info->start_url,
+        {
+            {"app_icon_16.png", 16, IDR_MEDIA_APP_APP_ICON_16_PNG},
+            {"app_icon_32.png", 32, IDR_MEDIA_APP_APP_ICON_32_PNG},
+            {"app_icon_48.png", 48, IDR_MEDIA_APP_APP_ICON_48_PNG},
+            {"app_icon_64.png", 64, IDR_MEDIA_APP_APP_ICON_64_PNG},
+            {"app_icon_96.png", 96, IDR_MEDIA_APP_APP_ICON_96_PNG},
+            {"app_icon_128.png", 128, IDR_MEDIA_APP_APP_ICON_128_PNG},
+            {"app_icon_192.png", 192, IDR_MEDIA_APP_APP_ICON_192_PNG},
+            {"app_icon_256.png", 256, IDR_MEDIA_APP_APP_ICON_256_PNG},
+        },
+        *info);
+    app_icons_added = true;
+#endif  // BUILDFLAG(ENABLE_CROS_MEDIA_APP)
+  }
+  if (!app_icons_added) {
+    web_app::CreateIconInfoForSystemWebApp(
+        info->start_url,
+        {
+            {"app_icon_16.png", 16, IDR_MEDIA_APP_GALLERY_ICON_16_PNG},
+            {"app_icon_32.png", 32, IDR_MEDIA_APP_GALLERY_ICON_32_PNG},
+            {"app_icon_48.png", 48, IDR_MEDIA_APP_GALLERY_ICON_48_PNG},
+            {"app_icon_64.png", 64, IDR_MEDIA_APP_GALLERY_ICON_64_PNG},
+            {"app_icon_96.png", 96, IDR_MEDIA_APP_GALLERY_ICON_96_PNG},
+            {"app_icon_128.png", 128, IDR_MEDIA_APP_GALLERY_ICON_128_PNG},
+            {"app_icon_192.png", 192, IDR_MEDIA_APP_GALLERY_ICON_192_PNG},
+            {"app_icon_256.png", 256, IDR_MEDIA_APP_GALLERY_ICON_256_PNG},
+        },
+        *info);
+  }
 
   if (chromeos::features::IsDarkLightModeEnabled()) {
     auto* color_provider = ash::AshColorProvider::Get();
diff --git a/chrome/browser/chrome_worker_browsertest.cc b/chrome/browser/chrome_worker_browsertest.cc
index 297d290..cf8b7dd 100644
--- a/chrome/browser/chrome_worker_browsertest.cc
+++ b/chrome/browser/chrome_worker_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/containers/contains.h"
+#include "base/feature_list.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
@@ -25,6 +26,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/re2/src/re2/re2.h"
 
 namespace {
@@ -259,7 +261,9 @@
     std::string minor_version;
     EXPECT_TRUE(re2::RE2::PartialMatch(user_agent_value, kChromeVersionRegex,
                                        &minor_version));
-    if (expected_user_agent_reduced) {
+    if (expected_user_agent_reduced ||
+        base::FeatureList::IsEnabled(
+            blink::features::kReduceUserAgentMinorVersion)) {
       EXPECT_EQ(minor_version, kReducedMinorVersion);
     } else {
       EXPECT_NE(minor_version, kReducedMinorVersion);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 5e6280a..7a6aff4 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -153,6 +153,7 @@
     "//ash/webui/firmware_update_ui",
     "//ash/webui/help_app_ui",
     "//ash/webui/media_app_ui",
+    "//ash/webui/media_app_ui:buildflags",
     "//ash/webui/os_feedback_ui",
     "//ash/webui/personalization_app",
     "//ash/webui/personalization_app",
@@ -167,6 +168,7 @@
     "//ash/webui/resources:eche_bundle_resources_grit",
     "//ash/webui/resources:firmware_update_app_resources_grit",
     "//ash/webui/resources:help_app_resources_grit",
+    "//ash/webui/resources:media_app_bundle_resources_grit",
     "//ash/webui/resources:media_app_resources_grit",
     "//ash/webui/resources:os_feedback_resources_grit",
     "//ash/webui/resources:os_feedback_untrusted_resources_grit",
@@ -1686,6 +1688,8 @@
     "../ash/guest_os/guest_os_share_path_factory.h",
     "../ash/guest_os/guest_os_stability_monitor.cc",
     "../ash/guest_os/guest_os_stability_monitor.h",
+    "../ash/guest_os/guest_os_wayland_server.cc",
+    "../ash/guest_os/guest_os_wayland_server.h",
     "../ash/guest_os/infra/cached_callback.h",
     "../ash/guest_os/public/guest_os_mount_provider.cc",
     "../ash/guest_os/public/guest_os_mount_provider.h",
@@ -3612,12 +3616,16 @@
     "extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h",
     "extensions/login_screen/login/cleanup/cleanup_manager.cc",
     "extensions/login_screen/login/cleanup/cleanup_manager.h",
+    "extensions/login_screen/login/cleanup/cleanup_manager_ash.cc",
+    "extensions/login_screen/login/cleanup/cleanup_manager_ash.h",
     "extensions/login_screen/login/cleanup/clipboard_cleanup_handler.cc",
     "extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h",
     "extensions/login_screen/login/cleanup/extension_cleanup_handler.cc",
     "extensions/login_screen/login/cleanup/extension_cleanup_handler.h",
     "extensions/login_screen/login/cleanup/files_cleanup_handler.cc",
     "extensions/login_screen/login/cleanup/files_cleanup_handler.h",
+    "extensions/login_screen/login/cleanup/lacros_cleanup_handler.cc",
+    "extensions/login_screen/login/cleanup/lacros_cleanup_handler.h",
     "extensions/login_screen/login/cleanup/open_windows_cleanup_handler.cc",
     "extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h",
     "extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.cc",
@@ -4801,10 +4809,11 @@
     "extensions/install_limiter_unittest.cc",
     "extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc",
     "extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc",
+    "extensions/login_screen/login/cleanup/lacros_cleanup_handler_unittest.cc",
     "extensions/login_screen/login/cleanup/mock_cleanup_handler.cc",
     "extensions/login_screen/login/cleanup/mock_cleanup_handler.h",
     "extensions/login_screen/login/cleanup/print_jobs_cleanup_handler_unittest.cc",
-    "extensions/login_screen/login/login_api_unittest.cc",
+    "extensions/login_screen/login/login_api_ash_unittest.cc",
     "extensions/login_screen/login_screen_ui/ui_handler_unittest.cc",
     "extensions/permissions_updater_delegate_chromeos_unittest.cc",
     "extensions/public_session_permission_helper_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index d2221ca..dfc76db 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -1218,6 +1218,9 @@
     case api::file_manager_private::RECENT_FILE_TYPE_VIDEO:
       file_type = chromeos::RecentModel::FileType::kVideo;
       break;
+    case api::file_manager_private::RECENT_FILE_TYPE_DOCUMENT:
+      file_type = chromeos::RecentModel::FileType::kDocument;
+      break;
     default:
       NOTREACHED();
       return RespondNow(Error("Unknown recent file type is specified."));
diff --git a/chrome/browser/chromeos/extensions/login_screen/BUILD.gn b/chrome/browser/chromeos/extensions/login_screen/BUILD.gn
index 0c2d966..8d3f439 100644
--- a/chrome/browser/chromeos/extensions/login_screen/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/login_screen/BUILD.gn
@@ -36,7 +36,23 @@
   }
 
   if (is_chromeos_lacros) {
-    deps += [ "//chromeos/lacros" ]
+    sources += [
+      "login/cleanup/browsing_data_cleanup_handler.cc",
+      "login/cleanup/browsing_data_cleanup_handler.h",
+      "login/cleanup/cleanup_handler.h",
+      "login/cleanup/cleanup_manager.cc",
+      "login/cleanup/cleanup_manager.h",
+      "login/cleanup/cleanup_manager_lacros.cc",
+      "login/cleanup/cleanup_manager_lacros.h",
+      "login/cleanup/cleanup_manager_lacros_factory.cc",
+      "login/cleanup/cleanup_manager_lacros_factory.h",
+    ]
+    deps += [
+      "//chrome/browser/browsing_data:constants",
+      "//chrome/browser/profiles:profile",
+      "//chromeos/lacros",
+      "//components/keyed_service/content",
+    ]
   }
 }
 
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.cc
index 7299ef8a..02af95f 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.cc
@@ -15,47 +15,14 @@
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h"
-
-#include "base/logging.h"
+#include "mojo/public/cpp/bindings/remote.h"
 
 namespace chromeos {
 
-namespace {
-
-// Must kept in sync with the CleanupHandler variant in
-// tools/metrics/histograms/metadata/enterprise/histograms.xml
-constexpr char kBrowsingDataCleanupHandlerHistogramName[] = "BrowsingData";
-constexpr char kClipboardCleanupHandlerHistogramName[] = "Clipboard";
-constexpr char kExtensionCleanupHandlerHistogramName[] = "Extension";
-constexpr char kFilesCleanupHandlerHistogramName[] = "Files";
-constexpr char kOpenWindowsCleanupHandlerHistogramName[] = "OpenWindows";
-constexpr char kPrintJobsCleanupHandlerHistogramName[] = "PrintJobs";
-
-void RecordHandlerMetrics(const std::string& handler_name,
-                          const base::Time& start_time,
-                          bool success) {
-  base::TimeDelta delta = base::Time::Now() - start_time;
-
-  std::string histogram_prefix =
-      "Enterprise.LoginApiCleanup." + handler_name + ".";
-
-  base::UmaHistogramTimes(histogram_prefix + "Timing", delta);
-  base::UmaHistogramBoolean(histogram_prefix + "Success", success);
-}
-
-}  // namespace
-
-// static
-CleanupManager* CleanupManager::Get() {
-  static base::NoDestructor<CleanupManager> instance;
-  return instance.get();
-}
-
-CleanupManager::CleanupManager() {
-  InitializeCleanupHandlers();
-}
+CleanupManager::CleanupManager() = default;
 
 CleanupManager::~CleanupManager() = default;
 
@@ -65,6 +32,9 @@
     return;
   }
 
+  if (cleanup_handlers_.empty())
+    InitializeCleanupHandlers();
+
   callback_ = std::move(callback);
   errors_.clear();
   is_cleanup_in_progress_ = true;
@@ -72,13 +42,13 @@
   base::RepeatingClosure barrier_closure = base::BarrierClosure(
       cleanup_handlers_.size(),
       base::BindOnce(&CleanupManager::OnAllCleanupHandlersDone,
-                     base::Unretained(this)));
+                     weak_factory_.GetWeakPtr()));
 
   start_time_ = base::Time::Now();
   for (auto& kv : cleanup_handlers_) {
     kv.second->Cleanup(base::BindOnce(&CleanupManager::OnCleanupHandlerDone,
-                                      base::Unretained(this), barrier_closure,
-                                      kv.first));
+                                      weak_factory_.GetWeakPtr(),
+                                      barrier_closure, kv.first));
   }
 }
 
@@ -98,21 +68,6 @@
   is_cleanup_in_progress_ = is_cleanup_in_progress;
 }
 
-void CleanupManager::InitializeCleanupHandlers() {
-  cleanup_handlers_.insert({kBrowsingDataCleanupHandlerHistogramName,
-                            std::make_unique<BrowsingDataCleanupHandler>()});
-  cleanup_handlers_.insert({kOpenWindowsCleanupHandlerHistogramName,
-                            std::make_unique<OpenWindowsCleanupHandler>()});
-  cleanup_handlers_.insert({kFilesCleanupHandlerHistogramName,
-                            std::make_unique<FilesCleanupHandler>()});
-  cleanup_handlers_.insert({kClipboardCleanupHandlerHistogramName,
-                            std::make_unique<ClipboardCleanupHandler>()});
-  cleanup_handlers_.insert({kPrintJobsCleanupHandlerHistogramName,
-                            std::make_unique<PrintJobsCleanupHandler>()});
-  cleanup_handlers_.insert({kExtensionCleanupHandlerHistogramName,
-                            std::make_unique<ExtensionCleanupHandler>()});
-}
-
 void CleanupManager::OnCleanupHandlerDone(
     base::RepeatingClosure barrier_closure,
     const std::string& handler_name,
@@ -138,4 +93,16 @@
   std::move(callback_).Run(errors);
 }
 
+void CleanupManager::RecordHandlerMetrics(const std::string& handler_name,
+                                          const base::Time& start_time,
+                                          bool success) {
+  base::TimeDelta delta = base::Time::Now() - start_time;
+
+  std::string histogram_prefix =
+      "Enterprise.LoginApiCleanup." + handler_name + ".";
+
+  base::UmaHistogramTimes(histogram_prefix + "Timing", delta);
+  base::UmaHistogramBoolean(histogram_prefix + "Success", success);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h
index 02442f7..ea1acac 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h
@@ -12,22 +12,29 @@
 
 #include "base/callback.h"
 #include "base/callback_forward.h"
-#include "base/no_destructor.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_handler.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos {
 
-// A singleton which manages the cleanup handlers.
+// Common interface between `CleanupManagerAsh` and `CleanupManagerLacros`.
+// Performs cleanup operations for the chrome.login.endSharedSession()
+// extension API. A `CleanupManager` owns several `CleanupHandler`s. The
+// individual handlers are in charge of cleaning up a specific
+// component/service.
 class CleanupManager {
  public:
-  static CleanupManager* Get();
+  CleanupManager();
 
   CleanupManager(const CleanupManager&) = delete;
   CleanupManager& operator=(const CleanupManager&) = delete;
 
-  using CleanupCallback = base::OnceCallback<void(absl::optional<std::string>)>;
+  virtual ~CleanupManager();
+
+  using CleanupCallback =
+      base::OnceCallback<void(const absl::optional<std::string>& error)>;
   // Calls the cleanup handlers  and runs `callback` when the cleanup has
   // finished. After `Cleanup` is called and before `callback` is run,
   // `is_cleanup_in_progress()` returns true. Fails if there is another cleanup
@@ -43,13 +50,9 @@
 
   void SetIsCleanupInProgressForTesting(bool is_cleanup_in_progress);
 
- private:
-  friend class base::NoDestructor<CleanupManager>;
-
-  CleanupManager();
-  ~CleanupManager();
-
-  void InitializeCleanupHandlers();
+ protected:
+  // Overridden to initialize `CleanupHandler`s for Ash and Lacros.
+  virtual void InitializeCleanupHandlers() = 0;
 
   void OnCleanupHandlerDone(base::RepeatingClosure barrier_closure,
                             const std::string& handler_name,
@@ -57,12 +60,17 @@
 
   void OnAllCleanupHandlersDone();
 
+  void RecordHandlerMetrics(const std::string& handler_name,
+                            const base::Time& start_time,
+                            bool success);
+
   // Map of handler name to handler.
   std::map<std::string, std::unique_ptr<CleanupHandler>> cleanup_handlers_;
   std::vector<std::string> errors_;
   bool is_cleanup_in_progress_ = false;
   base::Time start_time_;
   CleanupCallback callback_;
+  base::WeakPtrFactory<CleanupManager> weak_factory_{this};
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.cc
new file mode 100644
index 0000000..e9185215
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.cc
@@ -0,0 +1,62 @@
+// 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/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h"
+
+#include <utility>
+
+#include "base/no_destructor.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/clipboard_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/extension_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/open_windows_cleanup_handler.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/print_jobs_cleanup_handler.h"
+
+namespace chromeos {
+
+namespace {
+
+// Must kept in sync with the CleanupHandler variant in
+// tools/metrics/histograms/metadata/enterprise/histograms.xml
+constexpr char kBrowsingDataCleanupHandlerHistogramName[] = "BrowsingData";
+constexpr char kClipboardCleanupHandlerHistogramName[] = "Clipboard";
+constexpr char kExtensionCleanupHandlerHistogramName[] = "Extension";
+constexpr char kFilesCleanupHandlerHistogramName[] = "Files";
+constexpr char kLacrosCleanupHandlerHistogramName[] = "Lacros";
+constexpr char kOpenWindowsCleanupHandlerHistogramName[] = "OpenWindows";
+constexpr char kPrintJobsCleanupHandlerHistogramName[] = "PrintJobs";
+
+}  // namespace
+
+// static
+CleanupManagerAsh* CleanupManagerAsh::Get() {
+  static base::NoDestructor<CleanupManagerAsh> instance;
+  return instance.get();
+}
+
+CleanupManagerAsh::CleanupManagerAsh() = default;
+
+CleanupManagerAsh::~CleanupManagerAsh() = default;
+
+void CleanupManagerAsh::InitializeCleanupHandlers() {
+  cleanup_handlers_.insert({kBrowsingDataCleanupHandlerHistogramName,
+                            std::make_unique<BrowsingDataCleanupHandler>()});
+  cleanup_handlers_.insert({kOpenWindowsCleanupHandlerHistogramName,
+                            std::make_unique<OpenWindowsCleanupHandler>()});
+  cleanup_handlers_.insert({kFilesCleanupHandlerHistogramName,
+                            std::make_unique<FilesCleanupHandler>()});
+  cleanup_handlers_.insert({kLacrosCleanupHandlerHistogramName,
+                            std::make_unique<LacrosCleanupHandler>()});
+  cleanup_handlers_.insert({kClipboardCleanupHandlerHistogramName,
+                            std::make_unique<ClipboardCleanupHandler>()});
+  cleanup_handlers_.insert({kPrintJobsCleanupHandlerHistogramName,
+                            std::make_unique<PrintJobsCleanupHandler>()});
+  cleanup_handlers_.insert({kExtensionCleanupHandlerHistogramName,
+                            std::make_unique<ExtensionCleanupHandler>()});
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h
new file mode 100644
index 0000000..9fb9b9f1
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h
@@ -0,0 +1,34 @@
+// 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_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_ASH_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_ASH_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+// A singleton which manages the cleanup handlers in Ash.
+class CleanupManagerAsh : public CleanupManager {
+ public:
+  static CleanupManagerAsh* Get();
+
+  CleanupManagerAsh(const CleanupManagerAsh&) = delete;
+  CleanupManagerAsh& operator=(const CleanupManagerAsh&) = delete;
+
+ private:
+  friend class base::NoDestructor<CleanupManagerAsh>;
+
+  CleanupManagerAsh();
+  ~CleanupManagerAsh() override;
+
+  // CleanupManager:
+  void InitializeCleanupHandlers() override;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_ASH_H_
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.cc
new file mode 100644
index 0000000..47705c0
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.cc
@@ -0,0 +1,46 @@
+// 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/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.h"
+
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/browsing_data_cleanup_handler.h"
+#include "chromeos/lacros/lacros_service.h"
+#include "content/public/browser/browser_context.h"
+
+namespace chromeos {
+
+namespace {
+
+// Must kept in sync with the CleanupHandler variant in
+// tools/metrics/histograms/metadata/enterprise/histograms.xml
+constexpr char kLacrosBrowsingDataCleanupHandlerHistogramName[] =
+    "LacrosBrowsingData";
+
+}  // namespace
+
+CleanupManagerLacros::CleanupManagerLacros(
+    content::BrowserContext* browser_context) {
+  chromeos::LacrosService* lacros_service = chromeos::LacrosService::Get();
+  DCHECK(lacros_service);
+  if (LacrosService::Get()->IsAvailable<crosapi::mojom::Login>()) {
+    lacros_service->GetRemote<crosapi::mojom::Login>()
+        ->AddLacrosCleanupTriggeredObserver(
+            receiver_.BindNewPipeAndPassRemoteWithVersion());
+  }
+}
+
+CleanupManagerLacros::~CleanupManagerLacros() = default;
+
+void CleanupManagerLacros::InitializeCleanupHandlers() {
+  // TODO(jityao, b:217155485): Add ExtensionCleanupHandler.
+  cleanup_handlers_.insert({kLacrosBrowsingDataCleanupHandlerHistogramName,
+                            std::make_unique<BrowsingDataCleanupHandler>()});
+}
+
+void CleanupManagerLacros::OnLacrosCleanupTriggered(
+    OnLacrosCleanupTriggeredCallback callback) {
+  Cleanup(std::move(callback));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.h
new file mode 100644
index 0000000..e8df1cb
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.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_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_LACROS_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_LACROS_H_
+
+#include "base/callback.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h"
+#include "chromeos/crosapi/mojom/login.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace chromeos {
+
+// A KeyedService which manages the cleanup handlers in Lacros.
+class CleanupManagerLacros
+    : public CleanupManager,
+      public crosapi::mojom::LacrosCleanupTriggeredObserver,
+      public KeyedService {
+ public:
+  explicit CleanupManagerLacros(content::BrowserContext* browser_context);
+
+  CleanupManagerLacros(const CleanupManagerLacros&) = delete;
+  CleanupManagerLacros& operator=(const CleanupManagerLacros&) = delete;
+
+  ~CleanupManagerLacros() override;
+
+  // crosapi::mojom::LacrosTriggeredObserver:
+  void OnLacrosCleanupTriggered(
+      OnLacrosCleanupTriggeredCallback callback) override;
+
+ private:
+  void InitializeCleanupHandlers() override;
+
+  mojo::Receiver<crosapi::mojom::LacrosCleanupTriggeredObserver> receiver_{
+      this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_LACROS_H_
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.cc
new file mode 100644
index 0000000..d281faa
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.cc
@@ -0,0 +1,53 @@
+// 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/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.h"
+
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace chromeos {
+
+// static
+CleanupManagerLacros* CleanupManagerLacrosFactory::GetForBrowserContext(
+    content::BrowserContext* browser_context) {
+  return static_cast<CleanupManagerLacros*>(
+      GetInstance()->GetServiceForBrowserContext(browser_context,
+                                                 /* create= */ true));
+}
+
+// static
+CleanupManagerLacrosFactory* CleanupManagerLacrosFactory::GetInstance() {
+  static base::NoDestructor<CleanupManagerLacrosFactory> instance;
+  return instance.get();
+}
+
+CleanupManagerLacrosFactory::CleanupManagerLacrosFactory()
+    : BrowserContextKeyedServiceFactory(
+          "CleanupManagerLacros",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+CleanupManagerLacrosFactory::~CleanupManagerLacrosFactory() = default;
+
+KeyedService* CleanupManagerLacrosFactory::BuildServiceInstanceFor(
+    content::BrowserContext* browser_context) const {
+  return new CleanupManagerLacros(browser_context);
+}
+
+content::BrowserContext* CleanupManagerLacrosFactory::GetBrowserContextToUse(
+    content::BrowserContext* browser_context) const {
+  // Service is available for incognito profiles.
+  return Profile::FromBrowserContext(browser_context);
+}
+
+bool CleanupManagerLacrosFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
+bool CleanupManagerLacrosFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.h
new file mode 100644
index 0000000..7a3538f
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.h
@@ -0,0 +1,44 @@
+// 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_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_LACROS_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_LACROS_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace chromeos {
+
+class CleanupManagerLacros;
+
+// Factory for the `CleanupManagerLacros` KeyedService.
+class CleanupManagerLacrosFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static CleanupManagerLacros* GetForBrowserContext(
+      content::BrowserContext* browser_context);
+
+  static CleanupManagerLacrosFactory* GetInstance();
+
+  CleanupManagerLacrosFactory(const CleanupManagerLacrosFactory&) = delete;
+  CleanupManagerLacrosFactory& operator=(const CleanupManagerLacrosFactory&) =
+      delete;
+
+ private:
+  friend class base::NoDestructor<CleanupManagerLacrosFactory>;
+
+  CleanupManagerLacrosFactory();
+  ~CleanupManagerLacrosFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* browser_context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* browser_context) const override;
+  bool ServiceIsNULLWhileTesting() const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_CLEANUP_MANAGER_LACROS_FACTORY_H_
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_unittest.cc
new file mode 100644
index 0000000..127f306
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_unittest.cc
@@ -0,0 +1,100 @@
+// 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/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/mock_cleanup_handler.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/crosapi/mojom/login.mojom.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::WithArg;
+
+namespace extensions {
+
+class CleanupManagerLacrosUnittest : public testing::Test {
+ public:
+  CleanupManagerLacrosUnittest() = default;
+  ~CleanupManagerLacrosUnittest() override = default;
+
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    TestingProfile::Builder builder;
+    builder.SetPath(base::FilePath(FILE_PATH_LITERAL(chrome::kInitialProfile)));
+    profile_ = builder.Build();
+
+    manager_ = CleanupManagerLacros::GetFactoryInstance()->Get(profile_.get());
+  }
+
+  void TearDown() override {
+    manager_->ResetCleanupHandlersForTesting();
+    testing::Test::TearDown();
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  CleanupManagerLacros* manager_;
+  std::unique_ptr<Profile> profile_;
+};
+
+TEST_F(CleanupManagerLacrosUnittest, Cleanup) {
+  auto no_error_callback =
+      [](chromeos::CleanupHandler::CleanupHandlerCallback callback) {
+        std::move(callback).Run(absl::nullopt);
+      };
+  std::unique_ptr<chromeos::MockCleanupHandler> mock_cleanup_handler =
+      std::make_unique<chromeos::MockCleanupHandler>();
+  EXPECT_CALL(*mock_cleanup_handler, Cleanup(_))
+      .WillOnce(WithArg<0>(Invoke(no_error_callback)));
+
+  std::map<std::string, std::unique_ptr<chromeos::CleanupHandler>>
+      cleanup_handlers;
+  cleanup_handlers.insert({"Handler", std::move(mock_cleanup_handler)});
+  manager_->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
+
+  base::RunLoop run_loop;
+  manager_->Cleanup(
+      base::BindLambdaForTesting([&](const absl::optional<std::string>& error) {
+        EXPECT_EQ(absl::nullopt, error);
+        run_loop.QuitClosure().Run();
+      }));
+  run_loop.Run();
+}
+
+TEST_F(CleanupManagerLacrosUnittest, CleanupError) {
+  auto error_callback =
+      [](chromeos::CleanupHandler::CleanupHandlerCallback callback) {
+        std::move(callback).Run("Error");
+      };
+  std::unique_ptr<chromeos::MockCleanupHandler> mock_cleanup_handler =
+      std::make_unique<chromeos::MockCleanupHandler>();
+  EXPECT_CALL(*mock_cleanup_handler, Cleanup(_))
+      .WillOnce(WithArg<0>(Invoke(error_callback)));
+
+  std::map<std::string, std::unique_ptr<chromeos::CleanupHandler>>
+      cleanup_handlers;
+  cleanup_handlers.insert({"Handler", std::move(mock_cleanup_handler)});
+  manager_->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
+
+  base::RunLoop run_loop;
+  manager_->Cleanup(
+      base::BindLambdaForTesting([&](const absl::optional<std::string>& error) {
+        EXPECT_EQ("Handler: Error", error);
+        run_loop.QuitClosure().Run();
+      }));
+  run_loop.Run();
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc
index 186c5d8..4b52c36b 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/test_future.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/mock_cleanup_handler.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -28,6 +29,14 @@
 constexpr char kHandler1Name[] = "Handler1";
 constexpr char kHandler2Name[] = "Handler2";
 
+class TestCleanupManager : public CleanupManager {
+ public:
+  TestCleanupManager() = default;
+  ~TestCleanupManager() override = default;
+
+  void InitializeCleanupHandlers() override {}
+};
+
 }  // namespace
 
 class CleanupManagerUnittest : public testing::Test {
@@ -35,12 +44,18 @@
   CleanupManagerUnittest() = default;
   ~CleanupManagerUnittest() override = default;
 
+  void SetUp() override {
+    testing::Test::SetUp();
+    manager_ = std::make_unique<TestCleanupManager>();
+  }
+
   void TearDown() override {
-    CleanupManager::Get()->ResetCleanupHandlersForTesting();
+    manager_.reset();
     testing::Test::TearDown();
   }
 
   content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<TestCleanupManager> manager_;
 };
 
 TEST_F(CleanupManagerUnittest, Cleanup) {
@@ -61,16 +76,11 @@
   std::map<std::string, std::unique_ptr<CleanupHandler>> cleanup_handlers;
   cleanup_handlers.insert({kHandler1Name, std::move(mock_cleanup_handler1)});
   cleanup_handlers.insert({kHandler2Name, std::move(mock_cleanup_handler2)});
-  CleanupManager* manager = CleanupManager::Get();
-  manager->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
+  manager_->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
 
-  base::RunLoop run_loop;
-  manager->Cleanup(
-      base::BindLambdaForTesting([&](absl::optional<std::string> error) {
-        EXPECT_EQ(absl::nullopt, error);
-        run_loop.QuitClosure().Run();
-      }));
-  run_loop.Run();
+  base::test::TestFuture<absl::optional<std::string>> future;
+  manager_->Cleanup(future.GetCallback<const absl::optional<std::string>&>());
+  EXPECT_EQ(absl::nullopt, future.Get());
 
   histogram_tester.ExpectBucketCount(
       "Enterprise.LoginApiCleanup.Handler1.Success", 1, 1);
@@ -94,21 +104,20 @@
 
   std::map<std::string, std::unique_ptr<CleanupHandler>> cleanup_handlers;
   cleanup_handlers.insert({kHandler1Name, std::move(mock_cleanup_handler)});
-  CleanupManager* manager = CleanupManager::Get();
-  manager->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
+  manager_->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
 
   base::RunLoop run_loop;
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
 
-  manager->Cleanup(
-      base::BindLambdaForTesting([&](absl::optional<std::string> error) {
+  manager_->Cleanup(
+      base::BindLambdaForTesting([&](const absl::optional<std::string>& error) {
         EXPECT_EQ(absl::nullopt, error);
         barrier_closure.Run();
       }));
 
-  manager->Cleanup(
-      base::BindLambdaForTesting([&](absl::optional<std::string> error) {
+  manager_->Cleanup(
+      base::BindLambdaForTesting([&](const absl::optional<std::string>& error) {
         EXPECT_EQ("Cleanup is already in progress", *error);
         barrier_closure.Run();
       }));
@@ -138,16 +147,11 @@
   std::map<std::string, std::unique_ptr<CleanupHandler>> cleanup_handlers;
   cleanup_handlers.insert({kHandler1Name, std::move(mock_cleanup_handler1)});
   cleanup_handlers.insert({kHandler2Name, std::move(mock_cleanup_handler2)});
-  CleanupManager* manager = CleanupManager::Get();
-  manager->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
+  manager_->SetCleanupHandlersForTesting(std::move(cleanup_handlers));
 
-  base::RunLoop run_loop;
-  manager->Cleanup(
-      base::BindLambdaForTesting([&](absl::optional<std::string> error) {
-        EXPECT_EQ("Handler1: Error 1\nHandler2: Error 2", *error);
-        run_loop.QuitClosure().Run();
-      }));
-  run_loop.Run();
+  base::test::TestFuture<absl::optional<std::string>> future;
+  manager_->Cleanup(future.GetCallback<const absl::optional<std::string>&>());
+  EXPECT_EQ("Handler1: Error 1\nHandler2: Error 2", future.Get());
 
   histogram_tester.ExpectBucketCount(
       "Enterprise.LoginApiCleanup.Handler1.Success", 0, 1);
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.cc
new file mode 100644
index 0000000..d40daa9
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.cc
@@ -0,0 +1,108 @@
+// 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/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/ash/crosapi/crosapi_ash.h"
+#include "chrome/browser/ash/crosapi/crosapi_manager.h"
+#include "chrome/browser/ash/crosapi/login_ash.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+LacrosCleanupHandler::LacrosCleanupHandler() = default;
+LacrosCleanupHandler::~LacrosCleanupHandler() = default;
+
+void LacrosCleanupHandler::Cleanup(CleanupHandlerCallback callback) {
+  mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver>* observers =
+      GetCleanupTriggeredObservers();
+  if (!has_set_disconnect_handlers_) {
+    observers->set_disconnect_handler(base::BindRepeating(
+        &LacrosCleanupHandler::OnDisconnect, base::Unretained(this)));
+    has_set_disconnect_handlers_ = true;
+  }
+
+  callback_ = std::move(callback);
+  errors_.clear();
+  DCHECK(pending_observers_.empty());
+
+  if (observers->empty()) {
+    OnAllObserversDone();
+    return;
+  }
+
+  // Add `pending_observers_` in a separate loop as observers might finish
+  // synchronously.
+  for (auto it = observers->begin(); it != observers->end(); ++it) {
+    mojo::RemoteSetElementId id = it.id();
+    pending_observers_.emplace(id);
+  }
+
+  for (auto it = observers->begin(); it != observers->end(); ++it) {
+    mojo::RemoteSetElementId id = it.id();
+    (*it)->OnLacrosCleanupTriggered(base::BindOnce(
+        &LacrosCleanupHandler::OnObserverDone, base::Unretained(this), id));
+  }
+}
+
+void LacrosCleanupHandler::SetCleanupTriggeredObserversForTesting(
+    mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver>*
+        observers_for_testing) {
+  observers_for_testing_ = observers_for_testing;
+}
+
+void LacrosCleanupHandler::OnDisconnect(mojo::RemoteSetElementId id) {
+  // Observer might have already finished before disconnecting.
+  if (pending_observers_.find(id) == pending_observers_.end())
+    return;
+
+  pending_observers_.erase(id);
+  if (pending_observers_.empty())
+    OnAllObserversDone();
+}
+
+void LacrosCleanupHandler::OnObserverDone(
+    mojo::RemoteSetElementId id,
+    const absl::optional<std::string>& error) {
+  // Sanity check - observers should have flushed pending messages before
+  // disconnecting.
+  if (pending_observers_.find(id) == pending_observers_.end())
+    return;
+
+  if (error)
+    errors_.push_back(*error);
+
+  pending_observers_.erase(id);
+  if (pending_observers_.empty())
+    OnAllObserversDone();
+}
+
+void LacrosCleanupHandler::OnAllObserversDone() {
+  if (errors_.empty()) {
+    std::move(callback_).Run(absl::nullopt);
+    return;
+  }
+
+  std::string errors = base::JoinString(errors_, "\n");
+  std::move(callback_).Run(errors);
+}
+
+mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver>*
+LacrosCleanupHandler::GetCleanupTriggeredObservers() {
+  if (observers_for_testing_)
+    return observers_for_testing_;
+
+  return &crosapi::CrosapiManager::Get()
+              ->crosapi_ash()
+              ->login_ash()
+              ->GetCleanupTriggeredObservers();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h
new file mode 100644
index 0000000..05bb9af
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h
@@ -0,0 +1,56 @@
+// 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_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_LACROS_CLEANUP_HANDLER_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_LACROS_CLEANUP_HANDLER_H_
+
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_handler.h"
+
+#include <set>
+
+#include "base/barrier_closure.h"
+#include "base/callback_forward.h"
+#include "chromeos/crosapi/mojom/login.mojom.h"
+#include "mojo/public/cpp/bindings/remote_set.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+// A cleanup handler which notifies `CleanupManagerLacros` and waits for its
+// reply.
+class LacrosCleanupHandler : public CleanupHandler {
+ public:
+  LacrosCleanupHandler();
+  ~LacrosCleanupHandler() override;
+
+  // CleanupHandler:
+  void Cleanup(CleanupHandlerCallback callback) override;
+
+  void SetCleanupTriggeredObserversForTesting(
+      mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver>*
+          observers);
+
+ private:
+  void OnDisconnect(mojo::RemoteSetElementId id);
+
+  void OnObserverDone(mojo::RemoteSetElementId id,
+                      const absl::optional<std::string>& error);
+
+  void OnAllObserversDone();
+
+  mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver>*
+  GetCleanupTriggeredObservers();
+
+  std::set<mojo::RemoteSetElementId> pending_observers_;
+  mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver>*
+      observers_for_testing_ = nullptr;
+  std::vector<std::string> errors_;
+  base::RepeatingClosure barrier_closure_;
+  bool has_set_disconnect_handlers_ = false;
+  CleanupHandlerCallback callback_;
+};
+
+}  // namespace chromeos
+
+#endif  //  CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_LACROS_CLEANUP_HANDLER_H_
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler_unittest.cc
new file mode 100644
index 0000000..3a5bb11
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler_unittest.cc
@@ -0,0 +1,142 @@
+// 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/chromeos/extensions/login_screen/login/cleanup/lacros_cleanup_handler.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "base/test/test_future.h"
+#include "chromeos/crosapi/mojom/login.mojom.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::WithArg;
+
+namespace chromeos {
+
+namespace {
+
+class TestCleanupTriggeredObserver
+    : public crosapi::mojom::LacrosCleanupTriggeredObserver {
+ public:
+  explicit TestCleanupTriggeredObserver(absl::optional<std::string> error)
+      : error_(error) {}
+
+  explicit TestCleanupTriggeredObserver(bool should_reset)
+      : should_reset_(should_reset) {}
+
+  void OnLacrosCleanupTriggered(
+      OnLacrosCleanupTriggeredCallback callback) override {
+    if (should_reset_) {
+      receiver_->reset();
+      // Note that `callback` is not run since the Mojo pipe disconnects.
+      return;
+    }
+
+    std::move(callback).Run(error_);
+  }
+
+  void SetReceiver(
+      mojo::Receiver<crosapi::mojom::LacrosCleanupTriggeredObserver>*
+          receiver) {
+    receiver_ = receiver;
+  }
+
+  absl::optional<std::string> error_;
+  bool should_reset_ = false;
+  mojo::Receiver<crosapi::mojom::LacrosCleanupTriggeredObserver>* receiver_;
+};
+
+}  // namespace
+
+class LacrosCleanupHandlerUnittest : public testing::Test {
+ public:
+  LacrosCleanupHandlerUnittest() = default;
+  ~LacrosCleanupHandlerUnittest() override = default;
+
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    lacros_cleanup_handler_ = std::make_unique<LacrosCleanupHandler>();
+    lacros_cleanup_handler_->SetCleanupTriggeredObserversForTesting(
+        &remote_set_);
+  }
+
+  void SetUpLacrosCleanupTriggeredObserver(
+      std::unique_ptr<TestCleanupTriggeredObserver> observer) {
+    // Set up pending receiver and remote.
+    mojo::PendingReceiver<crosapi::mojom::LacrosCleanupTriggeredObserver>
+        pending_receiver;
+    mojo::PendingRemote<crosapi::mojom::LacrosCleanupTriggeredObserver>
+        pending_remote = pending_receiver.InitWithNewPipeAndPassRemote();
+
+    // Create receiver bound to test observer.
+    observer_ = std::move(observer);
+    receiver_ = std::make_unique<
+        mojo::Receiver<crosapi::mojom::LacrosCleanupTriggeredObserver>>(
+        observer_.get(), std::move(pending_receiver));
+    observer_->SetReceiver(receiver_.get());
+
+    remote_set_.Add(std::move(pending_remote));
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<LacrosCleanupHandler> lacros_cleanup_handler_;
+  mojo::RemoteSet<crosapi::mojom::LacrosCleanupTriggeredObserver> remote_set_;
+  std::unique_ptr<TestCleanupTriggeredObserver> observer_;
+  std::unique_ptr<
+      mojo::Receiver<crosapi::mojom::LacrosCleanupTriggeredObserver>>
+      receiver_;
+};
+
+TEST_F(LacrosCleanupHandlerUnittest, Cleanup) {
+  SetUpLacrosCleanupTriggeredObserver(
+      std::make_unique<TestCleanupTriggeredObserver>(absl::nullopt));
+
+  base::test::TestFuture<absl::optional<std::string>> future;
+  lacros_cleanup_handler_->Cleanup(
+      future.GetCallback<const absl::optional<std::string>&>());
+
+  EXPECT_EQ(absl::nullopt, future.Get());
+}
+
+TEST_F(LacrosCleanupHandlerUnittest, CleanupError) {
+  std::string error("error");
+  SetUpLacrosCleanupTriggeredObserver(
+      std::make_unique<TestCleanupTriggeredObserver>(error));
+
+  base::test::TestFuture<absl::optional<std::string>> future;
+  lacros_cleanup_handler_->Cleanup(
+      future.GetCallback<const absl::optional<std::string>&>());
+
+  EXPECT_EQ(error, future.Get());
+}
+
+TEST_F(LacrosCleanupHandlerUnittest, NoObservers) {
+  base::test::TestFuture<absl::optional<std::string>> future;
+  lacros_cleanup_handler_->Cleanup(
+      future.GetCallback<const absl::optional<std::string>&>());
+
+  EXPECT_EQ(absl::nullopt, future.Get());
+}
+
+TEST_F(LacrosCleanupHandlerUnittest, Disconnect) {
+  SetUpLacrosCleanupTriggeredObserver(
+      std::make_unique<TestCleanupTriggeredObserver>(/*should_reset=*/true));
+
+  base::test::TestFuture<absl::optional<std::string>> future;
+  lacros_cleanup_handler_->Cleanup(
+      future.GetCallback<const absl::optional<std::string>&>());
+
+  EXPECT_EQ(absl::nullopt, future.Get());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
rename to chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc
index 8f0be1f..5bf76de 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api_unittest.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api_ash_unittest.cc
@@ -25,7 +25,7 @@
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/mock_cleanup_handler.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/errors.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h"
@@ -768,7 +768,7 @@
     GetCrosSettingsHelper()->SetBoolean(
         ash::kDeviceRestrictedManagedGuestSessionEnabled, true);
     // Remove cleanup handlers.
-    chromeos::CleanupManager::Get()->SetCleanupHandlersForTesting({});
+    chromeos::CleanupManagerAsh::Get()->SetCleanupHandlersForTesting({});
 
     LoginApiUnittest::SetUp();
   }
@@ -776,7 +776,7 @@
   void TearDown() override {
     GetCrosSettingsHelper()->RestoreRealDeviceSettingsProvider();
     chromeos::SharedSessionHandler::Get()->ResetStateForTesting();
-    chromeos::CleanupManager::Get()->ResetCleanupHandlersForTesting();
+    chromeos::CleanupManagerAsh::Get()->ResetCleanupHandlersForTesting();
     testing_profile_.reset();
 
     LoginApiUnittest::TearDown();
@@ -806,7 +806,7 @@
         cleanup_handlers;
     cleanup_handlers.insert({"Handler1", std::move(mock_cleanup_handler1)});
     cleanup_handlers.insert({"Handler2", std::move(mock_cleanup_handler2)});
-    chromeos::CleanupManager::Get()->SetCleanupHandlersForTesting(
+    chromeos::CleanupManagerAsh::Get()->SetCleanupHandlersForTesting(
         std::move(cleanup_handlers));
   }
 
@@ -817,7 +817,7 @@
     std::map<std::string, std::unique_ptr<chromeos::CleanupHandler>>
         cleanup_handlers;
     cleanup_handlers.insert({"Handler", std::move(mock_cleanup_handler)});
-    chromeos::CleanupManager::Get()->SetCleanupHandlersForTesting(
+    chromeos::CleanupManagerAsh::Get()->SetCleanupHandlersForTesting(
         std::move(cleanup_handlers));
   }
 
@@ -972,6 +972,7 @@
 // Test that calling `login.unlockSharedSession()` returns an error when there
 // is no shared session active.
 TEST_F(LoginApiSharedSessionUnittest, UnlockSharedSessionNoSharedSession) {
+  SetUpCleanupHandlerMocks();
   LaunchSharedManagedGuestSession("foo");
   EXPECT_CALL(*mock_lock_handler_, RequestLockScreen()).WillOnce(Return());
   RunFunction(new LoginEndSharedSessionFunction(), "[]");
@@ -1029,7 +1030,7 @@
   session_manager::SessionManager::Get()->SetSessionState(
       session_manager::SessionState::LOCKED);
 
-  chromeos::CleanupManager::Get()->SetIsCleanupInProgressForTesting(true);
+  chromeos::CleanupManagerAsh::Get()->SetIsCleanupInProgressForTesting(true);
 
   ASSERT_EQ(login_api_errors::kCleanupInProgress,
             RunFunctionAndReturnError(new LoginUnlockSharedSessionFunction(),
@@ -1111,7 +1112,7 @@
 // is a cleanup in progress.
 TEST_F(LoginApiSharedSessionUnittest, EndSharedSessionCleanupInProgress) {
   LaunchSharedManagedGuestSession("foo");
-  chromeos::CleanupManager::Get()->SetIsCleanupInProgressForTesting(true);
+  chromeos::CleanupManagerAsh::Get()->SetIsCleanupInProgressForTesting(true);
 
   ASSERT_EQ(login_api_errors::kCleanupInProgress,
             RunFunctionAndReturnError(new LoginEndSharedSessionFunction(),
@@ -1188,6 +1189,7 @@
 // Test that calling `login.enterSharedSession()` returns an error when there is
 // an error when unlocking the screen.
 TEST_F(LoginApiSharedSessionUnittest, EnterSharedSessionUnlockFailed) {
+  SetUpCleanupHandlerMocks();
   LaunchSharedManagedGuestSession("foo");
   RunFunction(new LoginEndSharedSessionFunction(), "[]");
   session_manager::SessionManager::Get()->SetSessionState(
@@ -1203,11 +1205,12 @@
 // Test that calling `login.enterSharedSession()` returns an error when there
 // is a cleanup in progress.
 TEST_F(LoginApiSharedSessionUnittest, EnterSharedSessionCleanupInProgress) {
+  SetUpCleanupHandlerMocks();
   LaunchSharedManagedGuestSession("foo");
   RunFunction(new LoginEndSharedSessionFunction(), "[]");
   session_manager::SessionManager::Get()->SetSessionState(
       session_manager::SessionState::LOCKED);
-  chromeos::CleanupManager::Get()->SetIsCleanupInProgressForTesting(true);
+  chromeos::CleanupManagerAsh::Get()->SetIsCleanupInProgressForTesting(true);
 
   ASSERT_EQ(login_api_errors::kCleanupInProgress,
             RunFunctionAndReturnError(new LoginEnterSharedSessionFunction(),
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc b/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc
index 1333e882..112da96 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.cc
@@ -13,7 +13,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
-#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager.h"
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_ash.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/errors.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/login_api_lock_handler.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
@@ -134,7 +134,7 @@
 
   CHECK(user_secret_salt_.empty());
 
-  if (chromeos::CleanupManager::Get()->is_cleanup_in_progress()) {
+  if (chromeos::CleanupManagerAsh::Get()->is_cleanup_in_progress()) {
     std::move(callback).Run(extensions::login_api_errors::kCleanupInProgress);
     return;
   }
@@ -177,7 +177,7 @@
 
   CHECK(!user_secret_salt_.empty());
 
-  if (chromeos::CleanupManager::Get()->is_cleanup_in_progress()) {
+  if (chromeos::CleanupManagerAsh::Get()->is_cleanup_in_progress()) {
     std::move(callback).Run(extensions::login_api_errors::kCleanupInProgress);
     return;
   }
@@ -225,7 +225,8 @@
     return;
   }
 
-  chromeos::CleanupManager* cleanup_manager = chromeos::CleanupManager::Get();
+  chromeos::CleanupManagerAsh* cleanup_manager =
+      chromeos::CleanupManagerAsh::Get();
   if (cleanup_manager->is_cleanup_in_progress()) {
     std::move(callback).Run(extensions::login_api_errors::kCleanupInProgress);
     return;
@@ -319,8 +320,9 @@
   std::move(callback).Run(absl::nullopt);
 }
 
-void SharedSessionHandler::OnCleanupDone(CallbackWithOptionalError callback,
-                                         absl::optional<std::string> errors) {
+void SharedSessionHandler::OnCleanupDone(
+    CallbackWithOptionalError callback,
+    const absl::optional<std::string>& errors) {
   if (errors) {
     std::move(callback).Run(*errors);
     return;
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h b/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h
index 125f2c3..f093321 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/shared_session_handler.h
@@ -25,7 +25,7 @@
   SharedSessionHandler& operator=(const SharedSessionHandler&) = delete;
 
   using CallbackWithOptionalError =
-      base::OnceCallback<void(absl::optional<std::string>)>;
+      base::OnceCallback<void(const absl::optional<std::string>&)>;
 
   // Starts a lockable Managed Guest Session with a randomly generated secret
   // as the Cryptohome key. The session secret is stored in memory to be used
@@ -75,7 +75,7 @@
                           bool auth_success);
 
   void OnCleanupDone(CallbackWithOptionalError callback,
-                     absl::optional<std::string> errors);
+                     const absl::optional<std::string>& errors);
 
   std::string GenerateRandomString(size_t size);
 
diff --git a/chrome/browser/chromeos/fileapi/recent_disk_source.cc b/chrome/browser/chromeos/fileapi/recent_disk_source.cc
index 7b40d5e..6ceb6da 100644
--- a/chrome/browser/chromeos/fileapi/recent_disk_source.cc
+++ b/chrome/browser/chromeos/fileapi/recent_disk_source.cc
@@ -101,6 +101,8 @@
       return net::MatchesMimeType(kImageMimeType, mime_type);
     case RecentSource::FileType::kVideo:
       return net::MatchesMimeType(kVideoMimeType, mime_type);
+    case RecentSource::FileType::kDocument:
+      return file_types_data::kDocumentMIMETypes.contains(mime_type);
     default:
       return false;
   }
diff --git a/chrome/browser/chromeos/fileapi/recent_disk_source_unittest.cc b/chrome/browser/chromeos/fileapi/recent_disk_source_unittest.cc
index b5c9097..d120b9306 100644
--- a/chrome/browser/chromeos/fileapi/recent_disk_source_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/recent_disk_source_unittest.cc
@@ -302,6 +302,32 @@
   EXPECT_EQ(base::Time::FromJavaTime(2000), files[3].last_modified());
 }
 
+TEST_F(RecentDiskSourceTest, GetDocumentFiles) {
+  // Oldest
+  ASSERT_TRUE(CreateEmptyFile("1.jpg", base::Time::FromJavaTime(1000)));
+  ASSERT_TRUE(CreateEmptyFile("2.mp4", base::Time::FromJavaTime(2000)));
+  ASSERT_TRUE(CreateEmptyFile("3.png", base::Time::FromJavaTime(3000)));
+  ASSERT_TRUE(CreateEmptyFile("4.doc", base::Time::FromJavaTime(4000)));
+  ASSERT_TRUE(CreateEmptyFile("5.gif", base::Time::FromJavaTime(5000)));
+  ASSERT_TRUE(CreateEmptyFile("6.txt", base::Time::FromJavaTime(6000)));
+  ASSERT_TRUE(CreateEmptyFile("7.avi", base::Time::FromJavaTime(7000)));
+  ASSERT_TRUE(CreateEmptyFile("8.gdoc", base::Time::FromJavaTime(8000)));
+  // Newest
+
+  std::vector<RecentFile> files =
+      GetRecentFiles(8, base::Time(), RecentSource::FileType::kDocument);
+
+  std::sort(files.begin(), files.end(), RecentFileComparator());
+
+  ASSERT_EQ(3u, files.size());
+  EXPECT_EQ("8.gdoc", files[0].url().path().BaseName().value());
+  EXPECT_EQ(base::Time::FromJavaTime(8000), files[0].last_modified());
+  EXPECT_EQ("6.txt", files[1].url().path().BaseName().value());
+  EXPECT_EQ(base::Time::FromJavaTime(6000), files[1].last_modified());
+  EXPECT_EQ("4.doc", files[2].url().path().BaseName().value());
+  EXPECT_EQ(base::Time::FromJavaTime(4000), files[2].last_modified());
+}
+
 TEST_F(RecentDiskSourceTest, GetRecentFiles_UmaStats) {
   base::HistogramTester histogram_tester;
 
diff --git a/chrome/browser/chromeos/fileapi/recent_source.h b/chrome/browser/chromeos/fileapi/recent_source.h
index 128f1db..5354d84 100644
--- a/chrome/browser/chromeos/fileapi/recent_source.h
+++ b/chrome/browser/chromeos/fileapi/recent_source.h
@@ -38,6 +38,7 @@
   enum class FileType {
     kAll,
     kAudio,
+    kDocument,
     kImage,
     kVideo,
   };
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index 8132f62..502f90e 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -251,7 +251,9 @@
   std::string minor_version;
   EXPECT_TRUE(re2::RE2::PartialMatch(user_agent_value, kChromeVersionRegex,
                                      &minor_version));
-  if (expected_user_agent_reduced) {
+  if (expected_user_agent_reduced ||
+      base::FeatureList::IsEnabled(
+          blink::features::kReduceUserAgentMinorVersion)) {
     EXPECT_EQ(minor_version, kReducedMinorVersion);
   } else {
     EXPECT_NE(minor_version, kReducedMinorVersion);
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index 9057e83..f94ebd9 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -5427,17 +5427,19 @@
     bool success = false;
     std::string script = base::StringPrintf(R"(
           (() => {
-            const link = document.createElement('link');
-            link.rel = 'webbundle';
-            link.addEventListener('load', () => {
+            const script = document.createElement('script');
+            script.type = 'webbundle';
+            script.addEventListener('load', () => {
               window.domAutomationController.send(true);
             });
-            link.addEventListener('error', () => {
+            script.addEventListener('error', () => {
               window.domAutomationController.send(false);
             });
-            link.href = '%s';
-            link.resources = '%s';
-            document.body.appendChild(link);
+            script.textContent = JSON.stringify({
+              source: '%s',
+              resources: ['%s'],
+            });
+            document.body.appendChild(script);
           })();
         )",
                                             href.c_str(), resources.c_str());
@@ -5518,11 +5520,13 @@
           const pass_js_url = new URL('./pass.js', location.href).toString();
           const cancel_js_url =
               new URL('./cancel.js', location.href).toString();
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources = pass_js_url + ' ' + cancel_js_url;
-          document.body.appendChild(link);
+          const script = document.createElement('script');
+          script.type = 'webbundle';
+          script.textContent = JSON.stringify({
+            source: wbn_url,
+            resources: [pass_js_url, cancel_js_url]
+          });
+          document.body.appendChild(script);
         })();
         </script>
         </body>
@@ -5595,11 +5599,13 @@
               new URL('./web_bundle.wbn', location.href).toString();
           const pass_js_url = '%s';
           const cancel_js_url = '%s';
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources = pass_js_url + ' ' + cancel_js_url;
-          document.body.appendChild(link);
+          const script = document.createElement('script');
+          script.type = 'webbundle';
+          script.textContent = JSON.stringify({
+            source: wbn_url,
+            resources: [pass_js_url, cancel_js_url]
+          });
+          document.body.appendChild(script);
         })();
         </script>
         </body>
@@ -5662,13 +5668,15 @@
               new URL('./redirect_to_unlisted.js', location.href).toString();
           const redirect_to_server =
               new URL('./redirect_to_server.js', location.href).toString();
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources = redirect_js_url + ' ' + redirected_js_url + ' ' +
-                           redirect_to_unlisted_js_url + ' ' +
-                           redirect_to_server;
-          document.body.appendChild(link);
+
+          const script = document.createElement('script');
+          script.type = 'webbundle';
+          script.textContent = JSON.stringify({
+            source: wbn_url,
+            resources: [redirect_js_url, redirected_js_url,
+                        redirect_to_unlisted_js_url, redirect_to_server]
+          });
+          document.body.appendChild(script);
         })();
         </script>
         </body>
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index cb765e2..41cd9477 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -3426,17 +3426,19 @@
     bool success = false;
     std::string script = base::StringPrintf(R"(
           (() => {
-            const link = document.createElement('link');
-            link.rel = 'webbundle';
-            link.addEventListener('load', () => {
+            const script = document.createElement('script');
+            script.type = 'webbundle';
+            script.addEventListener('load', () => {
               window.domAutomationController.send(true);
             });
-            link.addEventListener('error', () => {
+            script.addEventListener('error', () => {
               window.domAutomationController.send(false);
             });
-            link.href = '%s';
-            link.resources = '%s';
-            document.body.appendChild(link);
+            script.textContent = JSON.stringify({
+              'source': '%s',
+              'resources': ['%s']
+            });
+            document.body.appendChild(script);
           })();
         )",
                                             href.c_str(), resources.c_str());
@@ -3557,11 +3559,14 @@
                 new URL('./web_bundle.wbn', location.href).toString();
             const script_url = new URL('./test.js', location.href).toString();
             const uuid_in_package_script_url = '%s';
-            const link = document.createElement('link');
-            link.rel = 'webbundle';
-            link.href = wbn_url;
-            link.resources.add(script_url, uuid_in_package_script_url);
-            document.body.appendChild(link);
+
+            const script_web_bundle = document.createElement('script');
+            script_web_bundle.type = 'webbundle';
+            script_web_bundle.textContent = JSON.stringify({
+              'source': wbn_url,
+              'resources': [script_url, uuid_in_package_script_url]
+            });
+            document.body.appendChild(script);
             const script = document.createElement('script');
             script.src = script_url;
             script.addEventListener('load', () => {
@@ -3684,14 +3689,15 @@
               new URL('./cancel.js', location.href).toString();
           const pass_uuid_in_package_js_url = '%s';
           const cancel_uuid_in_package_js_url = '%s';
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources.add(pass_js_url,
-                             cancel_js_url,
-                             pass_uuid_in_package_js_url,
-                             cancel_uuid_in_package_js_url);
-          document.body.appendChild(link);
+          const script = document.createElement('script');
+          script.type = 'webbundle';
+          script.textContent = JSON.stringify({
+            'source': wbn_url,
+            'resources': [pass_js_url, cancel_js_url,
+                          pass_uuid_in_package_js_url,
+                          cancel_uuid_in_package_js_url]
+          });
+          document.body.appendChild(script);
         })();
         </script>
         </body>
@@ -3814,11 +3820,13 @@
         (async () => {
           const wbn_url = new URL('./web_bundle.wbn', location.href).toString();
           const target_url = new URL('./target.txt', location.href).toString();
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources = target_url;
-          document.body.appendChild(link);
+          const script = document.createElement('script');
+          script.type = 'webbundle';
+          script.textContent = JSON.stringify({
+            'source': wbn_url,
+            'resources': [target_url]
+          });
+          document.body.appendChild(script);
           const res = await fetch(target_url);
           document.title = res.status + ':' + res.headers.get('foo');
         })();
@@ -3914,11 +3922,12 @@
         (async () => {
           const wbn_url = new URL('./web_bundle.wbn', location.href).toString();
           const uuid_url = '%s';
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources = uuid_url;
-          document.body.appendChild(link);
+          const script_web_bundle = document.createElement('script');
+          script_web_bundle.type = 'webbundle';
+          script_web_bundle.textContent = JSON.stringify({
+            'source': wbn_url,
+            'resources': [uuid_url]
+          });
           const script = document.createElement('script');
           script.src = uuid_url;
           script.addEventListener('error', () => {
@@ -4019,13 +4028,15 @@
               new URL('./redirect_to_unlisted.js', location.href).toString();
           const redirect_to_server =
               new URL('./redirect_to_server.js', location.href).toString();
-          const link = document.createElement('link');
-          link.rel = 'webbundle';
-          link.href = wbn_url;
-          link.resources = redirect_js_url + ' ' + redirected_js_url + ' ' +
-                           redirect_to_unlisted_js_url + ' ' +
-                           redirect_to_server;
-          document.body.appendChild(link);
+          const script = document.createElement('script');
+          script.type = 'webbundle';
+          script.textContent = JSON.stringify({
+            'source': wbn_url,
+            'resources': [redirect_js_url, redirected_js_url,
+                          redirect_to_unlisted_js_url, redirect_to_server]
+          });
+          document.body.appendChild(script);
+
         })();
         </script>
         </body>
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index 3bfc6ee..9ffe8ab 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -2122,7 +2122,12 @@
                          true /* nosniff */);
 
   const std::string page_html = base::StringPrintf(R"(
-        <link rel="webbundle" href="./test.wbn" scopes="uuid-in-package:">
+        <script type="webbundle">
+        {
+          "source": "./test.wbn",
+          "scopes": ["uuid-in-package:"]
+        }
+        </script>
         <iframe src="%s"></iframe>
       )",
                                                    uuid_html_url.c_str());
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f9e4664..c748410 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1723,7 +1723,7 @@
   {
     "name": "enable-cros-ime-system-emoji-picker-clipboard",
     "owners": [ "jopalmer", "essential-inputs-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "enable-cros-ime-system-emoji-picker-extension",
@@ -1748,7 +1748,7 @@
   {
     "name": "enable-cros-multilingual-typing",
     "owners": [ "tranbaoduy", "essential-inputs-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 105
   },
   {
     "name": "enable-cros-on-device-grammar-check",
@@ -1875,7 +1875,7 @@
   {
     "name": "enable-desktop-pwas-launch-handler",
     "owners": [ "alancutter@chromium.org", "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 100
+    "expiry_milestone": 110
   },
   {
     "name": "enable-desktop-pwas-link-capturing",
@@ -2384,6 +2384,11 @@
     "expiry_milestone": 130
   },
   {
+    "name": "enable-neural-palm-rejection-beta-model",
+    "owners": [ "robsc", "jiwan" ],
+    "expiry_milestone": 130
+  },
+  {
     "name": "enable-neural-palm-rejection-model-v2",
     "owners": [ "robsc", "napper", "alanlxl" ],
     "expiry_milestone": 130
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d095e3d..c02d8c9 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4572,6 +4572,11 @@
 const char kEnableNeuralPalmAdaptiveHoldDescription[] =
     "Enable adaptive hold in palm rejection.  Not compatible with all devices.";
 
+const char kEnableNeuralPalmRejectionBetaModelName[] =
+    "Palm Rejection Beta Model";
+const char kEnableNeuralPalmRejectionBetaModelDescription[] =
+    "Enable NN palm rejection on devices registered with beta model.";
+
 const char kEnableNeuralPalmRejectionModelV2Name[] = "Palm Rejection Model V2";
 const char kEnableNeuralPalmRejectionModelV2Description[] =
     "Uses an updated model for palm rejection.  Not compatible with all "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index ffbc767..2823f01e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2613,6 +2613,9 @@
 extern const char kEnableNeuralPalmAdaptiveHoldName[];
 extern const char kEnableNeuralPalmAdaptiveHoldDescription[];
 
+extern const char kEnableNeuralPalmRejectionBetaModelName[];
+extern const char kEnableNeuralPalmRejectionBetaModelDescription[];
+
 extern const char kEnableNeuralPalmRejectionModelV2Name[];
 extern const char kEnableNeuralPalmRejectionModelV2Description[];
 
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc
index ffe85ec..a2b1912 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.cc
@@ -111,111 +111,9 @@
   device_time_pref->SetKey(sink_id, base::TimeToValue(base::Time::Now()));
 }
 
-const base::Value* AccessCodeCastPrefUpdater::GetDevicesDict() {
-  return pref_service_->GetDictionary(prefs::kAccessCodeCastDevices);
-}
-
-const base::Value* AccessCodeCastPrefUpdater::GetDiscoveredNetworksDict() {
-  return pref_service_->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks);
-}
-
-const base::Value* AccessCodeCastPrefUpdater::GetDeviceAdditionTimeDict() {
-  return pref_service_->GetDictionary(prefs::kAccessCodeCastDeviceAdditionTime);
-}
-
-const base::Value::List* AccessCodeCastPrefUpdater::GetSinkIdsByNetworkId(
-    const std::string& network_id) {
-  auto* network_dict = GetDiscoveredNetworksDict();
-  if (!network_dict)
-    return nullptr;
-
-  // If found, it returns a pointer to the element. Otherwise it returns
-  // nullptr.
-  auto* network_list = network_dict->FindKey(network_id);
-
-  if (!network_list)
-    return nullptr;
-  return network_list->GetIfList();
-}
-
-const base::Value* AccessCodeCastPrefUpdater::GetMediaSinkInternalValueBySinkId(
-    const MediaSink::Id sink_id) {
-  auto* device_dict = GetDevicesDict();
-  if (!device_dict)
-    return nullptr;
-
-  // If found, it returns a pointer to the element. Otherwise it returns
-  // nullptr.
-  auto* device_value = device_dict->FindKey(sink_id);
-
-  if (!device_value)
-    return nullptr;
-  return device_value;
-}
-
-void AccessCodeCastPrefUpdater::RemoveSinkIdFromDevicesDict(
-    const MediaSink::Id sink_id) {
-  ScopedDictionaryPrefUpdate update(pref_service_,
-                                    prefs::kAccessCodeCastDevices);
-  std::unique_ptr<DictionaryValueUpdate> devices_pref = update.Get();
-  DCHECK(devices_pref) << "The " << prefs::kAccessCodeCastDevices
-                       << " pref does not exist.";
-  devices_pref->Remove(sink_id);
-}
-
-void AccessCodeCastPrefUpdater::RemoveSinkIdFromDiscoveredNetworksDict(
-    const MediaSink::Id sink_id) {
-  ScopedDictionaryPrefUpdate update(pref_service_,
-                                    prefs::kAccessCodeCastDiscoveredNetworks);
-  std::unique_ptr<DictionaryValueUpdate> discovered_networks_pref =
-      update.Get();
-  DCHECK(discovered_networks_pref)
-      << "The " << prefs::kAccessCodeCastDiscoveredNetworks
-      << " pref does not exist.";
-
-  const base::Value::Dict& network_dict =
-      GetDiscoveredNetworksDict()->GetDict();
-
-  // Iterate through network id's
-  for (auto key_value_pair : network_dict) {
-    const auto network_id = key_value_pair.first;
-
-    // We need to clone here because the GetList() method returns a const value
-    // and we might need to modify it later on.
-    base::Value::List network_list = key_value_pair.second.GetList().Clone();
-
-    // Check to see if the media sink was erased from the list. If there is no
-    // value in the list simply continue in the iteration.
-    if (!network_list.EraseValue(base::Value(sink_id)))
-      continue;
-
-    // If the list is now empty, remove the key from the dictionary
-    // entirely and continue from this iteration.
-
-    if (network_list.empty()) {
-      DCHECK(discovered_networks_pref->Remove(network_id))
-          << "The network_id list value exists but the key does not.";
-      continue;
-    }
-    discovered_networks_pref->Set(
-        network_id,
-        base::Value::ToUniquePtrValue(base::Value(std::move(network_list))));
-  }
-}
-
-void AccessCodeCastPrefUpdater::RemoveSinkIdFromDeviceAdditionTimeDict(
-    const MediaSink::Id sink_id) {
-  ScopedDictionaryPrefUpdate update(pref_service_,
-                                    prefs::kAccessCodeCastDeviceAdditionTime);
-
-  std::unique_ptr<DictionaryValueUpdate> device_time_pref = update.Get();
-  DCHECK(device_time_pref) << "The " << prefs::kAccessCodeCastDeviceAdditionTime
-                           << " pref does not exist.";
-  device_time_pref->Remove(sink_id);
-}
-
 base::WeakPtr<AccessCodeCastPrefUpdater>
 AccessCodeCastPrefUpdater::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
 }
+
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h
index 0990101..ee379cec 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater.h
@@ -41,42 +41,6 @@
   // exist, then update the value of that |sink_id| with a new time.
   void UpdateDeviceAdditionTimeDict(const MediaSink::Id sink_id);
 
-  // Returns a nullptr if the device dictionary does not exist in the pref
-  // service for some reason.
-  const base::Value* GetDevicesDict();
-
-  // Returns a nullptr if the network dictionary does not exist in the pref
-  // service for some reason.
-  const base::Value* GetDiscoveredNetworksDict();
-
-  // Returns a nullptr if the device addition dictionary does not exist in the
-  // pref service for some reason.
-  const base::Value* GetDeviceAdditionTimeDict();
-
-  // If found, it returns a pointer to the element. Otherwise it returns
-  // nullptr.
-  const base::Value::List* GetSinkIdsByNetworkId(const std::string& network_id);
-
-  // If found, it returns a pointer to the element. Otherwise it returns
-  // nullptr.
-  const base::Value* GetMediaSinkInternalValueBySinkId(
-      const MediaSink::Id sink_id);
-
-  // Removes the given |sink_id| from all instances in the devices dictionary
-  // stored in the pref service. Nothing occurs if the |sink_id| was not there
-  // in the first place.
-  void RemoveSinkIdFromDevicesDict(const MediaSink::Id sink_id);
-
-  // Removes the given |sink_id| from all instances in the network dictionary
-  // stored in the pref service. Nothing occurs if the |sink_id| was not there
-  // in the first place.
-  void RemoveSinkIdFromDiscoveredNetworksDict(const MediaSink::Id sink_id);
-
-  // Removes the given |sink_id| from all instances in the device addition
-  // dictionary stored in the pref service. Nothing occurs if the |sink_id| was
-  // not there in the first place.
-  void RemoveSinkIdFromDeviceAdditionTimeDict(const MediaSink::Id sink_id);
-
   base::WeakPtr<AccessCodeCastPrefUpdater> GetWeakPtr();
 
  private:
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc
index bbc1bea..efd9356 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_pref_updater_unittest.cc
@@ -195,137 +195,4 @@
   EXPECT_GE(final_time_of_addition, initial_time_of_addition);
 }
 
-TEST_F(AccessCodeCastPrefUpdaterTest, TestGetSinkIdsByNetworkId) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
-
-  EXPECT_TRUE(pref_updater()->GetSinkIdsByNetworkId(network1));
-  EXPECT_FALSE(pref_updater()->GetSinkIdsByNetworkId(network2));
-}
-
-TEST_F(AccessCodeCastPrefUpdaterTest, TestGetMediaSinkInternalValueBySinkId) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-  MediaSinkInternal cast_sink2 = CreateCastSink(2);
-
-  pref_updater()->UpdateDevicesDict(cast_sink);
-
-  EXPECT_TRUE(
-      pref_updater()->GetMediaSinkInternalValueBySinkId(cast_sink.id()));
-  EXPECT_FALSE(
-      pref_updater()->GetMediaSinkInternalValueBySinkId(cast_sink2.id()));
-}
-
-TEST_F(AccessCodeCastPrefUpdaterTest, TestRemoveSinkIdFromDevicesDict) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-  MediaSinkInternal cast_sink2 = CreateCastSink(2);
-
-  pref_updater()->UpdateDevicesDict(cast_sink);
-
-  pref_updater()->RemoveSinkIdFromDevicesDict(cast_sink.id());
-  auto* dict = prefs()->GetDictionary(prefs::kAccessCodeCastDevices);
-  EXPECT_FALSE(dict->FindKey(cast_sink.id()));
-  pref_updater()->RemoveSinkIdFromDevicesDict(cast_sink2.id());
-}
-
-TEST_F(AccessCodeCastPrefUpdaterTest,
-       TestRemoveSinkIdFromDiscoveredNetworksDict) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-  MediaSinkInternal cast_sink2 = CreateCastSink(2);
-
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
-
-  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink.id());
-  auto& dict = prefs()
-                   ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
-                   ->GetDict();
-  EXPECT_FALSE(dict.FindList(network1));
-}
-
-TEST_F(AccessCodeCastPrefUpdaterTest,
-       TestRemoveSinkIdFromDiscoveredNetworksDictMultipleSinks) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-  MediaSinkInternal cast_sink2 = CreateCastSink(2);
-
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink2.id(), network1);
-
-  // After removal of a single sink_id, the network key should still exist but
-  // the list no longer should contain the removed cast sink id.
-
-  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink.id());
-  auto& dict = prefs()
-                   ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
-                   ->GetDict();
-  auto network_list_value = dict.FindList(network1)->Clone();
-
-  // This method returns the amount of values erased from the list. The second
-  // cast sink should still be in this network list so it should return 1.
-  EXPECT_FALSE(network_list_value.EraseValue(base::Value(cast_sink.id())));
-  EXPECT_EQ(1u, network_list_value.EraseValue(base::Value(cast_sink2.id())));
-
-  // Remove the final sink.
-
-  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink2.id());
-  auto& updated_dict =
-      prefs()
-          ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
-          ->GetDict();
-  EXPECT_FALSE(updated_dict.FindList(network1));
-}
-
-TEST_F(AccessCodeCastPrefUpdaterTest,
-       TestRemoveSinkIdFromDiscoveredNetworksDictMultipleNetworks) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-  MediaSinkInternal cast_sink2 = CreateCastSink(2);
-  MediaSinkInternal cast_sink3 = CreateCastSink(3);
-
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink.id(), network1);
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink2.id(), network1);
-  pref_updater()->UpdateDiscoveredNetworksDict(cast_sink3.id(), network2);
-
-  // After removal of a single sink_id, the network key should still exist but
-  // the list no longer should contain the removed cast sink id.
-
-  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink.id());
-  auto& dict = prefs()
-                   ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
-                   ->GetDict();
-  auto network1_list_value = dict.FindList(network1)->Clone();
-  auto network2_list_value = dict.FindList(network2)->Clone();
-
-  // This method returns the amount of values erased from the list. The second
-  // cast sink should still be in this network list so it should return 1.
-  EXPECT_FALSE(network1_list_value.EraseValue(base::Value(cast_sink.id())));
-  EXPECT_EQ(1u, network1_list_value.EraseValue(base::Value(cast_sink2.id())));
-  EXPECT_EQ(1u, network2_list_value.EraseValue(base::Value(cast_sink3.id())));
-
-  // Remove the final two sinks.
-
-  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink2.id());
-
-  pref_updater()->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink3.id());
-  auto& updated_dict =
-      prefs()
-          ->GetDictionary(prefs::kAccessCodeCastDiscoveredNetworks)
-          ->GetDict();
-
-  EXPECT_FALSE(updated_dict.FindList(network1));
-  EXPECT_FALSE(updated_dict.FindList(network2));
-}
-
-TEST_F(AccessCodeCastPrefUpdaterTest,
-       TestRemoveSinkIdFromDeviceAdditionTimeDict) {
-  MediaSinkInternal cast_sink = CreateCastSink(1);
-  MediaSinkInternal cast_sink2 = CreateCastSink(2);
-
-  pref_updater()->UpdateDeviceAdditionTimeDict(cast_sink.id());
-
-  pref_updater()->RemoveSinkIdFromDeviceAdditionTimeDict(cast_sink.id());
-  auto* dict = prefs()->GetDictionary(prefs::kAccessCodeCastDeviceAdditionTime);
-  EXPECT_FALSE(dict->FindKey(cast_sink.id()));
-
-  pref_updater()->RemoveSinkIdFromDeviceAdditionTimeDict(cast_sink2.id());
-}
-
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
index b8c02f0..e8baaa5 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.cc
@@ -61,20 +61,17 @@
 AccessCodeCastSinkService::AccessCodeCastSinkService(
     Profile* profile,
     MediaRouter* media_router,
-    CastMediaSinkServiceImpl* cast_media_sink_service_impl,
-    DiscoveryNetworkMonitor* network_monitor,
-    PrefService* prefs)
+    CastMediaSinkServiceImpl* cast_media_sink_service_impl)
     : profile_(profile),
       media_router_(media_router),
       media_routes_observer_(
           std::make_unique<AccessCodeMediaRoutesObserver>(media_router, this)),
       cast_media_sink_service_impl_(cast_media_sink_service_impl),
       task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      network_monitor_(network_monitor) {
-  DCHECK(profile_) << "The profile does not exist.";
-  DCHECK(prefs)
-      << "Prefs could not be fetched from the profile for some reason.";
-  pref_updater_ = std::make_unique<AccessCodeCastPrefUpdater>(prefs);
+      network_monitor_(DiscoveryNetworkMonitor::GetInstance()) {
+  DCHECK(profile_);
+  pref_updater_ =
+      std::make_unique<AccessCodeCastPrefUpdater>(profile_->GetPrefs());
   backoff_policy_ = {
       // Number of initial errors (in sequence) to ignore before going into
       // exponential backoff.
@@ -103,11 +100,6 @@
       // successful requests.
       false,
   };
-  // We don't need to post this task per the DiscoveryNetworkMonitor's promise:
-  // "All observers will be notified of network changes on the thread from which
-  // they registered."
-  network_monitor_->AddObserver(this);
-  InitStoredDeviceConnections();
 }
 
 AccessCodeCastSinkService::AccessCodeCastSinkService(Profile* profile)
@@ -115,9 +107,7 @@
           profile,
           MediaRouterFactory::GetApiForBrowserContext(profile),
           media_router::DualMediaSinkService::GetInstance()
-              ->GetCastMediaSinkServiceImpl(),
-          DiscoveryNetworkMonitor::GetInstance(),
-          profile->GetPrefs()) {}
+              ->GetCastMediaSinkServiceImpl()) {}
 
 AccessCodeCastSinkService::~AccessCodeCastSinkService() = default;
 
@@ -146,7 +136,7 @@
   // There should only be 1 element in the |removed_routes| set.
   DCHECK(removed_routes.size() < 2);
   auto first = removed_routes.begin();
-  removed_route_id_ = *first;
+  MediaRoute::Id removed_route_id = *first;
 
   base::PostTaskAndReplyWithResult(
       access_code_sink_service_->cast_media_sink_service_impl_->task_runner()
@@ -156,7 +146,7 @@
           &CastMediaSinkServiceImpl::GetSinkById,
           base::Unretained(
               access_code_sink_service_->cast_media_sink_service_impl_),
-          MediaRoute::GetSinkIdFromMediaRouteId(removed_route_id_)),
+          MediaRoute::GetSinkIdFromMediaRouteId(removed_route_id)),
       base::BindOnce(
           &AccessCodeCastSinkService::HandleMediaRouteDiscoveredByAccessCode,
           access_code_sink_service_->GetWeakPtr()));
@@ -414,143 +404,6 @@
   pref_updater_->UpdateDeviceAdditionTimeDict(sink->id());
 }
 
-void AccessCodeCastSinkService::InitStoredDeviceConnections() {
-  // The AddStoredDevicesToMediaRouter() callback needs to be be
-  // bound with BindPostTask() to ensure that the callback is invoked on this
-  // specific task runner.
-  auto network_cb =
-      base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
-                     weak_ptr_factory_.GetWeakPtr());
-
-  auto returned_network_cb =
-      base::BindPostTask(task_runner_, std::move(network_cb));
-
-  // We need to run this task on the IO thread since the DiscoveryNetworkMonitor
-  // runs on the IO thread.
-  cast_media_sink_service_impl_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&DiscoveryNetworkMonitor::Refresh,
-                                base::Unretained(network_monitor_),
-                                std::move(returned_network_cb)));
-}
-
-void AccessCodeCastSinkService::FetchAndAddStoredDevices(
-    const std::string& network_id) {
-  // If the network id exists within the pref service, attempt to open any
-  // devices in that list.
-  auto* network_list = pref_updater_->GetSinkIdsByNetworkId(network_id);
-  if (!network_list) {
-    media_router_->GetLogger()->LogInfo(
-        mojom::LogCategory::kDiscovery, kLoggerComponent,
-        "There are no saved Access Code Cast devices on the network_id: " +
-            network_id,
-        "", "", "");
-    return;
-  }
-  media_router_->GetLogger()->LogInfo(
-      mojom::LogCategory::kDiscovery, kLoggerComponent,
-      "Found Access Code Cast devices on the network_id: " + network_id +
-          " : " + network_list->DebugString() +
-          ". Attempting to add these cast devices to the media router.",
-      "", "", "");
-  AddStoredDevicesToMediaRouter(*network_list);
-}
-
-void AccessCodeCastSinkService::AddStoredDevicesToMediaRouter(
-    const base::Value::List& sink_ids) {
-  // We can assume the network_list variable will never be empty since after
-  // removal of a device, a check is made to ensure that keys with empty lists
-  // are removed.
-  std::vector<MediaSinkInternal> cast_sinks;
-  for (const auto& sink_id : sink_ids) {
-    const std::string* sink_id_string = sink_id.GetIfString();
-    if (!sink_id_string) {
-      media_router_->GetLogger()->LogError(
-          mojom::LogCategory::kDiscovery, kLoggerComponent,
-          "The Media Sink id is not stored as a string in the network list: " +
-              sink_ids.DebugString() +
-              ". This means something went wrong when storing cast devices on "
-              "this network.",
-          "", "", "");
-      return;
-    }
-    auto validation_result = ValidateDeviceFromSinkId(*sink_id_string);
-
-    // Ensure that stored media sink_id corresponds to a properly stored
-    // MediaSinkInternal before adding the given sink_id to the media router.
-    if (!validation_result.has_value()) {
-      media_router_->GetLogger()->LogError(
-          mojom::LogCategory::kDiscovery, kLoggerComponent,
-          "The Media Sink id " + *sink_id_string +
-              " is missing from one or more of the pref "
-              "services. Attempting to remove all sink_id references right "
-              "now.",
-          "", "", "");
-      RemoveSinkIdFromAllEntries(*sink_id_string);
-      return;
-    }
-    cast_sinks.push_back(validation_result.value());
-  }
-
-  // Let the media router handle addition.
-  cast_media_sink_service_impl_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&CastMediaSinkServiceImpl::OpenChannelsWithRandomizedDelay,
-                     base::Unretained(cast_media_sink_service_impl_),
-                     cast_sinks, SinkSource::kAccessCode));
-}
-
-void AccessCodeCastSinkService::RemoveSinkIdFromAllEntries(
-    const MediaSink::Id& sink_id) {
-  pref_updater_->RemoveSinkIdFromDevicesDict(sink_id);
-  pref_updater_->RemoveSinkIdFromDiscoveredNetworksDict(sink_id);
-  pref_updater_->RemoveSinkIdFromDeviceAdditionTimeDict(sink_id);
-}
-
-const absl::optional<MediaSinkInternal>
-AccessCodeCastSinkService::ValidateDeviceFromSinkId(
-    const MediaSink::Id& sink_id) {
-  const auto* sink_value =
-      pref_updater_->GetMediaSinkInternalValueBySinkId(sink_id);
-  if (!sink_value) {
-    media_router_->GetLogger()->LogError(
-        mojom::LogCategory::kDiscovery, kLoggerComponent,
-        "The Media Sink id: " + sink_id +
-            " is either stored improperly or doesn't exist within the pref "
-            "service.",
-        "", "", "");
-    return absl::nullopt;
-  }
-  const auto* dict_value = sink_value->GetIfDict();
-  if (!dict_value) {
-    media_router_->GetLogger()->LogError(
-        mojom::LogCategory::kDiscovery, kLoggerComponent,
-        "The Media Sink id: " + sink_id +
-            " was not stored as a dictionary value in the pref service. Its "
-            "storage type is: " +
-            base::Value::GetTypeName(sink_value->type()),
-        "", "", "");
-    return absl::nullopt;
-  }
-  const absl::optional<MediaSinkInternal> media_sink =
-      ParseValueDictIntoMediaSinkInternal(*dict_value);
-  if (!media_sink.has_value()) {
-    media_router_->GetLogger()->LogError(
-        mojom::LogCategory::kDiscovery, kLoggerComponent,
-        "The Media Sink " + dict_value->DebugString() +
-            " could not be parsed from the pref service.",
-        "", "", "");
-    return absl::nullopt;
-  }
-  // TODO(gbj): Include a validation check for the
-  // prefs::kAccessCodeCastDeviceAdditionTime pref once it is used.
-  return media_sink.value();
-}
-
-void AccessCodeCastSinkService::OnNetworksChanged(
-    const std::string& network_id) {
-  FetchAndAddStoredDevices(network_id);
-}
-
 void AccessCodeCastSinkService::Shutdown() {
   // There's no guarantee that MediaRouter is still in the
   // MediaRoutesObserver. |media_routes_observer_| accesses MediaRouter in its
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h
index a28e33d..26b3f781 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service.h
@@ -27,8 +27,7 @@
 using ChannelOpenedCallback = base::OnceCallback<void(bool)>;
 using AddSinkResultCode = access_code_cast::mojom::AddSinkResultCode;
 
-class AccessCodeCastSinkService : public KeyedService,
-                                  public DiscoveryNetworkMonitor::Observer {
+class AccessCodeCastSinkService : public KeyedService {
  public:
   using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
 
@@ -87,8 +86,6 @@
     // Set of route ids that is updated whenever OnRoutesUpdated is called.
     std::vector<MediaRoute::Id> old_routes_;
 
-    MediaRoute::Id removed_route_id_;
-
     const raw_ptr<AccessCodeCastSinkService> access_code_sink_service_;
 
     base::WeakPtrFactory<AccessCodeMediaRoutesObserver> weak_ptr_factory_{this};
@@ -118,21 +115,12 @@
                            OnChannelOpenedFailure);
   FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
                            SinkDoesntExistForPrefs);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
-                           TestFetchAndAddStoredDevices);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest, TestChangeNetworks);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
-                           TestAddInvalidDevicesNoMediaSinkInternal);
-  FRIEND_TEST_ALL_PREFIXES(AccessCodeCastSinkServiceTest,
-                           TestFetchAndAddStoredDevicesNoNetwork);
 
   // Constructor used for testing.
   AccessCodeCastSinkService(
       Profile* profile,
       MediaRouter* media_router,
-      CastMediaSinkServiceImpl* cast_media_sink_service_impl,
-      DiscoveryNetworkMonitor* network_monitor,
-      PrefService* prefs);
+      CastMediaSinkServiceImpl* cast_media_sink_service_impl);
 
   // Use |AccessCodeCastSinkServiceFactory::GetForProfile(..)| to get
   // an instance of this service.
@@ -152,23 +140,6 @@
                               AddSinkResultCallback add_sink_callback,
                               bool has_sink);
 
-  void InitStoredDeviceConnections();
-  void FetchAndAddStoredDevices(const std::string& network_id);
-  void AddStoredDevicesToMediaRouter(const base::Value::List& sink_ids);
-
-  // Removes the given |sink_id| from all entries in the AccessCodeCast pref
-  // service.
-  void RemoveSinkIdFromAllEntries(const MediaSink::Id& sink_id);
-
-  // Validates the given |sink_id| is present and properly stored as a
-  // MediaSinkInternal in the pref store. If the sink is present, a
-  // MediaSinkInternal will be returned in the optional value.
-  const absl::optional<MediaSinkInternal> ValidateDeviceFromSinkId(
-      const MediaSink::Id& sink_id);
-
-  // DiscoveryNetworkMonitor::Observer implementation
-  void OnNetworksChanged(const std::string& network_id) override;
-
   cast_channel::CastSocketOpenParams CreateCastSocketOpenParams(
       const MediaSinkInternal& sink);
 
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc
index 31c84fd..e4017cd1c 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_factory.cc
@@ -22,7 +22,7 @@
   // GetServiceForBrowserContext returns a KeyedService hence the static_cast<>
   // to return a pointer to AccessCodeCastSinkService.
   return static_cast<AccessCodeCastSinkService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, false));
+      GetInstance()->GetServiceForBrowserContext(profile, true));
 }
 
 // static
@@ -42,16 +42,12 @@
 
 KeyedService* AccessCodeCastSinkServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
-  if (!GetAccessCodeCastEnabledPref(
-          Profile::FromBrowserContext(profile)->GetPrefs())) {
-    return nullptr;
-  }
   return new AccessCodeCastSinkService(static_cast<Profile*>(profile));
 }
 
 bool AccessCodeCastSinkServiceFactory::ServiceIsCreatedWithBrowserContext()
     const {
-  return true;
+  return false;
 }
 
 bool AccessCodeCastSinkServiceFactory::ServiceIsNULLWhileTesting() const {
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc
index 2e6d579..bccf164f 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_sink_service_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/timer/mock_timer.h"
 #include "chrome/browser/media/router/chrome_media_router_factory.h"
-#include "chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h"
 #include "chrome/browser/media/router/discovery/access_code/access_code_test_util.h"
 #include "chrome/browser/media/router/discovery/discovery_network_monitor.h"
 #include "chrome/browser/media/router/discovery/mdns/cast_media_sink_service_impl.h"
@@ -36,7 +35,6 @@
 #include "components/media_router/browser/test/mock_media_router.h"
 #include "components/media_router/common/discovery/media_sink_service_base.h"
 #include "components/media_router/common/test/test_helper.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/test_utils.h"
@@ -77,10 +75,9 @@
             std::make_unique<MockCastMediaSinkServiceImpl>(
                 OnSinksDiscoveredCallback(),
                 mock_cast_socket_service_.get(),
-                discovery_network_monitor_.get(),
+                DiscoveryNetworkMonitor::GetInstance(),
                 &dual_media_sink_service_)) {
     mock_cast_socket_service_->SetTaskRunnerForTest(mock_time_task_runner_);
-    RegisterAccessCodeProfilePrefs(prefs_.registry());
   }
   AccessCodeCastSinkServiceTest(AccessCodeCastSinkServiceTest&) = delete;
   AccessCodeCastSinkServiceTest& operator=(AccessCodeCastSinkServiceTest&) =
@@ -98,11 +95,9 @@
 
     access_code_cast_sink_service_ =
         base::WrapUnique(new AccessCodeCastSinkService(
-            profile_, router_.get(), cast_media_sink_service_impl_.get(),
-            discovery_network_monitor_.get(), prefs()));
+            profile_, router_.get(), cast_media_sink_service_impl_.get()));
     access_code_cast_sink_service_->SetTaskRunnerForTest(
         mock_time_task_runner_);
-    task_environment_.RunUntilIdle();
   }
 
   void TearDown() override {
@@ -123,33 +118,12 @@
   }
   TestingProfileManager* profile_manager() { return profile_manager_.get(); }
 
-  sync_preferences::TestingPrefServiceSyncable* prefs() { return &prefs_; }
-
-  void ChangeConnectionType(network::mojom::ConnectionType connection_type) {
-    discovery_network_monitor_->OnConnectionChanged(connection_type);
-  }
-
  protected:
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   std::unique_ptr<media_router::MockMediaRouter> router_;
   std::unique_ptr<LoggerImpl> logger_;
 
-  sync_preferences::TestingPrefServiceSyncable prefs_;
-
-  static std::vector<DiscoveryNetworkInfo> fake_network_info_;
-
-  static const std::vector<DiscoveryNetworkInfo> fake_ethernet_info_;
-  static const std::vector<DiscoveryNetworkInfo> fake_wifi_info_;
-  static const std::vector<DiscoveryNetworkInfo> fake_unknown_info_;
-
-  static std::vector<DiscoveryNetworkInfo> FakeGetNetworkInfo() {
-    return fake_network_info_;
-  }
-
-  std::unique_ptr<DiscoveryNetworkMonitor> discovery_network_monitor_ =
-      DiscoveryNetworkMonitor::CreateInstanceForTest(&FakeGetNetworkInfo);
-
   raw_ptr<TestingProfile> profile_;
   scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_;
 
@@ -164,25 +138,6 @@
   std::unique_ptr<AccessCodeCastSinkService> access_code_cast_sink_service_;
 };
 
-// static
-const std::vector<DiscoveryNetworkInfo>
-    AccessCodeCastSinkServiceTest::fake_ethernet_info_ = {
-        DiscoveryNetworkInfo{std::string("enp0s2"), std::string("ethernet1")}};
-// static
-const std::vector<DiscoveryNetworkInfo>
-    AccessCodeCastSinkServiceTest::fake_wifi_info_ = {
-        DiscoveryNetworkInfo{std::string("wlp3s0"), std::string("wifi1")},
-        DiscoveryNetworkInfo{std::string("wlp3s1"), std::string("wifi2")}};
-// static
-const std::vector<DiscoveryNetworkInfo>
-    AccessCodeCastSinkServiceTest::fake_unknown_info_ = {
-        DiscoveryNetworkInfo{std::string("enp0s2"), std::string()}};
-
-// static
-std::vector<DiscoveryNetworkInfo>
-    AccessCodeCastSinkServiceTest::fake_network_info_ =
-        AccessCodeCastSinkServiceTest::fake_ethernet_info_;
-
 TEST_F(AccessCodeCastSinkServiceTest,
        AccessCodeCastDeviceRemovedAfterRouteEnds) {
   // Test to see that an AccessCode cast sink will be removed after the session
@@ -194,12 +149,10 @@
   MediaRoute media_route_cast = CreateRouteForTesting(cast_sink1);
   std::vector<MediaRoute> route_list = {media_route_cast};
 
-  // Expect that the removed_route_id_ member variable has not changes since no
-  // route was removed.
+  // Expect that no Task is posted since no routes were removed.
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_TRUE(access_code_cast_sink_service_->media_routes_observer_
-                  ->removed_route_id_.empty());
+  EXPECT_EQ(0u, mock_time_task_runner()->GetPendingTaskCount());
 
   // Add a cast sink discovered by access code to the list of routes.
   MediaSinkInternal access_code_sink2 = CreateCastSink(2);
@@ -208,12 +161,10 @@
 
   route_list.push_back(media_route_access);
 
-  // Expect that the removed_route_id_ member variable has not changes since no
-  // route was removed.
+  // Expect that no Task is posted since no routes were removed.
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_TRUE(access_code_cast_sink_service_->media_routes_observer_
-                  ->removed_route_id_.empty());
+  EXPECT_EQ(0u, mock_time_task_runner()->GetPendingTaskCount());
 
   // Remove the non-access code sink from the list of routes.
   route_list.erase(
@@ -221,9 +172,7 @@
       route_list.end());
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_EQ(
-      access_code_cast_sink_service_->media_routes_observer_->removed_route_id_,
-      media_route_cast.media_route_id());
+  EXPECT_EQ(1u, mock_time_task_runner()->GetPendingTaskCount());
   mock_time_task_runner()->FastForwardUntilNoTasksRemain();
 
   // Expect cast sink is NOT removed from the media router since it
@@ -241,9 +190,7 @@
 
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated(
       route_list);
-  EXPECT_EQ(
-      access_code_cast_sink_service_->media_routes_observer_->removed_route_id_,
-      media_route_access.media_route_id());
+  EXPECT_EQ(1u, mock_time_task_runner()->GetPendingTaskCount());
   mock_time_task_runner()->FastForwardUntilNoTasksRemain();
 
   access_code_cast_sink_service_->HandleMediaRouteDiscoveredByAccessCode(
@@ -300,9 +247,7 @@
   access_code_cast_sink_service_->media_routes_observer_->OnRoutesUpdated({});
   // Since a route has been removed, there should be a pending task to examine
   // whether the route's sink is an access code sink.
-  EXPECT_EQ(
-      access_code_cast_sink_service_->media_routes_observer_->removed_route_id_,
-      media_route_cast.media_route_id());
+  EXPECT_EQ(1u, mock_time_task_runner()->GetPendingTaskCount());
 
   access_code_cast_sink_service_->HandleMediaRouteDiscoveredByAccessCode(
       &cast_sink1);
@@ -414,272 +359,4 @@
   EXPECT_FALSE(mock_time_task_runner()->GetPendingTaskCount());
 }
 
-TEST_F(AccessCodeCastSinkServiceTest, TestFetchAndAddStoredDevices) {
-  // Test that ensures OpenChannels is called after valid sinks are fetched from
-  // the internal pref service.
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
-  const MediaSinkInternal cast_sink2 = CreateCastSink(2);
-  const MediaSinkInternal cast_sink3 = CreateCastSink(3);
-
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink2);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink3);
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  std::vector<MediaSinkInternal> cast_sinks;
-  cast_sinks.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
-          .value());
-  cast_sinks.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink2.id())
-          .value());
-  cast_sinks.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink3.id())
-          .value());
-
-  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
-              OpenChannels(cast_sinks, SinkSource::kAccessCode))
-      .Times(1);
-
-  mock_time_task_runner()->PostNonNestableDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          &DiscoveryNetworkMonitor::GetNetworkId,
-          base::Unretained(discovery_network_monitor_.get()),
-          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
-                         access_code_cast_sink_service_->GetWeakPtr())),
-      base::Seconds(0));
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-}
-
-TEST_F(AccessCodeCastSinkServiceTest, TestChangeNetworks) {
-  // Test that ensures sinks are stored on a network and then reopened when we
-  // connect back to that network.
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
-  const MediaSinkInternal cast_sink2 = CreateCastSink(2);
-  const MediaSinkInternal cast_sink3 = CreateCastSink(3);
-
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink2);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink3);
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  std::vector<MediaSinkInternal> cast_sinks_ethernet;
-  cast_sinks_ethernet.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
-          .value());
-  cast_sinks_ethernet.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink2.id())
-          .value());
-  cast_sinks_ethernet.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink3.id())
-          .value());
-
-  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
-              OpenChannels(cast_sinks_ethernet, SinkSource::kAccessCode))
-      .Times(1);
-
-  mock_time_task_runner()->PostNonNestableDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          &DiscoveryNetworkMonitor::GetNetworkId,
-          base::Unretained(discovery_network_monitor_.get()),
-          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
-                         access_code_cast_sink_service_->GetWeakPtr())),
-      base::Seconds(0));
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  // Connect to a new network with different sinks.
-  fake_network_info_.clear();
-  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
-  content::RunAllTasksUntilIdle();
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-
-  fake_network_info_ = fake_wifi_info_;
-  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_WIFI);
-  content::RunAllTasksUntilIdle();
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-
-  const MediaSinkInternal cast_sink4 = CreateCastSink(4);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink4);
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  std::vector<MediaSinkInternal> cast_sinks_wifi;
-  cast_sinks_wifi.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink4.id())
-          .value());
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
-              OpenChannels(cast_sinks_wifi, SinkSource::kAccessCode))
-      .Times(1);
-
-  mock_time_task_runner()->PostNonNestableDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          &DiscoveryNetworkMonitor::GetNetworkId,
-          base::Unretained(discovery_network_monitor_.get()),
-          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
-                         access_code_cast_sink_service_->GetWeakPtr())),
-      base::Seconds(0));
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  // Reconnecting to the previous ethernet network should restore the same sinks
-  // from the cache and attempt to resolve them.
-  fake_network_info_.clear();
-  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_NONE);
-  content::RunAllTasksUntilIdle();
-  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
-
-  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
-              OpenChannels(cast_sinks_ethernet, SinkSource::kAccessCode))
-      .Times(1);
-
-  fake_network_info_ = fake_ethernet_info_;
-  ChangeConnectionType(network::mojom::ConnectionType::CONNECTION_ETHERNET);
-  content::RunAllTasksUntilIdle();
-  mock_time_task_runner_->FastForwardUntilNoTasksRemain();
-}
-
-TEST_F(AccessCodeCastSinkServiceTest,
-       TestAddInvalidDevicesNoMediaSinkInternal) {
-  // Test that to check that if a sink is not stored in each pref, it will be
-  // removed from the pref service and no call to open channels is made.
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  std::vector<MediaSinkInternal> cast_sinks;
-  cast_sinks.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
-          .value());
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  // Remove the cast sink from the devices dict -- now the cast sink is
-  // incompletely stored since it only exists in 2/3 of the prefs.
-  access_code_cast_sink_service_->pref_updater_->RemoveSinkIdFromDevicesDict(
-      cast_sink1.id());
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-
-  base::Value::List sink_ids;
-  sink_ids.Append(cast_sink1.id());
-
-  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
-              OpenChannels(cast_sinks, SinkSource::kAccessCode))
-      .Times(0);
-
-  EXPECT_FALSE(
-      access_code_cast_sink_service_->pref_updater_->GetDiscoveredNetworksDict()
-          ->GetDict()
-          .empty());
-  EXPECT_FALSE(
-      access_code_cast_sink_service_->pref_updater_->GetDeviceAdditionTimeDict()
-          ->GetDict()
-          .empty());
-  EXPECT_TRUE(access_code_cast_sink_service_->pref_updater_->GetDevicesDict()
-                  ->GetDict()
-                  .empty());
-
-  // Expect that the sink id is removed from all instance in the pref service.
-  access_code_cast_sink_service_->AddStoredDevicesToMediaRouter(sink_ids);
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  EXPECT_TRUE(
-      access_code_cast_sink_service_->pref_updater_->GetDiscoveredNetworksDict()
-          ->GetDict()
-          .empty());
-  EXPECT_TRUE(
-      access_code_cast_sink_service_->pref_updater_->GetDeviceAdditionTimeDict()
-          ->GetDict()
-          .empty());
-  EXPECT_TRUE(access_code_cast_sink_service_->pref_updater_->GetDevicesDict()
-                  ->GetDict()
-                  .empty());
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-}
-
-TEST_F(AccessCodeCastSinkServiceTest, TestFetchAndAddStoredDevicesNoNetwork) {
-  // Test that to check that if a sink is not stored on the network, it won't
-  // attempted to be added. In this case no sink_ids should be removed.
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  const MediaSinkInternal cast_sink1 = CreateCastSink(1);
-  access_code_cast_sink_service_->StoreSinkInPrefs(&cast_sink1);
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  std::vector<MediaSinkInternal> cast_sinks;
-  cast_sinks.push_back(
-      access_code_cast_sink_service_->ValidateDeviceFromSinkId(cast_sink1.id())
-          .value());
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-
-  // Remove the cast sink from the networks dict -- now the cast sink is
-  // incompletely stored since it only exists in 2/3 of the prefs.
-  access_code_cast_sink_service_->pref_updater_
-      ->RemoveSinkIdFromDiscoveredNetworksDict(cast_sink1.id());
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-
-  EXPECT_CALL(*mock_cast_media_sink_service_impl(),
-              OpenChannels(cast_sinks, SinkSource::kAccessCode))
-      .Times(0);
-  EXPECT_TRUE(
-      access_code_cast_sink_service_->pref_updater_->GetDiscoveredNetworksDict()
-          ->GetDict()
-          .empty());
-  EXPECT_FALSE(
-      access_code_cast_sink_service_->pref_updater_->GetDeviceAdditionTimeDict()
-          ->GetDict()
-          .empty());
-  EXPECT_FALSE(access_code_cast_sink_service_->pref_updater_->GetDevicesDict()
-                   ->GetDict()
-                   .empty());
-
-  mock_time_task_runner()->PostNonNestableDelayedTask(
-      FROM_HERE,
-      base::BindOnce(
-          &DiscoveryNetworkMonitor::GetNetworkId,
-          base::Unretained(discovery_network_monitor_.get()),
-          base::BindOnce(&AccessCodeCastSinkService::FetchAndAddStoredDevices,
-                         access_code_cast_sink_service_->GetWeakPtr())),
-      base::Seconds(0));
-
-  mock_time_task_runner()->FastForwardUntilNoTasksRemain();
-  task_environment_.RunUntilIdle();
-}
-
 }  // namespace media_router
diff --git a/chrome/browser/media/router/discovery/discovery_network_monitor.h b/chrome/browser/media/router/discovery/discovery_network_monitor.h
index 09bb407..47c30ff 100644
--- a/chrome/browser/media/router/discovery/discovery_network_monitor.h
+++ b/chrome/browser/media/router/discovery/discovery_network_monitor.h
@@ -72,7 +72,6 @@
  private:
   friend class CastMediaSinkServiceImplTest;
   friend class DiscoveryNetworkMonitorTest;
-  friend class AccessCodeCastSinkServiceTest;
   friend struct std::default_delete<DiscoveryNetworkMonitor>;
   friend struct base::LazyInstanceTraitsBase<DiscoveryNetworkMonitor>;
 
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 379ee74c..c898615a 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
@@ -83,8 +83,8 @@
     void addLogin(@JobId int jobId, byte[] pwdWithLocalData, String syncingAccount) {
         mBackend.addLogin(pwdWithLocalData, getAccount(syncingAccount), () -> {
             if (mNativeBackendBridge == 0) return;
-            PasswordStoreAndroidBackendBridgeImplJni.get().onLoginAdded(
-                    mNativeBackendBridge, jobId, pwdWithLocalData);
+            PasswordStoreAndroidBackendBridgeImplJni.get().onLoginChanged(
+                    mNativeBackendBridge, jobId);
         }, exception -> handleAndroidBackendException(jobId, exception));
     }
 
@@ -92,8 +92,8 @@
     void updateLogin(@JobId int jobId, byte[] pwdWithLocalData, String syncingAccount) {
         mBackend.updateLogin(pwdWithLocalData, getAccount(syncingAccount), () -> {
             if (mNativeBackendBridge == 0) return;
-            PasswordStoreAndroidBackendBridgeImplJni.get().onLoginUpdated(
-                    mNativeBackendBridge, jobId, pwdWithLocalData);
+            PasswordStoreAndroidBackendBridgeImplJni.get().onLoginChanged(
+                    mNativeBackendBridge, jobId);
         }, exception -> handleAndroidBackendException(jobId, exception));
     }
 
@@ -101,8 +101,8 @@
     void removeLogin(@JobId int jobId, byte[] pwdSpecificsData, String syncingAccount) {
         mBackend.removeLogin(pwdSpecificsData, getAccount(syncingAccount), () -> {
             if (mNativeBackendBridge == 0) return;
-            PasswordStoreAndroidBackendBridgeImplJni.get().onLoginDeleted(
-                    mNativeBackendBridge, jobId, pwdSpecificsData);
+            PasswordStoreAndroidBackendBridgeImplJni.get().onLoginChanged(
+                    mNativeBackendBridge, jobId);
         }, exception -> handleAndroidBackendException(jobId, exception));
     }
 
@@ -135,12 +135,7 @@
     interface Natives {
         void onCompleteWithLogins(long nativePasswordStoreAndroidBackendBridgeImpl,
                 @JobId int jobId, byte[] passwords);
-        void onLoginAdded(long nativePasswordStoreAndroidBackendBridgeImpl, @JobId int jobId,
-                byte[] pwdWithLocalData);
-        void onLoginUpdated(long nativePasswordStoreAndroidBackendBridgeImpl, @JobId int jobId,
-                byte[] pwdWithLocalData);
-        void onLoginDeleted(long nativePasswordStoreAndroidBackendBridgeImpl, @JobId int jobId,
-                byte[] pwdSpecificsData);
+        void onLoginChanged(long nativePasswordStoreAndroidBackendBridgeImpl, @JobId int jobId);
         void onError(long nativePasswordStoreAndroidBackendBridgeImpl, @JobId int jobId,
                 int errorType, int apiErrorCode);
     }
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 3eede87d..82d32066 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
@@ -291,7 +291,7 @@
         assertNotNull(successCallback.getValue());
 
         successCallback.getValue().run();
-        verify(mBridgeJniMock).onLoginAdded(sDummyNativePointer, kTestTaskId, pwdWithLocalData);
+        verify(mBridgeJniMock).onLoginChanged(sDummyNativePointer, kTestTaskId);
     }
 
     @Test
@@ -326,7 +326,7 @@
         assertNotNull(successCallback.getValue());
 
         successCallback.getValue().run();
-        verify(mBridgeJniMock).onLoginUpdated(sDummyNativePointer, kTestTaskId, pwdWithLocalData);
+        verify(mBridgeJniMock).onLoginChanged(sDummyNativePointer, kTestTaskId);
     }
 
     @Test
@@ -361,7 +361,7 @@
         assertNotNull(successCallback.getValue());
 
         successCallback.getValue().run();
-        verify(mBridgeJniMock).onLoginDeleted(sDummyNativePointer, kTestTaskId, pwdSpecificsData);
+        verify(mBridgeJniMock).onLoginChanged(sDummyNativePointer, kTestTaskId);
     }
 
     @Test
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 02b4db3..3821d32 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
@@ -26,9 +26,9 @@
   base::android::JavaByteArrayToByteVector(base::android::AttachCurrentThread(),
                                            passwords, &serialized_result);
   sync_pb::ListPasswordsResult list_passwords_result;
-  // TODO(crbug.com/1229655): Check that parsing executes successfully.
-  list_passwords_result.ParseFromArray(serialized_result.data(),
-                                       serialized_result.size());
+  bool parsing_succeeds = list_passwords_result.ParseFromArray(
+      serialized_result.data(), serialized_result.size());
+  DCHECK(parsing_succeeds);
   auto forms =
       password_manager::PasswordVectorFromListResult(list_passwords_result);
   for (auto& form : forms) {
@@ -38,28 +38,6 @@
   return forms;
 }
 
-sync_pb::PasswordSpecificsData CreatePasswordSpecificsData(
-    const base::android::JavaRef<jbyteArray>& login) {
-  std::vector<uint8_t> serialized_result;
-  base::android::JavaByteArrayToByteVector(base::android::AttachCurrentThread(),
-                                           login, &serialized_result);
-  sync_pb::PasswordSpecificsData result;
-  // TODO(crbug.com/1229655): Check that parsing executes successfully.
-  result.ParseFromArray(serialized_result.data(), serialized_result.size());
-  return result;
-}
-
-sync_pb::PasswordWithLocalData CreatePasswordWithLocalData(
-    const base::android::JavaRef<jbyteArray>& login) {
-  std::vector<uint8_t> serialized_result;
-  base::android::JavaByteArrayToByteVector(base::android::AttachCurrentThread(),
-                                           login, &serialized_result);
-  sync_pb::PasswordWithLocalData result;
-  // TODO(crbug.com/1229655): Check that parsing executes successfully.
-  result.ParseFromArray(serialized_result.data(), serialized_result.size());
-  return result;
-}
-
 base::android::ScopedJavaLocalRef<jstring> GetJavaStringFromAccount(
     PasswordStoreAndroidBackendBridgeImpl::Account account) {
   if (absl::holds_alternative<password_manager::PasswordStoreOperationTarget>(
@@ -201,61 +179,12 @@
   return job_id;
 }
 
-void PasswordStoreAndroidBackendBridgeImpl::OnLoginAdded(
-    JNIEnv* env,
-    jint job_id,
-    const base::android::JavaParamRef<jbyteArray>& login) {
+void PasswordStoreAndroidBackendBridgeImpl::OnLoginChanged(JNIEnv* env,
+                                                           jint job_id) {
   DCHECK(consumer_);
-
-  // TODO(crbug.com/1229655): This is a temporary workaround, while store
-  // change deltas are not received to confirm the correct operation execution.
-  sync_pb::PasswordWithLocalData login_data =
-      CreatePasswordWithLocalData(login);
-  password_manager::PasswordStoreChangeList changelist;
-  password_manager::PasswordForm added_form =
-      password_manager::PasswordFromProtoWithLocalData(login_data);
-  changelist.emplace_back(password_manager::PasswordStoreChange::ADD,
-                          added_form);
-
-  consumer_->OnLoginsChanged(JobId(job_id), changelist);
-}
-
-void PasswordStoreAndroidBackendBridgeImpl::OnLoginUpdated(
-    JNIEnv* env,
-    jint job_id,
-    const base::android::JavaParamRef<jbyteArray>& login) {
-  DCHECK(consumer_);
-
-  // TODO(crbug.com/1229655): This is a temporary workaround, while store
-  // change deltas are not received to confirm the correct operation execution.
-  sync_pb::PasswordWithLocalData login_data =
-      CreatePasswordWithLocalData(login);
-  password_manager::PasswordStoreChangeList changelist;
-  password_manager::PasswordForm updated_form =
-      password_manager::PasswordFromProtoWithLocalData(login_data);
-  changelist.emplace_back(password_manager::PasswordStoreChange::UPDATE,
-                          updated_form);
-
-  consumer_->OnLoginsChanged(JobId(job_id), changelist);
-}
-
-void PasswordStoreAndroidBackendBridgeImpl::OnLoginDeleted(
-    JNIEnv* env,
-    jint job_id,
-    const base::android::JavaParamRef<jbyteArray>& login) {
-  DCHECK(consumer_);
-
-  // TODO(crbug.com/1229655): This is a temporary workaround, while store
-  // change deltas are not received to confirm the correct operation execution.
-  sync_pb::PasswordSpecificsData login_data =
-      CreatePasswordSpecificsData(login);
-  password_manager::PasswordStoreChangeList changelist;
-  password_manager::PasswordForm removed_form =
-      password_manager::PasswordFromSpecifics(login_data);
-  changelist.emplace_back(password_manager::PasswordStoreChange::REMOVE,
-                          removed_form);
-
-  consumer_->OnLoginsChanged(JobId(job_id), changelist);
+  // Notifying that a login changed without providing a changelist prompts the
+  // caller to explicitly check the remaining logins.
+  consumer_->OnLoginsChanged(JobId(job_id), absl::nullopt);
 }
 
 JobId PasswordStoreAndroidBackendBridgeImpl::GetNextJobId() {
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 7f85229..fc507b8 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
@@ -34,23 +34,9 @@
       jint job_id,
       const base::android::JavaParamRef<jbyteArray>& passwords);
 
-  // Called via JNI. Called when the api call with `job_id` finished and
-  // provides serialized PasswordWithLocalData identifying the added 'login'.
-  void OnLoginAdded(JNIEnv* env,
-                    jint job_id,
-                    const base::android::JavaParamRef<jbyteArray>& login);
-
-  // Called via JNI. Called when the api call with `job_id` finished and
-  // provides serialized PasswordWithLocalData identifying the updated 'login'.
-  void OnLoginUpdated(JNIEnv* env,
-                      jint job_id,
-                      const base::android::JavaParamRef<jbyteArray>& login);
-
-  // Called via JNI. Called when the api call with `job_id` finished and
-  // provides Serialized PasswordSpecificsData identifying the deleted 'login'.
-  void OnLoginDeleted(JNIEnv* env,
-                      jint job_id,
-                      const base::android::JavaParamRef<jbyteArray>& login);
+  // Called via JNI. Called when the api call with `job_id` finished that could
+  // have added, modified or deleted a login.
+  void OnLoginChanged(JNIEnv* env, jint job_id);
 
   // Called via JNI. Called when the api call with `job_id` finished with
   // an exception.
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 42db6118..00461c5 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -99,6 +99,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/dump_accessibility_test_helper.h"
+#include "content/public/test/fenced_frame_test_util.h"
 #include "content/public/test/prerender_test_util.h"
 #include "content/public/test/scoped_time_zone.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -111,6 +112,7 @@
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h"
 #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
+#include "extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h"
 #include "extensions/common/manifest_handlers/mime_types_handler.h"
 #include "extensions/test/result_catcher.h"
 #include "extensions/test/test_extension_dir.h"
@@ -164,6 +166,8 @@
 using ::content::AXInspectFactory;
 using ::content::WebContents;
 using ::extensions::ExtensionsAPIClient;
+using ::extensions::MimeHandlerViewGuest;
+using ::extensions::TestMimeHandlerViewGuest;
 using ::guest_view::GuestViewManager;
 using ::guest_view::TestGuestViewManager;
 using ::guest_view::TestGuestViewManagerFactory;
@@ -4483,3 +4487,71 @@
 
   run_loop->Run();
 }
+
+class PDFExtensionPrerenderAndFencedFrameTest
+    : public PDFExtensionTestWithTestGuestViewManager {
+ public:
+  PDFExtensionPrerenderAndFencedFrameTest() = default;
+  ~PDFExtensionPrerenderAndFencedFrameTest() override = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    PDFExtensionTestWithTestGuestViewManager::SetUpCommandLine(command_line);
+    // `prerender_helper_` and `fenced_frame_helper_` has a ScopedFeatureList so
+    // we needed to delay its creation until now because PDFExtensionTest
+    // also uses a ScopedFeatureList and initialization order matters.
+    prerender_helper_ = std::make_unique<content::test::PrerenderTestHelper>(
+        base::BindRepeating(&PDFExtensionPrerenderTest::GetActiveWebContents,
+                            base::Unretained(this)));
+    fenced_frame_helper_ =
+        std::make_unique<content::test::FencedFrameTestHelper>();
+  }
+
+  content::test::PrerenderTestHelper& prerender_helper() {
+    return *prerender_helper_;
+  }
+
+  content::test::FencedFrameTestHelper& fenced_frame_helper() {
+    return *fenced_frame_helper_;
+  }
+
+ private:
+  std::unique_ptr<content::test::PrerenderTestHelper> prerender_helper_;
+  std::unique_ptr<content::test::FencedFrameTestHelper> fenced_frame_helper_;
+};
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionPrerenderAndFencedFrameTest,
+                       LoadPDFInPrerender) {
+  GURL url = embedded_test_server()->GetURL("/empty.html");
+  ASSERT_TRUE(content::NavigateToURL(GetActiveWebContents(), url));
+
+  GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
+      base::BindRepeating(&TestMimeHandlerViewGuest::Create));
+  // Set a 1s delay to delay MimeHandlerViewGuest's creation to ensure that the
+  // fenced frame is loaded while the PDF stream is not yet consumed.
+  const int creation_delay = TestTimeouts::tiny_timeout().InMilliseconds();
+  TestMimeHandlerViewGuest::DelayNextCreateWebContents(creation_delay);
+
+  // Load a PDF in the prerender.
+  GURL prerender_url = embedded_test_server()->GetURL("/pdf/test.pdf");
+
+  content::test::PrerenderHostRegistryObserver registry_observer(
+      *GetActiveWebContents());
+  prerender_helper().AddPrerenderAsync(prerender_url);
+  registry_observer.WaitForTrigger(prerender_url);
+
+  // Create a fenced frame.
+  const GURL fenced_frame_url =
+      embedded_test_server()->GetURL("/fenced_frames/title1.html");
+  content::RenderFrameHost* fenced_frame_host =
+      fenced_frame_helper().CreateFencedFrame(
+          GetActiveWebContents()->GetMainFrame(), fenced_frame_url);
+  ASSERT_TRUE(fenced_frame_host);
+
+  auto* guest_web_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
+  ASSERT_TRUE(guest_web_contents);
+  WaitForLoadStart(guest_web_contents);
+  EXPECT_TRUE(content::WaitForLoadStop(guest_web_contents));
+
+  // Ensure that the fenced frame's navigation should not abort the PDF stream.
+  EXPECT_EQ(1U, GetGuestViewManager()->GetNumGuestsActive());
+}
diff --git a/chrome/browser/profiles/avatar_menu.h b/chrome/browser/profiles/avatar_menu.h
index dbced36..bdfa4b3 100644
--- a/chrome/browser/profiles/avatar_menu.h
+++ b/chrome/browser/profiles/avatar_menu.h
@@ -16,8 +16,6 @@
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/common/buildflags.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
 #include "ui/gfx/image/image.h"
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 671a217..de095e3 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -225,6 +225,7 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros_factory.h"
 #include "chrome/browser/lacros/account_manager/profile_account_manager_factory.h"
 #include "chrome/browser/lacros/cert/cert_db_initializer_factory.h"
 #endif
@@ -307,6 +308,9 @@
   browser_sync::UserEventServiceFactory::GetInstance();
   BrowsingDataHistoryObserverService::Factory::GetInstance();
   browsing_topics::BrowsingTopicsServiceFactory::GetInstance();
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  chromeos::CleanupManagerLacrosFactory::GetInstance();
+#endif
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   CaptivePortalServiceFactory::GetInstance();
 #endif
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.cc b/chrome/browser/profiles/profile_avatar_icon_util.cc
index 84e9eb4..d315b90 100644
--- a/chrome/browser/profiles/profile_avatar_icon_util.cc
+++ b/chrome/browser/profiles/profile_avatar_icon_util.cc
@@ -16,6 +16,8 @@
 #include "base/path_service.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
 #include "build/build_config.h"
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 41ee2f87..5cf0713 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -61,7 +61,6 @@
 #include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
@@ -2297,7 +2296,6 @@
     case IDC_SPELLPANEL_TOGGLE:
     case IDC_CONTENT_CONTEXT_LANGUAGE_SETTINGS:
     case IDC_SEND_TAB_TO_SELF:
-    case IDC_CONTENT_LINK_SEND_TAB_TO_SELF:
       return true;
 
     case IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH:
diff --git a/chrome/browser/resources/extensions/error_page.ts b/chrome/browser/resources/extensions/error_page.ts
index f6ec898..a88d407 100644
--- a/chrome/browser/resources/extensions/error_page.ts
+++ b/chrome/browser/resources/extensions/error_page.ts
@@ -61,8 +61,9 @@
         return warn;
       case chrome.developerPrivate.ErrorLevel.ERROR:
         return error;
+      default:
+        assertNotReached();
     }
-    assertNotReached();
   }
   assert(item.type === chrome.developerPrivate.ErrorType.MANIFEST);
   return warn;
diff --git a/chrome/browser/resources/extensions/item.ts b/chrome/browser/resources/extensions/item.ts
index 3d088c2..a31b564 100644
--- a/chrome/browser/resources/extensions/item.ts
+++ b/chrome/browser/resources/extensions/item.ts
@@ -253,8 +253,9 @@
         return 'extensions-icons:unpacked';
       case SourceType.WEBSTORE:
         return '';
+      default:
+        assertNotReached();
     }
-    assertNotReached();
   }
 
   private computeSourceIndicatorText_(): string {
diff --git a/chrome/browser/resources/extensions/item_util.ts b/chrome/browser/resources/extensions/item_util.ts
index 4dc7338..9b42cb6 100644
--- a/chrome/browser/resources/extensions/item_util.ts
+++ b/chrome/browser/resources/extensions/item_util.ts
@@ -22,7 +22,6 @@
 }
 
 // TODO(tjudkins): This should be extracted to a shared metrics module.
-/** @enum {string} */
 export enum UserAction {
   ALL_TOGGLED_ON = 'Extensions.Settings.HostList.AllHostsToggledOn',
   ALL_TOGGLED_OFF = 'Extensions.Settings.HostList.AllHostsToggledOff',
@@ -44,12 +43,13 @@
     case chrome.developerPrivate.ExtensionState.BLACKLISTED:
     case chrome.developerPrivate.ExtensionState.DISABLED:
       return false;
+    default:
+      assertNotReached();
   }
-  assertNotReached();
 }
 
 /**
- * @return {boolean} Whether the user can change whether or not the extension is
+ * @return Whether the user can change whether or not the extension is
  *     enabled.
  */
 export function userCanChangeEnablement(
@@ -93,9 +93,9 @@
       return SourceType.UNKNOWN;
     case chrome.developerPrivate.Location.FROM_STORE:
       return SourceType.WEBSTORE;
+    default:
+      assertNotReached(item.location);
   }
-
-  assertNotReached(item.location);
 }
 
 export function getItemSourceString(source: SourceType): string {
@@ -112,8 +112,9 @@
       // Nothing to return. Calling code should use
       // chrome.developerPrivate.ExtensionInfo's |locationText| instead.
       return '';
+    default:
+      assertNotReached();
   }
-  assertNotReached();
 }
 
 /**
diff --git a/chrome/browser/resources/extensions/manager.ts b/chrome/browser/resources/extensions/manager.ts
index 4293ebaf6..e36c6a34 100644
--- a/chrome/browser/resources/extensions/manager.ts
+++ b/chrome/browser/resources/extensions/manager.ts
@@ -395,8 +395,9 @@
         return 'extensions_';
       case ExtensionType.THEME:
         assertNotReached('Don\'t send themes to the chrome://extensions page');
+      default:
+        assertNotReached();
     }
-    assertNotReached();
   }
 
   /**
diff --git a/chrome/browser/resources/extensions/shortcut_util.ts b/chrome/browser/resources/extensions/shortcut_util.ts
index 75a56d7..c761f935 100644
--- a/chrome/browser/resources/extensions/shortcut_util.ts
+++ b/chrome/browser/resources/extensions/shortcut_util.ts
@@ -186,6 +186,7 @@
       return hasModifier(e, false);
     case ModifierPolicy.NOT_ALLOWED:
       return !hasModifier(e, true);
+    default:
+      assertNotReached();
   }
-  assertNotReached();
 }
diff --git a/chrome/browser/resources/settings/autofill_page/password_check.ts b/chrome/browser/resources/settings/autofill_page/password_check.ts
index 44e48f7c..dd6b5e1 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_check.ts
@@ -564,8 +564,9 @@
             this.i18n('checkPasswordsErrorQuota');
       case CheckState.OTHER_ERROR:
         return this.i18n('checkPasswordsErrorGeneric');
+      default:
+        assertNotReached('Can\'t find a title for state: ' + this.status.state);
     }
-    assertNotReached('Can\'t find a title for state: ' + this.status.state);
   }
 
   /**
@@ -650,9 +651,10 @@
         return this.i18n('checkPasswordsAgain');
       case CheckState.QUOTA_LIMIT:
         return '';  // Undefined behavior. Don't show any misleading text.
+      default:
+        assertNotReached(
+            'Can\'t find a button text for state: ' + this.status.state);
     }
-    assertNotReached(
-        'Can\'t find a button text for state: ' + this.status.state);
   }
 
   /**
@@ -678,9 +680,11 @@
       case CheckState.NO_PASSWORDS:
       case CheckState.QUOTA_LIMIT:
         return true;
+      default:
+        assertNotReached(
+            'Can\'t determine button visibility for state: ' +
+            this.status.state);
     }
-    assertNotReached(
-        'Can\'t determine button visibility for state: ' + this.status.state);
   }
 
   /**
@@ -725,9 +729,10 @@
       case CheckState.QUOTA_LIMIT:
       case CheckState.OTHER_ERROR:
         return true;
+      default:
+        assertNotReached(
+            'Not specified whether to state is an error: ' + this.status.state);
     }
-    assertNotReached(
-        'Not specified whether to state is an error: ' + this.status.state);
   }
 
   /**
@@ -752,10 +757,11 @@
         // Shows "No security issues found" if user is signed out and doesn't
         // have insecure credentials.
         return true;
+      default:
+        assertNotReached(
+            'Not specified whether to show passwords for state: ' +
+            this.status.state);
     }
-    assertNotReached(
-        'Not specified whether to show passwords for state: ' +
-        this.status.state);
   }
 
   /**
diff --git a/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts b/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
index 4d9c041..8b086df 100644
--- a/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
+++ b/chrome/browser/resources/settings/autofill_page/password_check_list_item.ts
@@ -123,11 +123,11 @@
         return loadTimeData.getString('leakedPassword');
       case chrome.passwordsPrivate.CompromiseType.PHISHED_AND_LEAKED:
         return loadTimeData.getString('phishedAndLeakedPassword');
+      default:
+        assertNotReached(
+            'Can\'t find a string for type: ' +
+            this.item.compromisedInfo!.compromiseType);
     }
-
-    assertNotReached(
-        'Can\'t find a string for type: ' +
-        this.item.compromisedInfo!.compromiseType);
   }
 
   private fire_(eventName: string, detail?: any) {
diff --git a/chrome/browser/resources/settings/people_page/sync_controls.ts b/chrome/browser/resources/settings/people_page/sync_controls.ts
index 055e603b..177e113 100644
--- a/chrome/browser/resources/settings/people_page/sync_controls.ts
+++ b/chrome/browser/resources/settings/people_page/sync_controls.ts
@@ -115,7 +115,9 @@
     // <if expr="chromeos_ash">
     return loadTimeData.getBoolean('syncSettingsCategorizationEnabled');
     // </if>
+    // <if expr="chromeos_lacros">
     return true;  // Should always be shown on Lacros.
+    // </if>
   }
   // </if>
 
diff --git a/chrome/browser/resources/settings/people_page/sync_page.ts b/chrome/browser/resources/settings/people_page/sync_page.ts
index 1ae914f..13d4e7e2 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.ts
+++ b/chrome/browser/resources/settings/people_page/sync_page.ts
@@ -639,9 +639,9 @@
           passphraseInput.focusInput();
         }
         return;
+      default:
+        assertNotReached();
     }
-
-    assertNotReached();
   }
 
   private onLearnMoreTap_(event: Event) {
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc
index 412393f..0b13c81 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc
@@ -4,22 +4,17 @@
 
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 
-#include <string>
-
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/send_tab_to_self/desktop_notification_handler.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.h"
-#include "components/send_tab_to_self/features.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
-#include "components/send_tab_to_self/target_device_info.h"
+#include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
-#include "url/gurl.h"
 
 namespace send_tab_to_self {
 
@@ -68,36 +63,4 @@
   controller->ShowConfirmationMessage();
 }
 
-void ShareToSingleTarget(content::WebContents* tab, const GURL& link_url) {
-  Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
-  DCHECK_EQ(GetValidDeviceCount(profile), 1u);
-  const std::vector<TargetDeviceInfo>& devices =
-      SendTabToSelfSyncServiceFactory::GetForProfile(profile)
-          ->GetSendTabToSelfModel()
-          ->GetTargetDeviceInfoSortedList();
-  CreateNewEntry(tab, devices.begin()->device_name, devices.begin()->cache_guid,
-                 link_url);
-}
-
-size_t GetValidDeviceCount(Profile* profile) {
-  SendTabToSelfSyncService* service =
-      SendTabToSelfSyncServiceFactory::GetForProfile(profile);
-  DCHECK(service);
-  SendTabToSelfModel* model = service->GetSendTabToSelfModel();
-  DCHECK(model);
-  const std::vector<TargetDeviceInfo>& devices =
-      model->GetTargetDeviceInfoSortedList();
-  return devices.size();
-}
-
-std::u16string GetSingleTargetDeviceName(Profile* profile) {
-  DCHECK_EQ(GetValidDeviceCount(profile), 1u);
-  return base::UTF8ToUTF16(
-      SendTabToSelfSyncServiceFactory::GetForProfile(profile)
-          ->GetSendTabToSelfModel()
-          ->GetTargetDeviceInfoSortedList()
-          .begin()
-          ->device_name);
-}
-
 }  // namespace send_tab_to_self
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h
index b9bd40d..263e266 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h
@@ -9,36 +9,21 @@
 
 #include "url/gurl.h"
 
-class GURL;
-class Profile;
-
 namespace content {
 class WebContents;
 }
 
 namespace send_tab_to_self {
 
-enum SendTabToSelfMenuType { kTab, kOmnibox, kContent, kLink };
-
 // Adds a new entry to SendTabToSelfModel when user clicks a target device. Will
 // not show a confirmation notification if |show_notification| is false.
+// TODO(crbug.com/1288843): Remove the unused |target_device_name| parameter,
+// and consider inlining this function since it has a single callsite now.
 void CreateNewEntry(content::WebContents* tab,
                     const std::string& target_device_name,
                     const std::string& target_device_guid,
                     const GURL& link_url = GURL());
 
-// Adds a new entry to SendTabToSelfModel when user clicks the single valid
-// device. Will be called when GetValidDeviceCount() == 1.
-void ShareToSingleTarget(content::WebContents* tab,
-                         const GURL& link_url = GURL());
-
-// Gets the count of valid device number.
-size_t GetValidDeviceCount(Profile* profile);
-
-// Gets the name of the single valid device. Will be called when
-// GetValidDeviceCount() == 1.
-std::u16string GetSingleTargetDeviceName(Profile* profile);
-
 }  // namespace send_tab_to_self
 
 #endif  // CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_DESKTOP_UTIL_H_
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
index a97aaf1..efeb70e 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle.cc
@@ -58,7 +58,6 @@
     bool uncertain) {
   switch (behavior) {
     case SupervisedUserURLFilter::ALLOW:
-    case SupervisedUserURLFilter::WARN:
       if (reason == supervised_user_error_page::ALLOWLIST)
         return FILTERING_BEHAVIOR_ALLOW_ALLOWLIST;
       return uncertain ? FILTERING_BEHAVIOR_ALLOW_UNCERTAIN
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter.h b/chrome/browser/supervised_user/supervised_user_url_filter.h
index e66658eb..dc3615ad 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter.h
+++ b/chrome/browser/supervised_user/supervised_user_url_filter.h
@@ -35,24 +35,17 @@
 }  // namespace network
 
 // This class manages the filtering behavior for URLs, i.e. it tells callers
-// if a URL should be allowed, blocked or warned about. It uses information
+// if a URL should be allowed or blocked. It uses information
 // from multiple sources:
-//   * A default setting (allow, block or warn).
+//   * A default setting (allow or block).
 //   * User-specified manual overrides (allow or block) for either sites
 //     (hostnames) or exact URLs, which take precedence over the previous
 //     sources.
 class SupervisedUserURLFilter {
  public:
-  // TODO(crbug/1152622): Investigate whether FilteringBehavior::WARN is in
-  // use. If it is not in use, remove it.
   // A Java counterpart will be generated for this enum.
   // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.superviseduser
-  enum FilteringBehavior {
-    ALLOW,
-    WARN,
-    BLOCK,
-    INVALID
-  };
+  enum FilteringBehavior { ALLOW, BLOCK, INVALID };
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // This enum describes the filter types of Chrome on Chrome OS, which is
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index df2e5b1b..a2beddf 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -202,13 +202,6 @@
         syncer::kSyncExtensionTypesThrottling);
   }
 
-  void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
-    SyncTest::SetUpDefaultCommandLine(command_line);
-    // These tests commit way too many entities to make the failure output
-    // practical.
-    command_line->AppendSwitch(switches::kDisableFakeServerFailureOutput);
-  }
-
   void SetUpInProcessBrowserTestFixture() override {
     SyncTest::SetUpInProcessBrowserTestFixture();
     create_services_subscription_ =
@@ -1821,16 +1814,16 @@
 
   // Setup custom quota params: to effectively never refill.
   sync_pb::ClientCommand client_command;
-  client_command.set_extension_types_max_tokens(10);
+  client_command.set_extension_types_max_tokens(3);
   client_command.set_extension_types_refill_interval_seconds(10000);
   GetFakeServer()->SetClientCommand(client_command);
 
   // Add enough bookmarks to deplete quota in the initial cycle.
   const BookmarkNode* folder = AddFolder(
       kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "Title");
-  // The quota is fully depleted in 10 messages. As the default number of
-  // entities per message on the client is 25, that requires 25*9+1 entities.
-  for (int i = 0; i < (25 * 9 + 1); i++) {
+  // The quota is fully depleted in 3 messages. As the default number of
+  // entities per message on the client is 25, that requires 25*2+1 entities.
+  for (int i = 0; i < (25 * 2 + 1); i++) {
     AddURL(kSingleProfileIndex, folder, 0, base::StringPrintf("url %u", i),
            GURL(base::StringPrintf("http://mail.google.com/%u", i)));
   }
@@ -1857,18 +1850,18 @@
 
   // Setup custom quota params: to effectively never refill.
   sync_pb::ClientCommand client_command;
-  client_command.set_extension_types_max_tokens(10);
+  client_command.set_extension_types_max_tokens(3);
   client_command.set_extension_types_refill_interval_seconds(10000);
   GetFakeServer()->SetClientCommand(client_command);
 
   // Add enough bookmarks to deplete quota in the initial cycle.
   const BookmarkNode* folder = AddFolder(
       kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "Title");
-  // The quota is fully depleted in 10 messages. As the default number of
-  // entities per message on the client is 25, that requires 25*9+1 entities.
-  // If the browser commits 100 more entities, this means 4x more commits hit
+  // The quota is fully depleted in 3 messages. As the default number of
+  // entities per message on the client is 25, that requires 25*2+1 entities.
+  // If the browser commits 100 more entities, this means 4 more commits hit
   // quota depletion.
-  for (int i = 0; i < (25 * 9 + 101); i++) {
+  for (int i = 0; i < (25 * 2 + 101); i++) {
     AddURL(kSingleProfileIndex, folder, 0, base::StringPrintf("url %u", i),
            GURL(base::StringPrintf("http://mail.google.com/%u", i)));
   }
@@ -1894,17 +1887,17 @@
 
   // Setup custom quota params: to effectively never refill.
   sync_pb::ClientCommand client_command;
-  client_command.set_extension_types_max_tokens(10);
+  client_command.set_extension_types_max_tokens(4);
   client_command.set_extension_types_refill_interval_seconds(10000);
   GetFakeServer()->SetClientCommand(client_command);
 
   // Add not enough bookmarks to deplete quota in the initial cycle.
   const BookmarkNode* folder = AddFolder(
       kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "Title");
-  // The quota is still not fully depleted after 9 messages. As the default
-  // number of entities per message on the client is 25, sending 8 messages
-  // requires 25*7+1 entities. One extra message is sent later.
-  for (int i = 0; i < (25 * 7 + 1); i++) {
+  // The quota is still not fully depleted after 3 messages. As the default
+  // number of entities per message on the client is 25, sending 2 messages
+  // requires 25+1 entities. One extra message is sent later.
+  for (int i = 0; i < (25 + 1); i++) {
     AddURL(kSingleProfileIndex, folder, 0, base::StringPrintf("url %u", i),
            GURL(base::StringPrintf("http://mail.google.com/%u", i)));
   }
@@ -1937,10 +1930,10 @@
                        DISABLED_DepleteQuotaAndRecover) {
   ASSERT_TRUE(SetupClients());
 
-  // Setup custom quota params: 10 token that effectively never refill and
-  // custom nudge delay of only 2 seconds.
+  // Setup custom quota params: to effectively never refill, and custom nudge
+  // delay of only 2 seconds.
   sync_pb::ClientCommand client_command;
-  client_command.set_extension_types_max_tokens(10);
+  client_command.set_extension_types_max_tokens(3);
   client_command.set_extension_types_refill_interval_seconds(10000);
   client_command.set_extension_types_depleted_quota_nudge_delay_seconds(2);
   GetFakeServer()->SetClientCommand(client_command);
@@ -1948,9 +1941,9 @@
   // Add enough bookmarks to deplete quota in the initial cycle.
   const BookmarkNode* folder = AddFolder(
       kSingleProfileIndex, GetOtherNode(kSingleProfileIndex), 0, "Title");
-  // The quota is fully depleted in 10 messages. As the default number of
-  // entities per message on the client is 25, that requires 25*9+1 entities.
-  for (int i = 0; i < (25 * 9 + 1); i++) {
+  // The quota is fully depleted in 3 messages. As the default number of
+  // entities per message on the client is 25, that requires 25*2+1 entities.
+  for (int i = 0; i < (25 * 2 + 1); i++) {
     AddURL(kSingleProfileIndex, folder, 0, base::StringPrintf("url %u", i),
            GURL(base::StringPrintf("http://mail.google.com/%u", i)));
   }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 7f5b71b..5aa4673 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1240,8 +1240,6 @@
       "search/search_ipc_router_policy_impl.h",
       "search/search_tab_helper.cc",
       "search/search_tab_helper.h",
-      "send_tab_to_self/send_tab_to_self_sub_menu_model.cc",
-      "send_tab_to_self/send_tab_to_self_sub_menu_model.h",
       "serial/serial_chooser.cc",
       "serial/serial_chooser.h",
       "serial/serial_chooser_controller.cc",
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
index c118f20..b09f687 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
@@ -55,7 +55,8 @@
 #include "ui/views/controls/separator.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/grid_layout.h"
+#include "ui/views/layout/box_layout_view.h"
+#include "ui/views/layout/table_layout_view.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -90,16 +91,11 @@
 constexpr auto kQuickAnimateTime = base::Milliseconds(100);
 constexpr auto kSlowAnimateTime = base::Milliseconds(200);
 
-// Resize Percentage.
-constexpr int kStretchy = 1.0;
-
-enum { kColumnSetIdTitle, kColumnSetIdTargets };
-
-void SetUpTargetColumnSet(views::GridLayout* layout) {
-  views::ColumnSet* cs = layout->AddColumnSet(kColumnSetIdTargets);
+void SetUpTargetColumns(views::TableLayoutView* view) {
   for (int i = 0; i < kMaxTargetsPerRow; i++) {
-    cs->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING, 0,
-                  views::GridLayout::ColumnSize::kFixed, kButtonWidth, 0);
+    view->AddColumn(views::LayoutAlignment::kCenter,
+                    views::LayoutAlignment::kStart, 0,
+                    views::TableLayout::ColumnSize::kFixed, kButtonWidth, 0);
   }
 }
 
@@ -301,47 +297,40 @@
 std::unique_ptr<views::View> SharesheetBubbleView::MakeScrollableTargetView(
     std::vector<TargetInfo> targets) {
   // Set up default and expanded views.
-  auto default_view = std::make_unique<views::View>();
+  auto default_view = std::make_unique<views::TableLayoutView>();
   default_view->SetProperty(views::kMarginsKey, gfx::Insets::VH(0, kSpacing));
-  auto* default_layout =
-      default_view->SetLayoutManager(std::make_unique<views::GridLayout>());
-  SetUpTargetColumnSet(default_layout);
-  default_layout->AddPaddingRow(views::GridLayout::kFixedSize, kShortSpacing);
+  SetUpTargetColumns(default_view.get());
+  default_view->AddPaddingRow(views::TableLayout::kFixedSize, kShortSpacing);
 
-  views::GridLayout* expanded_layout = nullptr;
-  std::unique_ptr<views::View> expanded_view;
+  std::unique_ptr<views::BoxLayoutView> expanded_view_container;
+  views::TableLayoutView* expanded_view_table = nullptr;
   if (targets.size() > kMaxTargetsPerRow * kMaxRowsForDefaultView) {
-    expanded_view = std::make_unique<views::View>();
-    expanded_view->SetProperty(views::kMarginsKey,
-                               gfx::Insets::VH(0, kSpacing));
-    expanded_layout =
-        expanded_view->SetLayoutManager(std::make_unique<views::GridLayout>());
-    SetUpTargetColumnSet(expanded_layout);
-    views::ColumnSet* cs_expanded_view =
-        expanded_layout->AddColumnSet(kColumnSetIdTitle);
-    cs_expanded_view->AddColumn(/* h_align */ views::GridLayout::FILL,
-                                /* v_align */ views::GridLayout::LEADING,
-                                /* resize_percent */ kStretchy,
-                                views::GridLayout::ColumnSize::kUsePreferred,
-                                /* fixed_width */ 0, /* min_width */ 0);
-    // Add Extended View Title.
-    expanded_layout->AddPaddingRow(views::GridLayout::kFixedSize,
-                                   kExpandViewPaddingTop);
-    expanded_layout->StartRow(views::GridLayout::kFixedSize, kColumnSetIdTitle);
+    expanded_view_container = std::make_unique<views::BoxLayoutView>();
+    expanded_view_container->SetProperty(views::kMarginsKey,
+                                         gfx::Insets::VH(0, kSpacing));
+    expanded_view_container->SetOrientation(
+        views::BoxLayout::Orientation::kVertical);
+
     ScopedLightModeAsDefault scoped_light_mode_as_default;
-    expanded_layout->AddView(CreateShareLabel(
-        l10n_util::GetStringUTF16(IDS_SHARESHEET_APPS_LIST_LABEL),
-        CONTEXT_SHARESHEET_BUBBLE_BODY, kSubtitleTextLineHeight,
-        AshColorProvider::Get()->GetContentLayerColor(
-            AshColorProvider::ContentLayerType::kTextColorPrimary),
-        gfx::ALIGN_CENTER));
-    expanded_layout->AddPaddingRow(views::GridLayout::kFixedSize,
-                                   kExpandViewPaddingBottom);
+    expanded_view_container
+        ->AddChildView(CreateShareLabel(
+            l10n_util::GetStringUTF16(IDS_SHARESHEET_APPS_LIST_LABEL),
+            CONTEXT_SHARESHEET_BUBBLE_BODY, kSubtitleTextLineHeight,
+            AshColorProvider::Get()->GetContentLayerColor(
+                AshColorProvider::ContentLayerType::kTextColorPrimary),
+            gfx::ALIGN_CENTER))
+        ->SetProperty(views::kMarginsKey,
+                      gfx::Insets::TLBR(kExpandViewPaddingTop, 0,
+                                        kExpandViewPaddingBottom, 0));
+
+    expanded_view_table = expanded_view_container->AddChildView(
+        std::make_unique<views::TableLayoutView>());
+    SetUpTargetColumns(expanded_view_table);
   }
 
-  PopulateLayoutsWithTargets(std::move(targets), default_layout,
-                             expanded_layout);
-  default_layout->AddPaddingRow(views::GridLayout::kFixedSize, kShortSpacing);
+  PopulateLayoutsWithTargets(std::move(targets), default_view.get(),
+                             expanded_view_table);
+  default_view->AddPaddingRow(views::TableLayout::kFixedSize, kShortSpacing);
 
   auto scrollable_view = std::make_unique<views::View>();
   auto* layout =
@@ -350,13 +339,13 @@
   layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
   default_view_ = scrollable_view->AddChildView(std::move(default_view));
   default_view_->SetID(TARGETS_DEFAULT_VIEW_ID);
-  if (expanded_layout) {
+  if (expanded_view_container) {
     expanded_view_separator_ =
         scrollable_view->AddChildView(std::make_unique<views::Separator>());
     expanded_view_separator_->SetProperty(views::kMarginsKey,
                                           gfx::Insets::VH(0, kSpacing));
-    expanded_view_ = scrollable_view->AddChildView(std::move(expanded_view));
-    expanded_view_->SetID(TARGETS_EXPANDED_VIEW_ID);
+    expanded_view_ =
+        scrollable_view->AddChildView(std::move(expanded_view_container));
     // |expanded_view_| is not visible by default.
     expanded_view_->SetVisible(false);
     expanded_view_separator_->SetVisible(false);
@@ -367,24 +356,23 @@
 
 void SharesheetBubbleView::PopulateLayoutsWithTargets(
     std::vector<TargetInfo> targets,
-    views::GridLayout* default_layout,
-    views::GridLayout* expanded_layout) {
+    views::TableLayoutView* default_view,
+    views::TableLayoutView* expanded_view) {
   // Add first kMaxRowsForDefaultView*kMaxTargetsPerRow targets to
   // |default_view| and subsequent targets to |expanded_view|.
   size_t row_count = 0;
   size_t target_counter = 0;
-  auto* layout_for_target = default_layout;
+  auto* view_for_target = default_view;
   for (auto& target : targets) {
     if (target_counter % kMaxTargetsPerRow == 0) {
       // When we've reached kMaxRowsForDefaultView switch to populating
       // |expanded_layout|.
       if (row_count == kMaxRowsForDefaultView) {
-        DCHECK(expanded_layout);
-        layout_for_target = expanded_layout;
+        DCHECK(expanded_view);
+        view_for_target = expanded_view;
       }
       ++row_count;
-      layout_for_target->StartRow(views::GridLayout::kFixedSize,
-                                  kColumnSetIdTargets);
+      view_for_target->AddRows(1, views::TableLayout::kFixedSize);
     }
     ++target_counter;
 
@@ -394,13 +382,11 @@
         target.secondary_display_name.value_or(std::u16string());
     absl::optional<gfx::ImageSkia> icon = target.icon;
 
-    auto target_view = std::make_unique<SharesheetTargetButton>(
+    view_for_target->AddChildView(std::make_unique<SharesheetTargetButton>(
         base::BindRepeating(&SharesheetBubbleView::TargetButtonPressed,
                             base::Unretained(this), target),
         display_name, secondary_display_name, icon,
-        delegator_->GetVectorIcon(display_name));
-
-    layout_for_target->AddView(std::move(target_view));
+        delegator_->GetVectorIcon(display_name)));
   }
 }
 
@@ -541,10 +527,11 @@
   }
 
   const size_t default_views = default_view_->children().size();
-  // The -1 here and +1 below account for the app list label.
+  auto* expanded_view_table =
+      show_expanded_view_ ? expanded_view_->children()[1] : nullptr;
   const size_t targets =
       default_views +
-      (show_expanded_view_ ? (expanded_view_->children().size() - 1) : 0);
+      (show_expanded_view_ ? expanded_view_table->children().size() : 0);
   const int new_target = static_cast<int>(keyboard_highlighted_target_) + delta;
   keyboard_highlighted_target_ = static_cast<size_t>(
       base::clamp(new_target, 0, static_cast<int>(targets) - 1));
@@ -552,7 +539,8 @@
   if (keyboard_highlighted_target_ < default_views) {
     default_view_->children()[keyboard_highlighted_target_]->RequestFocus();
   } else {
-    expanded_view_->children()[keyboard_highlighted_target_ + 1 - default_views]
+    expanded_view_table
+        ->children()[keyboard_highlighted_target_ - default_views]
         ->RequestFocus();
   }
   return true;
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h
index 8b84b0e5..abf82c3 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h
@@ -16,7 +16,7 @@
 #include "ui/views/widget/widget.h"
 
 namespace views {
-class GridLayout;
+class TableLayoutView;
 class Separator;
 }  // namespace views
 
@@ -83,8 +83,8 @@
   std::unique_ptr<views::View> MakeScrollableTargetView(
       std::vector<TargetInfo> targets);
   void PopulateLayoutsWithTargets(std::vector<TargetInfo> targets,
-                                  views::GridLayout* default_layout,
-                                  views::GridLayout* expanded_layout);
+                                  views::TableLayoutView* default_view,
+                                  views::TableLayoutView* expanded_view);
   void ExpandButtonPressed();
   void AnimateToExpandedState();
   void TargetButtonPressed(TargetInfo target);
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_util.h b/chrome/browser/ui/ash/sharesheet/sharesheet_util.h
index c2c2fd1..72e18bf 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_util.h
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_util.h
@@ -27,9 +27,6 @@
   // ID for the view populated with targets that shows in the default
   // sharesheet.
   TARGETS_DEFAULT_VIEW_ID,
-  // ID for the view populated with targets that shows in the expanded
-  // sharesheet.
-  TARGETS_EXPANDED_VIEW_ID,
   HEADER_VIEW_TEXT_PREVIEW_ID,
 };
 
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 493f60f9..e5daf68 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1496,8 +1496,6 @@
                                         show_main_ui);
   command_updater_.UpdateCommandEnabled(IDC_SHOW_APP_MENU, show_main_ui);
   command_updater_.UpdateCommandEnabled(IDC_SEND_TAB_TO_SELF, show_main_ui);
-  command_updater_.UpdateCommandEnabled(IDC_SEND_TAB_TO_SELF_SINGLE_TARGET,
-                                        show_main_ui);
   command_updater_.UpdateCommandEnabled(IDC_SHOW_MANAGEMENT_PAGE, true);
 
   if (base::debug::IsProfilingSupported())
diff --git a/chrome/browser/ui/browser_command_controller_unittest.cc b/chrome/browser/ui/browser_command_controller_unittest.cc
index ded8282..62e795d 100644
--- a/chrome/browser/ui/browser_command_controller_unittest.cc
+++ b/chrome/browser/ui/browser_command_controller_unittest.cc
@@ -373,8 +373,6 @@
     { IDC_ABOUT,                   true,     false,     false,     false    },
     { IDC_SHOW_APP_MENU,           true,     false,     false,     false    },
     { IDC_SEND_TAB_TO_SELF,        true,     false,     false,     false    },
-    { IDC_SEND_TAB_TO_SELF_SINGLE_TARGET,
-                                   true,     false,     false,     false    },
     { IDC_FULLSCREEN,              true,     false,     true,      true     },
     { IDC_CLOSE_TAB,               true,     true,      true,      false    },
     { IDC_CLOSE_WINDOW,            true,     true,      true,      false    },
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc
index 911ad08..d657a561 100644
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc
+++ b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc
@@ -98,6 +98,8 @@
 void SendTabToSelfBubbleController::OnDeviceSelected(
     const std::string& target_device_name,
     const std::string& target_device_guid) {
+  // TODO(crbug.com/1288843): This is being recorded for entry points other
+  // than the omnibox. Make the entry point a ShowBubble() argument.
   send_tab_to_self::RecordDeviceClicked(ShareEntryPoint::kOmniboxIcon);
   CreateNewEntry(&GetWebContents(), target_device_name, target_device_guid,
                  GURL());
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
deleted file mode 100644
index fa96501..0000000
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2019 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/send_tab_to_self/send_tab_to_self_sub_menu_model.h"
-
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
-#include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/send_tab_to_self/metrics_util.h"
-#include "components/send_tab_to_self/send_tab_to_self_model.h"
-#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
-#include "components/send_tab_to_self/target_device_info.h"
-#include "content/public/browser/web_contents.h"
-
-namespace send_tab_to_self {
-
-namespace {
-
-// Each item of submenu has its unique command id. These ids should not be same
-// with the command ids of items in the menumodel. The range of all command
-// ID's used in SendTabToSelfSubMenuModel must be equal or larger than
-// |SendTabToSelfSubMenuModel::kMinCommandId| and less than
-// |SendTabToSelfSubMenuModel::kMaxCommandId|.
-// We assume that the user doesn't have more than 10 devices, if someone has,
-// then the device list will only show the first 10 lines.
-const int kMaxDevicesShown = 10;
-
-const int kShareTabCommandId = SendTabToSelfSubMenuModel::kMinCommandId;
-const int kShareLinkCommandId = 2010;
-static_assert(
-    kShareLinkCommandId - kShareTabCommandId == kMaxDevicesShown,
-    "The range of command id for sharing tab should be no more than 10.");
-const int kMaxCommandId = SendTabToSelfSubMenuModel::kMaxCommandId;
-static_assert(
-    kMaxCommandId - kShareLinkCommandId == kMaxDevicesShown,
-    "The range of command id for sharing link should be no more than 10.");
-
-// Returns true if the command id identifies a non-link contextual menu item.
-bool IsShareTabCommandId(int command_id) {
-  return command_id >= kShareTabCommandId && command_id < kShareLinkCommandId;
-}
-
-// Returns true if the command id identifies a link contextual menu item.
-bool IsShareLinkCommandId(int command_id) {
-  return command_id >= kShareLinkCommandId && command_id < kMaxCommandId;
-}
-
-// Converts |command_id| of menu item to index in |valid_device_items_|.
-int CommandIdToVectorIndex(int command_id) {
-  if (IsShareTabCommandId(command_id)) {
-    return command_id - kShareTabCommandId;
-  }
-  if (IsShareLinkCommandId(command_id)) {
-    return command_id - kShareLinkCommandId;
-  }
-  return -1;
-}
-
-// Converts menu type to string.
-ShareEntryPoint MenuTypeToEntryPoint(SendTabToSelfMenuType menu_type) {
-  switch (menu_type) {
-    case SendTabToSelfMenuType::kTab:
-      return ShareEntryPoint::kTabMenu;
-    case SendTabToSelfMenuType::kContent:
-      return ShareEntryPoint::kContentMenu;
-    case SendTabToSelfMenuType::kOmnibox:
-      return ShareEntryPoint::kOmniboxMenu;
-    case SendTabToSelfMenuType::kLink:
-      return ShareEntryPoint::kLinkMenu;
-  }
-}
-
-}  // namespace
-
-struct SendTabToSelfSubMenuModel::ValidDeviceItem {
-  ValidDeviceItem(const std::string& device_name, const std::string& cache_guid)
-      : device_name(device_name), cache_guid(cache_guid) {}
-
-  std::string device_name;
-  std::string cache_guid;
-};
-
-SendTabToSelfSubMenuModel::SendTabToSelfSubMenuModel(
-    content::WebContents* tab,
-    SendTabToSelfMenuType menu_type)
-    : SendTabToSelfSubMenuModel(tab, menu_type, GURL()) {}
-
-SendTabToSelfSubMenuModel::SendTabToSelfSubMenuModel(
-    content::WebContents* tab,
-    SendTabToSelfMenuType menu_type,
-    const GURL& link_url)
-    : ui::SimpleMenuModel(this),
-      tab_(tab->GetWeakPtr()),
-      menu_type_(menu_type),
-      link_url_(link_url) {
-  DCHECK(tab_);
-  Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext());
-  Build(profile);
-}
-
-SendTabToSelfSubMenuModel::~SendTabToSelfSubMenuModel() = default;
-
-bool SendTabToSelfSubMenuModel::IsCommandIdEnabled(int command_id) const {
-  // Only valid device names are shown, so all items are enabled.
-  return true;
-}
-
-void SendTabToSelfSubMenuModel::ExecuteCommand(int command_id,
-                                               int event_flags) {
-  int vector_index = CommandIdToVectorIndex(command_id);
-  if (vector_index == -1) {
-    return;
-  }
-
-  send_tab_to_self::RecordDeviceClicked(MenuTypeToEntryPoint(menu_type_));
-
-  if (!tab_) {
-    // The WebContents has already been destroyed, just close the menu.
-    return;
-  }
-
-  const ValidDeviceItem& item = valid_device_items_[vector_index];
-  if (menu_type_ == SendTabToSelfMenuType::kLink) {
-    // Is sharing a link from link menu.
-    CreateNewEntry(tab_.get(), item.device_name, item.cache_guid, link_url_);
-  } else {
-    // Is sharing a tab from tab menu, content menu or omnibox menu.
-    CreateNewEntry(tab_.get(), item.device_name, item.cache_guid);
-  }
-}
-
-void SendTabToSelfSubMenuModel::Build(Profile* profile) {
-  SendTabToSelfSyncService* service =
-      SendTabToSelfSyncServiceFactory::GetForProfile(profile);
-  DCHECK(service);
-  SendTabToSelfModel* model = service->GetSendTabToSelfModel();
-  DCHECK(model);
-  std::vector<TargetDeviceInfo> devices =
-      model->GetTargetDeviceInfoSortedList();
-  if (!devices.empty()) {
-    int index = 0;
-    for (const auto& item : devices) {
-      if (index == kMaxDevicesShown) {
-        return;
-      }
-      BuildDeviceItem(item.device_name, item.cache_guid, index++);
-    }
-  }
-  return;
-}
-
-void SendTabToSelfSubMenuModel::BuildDeviceItem(const std::string& device_name,
-                                                const std::string& cache_guid,
-                                                int index) {
-  ValidDeviceItem item(device_name, cache_guid);
-  int command_id =
-      ((menu_type_ == kTab) ? kShareTabCommandId : kShareLinkCommandId) + index;
-  InsertItemAt(index, command_id, base::UTF8ToUTF16(device_name));
-  valid_device_items_.push_back(item);
-}
-
-}  //  namespace send_tab_to_self
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.h b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.h
deleted file mode 100644
index 1bb455f..0000000
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_SUB_MENU_MODEL_H_
-#define CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_SUB_MENU_MODEL_H_
-
-// This is the secondary menu model of send tab to self context menu item. This
-// menu contains the imformation of valid devices, getting form
-// SendTabToSelfModel. Every item of this menu is a valid device.
-
-#include <vector>
-
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
-#include "ui/base/models/simple_menu_model.h"
-#include "url/gurl.h"
-
-class Profile;
-
-namespace content {
-class WebContents;
-}
-
-namespace send_tab_to_self {
-
-class SendTabToSelfSubMenuModel : public ui::SimpleMenuModel,
-                                  public ui::SimpleMenuModel::Delegate {
- public:
-  static const int kMinCommandId = 2000;
-  static const int kMaxCommandId = 2020;
-
-  struct ValidDeviceItem;
-
-  SendTabToSelfSubMenuModel(content::WebContents* tab,
-                            SendTabToSelfMenuType menu_type);
-  SendTabToSelfSubMenuModel(content::WebContents* tab,
-                            SendTabToSelfMenuType menu_type,
-                            const GURL& link_url);
-
-  SendTabToSelfSubMenuModel(const SendTabToSelfSubMenuModel&) = delete;
-  SendTabToSelfSubMenuModel& operator=(const SendTabToSelfSubMenuModel&) =
-      delete;
-
-  ~SendTabToSelfSubMenuModel() override;
-
-  // Overridden from ui::SimpleMenuModel::Delegate:
-  bool IsCommandIdEnabled(int command_id) const override;
-  void ExecuteCommand(int command_id, int event_flags) override;
-
- private:
-  void Build(Profile* profile);
-  void BuildDeviceItem(const std::string& device_name,
-                       const std::string& guid,
-                       int index);
-
-  base::WeakPtr<content::WebContents> tab_;
-  const SendTabToSelfMenuType menu_type_;
-  const GURL link_url_;
-  std::vector<ValidDeviceItem> valid_device_items_;
-};
-
-}  //  namespace send_tab_to_self
-
-#endif  // CHROME_BROWSER_UI_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_SUB_MENU_MODEL_H_
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc
deleted file mode 100644
index a8f6f5b7..0000000
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2019 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/send_tab_to_self/send_tab_to_self_sub_menu_model.h"
-
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
-#include "chrome/test/base/browser_with_test_window_test.h"
-#include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
-#include "components/send_tab_to_self/test_send_tab_to_self_model.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace send_tab_to_self {
-
-namespace {
-
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SaveArg;
-
-class SendTabToSelfModelMock : public TestSendTabToSelfModel {
- public:
-  SendTabToSelfModelMock() = default;
-  ~SendTabToSelfModelMock() override = default;
-
-  MOCK_METHOD0(GetTargetDeviceInfoSortedList, std::vector<TargetDeviceInfo>());
-
-  MOCK_METHOD4(AddEntry,
-               const SendTabToSelfEntry*(const GURL&,
-                                         const std::string&,
-                                         base::Time,
-                                         const std::string&));
-
-  bool IsReady() override { return true; }
-};
-
-class TestSendTabToSelfSyncService : public SendTabToSelfSyncService {
- public:
-  TestSendTabToSelfSyncService() = default;
-  ~TestSendTabToSelfSyncService() override = default;
-
-  SendTabToSelfModel* GetSendTabToSelfModel() override {
-    return &send_tab_to_self_model_mock_;
-  }
-
- protected:
-  SendTabToSelfModelMock send_tab_to_self_model_mock_;
-};
-
-std::unique_ptr<KeyedService> BuildTestSendTabToSelfSyncService(
-    content::BrowserContext* context) {
-  return std::make_unique<TestSendTabToSelfSyncService>();
-}
-
-TargetDeviceInfo BuildTargetDeviceInfo(const std::string& device_name,
-                                       const std::string& cache_guid) {
-  return TargetDeviceInfo(device_name, device_name, cache_guid,
-                          sync_pb::SyncEnums_DeviceType_TYPE_OTHER,
-                          base::Time());
-}
-
-class SendTabToSelfSubMenuModelTest : public BrowserWithTestWindowTest {
- public:
-  SendTabToSelfSubMenuModelTest() = default;
-  ~SendTabToSelfSubMenuModelTest() override = default;
-
-  void SetUp() override {
-    BrowserWithTestWindowTest::SetUp();
-
-    // Set up all test conditions to let ShouldOfferFeature() return true.
-    GURL url("https://www.test.com");
-    AddTab(browser(), url);
-    NavigateAndCommitActiveTabWithTitle(browser(), url, u"test");
-  }
-
-  void SetUpTestService() {
-    SendTabToSelfSyncServiceFactory::GetInstance()->SetTestingFactory(
-        browser()->profile(),
-        base::BindRepeating(&BuildTestSendTabToSelfSyncService));
-  }
-
-  SendTabToSelfModelMock* GetSendTabToSelfModelMock() {
-    return static_cast<SendTabToSelfModelMock*>(
-        SendTabToSelfSyncServiceFactory::GetForProfile(browser()->profile())
-            ->GetSendTabToSelfModel());
-  }
-};
-
-TEST_F(SendTabToSelfSubMenuModelTest, ExecuteCommandTab) {
-  SetUpTestService();
-
-  SendTabToSelfModelMock* model_mock = GetSendTabToSelfModelMock();
-  std::vector<TargetDeviceInfo> devices = {
-      BuildTargetDeviceInfo("device0", "0"),
-      BuildTargetDeviceInfo("device1", "1"),
-      BuildTargetDeviceInfo("device2", "2")};
-
-  EXPECT_CALL(*model_mock, GetTargetDeviceInfoSortedList())
-      .WillRepeatedly(Return(devices));
-  SendTabToSelfSubMenuModel sub_menu_model(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      send_tab_to_self::SendTabToSelfMenuType::kTab);
-
-  std::string device_guid;
-  EXPECT_CALL(*model_mock, AddEntry(_, _, _, _))
-      .WillRepeatedly(
-          DoAll(SaveArg<3>(&device_guid), testing::Return(nullptr)));
-
-  // Check that all devices can be selected.
-  for (int i = 0; i < (int)devices.size(); i++) {
-    device_guid = std::string();
-    sub_menu_model.ExecuteCommand(SendTabToSelfSubMenuModel::kMinCommandId + i,
-                                  -1);
-    EXPECT_EQ(devices[i].cache_guid, device_guid) << "for index: " << i;
-  }
-}
-
-}  // namespace
-
-}  // namespace send_tab_to_self
diff --git a/chrome/browser/ui/tabs/tab_menu_model.cc b/chrome/browser/ui/tabs/tab_menu_model.cc
index 4733998..157cc80a 100644
--- a/chrome/browser/ui/tabs/tab_menu_model.cc
+++ b/chrome/browser/ui/tabs/tab_menu_model.cc
@@ -10,7 +10,6 @@
 #include "build/build_config.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h"
 #include "chrome/browser/ui/tabs/existing_window_sub_menu_model.h"
@@ -122,39 +121,15 @@
   if (send_tab_to_self::ShouldOfferFeature(
           tab_strip->GetWebContentsAt(index))) {
     AddSeparator(ui::NORMAL_SEPARATOR);
-
-    if (send_tab_to_self::GetValidDeviceCount(tab_strip->profile()) == 1) {
 #if BUILDFLAG(IS_MAC)
-      AddItem(TabStripModel::CommandSendTabToSelfSingleTarget,
-              l10n_util::GetStringFUTF16(
-                  IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
-                  send_tab_to_self::GetSingleTargetDeviceName(
-                      tab_strip->profile())));
+    AddItem(TabStripModel::CommandSendTabToSelf,
+            l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF));
 #else
-      AddItemWithIcon(TabStripModel::CommandSendTabToSelfSingleTarget,
-                      l10n_util::GetStringFUTF16(
-                          IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
-                          (send_tab_to_self::GetSingleTargetDeviceName(
-                              tab_strip->profile()))),
-                      ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
+    AddItemWithIcon(
+        TabStripModel::CommandSendTabToSelf,
+        l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF),
+        ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
 #endif
-    } else {
-      send_tab_to_self_sub_menu_model_ =
-          std::make_unique<send_tab_to_self::SendTabToSelfSubMenuModel>(
-              tab_strip->GetWebContentsAt(index),
-              send_tab_to_self::SendTabToSelfMenuType::kTab);
-#if BUILDFLAG(IS_MAC)
-      AddSubMenuWithStringId(TabStripModel::CommandSendTabToSelf,
-                             IDS_CONTEXT_MENU_SEND_TAB_TO_SELF,
-                             send_tab_to_self_sub_menu_model_.get());
-#else
-      AddSubMenuWithStringIdAndIcon(
-          TabStripModel::CommandSendTabToSelf,
-          IDS_CONTEXT_MENU_SEND_TAB_TO_SELF,
-          send_tab_to_self_sub_menu_model_.get(),
-          ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
-#endif
-    }
   }
 
   AddSeparator(ui::NORMAL_SEPARATOR);
diff --git a/chrome/browser/ui/tabs/tab_menu_model.h b/chrome/browser/ui/tabs/tab_menu_model.h
index 174daca0..9daac48 100644
--- a/chrome/browser/ui/tabs/tab_menu_model.h
+++ b/chrome/browser/ui/tabs/tab_menu_model.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_UI_TABS_TAB_MENU_MODEL_H_
 
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/models/simple_menu_model.h"
 
@@ -24,7 +23,6 @@
 // likely having to expand it later on:
 //   ExistingTabGroupSubMenuModel
 //   ExistingWindowSubMenuModel
-//   SendTabToSelfSubMenuModel
 class TabMenuModel : public ui::SimpleMenuModel {
  public:
   TabMenuModel(ui::SimpleMenuModel::Delegate* delegate,
@@ -43,10 +41,6 @@
   std::unique_ptr<ui::SimpleMenuModel> add_to_existing_group_submenu_;
   std::unique_ptr<ui::SimpleMenuModel> add_to_existing_window_submenu_;
 
-  // Send tab to self submenu.
-  std::unique_ptr<send_tab_to_self::SendTabToSelfSubMenuModel>
-      send_tab_to_self_sub_menu_model_;
-
   raw_ptr<TabMenuModelDelegate> tab_menu_model_delegate_;
 };
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index c2fd689..be0988cd 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -30,7 +30,6 @@
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
 #include "chrome/browser/ui/browser.h"
@@ -38,6 +37,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/read_later/reading_list_model_factory.h"
+#include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.h"
 #include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
@@ -1376,9 +1376,6 @@
     case CommandSendTabToSelf:
       return true;
 
-    case CommandSendTabToSelfSingleTarget:
-      return true;
-
     case CommandAddToReadLater:
       return true;
 
@@ -1493,10 +1490,10 @@
       break;
     }
 
-    case CommandSendTabToSelfSingleTarget: {
-      send_tab_to_self::ShareToSingleTarget(GetWebContentsAt(context_index));
-      send_tab_to_self::RecordDeviceClicked(
-          send_tab_to_self::ShareEntryPoint::kTabMenu);
+    case CommandSendTabToSelf: {
+      send_tab_to_self::SendTabToSelfBubbleController::
+          CreateOrGetFromWebContents(GetWebContentsAt(context_index))
+              ->ShowBubble();
       break;
     }
 
@@ -1675,9 +1672,6 @@
     case CommandSendTabToSelf:
       *browser_cmd = IDC_SEND_TAB_TO_SELF;
       break;
-    case CommandSendTabToSelfSingleTarget:
-      *browser_cmd = IDC_SEND_TAB_TO_SELF_SINGLE_TARGET;
-      break;
     case CommandCloseTab:
       *browser_cmd = IDC_CLOSE_TAB;
       break;
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index 12f958e..2feb81d 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -588,7 +588,6 @@
     CommandToggleGrouped,
     CommandToggleSiteMuted,
     CommandSendTabToSelf,
-    CommandSendTabToSelfSingleTarget,
     CommandAddToReadLater,
     CommandAddToNewGroup,
     CommandAddToExistingGroup,
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index bbec78cf..a338dc5e 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -29,7 +29,6 @@
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/sharing/features.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 4445631..a8cfb58 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -27,13 +27,13 @@
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/history_clusters/history_clusters_tab_helper.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/omnibox/clipboard_utils.h"
 #include "chrome/browser/ui/omnibox/omnibox_theme.h"
+#include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -256,14 +256,6 @@
 }
 
 void OmniboxViewViews::OnTabChanged(content::WebContents* web_contents) {
-  // The context menu holds a reference to send_tab_to_self_sub_menu_model_;
-  // invalidate it here so we can destroy those below.
-  InvalidateContextMenu();
-
-  // This has a reference to the WebContents, which might be being destroyed
-  // here:
-  send_tab_to_self_sub_menu_model_.reset();
-
   const OmniboxState* state = static_cast<OmniboxState*>(
       web_contents->GetUserData(&OmniboxState::kKey));
   model()->RestoreState(state ? &state->model_state : nullptr);
@@ -514,11 +506,10 @@
       location_bar_view_->command_updater()->ExecuteCommand(command_id);
       return;
 
-    case IDC_SEND_TAB_TO_SELF_SINGLE_TARGET:
-      send_tab_to_self::ShareToSingleTarget(
-          location_bar_view_->GetWebContents());
-      send_tab_to_self::RecordDeviceClicked(
-          send_tab_to_self::ShareEntryPoint::kOmniboxMenu);
+    case IDC_SEND_TAB_TO_SELF:
+      send_tab_to_self::SendTabToSelfBubbleController::
+          CreateOrGetFromWebContents(location_bar_view_->GetWebContents())
+              ->ShowBubble();
       return;
 
     // These commands do invoke the popup.
@@ -1924,23 +1915,9 @@
     menu_contents->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR);
   }
 
-  if (send_tab_to_self::GetValidDeviceCount(location_bar_view_->profile()) ==
-      1) {
-    menu_contents->InsertItemAt(
-        index, IDC_SEND_TAB_TO_SELF_SINGLE_TARGET,
-        l10n_util::GetStringFUTF16(
-            IDS_CONTEXT_MENU_SEND_TAB_TO_SELF_SINGLE_TARGET,
-            send_tab_to_self::GetSingleTargetDeviceName(
-                location_bar_view_->profile())));
-  } else {
-    send_tab_to_self_sub_menu_model_ =
-        std::make_unique<send_tab_to_self::SendTabToSelfSubMenuModel>(
-            location_bar_view_->GetWebContents(),
-            send_tab_to_self::SendTabToSelfMenuType::kOmnibox);
-    menu_contents->InsertSubMenuWithStringIdAt(
-        index, IDC_SEND_TAB_TO_SELF, IDS_CONTEXT_MENU_SEND_TAB_TO_SELF,
-        send_tab_to_self_sub_menu_model_.get());
-  }
+  menu_contents->InsertItemAt(
+      index, IDC_SEND_TAB_TO_SELF,
+      l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF));
 #if !BUILDFLAG(IS_MAC)
   menu_contents->SetIcon(index,
                          ui::ImageModel::FromVectorIcon(kSendTabToSelfIcon));
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.h b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
index 1cb7e262..f8a3de4 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.h
@@ -17,7 +17,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
-#include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.h"
 #include "components/omnibox/browser/omnibox_view.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/search_engines/template_url_service.h"
@@ -412,11 +411,6 @@
   base::ScopedObservation<TemplateURLService, TemplateURLServiceObserver>
       scoped_template_url_service_observation_{this};
 
-  // Send tab to self submenu. This is tied to a WebContents, it's created when
-  // the user opens the menu and destroyed when the tab changes.
-  std::unique_ptr<send_tab_to_self::SendTabToSelfSubMenuModel>
-      send_tab_to_self_sub_menu_model_;
-
   PrefChangeRegistrar pref_change_registrar_;
 
   base::WeakPtrFactory<OmniboxViewViews> weak_factory_{this};
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index b6ae559..719fd1f 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -30,6 +30,7 @@
 #include "components/site_engagement/content/site_engagement_service.h"
 #include "components/webapps/browser/banners/app_banner_manager.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/views/view_class_properties.h"
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_browsertest.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_browsertest.cc
index a5807e6..e81d253 100644
--- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_browsertest.cc
+++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_bubble_view_impl_browsertest.cc
@@ -4,7 +4,6 @@
 
 #include "base/callback_helpers.h"
 #include "base/time/time.h"
-#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -74,13 +73,7 @@
   }
 };
 
-// crbug.com/1272360
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
-#define MAYBE_InvokeUi_default DISABLED_InvokeUi_default
-#else
-#define MAYBE_InvokeUi_default InvokeUi_default
-#endif
-IN_PROC_BROWSER_TEST_F(SendTabToSelfBubbleTest, MAYBE_InvokeUi_default) {
+IN_PROC_BROWSER_TEST_F(SendTabToSelfBubbleTest, InvokeUi_default) {
   ShowAndVerifyUi();
 }
 
diff --git a/chrome/browser/ui/views/side_search/side_search_icon_view.cc b/chrome/browser/ui/views/side_search/side_search_icon_view.cc
index 305bb38f..7ec9f7f 100644
--- a/chrome/browser/ui/views/side_search/side_search_icon_view.cc
+++ b/chrome/browser/ui/views/side_search/side_search_icon_view.cc
@@ -69,6 +69,7 @@
   if (should_show && !was_visible &&
       side_search_config->should_show_page_action_label()) {
     side_search_config->set_should_show_page_action_label(false);
+    should_extend_label_shown_duration_ = true;
     AnimateIn(absl::nullopt);
   }
 }
@@ -97,5 +98,26 @@
       IDS_TOOLTIP_SIDE_SEARCH_TOOLBAR_BUTTON_NOT_ACTIVATED);
 }
 
+void SideSearchIconView::AnimationProgressed(const gfx::Animation* animation) {
+  PageActionIconView::AnimationProgressed(animation);
+  // When the label is fully revealed pause the animation for
+  // kLabelPersistDuration before resuming the animation and allowing the label
+  // to animate out.
+  // TODO(crbug.com/1314206): This approach of inspecting the animation progress
+  // to extend the animation duration is quite hacky. This should be removed and
+  // the IconLabelBubbleView API expanded to support a finer level of control.
+  constexpr double kAnimationValueWhenLabelFullyShown = 0.5;
+  constexpr base::TimeDelta kLabelPersistDuration = base::Milliseconds(3200);
+  if (should_extend_label_shown_duration_ &&
+      GetAnimationValue() >= kAnimationValueWhenLabelFullyShown) {
+    should_extend_label_shown_duration_ = false;
+    PauseAnimation();
+    animate_out_timer_.Start(
+        FROM_HERE, kLabelPersistDuration,
+        base::BindOnce(&SideSearchIconView::UnpauseAnimation,
+                       base::Unretained(this)));
+  }
+}
+
 BEGIN_METADATA(SideSearchIconView, PageActionIconView)
 END_METADATA
diff --git a/chrome/browser/ui/views/side_search/side_search_icon_view.h b/chrome/browser/ui/views/side_search/side_search_icon_view.h
index 42feca1c..43f91ee 100644
--- a/chrome/browser/ui/views/side_search/side_search_icon_view.h
+++ b/chrome/browser/ui/views/side_search/side_search_icon_view.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_UI_VIEWS_SIDE_SEARCH_SIDE_SEARCH_ICON_VIEW_H_
 
 #include "base/memory/raw_ptr.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/side_search/default_search_icon_source.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -34,12 +35,22 @@
   const gfx::VectorIcon& GetVectorIcon() const override;
   ui::ImageModel GetSizedIconImage(int size) const override;
   std::u16string GetTextForTooltipAndAccessibleName() const override;
+  void AnimationProgressed(const gfx::Animation* animation) override;
 
  private:
   raw_ptr<Browser> browser_ = nullptr;
 
   // Source for the default search icon image used by this page action.
   DefaultSearchIconSource default_search_icon_source_;
+
+  // Animates out the side search icon label after a fixed period of time. This
+  // keeps the label visible for long enough to give users an opportunity to
+  // read the label text.
+  base::OneShotTimer animate_out_timer_;
+
+  // Boolean that tracks whether we should extend the duration for which the
+  // label is shown when it animates in.
+  bool should_extend_label_shown_duration_ = false;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_SIDE_SEARCH_SIDE_SEARCH_ICON_VIEW_H_
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 b6f4e08c..497e7e8 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
@@ -642,9 +642,9 @@
 #if !BUILDFLAG(IS_CHROMEOS)
 void WebAppIntegrationTestDriver::InstallLocally(const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state) << "App not installed: " << site_mode;
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
+      << "No app installed for site: " << site_mode;
   content::TestWebUI test_web_ui;
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetWebContentsAt(0);
@@ -653,7 +653,7 @@
   TestAppLauncherHandler handler(/*extension_service=*/nullptr, provider(),
                                  &test_web_ui);
   base::ListValue web_app_ids;
-  web_app_ids.Append(app_state->id);
+  web_app_ids.Append(app_id);
 
   WebAppTestInstallWithOsHooksObserver observer(profile());
   observer.BeginListening();
@@ -773,11 +773,9 @@
 void WebAppIntegrationTestDriver::LaunchFromChromeApps(
     const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
-  auto app_id = app_state->id;
   WebAppRegistrar& app_registrar = provider()->registrar();
   DisplayMode display_mode = app_registrar.GetAppEffectiveDisplayMode(app_id);
   if (display_mode == blink::mojom::DisplayMode::kBrowser) {
@@ -829,11 +827,11 @@
 void WebAppIntegrationTestDriver::LaunchFromMenuOption(
     const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
+      << "No app installed for site: " << site_mode;
+
   NavigateBrowser(site_mode);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state);
-  auto app_id = app_state->id;
 
   content::WindowedNotificationObserver app_loaded_observer(
       content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
@@ -846,7 +844,7 @@
   active_app_id_ = app_id;
 
   ASSERT_TRUE(AppBrowserController::IsForWebApp(app_browser(), active_app_id_));
-  ASSERT_EQ(GetBrowserWindowTitle(app_browser()), app_state->name);
+  EXPECT_EQ(app_browser()->app_controller()->app_id(), app_id);
   AfterStateChangeAction();
 }
 
@@ -854,10 +852,9 @@
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state);
-  auto app_id = app_state->id;
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
+      << "No app installed for site: " << site_mode;
 
   WebAppRegistrar& app_registrar = provider()->registrar();
   DisplayMode display_mode = app_registrar.GetAppEffectiveDisplayMode(app_id);
@@ -869,8 +866,7 @@
     browser_added_waiter.Wait();
     app_browser_ = browser_added_waiter.browser_added();
     active_app_id_ = app_id;
-    EXPECT_EQ(app_browser()->app_controller()->app_id(), app_state->id);
-    EXPECT_EQ(GetBrowserWindowTitle(app_browser()), app_state->name);
+    EXPECT_EQ(app_browser()->app_controller()->app_id(), app_id);
   } else {
     LaunchAppStartupBrowserCreator(app_id);
     auto* app_banner_manager =
@@ -888,11 +884,6 @@
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
-      << "No app installed for site: " << site_mode;
-
   Browser* app_browser = GetAppBrowserForSite(site_mode);
   ASSERT_TRUE(app_browser);
 
@@ -926,9 +917,8 @@
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
 
   content::TestWebUI test_web_ui;
@@ -939,7 +929,7 @@
   TestAppLauncherHandler handler(/*extension_service=*/nullptr, provider(),
                                  &test_web_ui);
   base::ListValue web_app_ids;
-  web_app_ids.Append(app_state->id);
+  web_app_ids.Append(app_id);
   content::WebContentsAddedObserver nav_observer;
   handler.HandleShowAppInfo(&web_app_ids);
   // Wait for new web content to be created.
@@ -1118,11 +1108,9 @@
 
 void WebAppIntegrationTestDriver::SetOpenInTab(const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
-  auto app_id = app_state->id;
   // Will need to add feature flag based condition for web app settings page
 #if BUILDFLAG(IS_CHROMEOS)
   auto& sync_bridge = WebAppProvider::GetForTest(profile())->sync_bridge();
@@ -1138,11 +1126,9 @@
 void WebAppIntegrationTestDriver::SetOpenInWindow(
     const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
-  auto app_id = app_state->id;
   // Will need to add feature flag based condition for web app settings page.
 #if BUILDFLAG(IS_CHROMEOS)
   auto& sync_bridge = WebAppProvider::GetForTest(profile())->sync_bridge();
@@ -1190,9 +1176,9 @@
 void WebAppIntegrationTestDriver::UninstallFromList(
     const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state) << "App not installed: " << site_mode;
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
+      << "No app installed for site: " << site_mode;
 
   WebAppTestUninstallObserver observer(profile());
   observer.BeginListening();
@@ -1204,7 +1190,7 @@
       apps::AppServiceProxyFactory::GetForProfile(profile());
   base::RunLoop run_loop;
   app_service_proxy->UninstallForTesting(
-      app_state->id, nullptr,
+      app_id, nullptr,
       base::BindLambdaForTesting([&](bool) { run_loop.Quit(); }));
   run_loop.Run();
 
@@ -1215,8 +1201,8 @@
   // call the normal method.
   apps::AppServiceProxy* app_service_proxy =
       apps::AppServiceProxyFactory::GetForProfile(profile());
-  app_service_proxy->Uninstall(app_state->id,
-                               apps::mojom::UninstallSource::kAppList, nullptr);
+  app_service_proxy->Uninstall(app_id, apps::mojom::UninstallSource::kAppList,
+                               nullptr);
 #else
   content::TestWebUI test_web_ui;
   content::WebContents* web_contents =
@@ -1226,7 +1212,7 @@
   TestAppLauncherHandler handler(/*extension_service=*/nullptr, provider(),
                                  &test_web_ui);
   base::ListValue web_app_ids;
-  web_app_ids.Append(app_state->id);
+  web_app_ids.Append(app_id);
   handler.HandleUninstallApp(&web_app_ids);
 #endif
 
@@ -1239,17 +1225,14 @@
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
-  auto app_id = app_state->id;
   WebAppTestUninstallObserver uninstall_observer(profile());
   uninstall_observer.BeginListening({app_id});
 
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
-  if (web_contents->GetURL() !=
-      GURL("chrome://app-settings/" + app_state->id)) {
+  if (web_contents->GetURL() != GURL("chrome://app-settings/" + app_id)) {
     OpenAppSettingsFromChromeApps(site_mode);
     CheckBrowserNavigationIsAppSettings(site_mode);
   }
@@ -1276,11 +1259,9 @@
 void WebAppIntegrationTestDriver::UninstallFromMenu(
     const std::string& site_mode) {
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
-  auto app_id = app_state->id;
   WebAppTestUninstallObserver observer(profile());
   observer.BeginListening({app_id});
 
@@ -1348,11 +1329,9 @@
     const std::string& site_mode) {
 #if BUILDFLAG(IS_WIN)
   BeforeStateChangeAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
-  auto app_id = app_state->id;
   WebAppTestUninstallObserver observer(profile());
   observer.BeginListening({app_id});
 
@@ -1419,7 +1398,8 @@
   BeforeStateCheckAction(__FUNCTION__);
   ASSERT_FALSE(active_app_id_.empty());
   ASSERT_TRUE(app_browser());
-  GURL url = app_browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
+  GURL url =
+      app_browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL();
   EXPECT_EQ(url, provider()->registrar().GetAppStartUrl(active_app_id_));
   AfterStateCheckAction();
 }
@@ -1428,13 +1408,13 @@
     const std::string& site_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
   BeforeStateCheckAction(__FUNCTION__);
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      after_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value());
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
+      << "No app installed for site: " << site_mode;
 
   ASSERT_TRUE(browser());
   GURL url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
-  EXPECT_EQ(url, GURL("chrome://app-settings/" + app_state->id));
+  EXPECT_EQ(url, GURL("chrome://app-settings/" + app_id));
   AfterStateCheckAction();
 #else
   NOTREACHED() << "Not implemented on Chrome OS.";
@@ -1912,6 +1892,20 @@
   DCHECK_EQ(*after_state_change_action_state_, *ConstructStateSnapshot());
 }
 
+AppId WebAppIntegrationTestDriver::GetAppIdBySiteMode(
+    const std::string& site_mode) {
+  DCHECK(g_site_mode_to_relative_manifest_id.contains(site_mode));
+  std::string manifest_id =
+      g_site_mode_to_relative_manifest_id.find(site_mode)->second;
+
+  DCHECK(g_site_mode_to_relative_start_url.contains(site_mode));
+  auto relative_start_url =
+      g_site_mode_to_relative_start_url.find(site_mode)->second;
+  GURL start_url = embedded_test_server()->GetURL(relative_start_url);
+
+  return GenerateAppId(manifest_id, start_url);
+}
+
 GURL WebAppIntegrationTestDriver::GetAppStartURL(const std::string& site_mode) {
   DCHECK(g_site_mode_to_relative_start_url.contains(site_mode));
   auto start_url_path =
@@ -1929,16 +1923,7 @@
     return absl::nullopt;
   }
 
-  DCHECK(g_site_mode_to_relative_manifest_id.contains(site_mode));
-  std::string manifest_id =
-      g_site_mode_to_relative_manifest_id.find(site_mode)->second;
-
-  DCHECK(g_site_mode_to_relative_start_url.contains(site_mode));
-  auto relative_start_url =
-      g_site_mode_to_relative_start_url.find(site_mode)->second;
-  GURL start_url = embedded_test_server()->GetURL(relative_start_url);
-
-  std::string app_id = GenerateAppId(manifest_id, start_url);
+  AppId app_id = GetAppIdBySiteMode(site_mode);
 
   auto it = profile_state->apps.find(app_id);
   return it == profile_state->apps.end()
@@ -2296,15 +2281,14 @@
     const std::string& site_mode,
     apps::RunOnOsLoginMode login_mode) {
 #if !BUILDFLAG(IS_CHROMEOS)
-  absl::optional<AppState> app_state = GetAppBySiteMode(
-      before_state_change_action_state_.get(), profile(), site_mode);
-  ASSERT_TRUE(app_state.has_value())
+  AppId app_id = GetAppIdBySiteMode(site_mode);
+  ASSERT_TRUE(provider()->registrar().GetAppById(app_id))
       << "No app installed for site: " << site_mode;
   base::RunLoop run_loop;
   web_app::SetRunOnOsLoginOsHooksChangedCallbackForTesting(
       run_loop.QuitClosure());
   auto app_management_page_handler = CreateAppManagementPageHandler(profile());
-  app_management_page_handler.SetRunOnOsLoginMode(app_state->id, login_mode);
+  app_management_page_handler.SetRunOnOsLoginMode(app_id, login_mode);
   run_loop.Run();
 #endif
 }
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
index db862b1c..5638afb 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.h
@@ -163,7 +163,6 @@
   void InstallPolicyAppTabbedShortcut(const std::string& site_mode);
   void InstallPolicyAppWindowedNoShortcut(const std::string& site_mode);
   void InstallPolicyAppWindowedShortcut(const std::string& site_mode);
-  // These functions install apps which are tabbed and creates shortcuts.
   void ApplyRunOnOsLoginPolicyAllowed(const std::string& site_mode);
   void ApplyRunOnOsLoginPolicyBlocked(const std::string& site_mode);
   void ApplyRunOnOsLoginPolicyRunWindowed(const std::string& site_mode);
@@ -244,6 +243,7 @@
   // Must be called at the end of every state check action function.
   void AfterStateCheckAction();
 
+  AppId GetAppIdBySiteMode(const std::string& site_mode);
   GURL GetAppStartURL(const std::string& site_mode);
   absl::optional<AppState> GetAppBySiteMode(StateSnapshot* state_snapshot,
                                             Profile* profile,
@@ -305,6 +305,8 @@
   PageActionIconView* pwa_install_view();
   PageActionIconView* intent_picker_view();
 
+  base::flat_set<AppId> previous_manifest_updates_;
+
   // Variables used to facilitate waiting for manifest updates, as there isn't
   // a formal 'action' that a user can take to wait for this, as it happens
   // behind the scenes.
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.h b/chrome/browser/ui/web_applications/app_browser_controller.h
index 9a592c9..1f4dd95 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/app_browser_controller.h
@@ -22,6 +22,7 @@
 #include "third_party/skia/include/core/SkRegion.h"
 #include "ui/color/color_provider.h"
 #include "ui/color/color_provider_manager.h"
+#include "url/gurl.h"
 
 class Browser;
 class BrowserThemePack;
diff --git a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
index 9611acd..69754397 100644
--- a/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
+++ b/chrome/browser/ui/webui/access_code_cast/access_code_cast_handler_unittest.cc
@@ -75,13 +75,10 @@
   MockAccessCodeCastSinkService(
       Profile* profile,
       MediaRouter* media_router,
-      CastMediaSinkServiceImpl* cast_media_sink_service_impl,
-      DiscoveryNetworkMonitor* network_monitor)
+      CastMediaSinkServiceImpl* cast_media_sink_service_impl)
       : AccessCodeCastSinkService(profile,
                                   media_router,
-                                  cast_media_sink_service_impl,
-                                  network_monitor,
-                                  profile->GetPrefs()) {}
+                                  cast_media_sink_service_impl) {}
   ~MockAccessCodeCastSinkService() override = default;
 
   MOCK_METHOD(void,
@@ -151,8 +148,7 @@
 
     access_code_cast_sink_service_ =
         std::make_unique<MockAccessCodeCastSinkService>(
-            profile_, router_, mock_cast_media_sink_service_impl_.get(),
-            discovery_network_monitor_.get());
+            profile_, router_, mock_cast_media_sink_service_impl_.get());
     access_code_cast_sink_service_->SetTaskRunnerForTest(
         mock_time_task_runner_);
 
diff --git a/chrome/browser/ui/webui/family_link_user_internals/OWNERS b/chrome/browser/ui/webui/family_link_user_internals/OWNERS
new file mode 100644
index 0000000..06a8064
--- /dev/null
+++ b/chrome/browser/ui/webui/family_link_user_internals/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/supervised_user/OWNERS
diff --git a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
index 09406dd0..3c33023 100644
--- a/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/family_link_user_internals/family_link_user_internals_message_handler.cc
@@ -75,8 +75,6 @@
   switch (behavior) {
     case SupervisedUserURLFilter::ALLOW:
       return "Allow";
-    case SupervisedUserURLFilter::WARN:
-      return "Warn";
     case SupervisedUserURLFilter::BLOCK:
       return "Block";
     case SupervisedUserURLFilter::INVALID:
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ddb9127a..87c6202c8 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1649289578-fe71f71af9afbec36ec003ab2700468ff94f2d05.profdata
+chrome-linux-main-1649310996-5e063cb1d5e4658ca61f1e533be67e725b68512f.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index ab711ca..56cac98 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1649289578-a7a9c144775f8c87a5e245f56507f984cbcab9fe.profdata
+chrome-mac-arm-main-1649310996-f6ed5735ec31e60a17b876f30204330679968f62.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index c8dff5f..6753e04 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1649267980-87dc5cb51853d07b4e965e33759a97850746852f.profdata
+chrome-mac-main-1649310996-ddc925702eeb863ce303c15107e1a9cd7c649f03.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index a422636..c999a8e 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1649278527-46a91bf9bffccc5a0271d7ea73e46552f392ae75.profdata
+chrome-win32-main-1649310996-21d2346031afc22c7896fe535104c79f8a00c14a.profdata
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index c352572..2574ad7 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -321,7 +321,8 @@
   all,
   audio,
   image,
-  video
+  video,
+  document
 };
 
 enum CrostiniEventType {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cc4b5ca3..f00ba80 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6532,7 +6532,6 @@
       "../browser/ui/search/ntp_user_data_logger_unittest.cc",
       "../browser/ui/search/search_ipc_router_policy_unittest.cc",
       "../browser/ui/search/search_ipc_router_unittest.cc",
-      "../browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc",
       "../browser/ui/serial/serial_chooser_controller_unittest.cc",
       "../browser/ui/tab_contents/chrome_web_contents_menu_helper_unittest.cc",
       "../browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc",
diff --git a/chrome/test/data/webui/settings/about_page_tests.ts b/chrome/test/data/webui/settings/about_page_tests.ts
index 4fd700f1..86ad24e2 100644
--- a/chrome/test/data/webui/settings/about_page_tests.ts
+++ b/chrome/test/data/webui/settings/about_page_tests.ts
@@ -82,7 +82,9 @@
     return Promise.resolve();
     // </if>
 
+    // <if expr="not chromeos_ash">
     return aboutBrowserProxy.whenCalled('refreshUpdateStatus');
+    // </if>
   }
 
   // <if expr="not chromeos_ash">
diff --git a/chrome/test/data/webui/settings/password_check_test.ts b/chrome/test/data/webui/settings/password_check_test.ts
index fcd2d0f..fe775f6 100644
--- a/chrome/test/data/webui/settings/password_check_test.ts
+++ b/chrome/test/data/webui/settings/password_check_test.ts
@@ -148,8 +148,9 @@
       return loadTimeData.getString('leakedPassword');
     case chrome.passwordsPrivate.CompromiseType.PHISHED_AND_LEAKED:
       return loadTimeData.getString('phishedAndLeakedPassword');
+    default:
+      assertNotReached();
   }
-  assertNotReached();
 }
 
 /**
diff --git a/chrome/test/data/webui/settings/payments_section_test.ts b/chrome/test/data/webui/settings/payments_section_test.ts
index 473f1e2..d36d50e 100644
--- a/chrome/test/data/webui/settings/payments_section_test.ts
+++ b/chrome/test/data/webui/settings/payments_section_test.ts
@@ -447,7 +447,6 @@
         // Fail the test because the save event should not be called
         // when cancel is clicked.
         assertTrue(false);
-        done();
       });
 
       eventToPromise('close', creditCardDialog).then(function() {
diff --git a/chromeos/components/hps/hps_configuration.cc b/chromeos/components/hps/hps_configuration.cc
index fb5c0a66..ae05c0d 100644
--- a/chromeos/components/hps/hps_configuration.cc
+++ b/chromeos/components/hps/hps_configuration.cc
@@ -22,10 +22,10 @@
     base::Seconds(4);
 
 // Default quick dim delay to configure power_manager.
-constexpr base::TimeDelta kQuickDimDelayDefault = base::Seconds(6);
+constexpr base::TimeDelta kQuickDimDelayDefault = base::Seconds(10);
 
 // Default quick lock delay to configure power_manager.
-constexpr base::TimeDelta kQuickLockDelayDefault = base::Seconds(126);
+constexpr base::TimeDelta kQuickLockDelayDefault = base::Seconds(130);
 
 // Default value determines whether send feedback to configure power_manager.
 constexpr int kShouldSendFeedbackIfUndimmed = false;
diff --git a/chromeos/crosapi/mojom/login.mojom b/chromeos/crosapi/mojom/login.mojom
index 75c4968..bcfc66e 100644
--- a/chromeos/crosapi/mojom/login.mojom
+++ b/chromeos/crosapi/mojom/login.mojom
@@ -18,6 +18,14 @@
   string oauth_code@3;
 };
 
+// Interface for the cleanup triggered observer. The cleanup is signaled to
+// Lacros from ash-chrome as part of the |endSharedSession()| API call.
+[Stable, Uuid="883697b4-1fe3-4144-95ae-84afbea67e5c"]
+interface LacrosCleanupTriggeredObserver {
+  // Triggers a session cleanup for the Lacros browser.
+  OnLacrosCleanupTriggered@0() => (string? error);
+};
+
 // This API allows Lacros to call the chrome.login extension API.
 [Stable, Uuid="639e9f04-981f-46d1-91da-583c2958265b"]
 interface Login {
@@ -92,4 +100,9 @@
   // to at launch.
   [MinVersion=1]
   UnlockCurrentSession@12(string password) => (string? error);
+
+  // Adds an observer for the cleanup triggered event.
+  [MinVersion=2]
+  AddLacrosCleanupTriggeredObserver@13(
+      pending_remote<LacrosCleanupTriggeredObserver> observer);
 };
diff --git a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
index 7f12a2a..768af5bb 100644
--- a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
+++ b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
@@ -46,6 +46,8 @@
 
     ON_CALL(mock_action_delegate_, GetUserData)
         .WillByDefault(Return(&user_data_));
+    ON_CALL(mock_action_delegate_, GetUserModel)
+        .WillByDefault(Return(&user_model_));
     ON_CALL(mock_action_delegate_, GetWebsiteLoginManager)
         .WillByDefault(Return(&mock_website_login_manager_));
   }
diff --git a/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc b/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
index fe23f94..bc2397bc 100644
--- a/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/get_element_status_action_unittest.cc
@@ -51,6 +51,8 @@
 
     ON_CALL(mock_action_delegate_, GetUserData)
         .WillByDefault(Return(&user_data_));
+    ON_CALL(mock_action_delegate_, GetUserModel)
+        .WillByDefault(Return(&user_model_));
     ON_CALL(mock_action_delegate_, GetWebController)
         .WillByDefault(Return(&mock_web_controller_));
     ON_CALL(mock_action_delegate_, GetWebsiteLoginManager)
diff --git a/components/autofill_assistant/browser/user_data_util.cc b/components/autofill_assistant/browser/user_data_util.cc
index 0a175dab..405d55ee 100644
--- a/components/autofill_assistant/browser/user_data_util.cc
+++ b/components/autofill_assistant/browser/user_data_util.cc
@@ -636,21 +636,39 @@
 
 ClientStatus GetClientMemoryStringValue(const std::string& client_memory_key,
                                         const UserData* user_data,
+                                        const UserModel* user_model,
                                         std::string* out_value) {
   if (client_memory_key.empty()) {
     return ClientStatus(INVALID_ACTION);
   }
-  if (!user_data->HasAdditionalValue(client_memory_key) ||
-      user_data->GetAdditionalValue(client_memory_key)
-              ->strings()
-              .values()
-              .size() != 1) {
+  bool user_data_has_value = user_data->HasAdditionalValue(client_memory_key) &&
+                             user_data->GetAdditionalValue(client_memory_key)
+                                     ->strings()
+                                     .values()
+                                     .size() == 1;
+  bool user_model_has_value =
+      user_model->GetValue(client_memory_key).has_value() &&
+      user_model->GetValue(client_memory_key)->strings().values_size() == 1;
+  if (!user_data_has_value && !user_model_has_value) {
     VLOG(1) << "Requested key '" << client_memory_key
-            << "' not available in client memory";
+            << "' not present in user data and user model";
+    return ClientStatus(PRECONDITION_FAILED);
+  } else if (user_data_has_value && user_model_has_value &&
+             user_data->GetAdditionalValue(client_memory_key)
+                     ->strings()
+                     .values(0) !=
+                 user_model->GetValue(client_memory_key)->strings().values(0)) {
+    VLOG(1) << "Requested key '" << client_memory_key
+            << "' has different values in user data and user model";
     return ClientStatus(PRECONDITION_FAILED);
   }
-  out_value->assign(
-      user_data->GetAdditionalValue(client_memory_key)->strings().values(0));
+  if (user_data_has_value) {
+    out_value->assign(
+        user_data->GetAdditionalValue(client_memory_key)->strings().values(0));
+  } else {
+    out_value->assign(
+        user_model->GetValue(client_memory_key)->strings().values(0));
+  }
   return OkClientStatus();
 }
 
@@ -678,9 +696,9 @@
       return;
     }
     case TextValue::kClientMemoryKey: {
-      status =
-          GetClientMemoryStringValue(text_value.client_memory_key(),
-                                     action_delegate->GetUserData(), &value);
+      status = GetClientMemoryStringValue(
+          text_value.client_memory_key(), action_delegate->GetUserData(),
+          action_delegate->GetUserModel(), &value);
       break;
     }
     case TextValue::VALUE_NOT_SET:
diff --git a/components/autofill_assistant/browser/user_data_util.h b/components/autofill_assistant/browser/user_data_util.h
index a539895..df3a589 100644
--- a/components/autofill_assistant/browser/user_data_util.h
+++ b/components/autofill_assistant/browser/user_data_util.h
@@ -15,6 +15,7 @@
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/user_data.h"
+#include "components/autofill_assistant/browser/user_model.h"
 #include "components/autofill_assistant/browser/web/element_finder.h"
 #include "components/autofill_assistant/browser/website_login_manager.h"
 
@@ -136,6 +137,7 @@
 // fails with |PRECONDITION_FAILED|.
 ClientStatus GetClientMemoryStringValue(const std::string& client_memory_key,
                                         const UserData* user_data,
+                                        const UserModel* user_model,
                                         std::string* out_value);
 
 // Take a |text_value| and resolve its content to a string. Reports the result
diff --git a/components/autofill_assistant/browser/user_data_util_unittest.cc b/components/autofill_assistant/browser/user_data_util_unittest.cc
index 80bae5f4..a4aee7b 100644
--- a/components/autofill_assistant/browser/user_data_util_unittest.cc
+++ b/components/autofill_assistant/browser/user_data_util_unittest.cc
@@ -993,6 +993,8 @@
 
     ON_CALL(mock_action_delegate_, GetUserData)
         .WillByDefault(Return(&user_data_));
+    ON_CALL(mock_action_delegate_, GetUserModel)
+        .WillByDefault(Return(&user_model_));
     ON_CALL(mock_action_delegate_, GetWebsiteLoginManager)
         .WillByDefault(Return(&mock_website_login_manager_));
   }
@@ -1318,26 +1320,61 @@
                                          base::Unretained(this)));
 }
 
-TEST_F(UserDataUtilTextValueTest, ClientMemoryKey) {
+TEST_F(UserDataUtilTextValueTest, ClientMemoryKeyFromUserData) {
   user_data_.SetAdditionalValue("key", SimpleValue(std::string("Hello World")));
 
   std::string result;
-  EXPECT_TRUE(GetClientMemoryStringValue("key", &user_data_, &result).ok());
+  EXPECT_TRUE(
+      GetClientMemoryStringValue("key", &user_data_, &user_model_, &result)
+          .ok());
+  EXPECT_EQ(result, "Hello World");
+}
+
+TEST_F(UserDataUtilTextValueTest, ClientMemoryKeyFromUserModel) {
+  user_model_.SetValue("key", SimpleValue(std::string("Hello World")));
+
+  std::string result;
+  EXPECT_TRUE(
+      GetClientMemoryStringValue("key", &user_data_, &user_model_, &result)
+          .ok());
+  EXPECT_EQ(result, "Hello World");
+}
+
+TEST_F(UserDataUtilTextValueTest, ClientMemoryValueDifferentInDataAndModel) {
+  user_data_.SetAdditionalValue(
+      "key", SimpleValue(std::string("Hello from UserData")));
+  user_model_.SetValue("key", SimpleValue(std::string("Hello from UserModel")));
+
+  std::string result;
+  EXPECT_EQ(PRECONDITION_FAILED, GetClientMemoryStringValue(
+                                     "key", &user_data_, &user_model_, &result)
+                                     .proto_status());
+}
+
+TEST_F(UserDataUtilTextValueTest, ClientMemoryValueDuplicateInDataAndModel) {
+  user_data_.SetAdditionalValue("key", SimpleValue(std::string("Hello World")));
+  user_model_.SetValue("key", SimpleValue(std::string("Hello World")));
+
+  std::string result;
+  EXPECT_TRUE(
+      GetClientMemoryStringValue("key", &user_data_, &user_model_, &result)
+          .ok());
   EXPECT_EQ(result, "Hello World");
 }
 
 TEST_F(UserDataUtilTextValueTest, EmptyClientMemoryKey) {
   std::string result;
   EXPECT_EQ(INVALID_ACTION,
-            GetClientMemoryStringValue(std::string(), &user_data_, &result)
+            GetClientMemoryStringValue(std::string(), &user_data_, &user_model_,
+                                       &result)
                 .proto_status());
 }
 
 TEST_F(UserDataUtilTextValueTest, NonExistingClientMemoryKey) {
   std::string result;
-  EXPECT_EQ(
-      PRECONDITION_FAILED,
-      GetClientMemoryStringValue("key", &user_data_, &result).proto_status());
+  EXPECT_EQ(PRECONDITION_FAILED, GetClientMemoryStringValue(
+                                     "key", &user_data_, &user_model_, &result)
+                                     .proto_status());
 }
 
 TEST_F(UserDataUtilTextValueTest, TextValueText) {
diff --git a/components/metrics/clean_exit_beacon.cc b/components/metrics/clean_exit_beacon.cc
index 1a0eb71..983d9c5 100644
--- a/components/metrics/clean_exit_beacon.cc
+++ b/components/metrics/clean_exit_beacon.cc
@@ -79,33 +79,31 @@
 }
 
 // Records the the combined state of two distinct beacons' values in the given
-// histogram. One beacon is stored in Local State while the other is stored
-// elsewhere (e.g. in platform-specific storage, like the Windows registry, or
-// in the beacon file).
+// histogram.
 void RecordBeaconConsistency(const std::string& histogram_name,
-                             absl::optional<bool> other_beacon_value,
-                             absl::optional<bool> local_state_beacon_value) {
+                             absl::optional<bool> beacon_value1,
+                             absl::optional<bool> beacon_value2) {
   CleanExitBeaconConsistency consistency =
       CleanExitBeaconConsistency::kDirtyDirty;
 
-  if (!other_beacon_value) {  // The non-Local-State-backed beacon is missing.
-    if (!local_state_beacon_value) {  // The Local State beacon is missing.
+  if (!beacon_value1) {
+    if (!beacon_value2) {
       consistency = CleanExitBeaconConsistency::kMissingMissing;
     } else {
-      consistency = local_state_beacon_value.value()
+      consistency = beacon_value2.value()
                         ? CleanExitBeaconConsistency::kMissingClean
                         : CleanExitBeaconConsistency::kMissingDirty;
     }
-  } else if (!local_state_beacon_value) {
-    consistency = other_beacon_value.value()
+  } else if (!beacon_value2) {
+    consistency = beacon_value1.value()
                       ? CleanExitBeaconConsistency::kCleanMissing
                       : CleanExitBeaconConsistency::kDirtyMissing;
-  } else if (other_beacon_value.value()) {
-    consistency = local_state_beacon_value.value()
+  } else if (beacon_value1.value()) {
+    consistency = beacon_value2.value()
                       ? CleanExitBeaconConsistency::kCleanClean
                       : CleanExitBeaconConsistency::kCleanDirty;
   } else {
-    consistency = local_state_beacon_value.value()
+    consistency = beacon_value2.value()
                       ? CleanExitBeaconConsistency::kDirtyClean
                       : CleanExitBeaconConsistency::kDirtyDirty;
   }
@@ -327,6 +325,10 @@
     }
     RecordBeaconConsistency("UMA.CleanExitBeacon.BeaconFileConsistency",
                             beacon_file_beacon_value, local_state_beacon_value);
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
+    RecordBeaconConsistency("UMA.CleanExitBeaconConsistency3",
+                            beacon_file_beacon_value, backup_beacon_value);
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
   }
 
   bool did_previous_session_exit_cleanly =
diff --git a/components/metrics/clean_exit_beacon_unittest.cc b/components/metrics/clean_exit_beacon_unittest.cc
index d51d9df..69227d6 100644
--- a/components/metrics/clean_exit_beacon_unittest.cc
+++ b/components/metrics/clean_exit_beacon_unittest.cc
@@ -124,14 +124,22 @@
   CleanExitBeaconConsistency expected_consistency;
 };
 
-// Used for testing the logic that emits CleanExitBeaconConsistency to
-// histograms.
 #if BUILDFLAG(IS_IOS)
-class BackupBeaconConsistencyTest
+// Used for testing the logic that emits to the UMA.CleanExitBeaconConsistency2
+// histogram.
+class PlatformBeaconAndLocalStateBeaconConsistencyTest
+    : public testing::WithParamInterface<BeaconConsistencyTestParams>,
+      public CleanExitBeaconTest {};
+
+// Used for testing the logic that emits to the UMA.CleanExitBeaconConsistency3
+// histogram.
+class BeaconFileAndPlatformBeaconConsistencyTest
     : public testing::WithParamInterface<BeaconConsistencyTestParams>,
       public CleanExitBeaconTest {};
 #endif  // BUILDFLAG(IS_IOS)
 
+// Used for testing the logic that emits to the
+// UMA.CleanExitBeacon.BeaconFileConsistency histogram.
 class BeaconFileConsistencyTest
     : public testing::WithParamInterface<BeaconConsistencyTestParams>,
       public CleanExitBeaconTest {};
@@ -363,9 +371,9 @@
                                        updated_num_crashes, 1);
 }
 
-// The below CleanExitBeaconTest.BeaconState*ExtendedSafeMode tests verify that
-// the logic for recording UMA.CleanExitBeacon.BeaconFileConsistency is correct
-// for clients in the SignalAndWriteViaFileUtil group.
+// Verify that the logic for recording UMA.CleanExitBeacon.BeaconFileConsistency
+// is correct for clients in the Extended Variations Safe Mode experiment's
+// enabled group.
 INSTANTIATE_TEST_SUITE_P(
     All,
     BeaconFileConsistencyTest,
@@ -440,6 +448,10 @@
                       params.local_state_beacon_value.value());
   }
 
+  SetUpExtendedSafeModeExperiment(variations::kEnabledGroup);
+  ASSERT_EQ(variations::kEnabledGroup, base::FieldTrialList::FindFullName(
+                                           variations::kExtendedSafeModeTrial));
+
   TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
   histogram_tester_.ExpectUniqueSample(
       "UMA.CleanExitBeacon.BeaconFileConsistency", params.expected_consistency,
@@ -652,12 +664,12 @@
                                          /*is_extended_safe_mode=*/true));
 }
 
-// The below CleanExitBeaconTest.BeaconState_* tests verify that the logic for
-// recording UMA.CleanExitBeaconConsistency2 is correct.
 #if BUILDFLAG(IS_IOS)
+// Verify that the logic for recording UMA.CleanExitBeaconConsistency2 is
+// correct.
 INSTANTIATE_TEST_SUITE_P(
     All,
-    BackupBeaconConsistencyTest,
+    PlatformBeaconAndLocalStateBeaconConsistencyTest,
     ::testing::Values(
         BeaconConsistencyTestParams{
             .test_name = "MissingMissing",
@@ -703,7 +715,7 @@
       return params.param.test_name;
     });
 
-TEST_P(BackupBeaconConsistencyTest, BeaconConsistency) {
+TEST_P(PlatformBeaconAndLocalStateBeaconConsistencyTest, BeaconConsistency) {
   // Clear the platform-specific and Local State beacons. Unless set below, the
   // beacons are considered missing.
   CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_);
@@ -722,6 +734,92 @@
   histogram_tester_.ExpectUniqueSample("UMA.CleanExitBeaconConsistency2",
                                        params.expected_consistency, 1);
 }
+
+// Verify that the logic for recording UMA.CleanExitBeaconConsistency3 is
+// correct for clients in the Extended Variations Safe Mode experiment's enabled
+// group.
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    BeaconFileAndPlatformBeaconConsistencyTest,
+    ::testing::Values(
+        BeaconConsistencyTestParams{
+            .test_name = "MissingMissing",
+            .expected_consistency =
+                CleanExitBeaconConsistency::kMissingMissing},
+        BeaconConsistencyTestParams{
+            .test_name = "MissingClean",
+            .platform_specific_beacon_value = true,
+            .expected_consistency = CleanExitBeaconConsistency::kMissingClean},
+        BeaconConsistencyTestParams{
+            .test_name = "MissingDirty",
+            .platform_specific_beacon_value = false,
+            .expected_consistency = CleanExitBeaconConsistency::kMissingDirty},
+        BeaconConsistencyTestParams{
+            .test_name = "CleanMissing",
+            .beacon_file_beacon_value = true,
+            .expected_consistency = CleanExitBeaconConsistency::kCleanMissing},
+        BeaconConsistencyTestParams{
+            .test_name = "DirtyMissing",
+            .beacon_file_beacon_value = false,
+            .expected_consistency = CleanExitBeaconConsistency::kDirtyMissing},
+        BeaconConsistencyTestParams{
+            .test_name = "CleanClean",
+            .beacon_file_beacon_value = true,
+            .platform_specific_beacon_value = true,
+            .expected_consistency = CleanExitBeaconConsistency::kCleanClean},
+        BeaconConsistencyTestParams{
+            .test_name = "CleanDirty",
+            .beacon_file_beacon_value = true,
+            .platform_specific_beacon_value = false,
+            .expected_consistency = CleanExitBeaconConsistency::kCleanDirty},
+        BeaconConsistencyTestParams{
+            .test_name = "DirtyClean",
+            .beacon_file_beacon_value = false,
+            .platform_specific_beacon_value = true,
+            .expected_consistency = CleanExitBeaconConsistency::kDirtyClean},
+        BeaconConsistencyTestParams{
+            .test_name = "DirtyDirty",
+            .beacon_file_beacon_value = false,
+            .platform_specific_beacon_value = false,
+            .expected_consistency = CleanExitBeaconConsistency::kDirtyDirty}),
+    [](const ::testing::TestParamInfo<BeaconConsistencyTestParams>& params) {
+      return params.param.test_name;
+    });
+
+TEST_P(BeaconFileAndPlatformBeaconConsistencyTest, BeaconConsistency) {
+  // Verify that the beacon file is not present. Unless set below, this beacon
+  // is considered missing.
+  const base::FilePath user_data_dir_path = user_data_dir_.GetPath();
+  const base::FilePath temp_beacon_file_path =
+      user_data_dir_path.Append(variations::kVariationsFilename);
+  ASSERT_FALSE(base::PathExists(temp_beacon_file_path));
+  // Clear the platform-specific beacon. Unless set below, this beacon is also
+  // considered missing.
+  CleanExitBeacon::ResetStabilityExitedCleanlyForTesting(&prefs_);
+
+  BeaconConsistencyTestParams params = GetParam();
+  if (params.beacon_file_beacon_value) {
+    ASSERT_LT(
+        0, base::WriteFile(
+               temp_beacon_file_path,
+               CreateWellFormedBeaconFileContents(
+                   /*exited_cleanly=*/params.beacon_file_beacon_value.value(),
+                   /*crash_streak=*/0)
+                   .data()));
+  }
+  if (params.platform_specific_beacon_value) {
+    CleanExitBeacon::SetUserDefaultsBeacon(
+        /*exited_cleanly=*/params.platform_specific_beacon_value.value());
+  }
+
+  SetUpExtendedSafeModeExperiment(variations::kEnabledGroup);
+  ASSERT_EQ(variations::kEnabledGroup, base::FieldTrialList::FindFullName(
+                                           variations::kExtendedSafeModeTrial));
+
+  TestCleanExitBeacon clean_exit_beacon(&prefs_, user_data_dir_path);
+  histogram_tester_.ExpectUniqueSample("UMA.CleanExitBeaconConsistency3",
+                                       params.expected_consistency, 1);
+}
 #endif  // BUILDFLAG(IS_IOS)
 
 }  // namespace metrics
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc b/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
index 7f8f82b..a11de67 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/flatbuffer_scorer.cc
@@ -183,8 +183,7 @@
   if (visual_tflite_model_.IsValid()) {
     base::Time start_post_task_time = base::Time::Now();
     base::ThreadPool::PostTaskAndReplyWithResult(
-        FROM_HERE,
-        {base::TaskPriority::BEST_EFFORT, base::WithBaseSyncPrimitives()},
+        FROM_HERE, {base::TaskPriority::BEST_EFFORT},
         base::BindOnce(&ApplyVisualTfLiteModelHelper, bitmap,
                        flatbuffer_model_->tflite_metadata()->input_width(),
                        flatbuffer_model_->tflite_metadata()->input_height(),
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc b/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
index 0c871ac..3d2dc99 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/protobuf_scorer.cc
@@ -105,8 +105,7 @@
   if (visual_tflite_model_.IsValid()) {
     base::Time start_post_task_time = base::Time::Now();
     base::ThreadPool::PostTaskAndReplyWithResult(
-        FROM_HERE,
-        {base::TaskPriority::BEST_EFFORT, base::WithBaseSyncPrimitives()},
+        FROM_HERE, {base::TaskPriority::BEST_EFFORT},
         base::BindOnce(&ApplyVisualTfLiteModelHelper, bitmap,
                        model_.tflite_metadata().input_width(),
                        model_.tflite_metadata().input_height(),
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc b/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc
index 2373837..88ea923 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/scorer.cc
@@ -24,6 +24,7 @@
 #include "components/safe_browsing/core/common/visual_utils.h"
 #include "content/public/renderer/render_thread.h"
 #include "crypto/sha2.h"
+#include "skia/ext/image_operations.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
@@ -100,14 +101,9 @@
       {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0},
       SkNamedGamut::kRec2020);
 
-  SkImageInfo downsampled_info = SkImageInfo::MakeN32(
-      width, height, SkAlphaType::kUnpremul_SkAlphaType, rec2020);
-  SkBitmap downsampled;
-  if (!downsampled.tryAllocPixels(downsampled_info))
-    return std::string();
-  bitmap.pixmap().scalePixels(
-      downsampled.pixmap(),
-      SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest));
+  SkBitmap downsampled = skia::ImageOperations::Resize(
+      bitmap, skia::ImageOperations::RESIZE_GOOD, static_cast<int>(width),
+      static_cast<int>(height));
 
   // Format as an RGB buffer for input into the model
   std::string data;
diff --git a/components/webapps/browser/android/bottomsheet/pwa_bottom_sheet_controller.h b/components/webapps/browser/android/bottomsheet/pwa_bottom_sheet_controller.h
index 01110ae..f2e08d3 100644
--- a/components/webapps/browser/android/bottomsheet/pwa_bottom_sheet_controller.h
+++ b/components/webapps/browser/android/bottomsheet/pwa_bottom_sheet_controller.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "components/webapps/browser/android/add_to_homescreen_installer.h"
 #include "components/webapps/browser/android/add_to_homescreen_params.h"
diff --git a/components/webapps/browser/banners/app_banner_manager.h b/components/webapps/browser/banners/app_banner_manager.h
index ee68072..f18ad63 100644
--- a/components/webapps/browser/banners/app_banner_manager.h
+++ b/components/webapps/browser/banners/app_banner_manager.h
@@ -24,10 +24,9 @@
 #include "third_party/blink/public/mojom/app_banner/app_banner.mojom.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-forward.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "url/gurl.h"
 
-class SkBitmap;
-
 namespace content {
 class RenderFrameHost;
 class WebContents;
diff --git a/components/webapps/browser/installable/installable_metrics.cc b/components/webapps/browser/installable/installable_metrics.cc
index 7afd45d..6f541a3 100644
--- a/components/webapps/browser/installable/installable_metrics.cc
+++ b/components/webapps/browser/installable/installable_metrics.cc
@@ -7,8 +7,10 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/webapps/browser/webapps_client.h"
+#include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/web_contents.h"
 
 namespace webapps {
diff --git a/components/webapps/browser/installable/installable_metrics.h b/components/webapps/browser/installable/installable_metrics.h
index f331369..e58dd6c 100644
--- a/components/webapps/browser/installable/installable_metrics.h
+++ b/components/webapps/browser/installable/installable_metrics.h
@@ -5,12 +5,15 @@
 #ifndef COMPONENTS_WEBAPPS_BROWSER_INSTALLABLE_INSTALLABLE_METRICS_H_
 #define COMPONENTS_WEBAPPS_BROWSER_INSTALLABLE_INSTALLABLE_METRICS_H_
 
-#include "base/time/time.h"
-#include "content/public/browser/service_worker_context.h"
+namespace base {
+class TimeDelta;
+}
 
 namespace content {
 class WebContents;
-}
+enum class OfflineCapability;
+enum class ServiceWorkerCapability;
+}  // namespace content
 
 namespace webapps {
 
diff --git a/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc b/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc
index 0fb88d7..ab5baa3 100644
--- a/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_scripts_browsertest.cc
@@ -26,6 +26,8 @@
 constexpr const char kMacSelection[]{"mac/selection"};
 constexpr const char kMacTextMarker[]{"mac/textmarker"};
 constexpr const char kMacMethods[]{"mac/methods"};
+constexpr const char kMacParameterizedAttributes[]{
+    "mac/parameterized-attributes"};
 constexpr const char kRegression[]{"mac/regression"};
 
 #endif
@@ -434,6 +436,12 @@
   RunTypedTest<kMacMethods>("is-accessibility-element.html");
 }
 
+// Parameterized attributes
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, AXStringForRange) {
+  RunTypedTest<kMacParameterizedAttributes>("ax-string-for-range.html");
+}
+
 // Regression tests
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityScriptTest, AXSelectedChildren) {
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 20cd5c6..f25fc4d 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -2717,82 +2717,6 @@
                   BlockListedFeatures()));
 }
 
-class BackForwardCacheOptInBrowserTest : public BackForwardCacheBrowserTest {
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    EnableFeatureAndSetParams(features::kBackForwardCache,
-                              "opt_in_header_required", "true");
-    BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(BackForwardCacheOptInBrowserTest, NoCacheWithoutHeader) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
-  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
-
-  // 1) Navigate to A.
-  EXPECT_TRUE(NavigateToURL(shell(), url_a));
-
-  // 2) Navigate to B.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b));
-
-  // 3) Go back.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-
-  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
-                         kOptInUnloadHeaderNotPresent},
-                    {}, {}, {}, {}, FROM_HERE);
-}
-
-IN_PROC_BROWSER_TEST_F(BackForwardCacheOptInBrowserTest,
-                       CacheIfHeaderIsPresent) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  GURL url_a(embedded_test_server()->GetURL("a.com",
-                                            "/set-header?"
-                                            "BFCache-Opt-In: unload"));
-  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
-
-  // 1) Navigate to A.
-  EXPECT_TRUE(NavigateToURL(shell(), url_a));
-
-  // 2) Navigate to B.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b));
-
-  // 3) Go back.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-
-  ExpectRestored(FROM_HERE);
-}
-
-IN_PROC_BROWSER_TEST_F(BackForwardCacheOptInBrowserTest,
-                       NoCacheIfHeaderOnlyPresentOnDestinationPage) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
-  GURL url_b(embedded_test_server()->GetURL("b.com",
-                                            "/set-header?"
-                                            "BFCache-Opt-In: unload"));
-
-  // 1) Navigate to A.
-  EXPECT_TRUE(NavigateToURL(shell(), url_a));
-
-  // 2) Navigate to B.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b));
-
-  // 3) Go back. - A doesn't have header so it shouldn't be cached.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-
-  ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
-                         kOptInUnloadHeaderNotPresent},
-                    {}, {}, {}, {}, FROM_HERE);
-
-  // 4) Go forward. - B has the header, so it should be cached.
-  ASSERT_TRUE(HistoryGoForward(web_contents()));
-
-  ExpectRestored(FROM_HERE);
-}
-
 class BackForwardCacheUnloadStrategyBrowserTest
     : public BackForwardCacheBrowserTest,
       public testing::WithParamInterface<std::string> {
@@ -2840,45 +2764,7 @@
 
 INSTANTIATE_TEST_SUITE_P(All,
                          BackForwardCacheUnloadStrategyBrowserTest,
-                         testing::Values("always",
-                                         "opt_in_header_required",
-                                         "no"));
-
-IN_PROC_BROWSER_TEST_P(BackForwardCacheUnloadStrategyBrowserTest,
-                       UnloadHandlerPresentWithOptInHeader) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  GURL url_a(embedded_test_server()->GetURL("a.com",
-                                            "/set-header?"
-                                            "BFCache-Opt-In: unload"));
-  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
-
-  // 1) Navigate to A.
-  EXPECT_TRUE(NavigateToURL(shell(), url_a));
-  InstallUnloadHandlerOnMainFrame();
-
-  // 2) Navigate to B.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b));
-
-  // 3) Go back.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-
-  if (GetParam() == "always" || GetParam() == "opt_in_header_required") {
-    ExpectRestored(FROM_HERE);
-    EXPECT_EQ("0", GetUnloadRunCount());
-  } else {
-    ASSERT_EQ("no", GetParam());
-    ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
-                           kUnloadHandlerExistsInMainFrame},
-                      {}, {}, {}, {}, FROM_HERE);
-    EXPECT_EQ("1", GetUnloadRunCount());
-  }
-
-  // 4) Go forward.
-  ASSERT_TRUE(HistoryGoForward(web_contents()));
-
-  ExpectRestored(FROM_HERE);
-}
+                         testing::Values("always", "no"));
 
 IN_PROC_BROWSER_TEST_P(BackForwardCacheUnloadStrategyBrowserTest,
                        UnloadHandlerPresentWithoutOptInHeader) {
@@ -2900,11 +2786,6 @@
   if (GetParam() == "always") {
     ExpectRestored(FROM_HERE);
     EXPECT_EQ("0", GetUnloadRunCount());
-  } else if (GetParam() == "opt_in_header_required") {
-    ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
-                           kOptInUnloadHeaderNotPresent},
-                      {}, {}, {}, {}, FROM_HERE);
-    EXPECT_EQ("1", GetUnloadRunCount());
   } else {
     ASSERT_EQ("no", GetParam());
     ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
@@ -2920,42 +2801,6 @@
 }
 
 IN_PROC_BROWSER_TEST_P(BackForwardCacheUnloadStrategyBrowserTest,
-                       UnloadHandlerPresentInSubFrameWithOptInHeader) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  GURL url_a(embedded_test_server()->GetURL("a.com",
-                                            "/set-header?"
-                                            "BFCache-Opt-In: unload"));
-  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
-
-  // 1) Navigate to A.
-  EXPECT_TRUE(NavigateToURL(shell(), url_a));
-  InstallUnloadHandlerOnSubFrame();
-
-  // 2) Navigate to B.
-  EXPECT_TRUE(NavigateToURL(shell(), url_b));
-
-  // 3) Go back.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-
-  if (GetParam() == "always" || GetParam() == "opt_in_header_required") {
-    ExpectRestored(FROM_HERE);
-    EXPECT_EQ("0", GetUnloadRunCount());
-  } else {
-    ASSERT_EQ("no", GetParam());
-    ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
-                           kUnloadHandlerExistsInSubFrame},
-                      {}, {}, {}, {}, FROM_HERE);
-    EXPECT_EQ("1", GetUnloadRunCount());
-  }
-
-  // 4) Go forward.
-  ASSERT_TRUE(HistoryGoForward(web_contents()));
-
-  ExpectRestored(FROM_HERE);
-}
-
-IN_PROC_BROWSER_TEST_P(BackForwardCacheUnloadStrategyBrowserTest,
                        UnloadHandlerPresentInSubFrameWithoutOptInHeader) {
   ASSERT_TRUE(embedded_test_server()->Start());
 
@@ -2976,7 +2821,7 @@
     ExpectRestored(FROM_HERE);
     EXPECT_EQ("0", GetUnloadRunCount());
   } else {
-    EXPECT_TRUE(GetParam() == "opt_in_header_required" || GetParam() == "no");
+    EXPECT_TRUE(GetParam() == "no");
     ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
                            kUnloadHandlerExistsInSubFrame},
                       {}, {}, {}, {}, FROM_HERE);
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index b843d9a3..f5c69372 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1385,9 +1385,6 @@
     case Reason::kBackForwardCacheDisabledForDelegate:
       return Page::BackForwardCacheNotRestoredReasonEnum::
           BackForwardCacheDisabledForDelegate;
-    case Reason::kOptInUnloadHeaderNotPresent:
-      return Page::BackForwardCacheNotRestoredReasonEnum::
-          OptInUnloadHeaderNotPresent;
     case Reason::kUnloadHandlerExistsInMainFrame:
       return Page::BackForwardCacheNotRestoredReasonEnum::
           UnloadHandlerExistsInMainFrame;
@@ -1692,7 +1689,6 @@
     case Reason::kErrorDocument:
     case Reason::kFencedFramesEmbedder:
       return Page::BackForwardCacheNotRestoredReasonTypeEnum::Circumstantial;
-    case Reason::kOptInUnloadHeaderNotPresent:
     case Reason::kUnloadHandlerExistsInMainFrame:
     case Reason::kUnloadHandlerExistsInSubFrame:
       return Page::BackForwardCacheNotRestoredReasonTypeEnum::PageSupportNeeded;
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 17a20a391..9fce4766 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -1533,8 +1533,8 @@
             LoadBasicRequest(partition->GetNetworkContext(), test_url));
 }
 
-// This test class sets up a link element for webbundle subresource loading.
-// e.g. <link rel=webbundle href=".../foo.wbn" resources="...">.
+// This test class sets up a script element for webbundle subresource loading.
+// e.g. <script type=webbundle>...</script>
 class CrossSiteDocumentBlockingWebBundleTest : public ContentBrowserTest {
  public:
   CrossSiteDocumentBlockingWebBundleTest() {
@@ -1550,25 +1550,27 @@
   net::EmbeddedTestServer* https_server() { return &https_server_; }
 
  protected:
-  void SetupLinkWebBundleElementAndImgElement(const GURL& bundle_url,
-                                              const GURL subresource_url) {
+  void SetupScriptWebBundleElementAndImgElement(const GURL& bundle_url,
+                                                const GURL subresource_url) {
     // Navigate to the test page.
     ASSERT_TRUE(
         NavigateToURL(shell(), GURL("https://same-origin.test/title1.html")));
 
     const char kScriptTemplate[] = R"(
-      const link = document.createElement('link');
-      link.rel = 'webbundle';
-      link.href = $1;
-      link.resources.add($2);
-      document.body.appendChild(link);
+      const script = document.createElement('script');
+      script.type = 'webbundle';
+      script.textContent = JSON.stringify({
+        source: $1,
+        resources: [$2],
+      });
+      document.body.appendChild(script);
 
       const img = document.createElement('img');
       img.src = $2;
       document.body.appendChild(img);
 )";
-    // Insert a <link> element for webbundle subresoruce loading, and insert an
-    // <img> element which loads a resource from the webbundle.
+    // Insert a <script> element for webbundle subresoruce loading, and insert
+    // an <img> element which loads a resource from the webbundle.
     ASSERT_TRUE(ExecJs(
         shell(), JsReplace(kScriptTemplate, bundle_url, subresource_url)));
   }
@@ -1616,7 +1618,7 @@
   GURL bundle_url("https://cross-origin.test/web_bundle/cross_origin_b2.wbn");
   GURL subresource_url("https://cross-origin.test/web_bundle/resource.json");
   RequestInterceptor interceptor(subresource_url);
-  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  SetupScriptWebBundleElementAndImgElement(bundle_url, subresource_url);
   interceptor.WaitForRequestCompletion();
 
   EXPECT_EQ(0, interceptor.completion_status().error_code);
@@ -1630,7 +1632,7 @@
   GURL bundle_url("https://cross-origin.test/web_bundle/cross_origin_b2.wbn");
   GURL subresource_url("https://cross-origin.test/web_bundle/resource.png");
   RequestInterceptor interceptor(subresource_url);
-  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  SetupScriptWebBundleElementAndImgElement(bundle_url, subresource_url);
   interceptor.WaitForRequestCompletion();
 
   EXPECT_EQ(0, interceptor.completion_status().error_code);
@@ -1644,7 +1646,7 @@
   GURL bundle_url("https://same-origin.test/web_bundle/same_origin_b2.wbn");
   GURL subresource_url("https://same-origin.test/web_bundle/resource.json");
   RequestInterceptor interceptor(subresource_url);
-  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  SetupScriptWebBundleElementAndImgElement(bundle_url, subresource_url);
   interceptor.WaitForRequestCompletion();
 
   EXPECT_EQ(0, interceptor.completion_status().error_code);
@@ -1658,7 +1660,7 @@
   GURL bundle_url("https://same-origin.test/web_bundle/same_origin_b2.wbn");
   GURL subresource_url("https://same-origin.test/web_bundle/resource.png");
   RequestInterceptor interceptor(subresource_url);
-  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  SetupScriptWebBundleElementAndImgElement(bundle_url, subresource_url);
   interceptor.WaitForRequestCompletion();
 
   EXPECT_EQ(0, interceptor.completion_status().error_code);
diff --git a/content/browser/loader/navigation_early_hints_browsertest.cc b/content/browser/loader/navigation_early_hints_browsertest.cc
index 8f8d730d..43b22487 100644
--- a/content/browser/loader/navigation_early_hints_browsertest.cc
+++ b/content/browser/loader/navigation_early_hints_browsertest.cc
@@ -747,7 +747,31 @@
   ASSERT_FALSE(is_cached.value());
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationEarlyHintsTest, SimplePreconnect) {
+class NavigationEarlyHintsPreconnectTest
+    : public NavigationEarlyHintsTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  NavigationEarlyHintsPreconnectTest() {
+    if (!IsPreconnectEnabled()) {
+      scoped_feature_list_.InitAndEnableFeatureWithParameters(
+          features::kEarlyHintsPreloadForNavigation,
+          {{"enable_preconnect", "false"}});
+    }
+  }
+
+  ~NavigationEarlyHintsPreconnectTest() override = default;
+
+ protected:
+  bool IsPreconnectEnabled() { return GetParam(); }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         NavigationEarlyHintsPreconnectTest,
+                         testing::Bool());
+
+IN_PROC_BROWSER_TEST_P(NavigationEarlyHintsPreconnectTest, SimplePreconnect) {
   const char kPageWithPreconnect[] = "/page_with_preconnect.html";
   const GURL kPreconnectUrl = cross_origin_server().GetURL("/");
   ResponseEntry page_entry(kPageWithPreconnect, net::HTTP_OK);
@@ -761,7 +785,11 @@
       shell(), net::QuicSimpleTestServer::GetFileURL(kPageWithPreconnect)));
   ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
 
-  EXPECT_EQ(preconnect_listener().num_accepted_sockets(), 1UL);
+  if (IsPreconnectEnabled()) {
+    EXPECT_EQ(preconnect_listener().num_accepted_sockets(), 1UL);
+  } else {
+    EXPECT_EQ(preconnect_listener().num_accepted_sockets(), 0UL);
+  }
   EXPECT_TRUE(GetEarlyHintsManager(static_cast<RenderFrameHostImpl*>(
                                        shell()->web_contents()->GetMainFrame()))
                   ->WasResourceHintsReceived());
diff --git a/content/browser/loader/navigation_early_hints_manager.cc b/content/browser/loader/navigation_early_hints_manager.cc
index d0f5f26..cb590f1 100644
--- a/content/browser/loader/navigation_early_hints_manager.cc
+++ b/content/browser/loader/navigation_early_hints_manager.cc
@@ -90,6 +90,11 @@
       features::kEarlyHintsPreloadForNavigation, "force_disable", false);
 }
 
+bool IsEnabledEarlyHintsPreconnect() {
+  return base::GetFieldTrialParamByFeatureAsBool(
+      features::kEarlyHintsPreloadForNavigation, "enable_preconnect", true);
+}
+
 network::mojom::CSPDirectiveName LinkAsAttributeToCSPDirective(
     network::mojom::LinkAsAttribute attr) {
   switch (attr) {
@@ -526,6 +531,9 @@
     bool enabled_by_origin_trial) {
   was_resource_hints_received_ = true;
 
+  if (!IsEnabledEarlyHintsPreconnect())
+    return;
+
   if (!ShouldHandleResourceHints(link, enabled_by_origin_trial))
     return;
 
diff --git a/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc b/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc
index f330f7f..e43f626 100644
--- a/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc
+++ b/content/browser/renderer_host/back_forward_cache_can_store_document_result.cc
@@ -165,8 +165,6 @@
       return ProtoEnum::BROWSING_INSTANCE_NOT_SWAPPED;
     case Reason::kBackForwardCacheDisabledForDelegate:
       return ProtoEnum::BACK_FORWARD_CACHE_DISABLED_FOR_DELEGATE;
-    case Reason::kOptInUnloadHeaderNotPresent:
-      return ProtoEnum::OPT_IN_UNLOAD_HEADER_NOT_PRESENT;
     case Reason::kUnloadHandlerExistsInMainFrame:
       return ProtoEnum::UNLOAD_HANDLER_EXISTS_IN_MAIN_FRAME;
     case Reason::kUnloadHandlerExistsInSubFrame:
@@ -367,9 +365,6 @@
       return "Browsing instance is not swapped";
     case Reason::kBackForwardCacheDisabledForDelegate:
       return "BackForwardCache is not supported by delegate";
-    case Reason::kOptInUnloadHeaderNotPresent:
-      return "BFCache-Opt-In header not present, or does not include `unload` "
-             "token, and an experimental config which requires it is active.";
     case Reason::kUnloadHandlerExistsInMainFrame:
       return "Unload handler exists in the main frame, and the current "
              "experimental config doesn't permit it to be BFCached.";
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index d78d2c48..dd67b91 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -101,43 +101,15 @@
   return content_injection_supported.Get();
 }
 
-bool IsOptInHeaderRequired() {
-  if (!IsBackForwardCacheEnabled())
-    return false;
-
-  // TODO(crbug.com/1201653): Remove this feature param and make it one of the
-  //                          `unload_support`.
-  static constexpr base::FeatureParam<bool> opt_in_header_required(
-      &features::kBackForwardCache, "opt_in_header_required", false);
-  return opt_in_header_required.Get();
-}
-
 enum class HeaderPresence {
   kNotPresent,
   kPresent,
   kUnsure,
 };
 
-HeaderPresence OptInUnloadHeaderPresence(RenderFrameHostImpl* rfh) {
-  const network::mojom::URLResponseHeadPtr& response_head =
-      rfh->last_response_head();
-  if (!response_head)
-    return HeaderPresence::kUnsure;
-
-  const network::mojom::ParsedHeadersPtr& headers =
-      response_head->parsed_headers;
-  if (!headers)
-    return HeaderPresence::kUnsure;
-
-  return headers->bfcache_opt_in_unload ? HeaderPresence::kPresent
-                                        : HeaderPresence::kNotPresent;
-}
-
 constexpr base::FeatureParam<BackForwardCacheImpl::UnloadSupportStrategy>::
     Option kUnloadSupportStrategyOptions[] = {
         {BackForwardCacheImpl::UnloadSupportStrategy::kAlways, "always"},
-        {BackForwardCacheImpl::UnloadSupportStrategy::kOptInHeaderRequired,
-         "opt_in_header_required"},
         {BackForwardCacheImpl::UnloadSupportStrategy::kNo, "no"},
 };
 
@@ -849,27 +821,6 @@
   // Only store documents that have URLs allowed through experiment.
   if (!IsAllowed(rfh->GetLastCommittedURL()))
     result.No(BackForwardCacheMetrics::NotRestoredReason::kDomainNotAllowed);
-
-  // TODO(yuzus): Remove this block entirely, as it is checked multiple times
-  // currently with |PopulateReasonsForDocumentAndDescendants|.
-  if (IsOptInHeaderRequired()) {
-    HeaderPresence presence = OptInUnloadHeaderPresence(rfh);
-    switch (presence) {
-      case HeaderPresence::kNotPresent:
-        result.No(BackForwardCacheMetrics::NotRestoredReason::
-                      kOptInUnloadHeaderNotPresent);
-        break;
-      case HeaderPresence::kPresent:
-        // The opt-in header is present, so the page is eligible for BFCache.
-        break;
-      case HeaderPresence::kUnsure:
-        // For the cases which we didn't parse the opt-in header, we should have
-        // already bailed out of BFCache for other reasons.
-        // TODO(yuzus): Specify the reasons |main_document_specific_result|
-        // should have.
-        DCHECK(!result.CanStore());
-    }
-  }
 }
 
 void BackForwardCacheImpl::PopulateStickyReasonsForDocument(
@@ -900,28 +851,6 @@
     case BackForwardCacheImpl::UnloadSupportStrategy::kAlways:
       break;
     case BackForwardCacheImpl::UnloadSupportStrategy::kOptInHeaderRequired:
-      if (has_unload_handler) {
-        HeaderPresence presence =
-            OptInUnloadHeaderPresence(rfh->GetMainFrame());
-        switch (presence) {
-          case HeaderPresence::kNotPresent:
-            result.No(rfh->GetParent()
-                          ? BackForwardCacheMetrics::NotRestoredReason::
-                                kUnloadHandlerExistsInSubFrame
-                          : BackForwardCacheMetrics::NotRestoredReason::
-                                kOptInUnloadHeaderNotPresent);
-            break;
-          case HeaderPresence::kPresent:
-            // The opt-in header is present for the main frame with an unload
-            // handler, so the page is eligible for BFCache.
-            break;
-          case HeaderPresence::kUnsure:
-            // For the cases which we didn't parse the opt-in header, we should
-            // have already bailed out of BFCache for other reasons.
-            break;
-        }
-      }
-      break;
     case BackForwardCacheImpl::UnloadSupportStrategy::kNo:
       if (has_unload_handler) {
         result.No(rfh->GetParent()
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.h b/content/browser/renderer_host/back_forward_cache_metrics.h
index f692311..1d7d179c 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.h
+++ b/content/browser/renderer_host/back_forward_cache_metrics.h
@@ -102,7 +102,7 @@
     kForegroundCacheLimit = 46,
     kBrowsingInstanceNotSwapped = 47,
     kBackForwardCacheDisabledForDelegate = 48,
-    kOptInUnloadHeaderNotPresent = 49,
+    // 49: kOptInUnloadHeaderNotPresent was removed as the experiments ended.
     kUnloadHandlerExistsInMainFrame = 50,
     kUnloadHandlerExistsInSubFrame = 51,
     kServiceWorkerUnregistration = 52,
diff --git a/content/browser/worker_host/worker_browsertest.cc b/content/browser/worker_host/worker_browsertest.cc
index c9af3d1..7fc191f 100644
--- a/content/browser/worker_host/worker_browsertest.cc
+++ b/content/browser/worker_host/worker_browsertest.cc
@@ -922,10 +922,8 @@
                          WorkerFromAnonymousIframeNikBrowserTest,
                          testing::Range(0, 3));
 
-// TODO(crbug.com/1309684): Test is flaky.
-IN_PROC_BROWSER_TEST_P(
-    WorkerFromAnonymousIframeNikBrowserTest,
-    DISABLED_SharedWorkerRequestIsDoneWithPartitionedNetworkState) {
+IN_PROC_BROWSER_TEST_P(WorkerFromAnonymousIframeNikBrowserTest,
+                       SharedWorkerRequestIsDoneWithPartitionedNetworkState) {
   if (!SupportsSharedWorker())
     return;
 
diff --git a/content/test/data/accessibility/mac/parameterized-attributes/ax-string-for-range-expected.txt b/content/test/data/accessibility/mac/parameterized-attributes/ax-string-for-range-expected.txt
new file mode 100644
index 0000000..2b0dc73
--- /dev/null
+++ b/content/test/data/accessibility/mac/parameterized-attributes/ax-string-for-range-expected.txt
@@ -0,0 +1,2 @@
+input.accessibilityParameterizedAttributeNames.has(AXStringForRange)='yes'
+input.accessibilityAttributeValue(AXStringForRange, {loc: 1, len: 2})='al'
diff --git a/content/test/data/accessibility/mac/parameterized-attributes/ax-string-for-range.html b/content/test/data/accessibility/mac/parameterized-attributes/ax-string-for-range.html
new file mode 100644
index 0000000..4c7e907
--- /dev/null
+++ b/content/test/data/accessibility/mac/parameterized-attributes/ax-string-for-range.html
@@ -0,0 +1,7 @@
+<!--
+@SCRIPT:
+  input.accessibilityParameterizedAttributeNames.has(AXStringForRange)
+  input.accessibilityAttributeValue(AXStringForRange, {loc: 1, len: 2})
+-->
+<!DOCTYPE html>
+<input id="input" value="value">
diff --git a/content/test/data/web_bundle/frame_parent.html b/content/test/data/web_bundle/frame_parent.html
index 782a800..fcb410b5 100644
--- a/content/test/data/web_bundle/frame_parent.html
+++ b/content/test/data/web_bundle/frame_parent.html
@@ -4,11 +4,13 @@
   (async () => {
       const params = new URL(location.href).searchParams;
       const frame_url = params.get('frame');
-      const link = document.createElement('link');
-      link.rel = 'webbundle';
-      link.href = params.get('wbn');
-      link.resources = frame_url;
-      document.body.appendChild(link);
+      const script = document.createElement('script');
+      script.type = 'webbundle';
+      script.textContent = JSON.stringify({
+        source: params.get('wbn'),
+        resources: [frame_url],
+      });
+      document.body.appendChild(script);
       const message_promise = new Promise((resolve) => {
 	  window.addEventListener('message', (e) => {
               resolve(e.data);
diff --git a/content/test/data/web_bundle/invalid_web_bundle.html b/content/test/data/web_bundle/invalid_web_bundle.html
index ed9bec3..9bab0d77 100644
--- a/content/test/data/web_bundle/invalid_web_bundle.html
+++ b/content/test/data/web_bundle/invalid_web_bundle.html
@@ -1,5 +1,9 @@
 <!DOCTYPE html>
-<link rel="webbundle" href="invalid_web_bundle.wbn"
-      resources="uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae">
+<script type="webbundle">
+{
+  "source": "invalid_web_bundle.wbn",
+  "resources": ["uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae"]
+}
+</script>
 <body>
 </body>
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc
index 7bde367..c3ad8ef 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc
@@ -230,9 +230,8 @@
 void MimeHandlerStreamManager::EmbedderObserver::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
   // If the top level frame is navigating away, clean up the stream.
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
+  // TODO(mcnee): It's incorrect to assume DidStartNavigation will lead to the
+  // document changing. This could cause the stream to be destroyed prematurely.
   if (navigation_handle->IsInPrimaryMainFrame() &&
       !navigation_handle->IsSameDocument()) {
     AbortStream();
diff --git a/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h b/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h
index 456e0f9..4f22a58e 100644
--- a/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h
+++ b/extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h
@@ -27,6 +27,10 @@
 
   // Set a delay in the next creation of a guest's WebContents by |delay|
   // milliseconds.
+  // TODO(mcnee): The use of a timed delay makes for tests with fragile timing
+  // dependencies. This should be implemented in a way that allows the test to
+  // control when to resume creation based on a condition (e.g. QuitClosure,
+  // OneShotEvent).
   static void DelayNextCreateWebContents(int delay);
 
   // Wait until the guest has attached to the embedder.
diff --git "a/infra/config/generated/builders/ci/Linux Builder \050dbg\051\05032\051/properties.json" "b/infra/config/generated/builders/ci/Linux Builder \050dbg\051\05032\051/properties.json"
deleted file mode 100644
index 8d13aab7..0000000
--- "a/infra/config/generated/builders/ci/Linux Builder \050dbg\051\05032\051/properties.json"
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "$build/reclient": {
-    "instance": "rbe-chromium-trusted",
-    "jobs": 250,
-    "metrics_project": "chromium-reclient-metrics"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.linux",
-  "recipe": "chromium",
-  "sheriff_rotations": [
-    "chromium"
-  ]
-}
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index bc631ea..673357f 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -9582,88 +9582,6 @@
       }
     }
     builders {
-      name: "Linux Builder (dbg)(32)"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/Linux Builder (dbg)(32)/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.linux",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium",'
-        '  "sheriff_rotations": ['
-        '    "chromium"'
-        '  ]'
-        '}'
-      execution_timeout_secs: 10800
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://[^/]*blink_web_tests/.+"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-    }
-    builders {
       name: "Linux Builder (j-500) (reclient)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 638c53a3..9b7e1b7 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -321,11 +321,6 @@
     short_name: "xen"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)(32)"
-    category: "chromium.linux|debug|builder"
-    short_name: "32"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)"
     category: "chromium.linux|debug|builder"
     short_name: "64"
@@ -8936,11 +8931,6 @@
     short_name: "xen"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)(32)"
-    category: "debug|builder"
-    short_name: "32"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Linux Builder (dbg)"
     category: "debug|builder"
     short_name: "64"
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg
index 5fd79778..41952b7 100644
--- a/infra/config/generated/luci/luci-notify.cfg
+++ b/infra/config/generated/luci/luci-notify.cfg
@@ -972,32 +972,6 @@
   }
   builders {
     bucket: "ci"
-    name: "Linux Builder (dbg)(32)"
-    repository: "https://chromium.googlesource.com/chromium/src"
-  }
-  tree_closers {
-    tree_status_host: "chromium-status.appspot.com"
-    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
-  }
-}
-notifiers {
-  notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
-    email {
-      rotation_urls: "https://chrome-ops-rotation-proxy.appspot.com/current/oncallator:chrome-build-sheriff"
-    }
-    template: "tree_closure_email_template"
-  }
-  notifications {
-    on_occurrence: FAILURE
-    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
-    email {
-      recipients: "thomasanderson@chromium.org"
-    }
-  }
-  builders {
-    bucket: "ci"
     name: "Linux CFI"
     repository: "https://chromium.googlesource.com/chromium/src"
   }
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 60e4a359..2c68b5e5 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -1626,16 +1626,6 @@
   }
 }
 job {
-  id: "Linux Builder (dbg)(32)"
-  realm: "ci"
-  acl_sets: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "Linux Builder (dbg)(32)"
-  }
-}
-job {
   id: "Linux Builder (j-500) (reclient)"
   realm: "ci"
   schedule: "triggered"
@@ -7171,7 +7161,6 @@
   triggers: "Linux Builder"
   triggers: "Linux Builder (Wayland)"
   triggers: "Linux Builder (dbg)"
-  triggers: "Linux Builder (dbg)(32)"
   triggers: "Linux Builder (j-500) (reclient)"
   triggers: "Linux CFI"
   triggers: "Linux CFI (reclient shadow)"
diff --git a/infra/config/generated/sheriff-rotations/chromium.txt b/infra/config/generated/sheriff-rotations/chromium.txt
index 8156cf1..4080294 100644
--- a/infra/config/generated/sheriff-rotations/chromium.txt
+++ b/infra/config/generated/sheriff-rotations/chromium.txt
@@ -13,7 +13,6 @@
 ci/Linux Builder
 ci/Linux Builder (Wayland)
 ci/Linux Builder (dbg)
-ci/Linux Builder (dbg)(32)
 ci/Linux CFI
 ci/Linux Chromium OS ASan LSan Builder
 ci/Linux Chromium OS ASan LSan Tests (1)
diff --git a/infra/config/subprojects/chromium/ci/chromium.linux.star b/infra/config/subprojects/chromium/ci/chromium.linux.star
index 8e892261..0ce2ced 100644
--- a/infra/config/subprojects/chromium/ci/chromium.linux.star
+++ b/infra/config/subprojects/chromium/ci/chromium.linux.star
@@ -237,17 +237,6 @@
 )
 
 ci.builder(
-    name = "Linux Builder (dbg)(32)",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|builder",
-        short_name = "32",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.builder(
     name = "Linux Builder (Wayland)",
     branch_selector = branches.STANDARD_MILESTONE,
     console_view_entry = consoles.console_view_entry(
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 9ba9d173..73ca2f16 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -109,6 +109,7 @@
     "//ios/chrome/browser/ui/find_bar",
     "//ios/chrome/browser/ui/first_run",
     "//ios/chrome/browser/ui/first_run:utils",
+    "//ios/chrome/browser/ui/follow",
     "//ios/chrome/browser/ui/follow:first_follow",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/fullscreen:feature_flags",
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 4faaf4b..745328a 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -71,6 +71,7 @@
 #import "ios/chrome/browser/ui/find_bar/find_bar_controller_ios.h"
 #import "ios/chrome/browser/ui/find_bar/find_bar_coordinator.h"
 #import "ios/chrome/browser/ui/follow/first_follow_coordinator.h"
+#import "ios/chrome/browser/ui/follow/followed_web_channel.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
 #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_mediator.h"
 #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h"
@@ -862,11 +863,11 @@
 
 #pragma mark - FeedCommands
 
-- (void)showFirstFollowModalWithWebChannelTitle:(NSString*)webChannelTitle {
+- (void)showFirstFollowUIForWebChannel:(FollowedWebChannel*)followedWebChannel {
   self.firstFollowCoordinator = [[FirstFollowCoordinator alloc]
       initWithBaseViewController:self.viewController
                          browser:self.browser];
-  self.firstFollowCoordinator.webChannelTitle = webChannelTitle;
+  self.firstFollowCoordinator.followedWebChannel = followedWebChannel;
   [self.firstFollowCoordinator start];
 }
 
diff --git a/ios/chrome/browser/ui/commands/feed_commands.h b/ios/chrome/browser/ui/commands/feed_commands.h
index 7fa01c1..5bad7bb 100644
--- a/ios/chrome/browser/ui/commands/feed_commands.h
+++ b/ios/chrome/browser/ui/commands/feed_commands.h
@@ -5,11 +5,13 @@
 #ifndef IOS_CHROME_BROWSER_UI_COMMANDS_FEED_COMMANDS_H_
 #define IOS_CHROME_BROWSER_UI_COMMANDS_FEED_COMMANDS_H_
 
+@class FollowedWebChannel;
+
 // Commands related to feed.
 @protocol FeedCommands
 
-// Displays the First Follow modal with |webChannelTitle|.
-- (void)showFirstFollowModalWithWebChannelTitle:(NSString*)webChannelTitle;
+// Displays the First Follow UI with |followedWebChannel|.
+- (void)showFirstFollowUIForWebChannel:(FollowedWebChannel*)followedWebChannel;
 
 @end
 
diff --git a/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm b/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm
index b8e85e7..dd1a829 100644
--- a/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm
+++ b/ios/chrome/browser/ui/download/ar_quick_look_egtest.mm
@@ -90,13 +90,7 @@
 }
 
 // Tests that QLPreviewController is shown for sucessfully downloaded USDZ file.
-// TODO(crbug.com/1060374): Test fails on iOS15 device.
-#if !TARGET_IPHONE_SIMULATOR
-#define MAYBE_testDownloadUsdz DISABLED_testDownloadUsdz
-#else
-#define MAYBE_testDownloadUsdz testDownloadUsdz
-#endif
-- (void)MAYBE_testDownloadUsdz {
+- (void)testDownloadUsdz {
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/")];
   [ChromeEarlGrey waitForWebStateContainingText:"Good"];
   [ChromeEarlGrey tapWebStateElementWithID:@"good"];
@@ -174,16 +168,7 @@
       @"AR preview dialog UI was presented");
 }
 
-// Tests that the visibilitychange event is fired when quicklook is
-// shown/hidden.
-// TODO(crbug.com/1060374): Test fails on iOS15 device.
-#if !TARGET_IPHONE_SIMULATOR
-#define MAYBE_testVisibilitychangeEventFired \
-  DISABLED_testVisibilitychangeEventFired
-#else
-#define MAYBE_testVisibilitychangeEventFired testVisibilitychangeEventFired
-#endif
-- (void)MAYBE_testVisibilitychangeEventFired {
+- (void)testVisibilitychangeEventFired {
   [ChromeEarlGrey loadURL:self.testServer->GetURL("/")];
   [ChromeEarlGrey waitForWebStateContainingText:"Good"];
   [ChromeEarlGrey tapWebStateElementWithID:@"good"];
diff --git a/ios/chrome/browser/ui/follow/BUILD.gn b/ios/chrome/browser/ui/follow/BUILD.gn
index 85b8526..441c7a0 100644
--- a/ios/chrome/browser/ui/follow/BUILD.gn
+++ b/ios/chrome/browser/ui/follow/BUILD.gn
@@ -33,6 +33,7 @@
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
+    ":follow",
     "//base",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/common/ui/colors",
diff --git a/ios/chrome/browser/ui/follow/first_follow_coordinator.h b/ios/chrome/browser/ui/follow/first_follow_coordinator.h
index 339fda6e..6a36465 100644
--- a/ios/chrome/browser/ui/follow/first_follow_coordinator.h
+++ b/ios/chrome/browser/ui/follow/first_follow_coordinator.h
@@ -9,13 +9,15 @@
 
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
 
+@class FollowedWebChannel;
+
 // Coordinator for the First Follow feature. This feature informs the user about
-// the feed and following channels after the first few times the user follows
-// any channel.
+// the feed and following channels the first few times the user follows any
+// channel.
 @interface FirstFollowCoordinator : ChromeCoordinator
 
-// The web channel title to display on the modal.
-@property(nonatomic, copy) NSString* webChannelTitle;
+// The web channel that was recently followed.
+@property(nonatomic, strong) FollowedWebChannel* followedWebChannel;
 
 @end
 
diff --git a/ios/chrome/browser/ui/follow/first_follow_coordinator.mm b/ios/chrome/browser/ui/follow/first_follow_coordinator.mm
index 157367c8..ea1b9e7d 100644
--- a/ios/chrome/browser/ui/follow/first_follow_coordinator.mm
+++ b/ios/chrome/browser/ui/follow/first_follow_coordinator.mm
@@ -24,7 +24,9 @@
 - (void)start {
   FirstFollowViewController* firstFollowViewController =
       [[FirstFollowViewController alloc] init];
-  firstFollowViewController.webChannelTitle = self.webChannelTitle;
+  firstFollowViewController.followedWebChannel = self.followedWebChannel;
+  // Ownership is passed to VC so this object is not retained after VC closes.
+  self.followedWebChannel = nil;
 
   if (@available(iOS 15, *)) {
     firstFollowViewController.modalPresentationStyle =
diff --git a/ios/chrome/browser/ui/follow/first_follow_view_controller.h b/ios/chrome/browser/ui/follow/first_follow_view_controller.h
index 5a409471..a8d7a36 100644
--- a/ios/chrome/browser/ui/follow/first_follow_view_controller.h
+++ b/ios/chrome/browser/ui/follow/first_follow_view_controller.h
@@ -7,12 +7,14 @@
 
 #import <UIKit/UIKit.h>
 
-// The UI that informs the user about the feed and following channels after the
+@class FollowedWebChannel;
+
+// The UI that informs the user about the feed and following channels the
 // first few times the user follows any channel.
 @interface FirstFollowViewController : UIViewController
 
-// The Web Channel title to be shown in the modal.
-@property(nonatomic, copy) NSString* webChannelTitle;
+// The web channel that was recently followed.
+@property(nonatomic, strong) FollowedWebChannel* followedWebChannel;
 
 @end
 
diff --git a/ios/chrome/browser/ui/follow/first_follow_view_controller.mm b/ios/chrome/browser/ui/follow/first_follow_view_controller.mm
index 8f419d1..dd6d120 100644
--- a/ios/chrome/browser/ui/follow/first_follow_view_controller.mm
+++ b/ios/chrome/browser/ui/follow/first_follow_view_controller.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/follow/first_follow_view_controller.h"
 
 #include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/ui/follow/followed_web_channel.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util_mac.h"
@@ -37,11 +38,11 @@
   [super viewDidLoad];
 
   // TODO(crbug.com/1312124): Polish this UI and add favicon.
-  UILabel* titleLabel =
-      [self labelWithText:l10n_util::GetNSStringF(
-                              IDS_IOS_FIRST_FOLLOW_TITLE,
-                              base::SysNSStringToUTF16(self.webChannelTitle))
-                textStyle:UIFontTextStyleTitle1];
+  UILabel* titleLabel = [self
+      labelWithText:l10n_util::GetNSStringF(
+                        IDS_IOS_FIRST_FOLLOW_TITLE,
+                        base::SysNSStringToUTF16(self.followedWebChannel.title))
+          textStyle:UIFontTextStyleTitle1];
   UILabel* subTitleLabel =
       [self labelWithText:l10n_util::GetNSString(IDS_IOS_FIRST_FOLLOW_SUBTITLE)
                 textStyle:UIFontTextStyleHeadline];
@@ -51,9 +52,16 @@
   UIButton* goToFeedButton = [self filledGoToFeedButton];
   UIButton* gotItButton = [self plainGotItButton];
 
-  UIStackView* verticalStack = [[UIStackView alloc] initWithArrangedSubviews:@[
-    titleLabel, subTitleLabel, bodyLabel, goToFeedButton, gotItButton
-  ]];
+  // Go To Feed button is only displayed if the web channel is available.
+  NSArray* subviews = nil;
+  if (self.followedWebChannel.available) {
+    subviews =
+        @[ titleLabel, subTitleLabel, bodyLabel, goToFeedButton, gotItButton ];
+  } else {
+    subviews = @[ titleLabel, subTitleLabel, bodyLabel, gotItButton ];
+  }
+  UIStackView* verticalStack =
+      [[UIStackView alloc] initWithArrangedSubviews:subviews];
   verticalStack.axis = UILayoutConstraintAxisVertical;
   verticalStack.distribution = UIStackViewDistributionFill;
   verticalStack.alignment = UIStackViewAlignmentCenter;
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 7d14df0..a10f876 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 @@
-c845e7497c3c4b7858bdb40c6d913b547f966703
\ No newline at end of file
+db63ebb6b62982b53e4d0975f17aed482356a9e8
\ 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 dcd00e4..cc0d5e4 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 @@
-c29e083107dfc413ed0fa18723f0b6d8f28b6b61
\ No newline at end of file
+68d07e1ec9e51642faac57846a23e8169324c985
\ 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 9c048c5..98fc9aa 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 @@
-061028041a0e316edcc94192ffb0f467797d7366
\ No newline at end of file
+4033f8dc5c68fb71d97e77dcab3949a46be003d5
\ 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 42f4b9be..2f3f222 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 @@
-d9e0efac4acf08da8b4c9b974f2e2c4bbdbc2552
\ No newline at end of file
+739c9546efb0148c1adc968a7198a81ba0ea3a41
\ 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 05d3e16..0c9f622 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 @@
-56e335418f365933b961d4cd98bc53686a136a39
\ No newline at end of file
+3420779f0e6269fe180f6ca62691b8384272e7b3
\ 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 4828938c..4b6bc9ae 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 @@
-d9b3ca488bd2564e0d57e2084ed7150d49880c08
\ No newline at end of file
+5b1451828d52d19af15f4f95176f7b1221696232
\ 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 f5e26f0..78ba05b8f 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 @@
-68c21bcbcc5d423a8c97980a5a3692bd0c54b45b
\ No newline at end of file
+11fb66a7748ecc1c4afca772f534e2519737411e
\ 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 160c315..533752ecc 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 @@
-e63cd9966c47495d07165f400cd27c593597b821
\ No newline at end of file
+f1cbf1629efc766b480c9ae5bdfc7d62f0b84d47
\ 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 df2bc11e..43b74dd 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 @@
-b106b323b5b636aa3d98ba17fb1cb7d3b099f4a2
\ No newline at end of file
+d0dd2cbc232dc6c59c992517e8f4fc832ec2db96
\ 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 d4146ac5..e4ae3db6 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 @@
-c8d28f670c5f9160e08327b407bc435af84e98fd
\ No newline at end of file
+b6458aee6d52721cac28e385bde806ba40e8e1fe
\ No newline at end of file
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index 5e1842b..60e3ec0 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -1356,6 +1356,20 @@
     return NO;
   }
 
+  // TODO(crbug.com/1308875): Remove this when |canShowMIMEType| is fixed.
+  // On iOS 15 |canShowMIMEType| returns true for AR files although WebKit is
+  // not capable of displaying them natively.
+  if (@available(iOS 15, *)) {
+    NSString* MIMEType = WKResponse.response.MIMEType;
+    if ([MIMEType isEqual:@"model/vnd.pixar.usd"] ||
+        [MIMEType isEqual:@"model/usd"] ||
+        [MIMEType isEqual:@"model/vnd.usdz+zip"] ||
+        [MIMEType isEqual:@"model/vnd.pixar.usd"] ||
+        [MIMEType isEqual:@"model/vnd.reality"]) {
+      return NO;
+    }
+  }
+
   GURL responseURL = net::GURLWithNSURL(WKResponse.response.URL);
   if (responseURL.SchemeIs(url::kDataScheme) && WKResponse.forMainFrame) {
     // Block rendering data URLs for renderer-initiated navigations in main
diff --git a/mojo/public/cpp/bindings/pending_receiver.h b/mojo/public/cpp/bindings/pending_receiver.h
index 7c3e53ec..3d0ec9a 100644
--- a/mojo/public/cpp/bindings/pending_receiver.h
+++ b/mojo/public/cpp/bindings/pending_receiver.h
@@ -98,9 +98,7 @@
 
   // Like above but provides a reason for the disconnection.
   void ResetWithReason(uint32_t reason, const std::string& description) {
-    if (!is_valid()) {
-      return;
-    }
+    CHECK(is_valid()) << "Cannot send reset reason to an invalid handle.";
 
     Message message =
         PipeControlMessageProxy::ConstructPeerEndpointClosedMessage(
diff --git a/net/test/embedded_test_server/connection_tracker.cc b/net/test/embedded_test_server/connection_tracker.cc
index 3e922b1..9f87ef1 100644
--- a/net/test/embedded_test_server/connection_tracker.cc
+++ b/net/test/embedded_test_server/connection_tracker.cc
@@ -11,7 +11,7 @@
 
 namespace {
 
-uint16_t GetPort(const net::StreamSocket& connection) {
+bool GetPort(const net::StreamSocket& connection, uint16_t* port) {
   // Gets the remote port of the peer, since the local port will always be
   // the port the test server is listening on. This isn't strictly correct -
   // it's possible for multiple peers to connect with the same remote port
@@ -21,8 +21,11 @@
   // two connections. This also would be problematic if the OS reused ports,
   // but that's not something to worry about for these tests.
   net::IPEndPoint address;
-  EXPECT_EQ(net::OK, connection.GetPeerAddress(&address));
-  return address.port();
+  int result = connection.GetPeerAddress(&address);
+  if (result != net::OK)
+    return false;
+  *port = address.port();
+  return true;
 }
 
 }  // namespace
@@ -123,10 +126,12 @@
 std::unique_ptr<net::StreamSocket>
 ConnectionTracker::ConnectionListener::AcceptedSocket(
     std::unique_ptr<net::StreamSocket> connection) {
-  uint16_t port = GetPort(*connection);
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&ConnectionTracker::AcceptedSocketWithPort,
-                                base::Unretained(tracker_), port));
+  uint16_t port;
+  if (GetPort(*connection, &port)) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ConnectionTracker::AcceptedSocketWithPort,
+                                  base::Unretained(tracker_), port));
+  }
   return connection;
 }
 
@@ -139,10 +144,12 @@
   // the sockets of the test server are being flushed and disconnected.
   if (rv <= 0)
     return;
-  uint16_t port = GetPort(connection);
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&ConnectionTracker::ReadFromSocketWithPort,
-                                base::Unretained(tracker_), port));
+  uint16_t port;
+  if (GetPort(connection, &port)) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ConnectionTracker::ReadFromSocketWithPort,
+                                  base::Unretained(tracker_), port));
+  }
 }
 
 }  // namespace test_server
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index a9f6fa9..82d6084 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8262,7 +8262,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8766,7 +8766,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 183244e..3d2e65c 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46013,7 +46013,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46517,7 +46517,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47025,7 +47025,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47529,7 +47529,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48104,7 +48104,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48608,7 +48608,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49183,7 +49183,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49687,7 +49687,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.24"
+              "revision": "version:101.0.4951.27"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 6d31385..c8c5ea6 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5833,7 +5833,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -5841,14 +5841,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "dimension_sets": [
@@ -5975,7 +5975,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -5983,14 +5983,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 00a10c9..edad1785 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -86203,7 +86203,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -86211,14 +86211,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -86320,7 +86320,7 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "isolate_profile_data": true,
@@ -86328,14 +86328,14 @@
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -87707,21 +87707,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "dimension_sets": [
@@ -87849,21 +87849,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "dimension_sets": [
@@ -89404,21 +89404,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "dimension_sets": [
@@ -89546,21 +89546,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "dimension_sets": [
@@ -90307,21 +90307,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -90403,21 +90403,21 @@
       },
       {
         "args": [
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome",
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter"
         ],
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
-        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4988.0",
+        "name": "lacros_chrome_browsertests_run_in_series Lacros version skew testing ash 102.0.4989.0",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v102.0.4988.0",
-              "revision": "version:102.0.4988.0"
+              "location": "lacros_version_skew_tests_v102.0.4989.0",
+              "revision": "version:102.0.4989.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index ae28f03..a66c9f4 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -4570,12 +4570,6 @@
       "all"
     ]
   },
-  "Linux Builder (dbg)(32)": {
-    "additional_compile_targets": [
-      "google_apis_unittests",
-      "sync_integration_tests"
-    ]
-  },
   "Linux Tests": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index dffa866..fdb2dbe 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -28,16 +28,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4988.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v102.0.4989.0/test_ash_chrome',
       '--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter',
     ],
-    'identifier': 'Lacros version skew testing ash 102.0.4988.0',
+    'identifier': 'Lacros version skew testing ash 102.0.4989.0',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v102.0.4988.0',
-          'revision': 'version:102.0.4988.0',
+          'location': 'lacros_version_skew_tests_v102.0.4989.0',
+          'revision': 'version:102.0.4989.0',
         },
       ],
     },
@@ -459,7 +459,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M101',
-          'revision': 'version:101.0.4951.24',
+          'revision': 'version:101.0.4951.27',
         }
       ],
     },
@@ -603,7 +603,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M101',
-          'revision': 'version:101.0.4951.24',
+          'revision': 'version:101.0.4951.27',
         }
       ],
     },
@@ -747,7 +747,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M101',
-          'revision': 'version:101.0.4951.24',
+          'revision': 'version:101.0.4951.27',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index edc22e41..bf349d8 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -5085,12 +5085,6 @@
           'all'
         ]
       },
-      'Linux Builder (dbg)(32)': {
-        'additional_compile_targets': [
-          'google_apis_unittests',
-          'sync_integration_tests'
-        ]
-      },
       'Linux Tests': {
         'mixins': [
           'isolate_profile_data',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6cb6b4f..719d58a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1387,8 +1387,7 @@
                         "enable_same_site": "true",
                         "extension_message_supported": "true",
                         "foreground_cache_size": "2",
-                        "max_buffered_bytes_per_process": "1024000",
-                        "unload_support": "opt_in_header_required"
+                        "max_buffered_bytes_per_process": "1024000"
                     },
                     "enable_features": [
                         "BFCachePerformanceManagerPolicy",
@@ -6010,6 +6009,27 @@
             ]
         }
     ],
+    "ReduceUserAgentMinorVersionStudy": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ReduceUserAgentMinorVersion"
+                    ]
+                }
+            ]
+        }
+    ],
     "RelatedSearches": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index d36ed97..f792cf2 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -1559,8 +1559,11 @@
   auto* identifier_value = DynamicTo<CSSIdentifierValue>(value);
   if (identifier_value &&
       identifier_value->GetValueID() == CSSValueID::kCurrentcolor) {
-    state.Style()->SetColorIsCurrentColor(true);
     ApplyInherit(state);
+    state.Style()->SetColorIsCurrentColor(true);
+    if (RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
+        state.IsForHighlight() && state.OriginatingElementStyle())
+      state.Style()->SetColor(state.OriginatingElementStyle()->GetColor());
     return;
   }
   if (value.IsInitialColorValue()) {
diff --git a/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h b/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h
index ebe3a9a..37788dd 100644
--- a/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h
@@ -67,6 +67,7 @@
   static void CounterStyleRulesChanged(TreeScope& scope);
 
   void RebuildCascadeLayerMap(const ActiveStyleSheetVector&);
+  bool HasCascadeLayerMap() const { return cascade_layer_map_.Get(); }
   const CascadeLayerMap* GetCascadeLayerMap() const {
     return cascade_layer_map_;
   }
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
index 6acbc273..40dcfe53 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.cc
@@ -65,6 +65,7 @@
                         ? ElementType::kPseudoElement
                         : ElementType::kElement),
       nearest_container_(style_recalc_context.container),
+      originating_element_style_(style_request.originating_element_style),
       is_for_highlight_(IsHighlightPseudoElement(style_request.pseudo_id)),
       is_for_custom_highlight_(style_request.pseudo_id ==
                                PseudoId::kPseudoIdHighlight),
@@ -82,6 +83,10 @@
     layout_parent_style_ = parent_style_;
 
   DCHECK(document.IsActive());
+
+  if (RuntimeEnabledFeatures::HighlightInheritanceEnabled() &&
+      is_for_highlight_)
+    DCHECK(originating_element_style_);
 }
 
 StyleResolverState::~StyleResolverState() {
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
index 981be91..209afaeb 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
@@ -160,6 +160,9 @@
   // reference to the passed value.
   const CSSValue& ResolveLightDarkPair(const CSSValue&);
 
+  const ComputedStyle* OriginatingElementStyle() const {
+    return originating_element_style_.get();
+  }
   bool IsForHighlight() const { return is_for_highlight_; }
 
   bool CanCacheBaseStyle() const { return can_cache_base_style_; }
@@ -212,6 +215,7 @@
   ElementType element_type_;
   Element* nearest_container_;
 
+  scoped_refptr<const ComputedStyle> originating_element_style_;
   // True if we are resolving styles for a highlight pseudo-element.
   const bool is_for_highlight_;
   // True if we are resolving styles for a custom highlight pseudo-element.
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index 1a7f9b5e..afbdf399 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/css/css_value_list.h"
 #include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
 #include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
+#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
 #include "third_party/blink/renderer/core/css/style_change_reason.h"
 #include "third_party/blink/renderer/core/css/style_engine.h"
@@ -1553,6 +1554,52 @@
             &GetDocument());
 }
 
+// https://crbug.com/1313357
+TEST_F(StyleResolverTest, CascadeLayersAfterModifyingAnotherSheet) {
+  GetDocument().documentElement()->setInnerHTML(R"HTML(
+    <style>
+      @layer {
+        target { color: red; }
+      }
+    </style>
+    <style id="addrule"></style>
+    <target></target>
+  )HTML");
+
+  UpdateAllLifecyclePhasesForTest();
+
+  GetDocument().getElementById("addrule")->appendChild(
+      GetDocument().createTextNode("target { font-size: 10px; }"));
+
+  UpdateAllLifecyclePhasesForTest();
+
+  ASSERT_TRUE(GetDocument().GetScopedStyleResolver()->GetCascadeLayerMap());
+
+  StyleResolverState state(GetDocument(),
+                           *GetDocument().QuerySelector("target"));
+  SelectorFilter filter;
+  MatchResult match_result;
+  ElementRuleCollector collector(state.ElementContext(), StyleRecalcContext(),
+                                 filter, match_result, state.Style(),
+                                 EInsideLink::kNotInsideLink);
+  MatchAllRules(state, collector);
+  const auto& properties = match_result.GetMatchedProperties();
+  ASSERT_EQ(properties.size(), 2u);
+
+  const uint16_t kImplicitOuterLayerOrder =
+      CascadeLayerMap::kImplicitOuterLayerOrder;
+
+  // @layer { target { color: red } }"
+  EXPECT_TRUE(properties[0].properties->HasProperty(CSSPropertyID::kColor));
+  EXPECT_EQ(0u, properties[0].types_.layer_order);
+  EXPECT_EQ(properties[0].types_.origin, CascadeOrigin::kAuthor);
+
+  // target { font-size: 10px }
+  EXPECT_TRUE(properties[1].properties->HasProperty(CSSPropertyID::kFontSize));
+  EXPECT_EQ(kImplicitOuterLayerOrder, properties[1].types_.layer_order);
+  EXPECT_EQ(properties[1].types_.origin, CascadeOrigin::kAuthor);
+}
+
 // TODO(crbug.com/1095765): We should have a WPT for this test case, but
 // currently Blink web test runner can't test @page rules in WPT.
 TEST_F(StyleResolverTest, CascadeLayersAndPageRules) {
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 23bdbf8b..65e9605 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -2049,29 +2049,30 @@
     MarkCounterStylesNeedUpdate();
 
   unsigned append_start_index = 0;
+  bool rebuild_cascade_layer_map = changed_rule_flags & kLayerRules;
   if (scoped_resolver) {
     // - If all sheets were removed, we remove the ScopedStyleResolver
     // - If new sheets were appended to existing ones, start appending after the
-    //   common prefix.
+    //   common prefix, and rebuild CascadeLayerMap only if layers are changed.
     // - For other diffs, reset author style and re-add all sheets for the
-    //   TreeScope.
-    if (new_style_sheets.IsEmpty())
+    //   TreeScope. If there is an existing CascadeLayerMap, rebuild it.
+    if (new_style_sheets.IsEmpty()) {
+      rebuild_cascade_layer_map = false;
       ResetAuthorStyle(tree_scope);
-    else if (change == kActiveSheetsAppended)
+    } else if (change == kActiveSheetsAppended) {
       append_start_index = old_style_sheets.size();
-    else
+    } else {
+      rebuild_cascade_layer_map = scoped_resolver->HasCascadeLayerMap();
       scoped_resolver->ResetStyle();
+    }
   }
 
-  // Cascade layer map must be built before adding other at-rules, because other
-  // at-rules rely on layer order to resolve name conflicts.
+  if (rebuild_cascade_layer_map) {
+    tree_scope.EnsureScopedStyleResolver().RebuildCascadeLayerMap(
+        new_style_sheets);
+  }
+
   if (changed_rule_flags & kLayerRules) {
-    if (!new_style_sheets.IsEmpty()) {
-      // Rebuild cascade layer map in all cases, because a newly inserted
-      // sub-layer can precede an original layer in the final ordering.
-      tree_scope.EnsureScopedStyleResolver().RebuildCascadeLayerMap(
-          new_style_sheets);
-    }
     if (resolver_)
       resolver_->InvalidateMatchedPropertiesCache();
 
diff --git a/third_party/blink/renderer/core/css/style_request.h b/third_party/blink/renderer/core/css/style_request.h
index 3c37c21..803b0976 100644
--- a/third_party/blink/renderer/core/css/style_request.h
+++ b/third_party/blink/renderer/core/css/style_request.h
@@ -23,6 +23,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_REQUEST_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_STYLE_REQUEST_H_
 
+#include "third_party/blink/renderer/core/css/style_color.h"
 #include "third_party/blink/renderer/core/layout/custom_scrollbar.h"
 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
@@ -46,6 +47,7 @@
 
   const ComputedStyle* parent_override{nullptr};
   const ComputedStyle* layout_parent_override{nullptr};
+  const ComputedStyle* originating_element_style{nullptr};
   RuleMatchingBehavior matching_behavior{kMatchAllRules};
 
   PseudoId pseudo_id{kPseudoIdNone};
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 57a5f248..3bf2415 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3610,6 +3610,7 @@
         const ComputedStyle* highlight_parent =
             parent_highlights ? parent_highlights->Selection() : nullptr;
         StyleRequest style_request{kPseudoIdSelection, highlight_parent};
+        style_request.originating_element_style = new_style.get();
         highlights.SetSelection(
             StyleForPseudoElement(new_style_recalc_context, style_request));
       }
@@ -3619,6 +3620,7 @@
         const ComputedStyle* highlight_parent =
             parent_highlights ? parent_highlights->TargetText() : nullptr;
         StyleRequest style_request{kPseudoIdTargetText, highlight_parent};
+        style_request.originating_element_style = new_style.get();
         highlights.SetTargetText(
             StyleForPseudoElement(new_style_recalc_context, style_request));
       }
@@ -3628,6 +3630,7 @@
         const ComputedStyle* highlight_parent =
             parent_highlights ? parent_highlights->SpellingError() : nullptr;
         StyleRequest style_request{kPseudoIdSpellingError, highlight_parent};
+        style_request.originating_element_style = new_style.get();
         highlights.SetSpellingError(
             StyleForPseudoElement(new_style_recalc_context, style_request));
       }
@@ -3637,6 +3640,7 @@
         const ComputedStyle* highlight_parent =
             parent_highlights ? parent_highlights->GrammarError() : nullptr;
         StyleRequest style_request{kPseudoIdGrammarError, highlight_parent};
+        style_request.originating_element_style = new_style.get();
         highlights.SetGrammarError(
             StyleForPseudoElement(new_style_recalc_context, style_request));
       }
@@ -3659,6 +3663,7 @@
                   : nullptr;
           StyleRequest style_request{kPseudoIdHighlight, highlight_parent,
                                      custom_highlight_name};
+          style_request.originating_element_style = new_style.get();
           highlights.SetCustomHighlight(
               custom_highlight_name,
               StyleForPseudoElement(new_style_recalc_context, style_request));
@@ -6024,6 +6029,7 @@
     // so we don't need to do anything special regarding display contents (see
     // https://drafts.csswg.org/css-pseudo/#highlight-cascade).
     style_request.layout_parent_override = highlight_element_style;
+    style_request.originating_element_style = element_style;
   } else {
     style_request.parent_override = element_style;
     style_request.layout_parent_override = layout_parent_style;
diff --git a/third_party/blink/renderer/core/frame/navigator_ua_data.cc b/third_party/blink/renderer/core/frame/navigator_ua_data.cc
index 7192ff0..2ce5eb004 100644
--- a/third_party/blink/renderer/core/frame/navigator_ua_data.cc
+++ b/third_party/blink/renderer/core/frame/navigator_ua_data.cc
@@ -15,6 +15,8 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_ua_data_values.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/dactyloscoper.h"
+#include "third_party/blink/renderer/core/frame/web_feature_forward.h"
 #include "third_party/blink/renderer/core/page/page.h"
 
 namespace blink {
@@ -166,20 +168,27 @@
       IdentifiabilityStudySettings::Get()->ShouldSampleType(
           IdentifiableSurface::Type::kNavigatorUAData_GetHighEntropyValues);
   UADataValues* values = MakeGarbageCollected<UADataValues>();
-  // According to
-  // https://wicg.github.io/ua-client-hints/#getHighEntropyValues, brands,
-  // mobile and platform should be included regardless of whether they were
-  // asked for.
-
   // TODO: It'd be faster to compare hint when turning |hints| into an
   // AtomicString vector and turning the const string literals |hint| into
   // AtomicStrings as well.
   for (const String& hint : hints) {
-    values->setBrands(brand_set_);
+    // According to
+    // https://wicg.github.io/ua-client-hints/#getHighEntropyValues, brands,
+    // mobile and platform should be included regardless of whether they were
+    // asked for.
+
+    // Use `brands()` and not `brand_set_` directly since the former also
+    // records IdentifiabilityStudy metrics.
+    values->setBrands(brands());
     values->setMobile(is_mobile_);
     values->setPlatform(platform_);
-    MaybeRecordMetric(record_identifiability, hint, platform_,
-                      execution_context);
+    // Record IdentifiabilityStudy metrics for `mobile()` and `platform()` (the
+    // `brands()` part is already recorded inside that function).
+    Dactyloscoper::RecordDirectSurface(
+        GetExecutionContext(), WebFeature::kNavigatorUAData_Mobile, mobile());
+    Dactyloscoper::RecordDirectSurface(GetExecutionContext(),
+                                       WebFeature::kNavigatorUAData_Platform,
+                                       platform());
 
     if (hint == "platformVersion") {
       values->setPlatformVersion(platform_version_);
@@ -224,6 +233,12 @@
   V8ObjectBuilder builder(script_state);
   builder.Add("brands", brands());
   builder.Add("mobile", mobile());
+
+  // Record IdentifiabilityStudy metrics for `mobile()` (the `brands()` part is
+  // already recorded inside that function).
+  Dactyloscoper::RecordDirectSurface(
+      GetExecutionContext(), WebFeature::kNavigatorUAData_Mobile, mobile());
+
   return builder.GetScriptValue();
 }
 
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni
index 63a3bc72..87583c0 100644
--- a/third_party/blink/renderer/core/html/build.gni
+++ b/third_party/blink/renderer/core/html/build.gni
@@ -435,8 +435,6 @@
   "link_resource.h",
   "link_style.cc",
   "link_style.h",
-  "link_web_bundle.cc",
-  "link_web_bundle.h",
   "list_item_ordinal.cc",
   "list_item_ordinal.h",
   "loading_attribute.cc",
@@ -650,6 +648,7 @@
   "custom/custom_element_test_helpers.h",
   "custom/custom_element_upgrade_sorter_test.cc",
   "fenced_frame/fenced_frame_shadow_dom_delegate_test.cc",
+  "fenced_frame/html_fenced_frame_element_test.cc",
   "forms/email_input_type_test.cc",
   "forms/external_date_time_chooser_test.cc",
   "forms/external_popup_menu_test.cc",
@@ -690,7 +689,6 @@
   "lazy_load_image_observer_test.cc",
   "link_element_loading_test.cc",
   "link_rel_attribute_test.cc",
-  "link_web_bundle_test.cc",
   "media/autoplay_uma_helper_test.cc",
   "media/html_media_element_event_listeners_test.cc",
   "media/html_media_element_test.cc",
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
index 742050c..3687dcc 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.cc
@@ -404,6 +404,16 @@
   return features::IsFencedFramesMPArchBased();
 }
 
+const absl::optional<PhysicalSize> HTMLFencedFrameElement::FrozenFrameSize()
+    const {
+  if (!frozen_frame_size_)
+    return absl::nullopt;
+  const float ratio = GetDocument().DevicePixelRatio();
+  return PhysicalSize(
+      LayoutUnit::FromFloatRound(frozen_frame_size_->width * ratio),
+      LayoutUnit::FromFloatRound(frozen_frame_size_->height * ratio));
+}
+
 void HTMLFencedFrameElement::FreezeFrameSize() {
   DCHECK(!frozen_frame_size_);
 
@@ -422,7 +432,11 @@
 
 void HTMLFencedFrameElement::FreezeFrameSize(const PhysicalSize& size) {
   DCHECK(!frozen_frame_size_);
-  frozen_frame_size_ = content_rect_->size;
+  // Store the value divided by the |DevicePixelRatio|, so that it scales with
+  // the browser zoom or when moved to different screens.
+  const float ratio = GetDocument().DevicePixelRatio();
+  frozen_frame_size_ = {LayoutUnit::FromFloatRound(size.width / ratio),
+                        LayoutUnit::FromFloatRound(size.height / ratio)};
 
   if (!features::IsFencedFramesMPArchBased()) {
     // With Shadow DOM, update the CSS `transform` property whenever
@@ -471,7 +485,7 @@
     FreezeFrameSize(content_rect_->size);
     return;
   }
-  if (frozen_frame_size_ && features::IsFencedFramesMPArchBased())
+  if (frozen_frame_size_ && !features::IsFencedFramesMPArchBased())
     UpdateInnerStyleOnFrozenInternalFrame();
 }
 
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
index 2cab46bff..57a7620 100644
--- a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h
@@ -84,9 +84,7 @@
   // The frame size is "frozen" when the `src` attribute is set.
   // The frozen state is kept in this element so that it can survive across
   // reattaches.
-  const absl::optional<PhysicalSize>& FrozenFrameSize() const {
-    return frozen_frame_size_;
-  }
+  const absl::optional<PhysicalSize> FrozenFrameSize() const;
   // True if the frame size should be frozen when the next resize completed.
   // When `src` is set but layout is not completed yet, the frame size is frozen
   // after the first layout.
@@ -159,6 +157,8 @@
   bool freeze_mode_attribute_ = false;
 
   friend class ResizeObserverDelegate;
+  FRIEND_TEST_ALL_PREFIXES(HTMLFencedFrameElementTest,
+                           FreezeSizePageZoomFactor);
 };
 
 // Type casting. Custom since adoption could lead to an HTMLFencedFrameElement
diff --git a/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element_test.cc b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element_test.cc
new file mode 100644
index 0000000..53bc58d
--- /dev/null
+++ b/third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element_test.cc
@@ -0,0 +1,60 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+
+namespace blink {
+
+class HTMLFencedFrameElementTest : private ScopedFencedFramesForTest,
+                                   public RenderingTest {
+ public:
+  HTMLFencedFrameElementTest()
+      : ScopedFencedFramesForTest(true),
+        RenderingTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
+
+ protected:
+  void SetUp() override {
+    RenderingTest::SetUp();
+    base::FieldTrialParams params;
+    params["implementation_type"] = "mparch";
+    enabled_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kFencedFrames, params);
+
+    SecurityContext& security_context =
+        GetDocument().GetFrame()->DomWindow()->GetSecurityContext();
+    security_context.SetSecurityOriginForTesting(nullptr);
+    security_context.SetSecurityOrigin(
+        SecurityOrigin::CreateFromString("https://fencedframedelegate.test"));
+    EXPECT_EQ(security_context.GetSecureContextMode(),
+              SecureContextMode::kSecureContext);
+  }
+
+ private:
+  base::test::ScopedFeatureList enabled_feature_list_;
+};
+
+TEST_F(HTMLFencedFrameElementTest, FreezeSizePageZoomFactor) {
+  Document& doc = GetDocument();
+  auto* fenced_frame = MakeGarbageCollected<HTMLFencedFrameElement>(doc);
+  doc.body()->AppendChild(fenced_frame);
+
+  LocalFrame& frame = GetFrame();
+  const float zoom_factor = frame.PageZoomFactor();
+  const PhysicalSize size(200, 100);
+  fenced_frame->FreezeFrameSize(size);
+  frame.SetPageZoomFactor(zoom_factor * 2);
+  EXPECT_EQ(*fenced_frame->FrozenFrameSize(),
+            PhysicalSize(size.width * 2, size.height * 2));
+
+  frame.SetPageZoomFactor(zoom_factor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index fb9881c..dcb27c1 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -295,7 +295,6 @@
     "referrerpolicy",
     "rel",
     "required",
-    "resources",
     "rev",
     "reversed",
     "role",
@@ -305,7 +304,6 @@
     "sandbox",
     "scheme",
     "scope",
-    "scopes",
     "scrollamount",
     "scrolldelay",
     "scrolling",
diff --git a/third_party/blink/renderer/core/html/html_link_element.cc b/third_party/blink/renderer/core/html/html_link_element.cc
index 4303595..6116761 100644
--- a/third_party/blink/renderer/core/html/html_link_element.cc
+++ b/third_party/blink/renderer/core/html/html_link_element.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/html/cross_origin_attribute.h"
 #include "third_party/blink/renderer/core/html/link_manifest.h"
-#include "third_party/blink/renderer/core/html/link_web_bundle.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/loader/link_loader.h"
@@ -52,27 +51,6 @@
 
 namespace blink {
 
-namespace {
-
-void ParseUrlsListValue(const AtomicString& value,
-                        HashSet<KURL>& url_hash,
-                        const Document& document) {
-  // Parse the attribute value as a space-separated list of urls
-  SpaceSplitString urls(value);
-  url_hash.clear();
-  url_hash.ReserveCapacityForSize(SafeCast<wtf_size_t>(urls.size()));
-  for (wtf_size_t i = 0; i < urls.size(); ++i) {
-    KURL url = LinkWebBundle::ParseResourceUrl(
-        urls[i], base::BindRepeating(&Document::CompleteURL,
-                                     base::Unretained(&document)));
-    if (url.IsValid()) {
-      url_hash.insert(std::move(url));
-    }
-  }
-}
-
-}  // namespace
-
 HTMLLinkElement::HTMLLinkElement(Document& document,
                                  const CreateElementFlags flags)
     : HTMLElement(html_names::kLinkTag, document),
@@ -80,11 +58,6 @@
       sizes_(MakeGarbageCollected<DOMTokenList>(*this, html_names::kSizesAttr)),
       rel_list_(MakeGarbageCollected<RelList>(this)),
       blocking_attribute_(MakeGarbageCollected<BlockingAttribute>(this)),
-      resources_(
-          MakeGarbageCollected<DOMTokenList>(*this,
-                                             html_names::kResourcesAttr)),
-      scopes_(
-          MakeGarbageCollected<DOMTokenList>(*this, html_names::kScopesAttr)),
       created_by_parser_(flags.IsCreatedByParser()) {}
 
 HTMLLinkElement::~HTMLLinkElement() = default;
@@ -147,16 +120,6 @@
                  GetExecutionContext())) {
     UseCounter::Count(GetDocument(), WebFeature::kPriorityHints);
     fetch_priority_hint_ = value;
-  } else if (name == html_names::kResourcesAttr &&
-             LinkWebBundle::IsFeatureEnabled(GetExecutionContext())) {
-    resources_->DidUpdateAttributeValue(params.old_value, value);
-    ParseUrlsListValue(value, valid_resource_urls_, GetDocument());
-    Process();
-  } else if (name == html_names::kScopesAttr &&
-             LinkWebBundle::IsFeatureEnabled(GetExecutionContext())) {
-    scopes_->DidUpdateAttributeValue(params.old_value, value);
-    ParseUrlsListValue(value, valid_scope_urls_, GetDocument());
-    Process();
   } else if (name == html_names::kDisabledAttr) {
     UseCounter::Count(GetDocument(), WebFeature::kHTMLLinkElementDisabled);
     if (params.reason == AttributeModificationReason::kByParser)
@@ -227,13 +190,7 @@
   }
 
   if (!link_) {
-    if (rel_attribute_.IsWebBundle()) {
-      // Only create a webbundle link when SubresourceWebBundles are enabled.
-      if (!LinkWebBundle::IsFeatureEnabled(GetExecutionContext())) {
-        return nullptr;
-      }
-      link_ = MakeGarbageCollected<LinkWebBundle>(this);
-    } else if (rel_attribute_.IsManifest()) {
+    if (rel_attribute_.IsManifest()) {
       link_ = MakeGarbageCollected<LinkManifest>(this);
     } else {
       auto* link = MakeGarbageCollected<LinkStyle>(this);
@@ -425,22 +382,12 @@
   return sizes_.Get();
 }
 
-DOMTokenList* HTMLLinkElement::resources() const {
-  return resources_.Get();
-}
-
-DOMTokenList* HTMLLinkElement::scopes() const {
-  return scopes_.Get();
-}
-
 void HTMLLinkElement::Trace(Visitor* visitor) const {
   visitor->Trace(link_);
   visitor->Trace(sizes_);
   visitor->Trace(link_loader_);
   visitor->Trace(rel_list_);
   visitor->Trace(blocking_attribute_);
-  visitor->Trace(resources_);
-  visitor->Trace(scopes_);
   HTMLElement::Trace(visitor);
   LinkLoaderClient::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/html/html_link_element.h b/third_party/blink/renderer/core/html/html_link_element.h
index 60e6e15..21fe84cb 100644
--- a/third_party/blink/renderer/core/html/html_link_element.h
+++ b/third_party/blink/renderer/core/html/html_link_element.h
@@ -96,15 +96,6 @@
 
   DOMTokenList* sizes() const;
 
-  // IDL method.
-  DOMTokenList* resources() const;
-  DOMTokenList* scopes() const;
-
-  const HashSet<KURL>& ValidResourceUrls() const {
-    return valid_resource_urls_;
-  }
-  const HashSet<KURL>& ValidScopeUrls() const { return valid_scope_urls_; }
-
   void ScheduleEvent();
 
   // From LinkLoaderClient
@@ -173,10 +164,6 @@
   Member<RelList> rel_list_;
   LinkRelAttribute rel_attribute_;
   Member<BlockingAttribute> blocking_attribute_;
-  Member<DOMTokenList> resources_;
-  HashSet<KURL> valid_resource_urls_;
-  Member<DOMTokenList> scopes_;
-  HashSet<KURL> valid_scope_urls_;
 
   bool created_by_parser_;
 };
diff --git a/third_party/blink/renderer/core/html/html_link_element.idl b/third_party/blink/renderer/core/html/html_link_element.idl
index f41ba47..e84067f 100644
--- a/third_party/blink/renderer/core/html/html_link_element.idl
+++ b/third_party/blink/renderer/core/html/html_link_element.idl
@@ -55,12 +55,6 @@
     // https://w3c.github.io/webappsec-subresource-integrity/#HTMLLinkElement
     [Reflect] attribute DOMString integrity;
 
-    // Subresource loading with Web Bundles
-    // https://github.com/WICG/webpackage/blob/master/explainers/subresource-loading.md
-    // crbug.com/1082020
-    [RuntimeEnabled=SubresourceWebBundles, PutForwards=value, SecureContext] readonly attribute DOMTokenList resources;
-    [RuntimeEnabled=SubresourceWebBundles, PutForwards=value, SecureContext] readonly attribute DOMTokenList scopes;
-
     // https://html.spec.whatwg.org/multipage/semantics.html#dom-link-blocking
     [RuntimeEnabled=BlockingAttribute, SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
 };
diff --git a/third_party/blink/renderer/core/html/link_web_bundle.h b/third_party/blink/renderer/core/html/link_web_bundle.h
deleted file mode 100644
index a954108..0000000
--- a/third_party/blink/renderer/core/html/link_web_bundle.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_LINK_WEB_BUNDLE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_LINK_WEB_BUNDLE_H_
-
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/html/link_resource.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-
-namespace base {
-class UnguessableToken;
-}
-
-namespace blink {
-
-class WebBundleLoader;
-
-// LinkWebBundle is used in the Subresource loading with Web Bundles feature.
-// See crbug.com/1082020 for details.
-// A <link rel="webbundle" ...> element creates LinkWebBundle.
-class CORE_EXPORT LinkWebBundle final : public LinkResource,
-                                        public SubresourceWebBundle {
- public:
-  using CompleteURLCallback = base::RepeatingCallback<KURL(const String&)>;
-
-  static bool IsFeatureEnabled(const ExecutionContext*);
-
-  explicit LinkWebBundle(HTMLLinkElement* owner);
-  ~LinkWebBundle() override;
-
-  LinkWebBundle(const LinkWebBundle&) = delete;
-  LinkWebBundle& operator=(const LinkWebBundle&) = delete;
-
-  void Trace(Visitor* visitor) const override;
-
-  // LinkResource overrides:
-  void Process() override;
-  LinkResourceType GetType() const override;
-  bool HasLoaded() const override;
-  void OwnerRemoved() override;
-
-  // SubresourceWebBundle overrides:
-  bool CanHandleRequest(const KURL& url) const override;
-  String GetCacheIdentifier() const override;
-  const KURL& GetBundleUrl() const override;
-  const base::UnguessableToken& WebBundleToken() const override;
-  void NotifyLoadingFinished() override;
-  void OnWebBundleError(const String& message) const override;
-  bool IsScriptWebBundle() const override;
-  bool WillBeReleased() const override;
-  network::mojom::CredentialsMode GetCredentialsMode() const override;
-
-  // Returns a valid absolute URL if |str| can be parsed as a valid
-  // absolute URL, or a relative URL with a given |base_url|.
-  // Document::CompleteURL(const String&) does the same thing, however we use
-  // this function instead, because we do not always have a Document present,
-  // like in html_preload_scanner (only document url).
-  // TODO(crbug.com/1244483): For now we ignore Encoding of the document here,
-  // as we don't have it available in the html_preload_scanner (the only user
-  // of this function as of now). Investigate if this is even fixable and how
-  // much it actually impacts the construction of the complete URL.
-  static KURL CompleteURL(const KURL& base_url, const String& str);
-
-  // Parse the given |str| as a url. If |str| doesn't meet the criteria which
-  // WebBundles specification requires, this returns invalid empty KURL as an
-  // error. |complete_url_callback| acts as a callable constructor that
-  // returns a complete url, accounting for both cases of |str| being a
-  // relative URL and absolute one.
-  static KURL ParseResourceUrl(const AtomicString& str,
-                               CompleteURLCallback complete_url_callback);
-
- private:
-  void AddConsoleMessage(const String& message) const;
-  bool ResourcesOrScopesMatch(const KURL& url) const;
-  void ReleaseBundleLoader();
-
-  Member<WebBundleLoader> bundle_loader_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_LINK_WEB_BUNDLE_H_
diff --git a/third_party/blink/renderer/core/html/link_web_bundle_test.cc b/third_party/blink/renderer/core/html/link_web_bundle_test.cc
deleted file mode 100644
index 5dc9a111..0000000
--- a/third_party/blink/renderer/core/html/link_web_bundle_test.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/html/link_web_bundle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/dom_token_list.h"
-#include "third_party/blink/renderer/core/html/html_link_element.h"
-#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
-#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
-
-namespace blink {
-
-namespace {
-
-void TestParseResourceUrl(const AtomicString& url, bool is_valid) {
-  ASSERT_EQ(LinkWebBundle::ParseResourceUrl(
-                url, base::BindRepeating(&LinkWebBundle::CompleteURL, KURL()))
-                .IsValid(),
-            is_valid);
-}
-
-}  // namespace
-
-class LinkWebBundleTest : public SimTest {};
-
-TEST_F(LinkWebBundleTest, ParseResourceUrl) {
-  TestParseResourceUrl("https://test.example.com/", true);
-  TestParseResourceUrl("http://test.example.com/", true);
-  TestParseResourceUrl("https://user@test.example.com/", false);
-  TestParseResourceUrl("https://user:password@test.example.com/", false);
-  TestParseResourceUrl("https://test.example.com/#fragment", false);
-  TestParseResourceUrl("ftp://test.example.com/", false);
-  TestParseResourceUrl("file:///test.html", false);
-}
-
-TEST_F(LinkWebBundleTest, ResourcesAttribute) {
-  SimRequest request("https://example.com/test.html", "text/html");
-  LoadURL("https://example.com/test.html");
-  request.Complete("<!DOCTYPE html>");
-
-  auto* link = MakeGarbageCollected<HTMLLinkElement>(GetDocument(),
-                                                     CreateElementFlags());
-  DOMTokenList* resources = link->resources();
-  EXPECT_EQ(g_null_atom, resources->value());
-
-  link->setAttribute(html_names::kRelAttr, "webbundle");
-
-  // Valid url
-  link->setAttribute(html_names::kResourcesAttr, "https://test.example.com");
-  EXPECT_EQ("https://test.example.com", resources->value());
-  EXPECT_EQ(1u, link->ValidResourceUrls().size());
-  EXPECT_TRUE(
-      link->ValidResourceUrls().Contains(KURL("https://test.example.com")));
-
-  // Invalid urls
-  link->setAttribute(html_names::kResourcesAttr,
-                     "https://user:test.example.com");
-  EXPECT_EQ("https://user:test.example.com", resources->value());
-  EXPECT_TRUE(link->ValidResourceUrls().IsEmpty());
-
-  link->setAttribute(html_names::kResourcesAttr,
-                     "https://:pass@test.example.com");
-  EXPECT_TRUE(link->ValidResourceUrls().IsEmpty());
-
-  link->setAttribute(html_names::kResourcesAttr,
-                     "https://test.example.com/#fragment");
-  EXPECT_TRUE(link->ValidResourceUrls().IsEmpty());
-
-  // Spece-separated valid urls
-  link->setAttribute(html_names::kResourcesAttr,
-                     "https://test1.example.com https://test2.example.com");
-  EXPECT_EQ(2u, link->ValidResourceUrls().size());
-  EXPECT_TRUE(
-      link->ValidResourceUrls().Contains(KURL("https://test1.example.com")));
-  EXPECT_TRUE(
-      link->ValidResourceUrls().Contains(KURL("https://test2.example.com")));
-
-  // Space-separated valid and invalid urls
-  link->setAttribute(html_names::kResourcesAttr,
-                     "https://test1.example.com https://user:test.example.com "
-                     "https://test2.example.com");
-  EXPECT_EQ(
-      "https://test1.example.com https://user:test.example.com "
-      "https://test2.example.com",
-      resources->value());
-  EXPECT_EQ(2u, link->ValidResourceUrls().size());
-  EXPECT_TRUE(
-      link->ValidResourceUrls().Contains(KURL("https://test1.example.com")));
-  EXPECT_TRUE(
-      link->ValidResourceUrls().Contains(KURL("https://test2.example.com")));
-}
-
-TEST_F(LinkWebBundleTest, DeprecationMessage) {
-  SimRequest request("https://example.com/test.html", "text/html");
-  LoadURL("https://example.com/test.html");
-  request.Complete("<!DOCTYPE html><link rel=\"webbundle\">");
-
-  EXPECT_TRUE(std::any_of(ConsoleMessages().begin(), ConsoleMessages().end(),
-                          [](const auto& console_message) {
-                            return console_message.Contains(
-                                "<link rel=\"webbundle\"> is deprecated.");
-                          }));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index b0e0ca0..1cb276d 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -48,7 +48,6 @@
 #include "third_party/blink/renderer/core/html/html_image_element.h"
 #include "third_party/blink/renderer/core/html/html_meta_element.h"
 #include "third_party/blink/renderer/core/html/link_rel_attribute.h"
-#include "third_party/blink/renderer/core/html/link_web_bundle.h"
 #include "third_party/blink/renderer/core/html/loading_attribute.h"
 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
 #include "third_party/blink/renderer/core/html/parser/html_srcset_parser.h"
@@ -117,28 +116,15 @@
   return media_query_evaluator.Eval(*media_queries);
 }
 
-void ParseWebBundleUrlsAndFillHash(const AtomicString& value,
-                                   HashSet<KURL>& url_hash,
-                                   const KURL& base_url) {
-  // Parse the attribute value as a space-separated list of urls
-  SpaceSplitString urls(value);
-  for (wtf_size_t i = 0; i < urls.size(); ++i) {
-    KURL url = LinkWebBundle::ParseResourceUrl(
-        urls[i], base::BindRepeating(&LinkWebBundle::CompleteURL, base_url));
-    if (url.IsValid()) {
-      url_hash.insert(std::move(url));
-    }
-  }
-}
-
 void ScanScriptWebBundle(
     const String& inline_text,
     const KURL& base_url,
     scoped_refptr<const PreloadRequest::ExclusionInfo>& exclusion_info) {
-  absl::optional<ScriptWebBundleRule> rule =
-      ScriptWebBundleRule::ParseJson(inline_text, base_url);
-  if (!rule)
+  auto rule_or_error =
+      ScriptWebBundleRule::ParseJson(inline_text, base_url, /*logger*/ nullptr);
+  if (!absl::holds_alternative<ScriptWebBundleRule>(rule_or_error))
     return;
+  auto& rule = absl::get<ScriptWebBundleRule>(rule_or_error);
 
   HashSet<KURL> scopes;
   HashSet<KURL> resources;
@@ -147,9 +133,9 @@
     resources = exclusion_info->resources();
   }
 
-  for (const KURL& scope_url : rule->scope_urls())
+  for (const KURL& scope_url : rule.scope_urls())
     scopes.insert(scope_url);
-  for (const KURL& resource_url : rule->resource_urls())
+  for (const KURL& resource_url : rule.resource_urls())
     resources.insert(resource_url);
 
   exclusion_info = base::MakeRefCounted<PreloadRequest::ExclusionInfo>(
@@ -234,33 +220,6 @@
     }
   }
 
-  bool MaybeUpdateExclusionInfo(
-      const KURL& predicted_base_url,
-      const KURL& document_url,
-      scoped_refptr<const PreloadRequest::ExclusionInfo>& exclusion_info) {
-    if (!IsLinkRelWebBundle())
-      return false;
-    HashSet<KURL> scopes;
-    HashSet<KURL> resources;
-    if (exclusion_info) {
-      scopes = exclusion_info->scopes();
-      resources = exclusion_info->resources();
-    }
-    KURL base_url;
-    if (!predicted_base_url.IsEmpty()) {
-      base_url = predicted_base_url;
-    } else {
-      base_url = document_url;
-    }
-    ParseWebBundleUrlsAndFillHash(scopes_attribute_value_, scopes, base_url);
-    ParseWebBundleUrlsAndFillHash(resources_attribute_value_, resources,
-                                  base_url);
-
-    exclusion_info = base::MakeRefCounted<PreloadRequest::ExclusionInfo>(
-        document_url, std::move(scopes), std::move(resources));
-    return true;
-  }
-
   std::unique_ptr<PreloadRequest> CreatePreloadRequest(
       const KURL& predicted_base_url,
       const SegmentedString& source,
@@ -494,7 +453,6 @@
       link_is_preconnect_ = rel.IsPreconnect();
       link_is_preload_ = rel.IsLinkPreload();
       link_is_modulepreload_ = rel.IsModulePreload();
-      link_is_webbundle_ = rel.IsWebBundle();
     } else if (Match(attribute_name, html_names::kMediaAttr)) {
       matched_ &= MediaAttributeMatches(*media_values_, attribute_value);
     } else if (Match(attribute_name, html_names::kCrossoriginAttr)) {
@@ -525,10 +483,6 @@
                Match(attribute_name, html_names::kFetchpriorityAttr) &&
                priority_hints_origin_trial_enabled_) {
       SetFetchPriorityHint(attribute_value);
-    } else if (Match(attribute_name, html_names::kScopesAttr)) {
-      scopes_attribute_value_ = AtomicString(attribute_value);
-    } else if (Match(attribute_name, html_names::kResourcesAttr)) {
-      resources_attribute_value_ = AtomicString(attribute_value);
     }
   }
 
@@ -666,10 +620,6 @@
            !url_to_load_.IsEmpty();
   }
 
-  bool IsLinkRelWebBundle() const {
-    return Match(tag_impl_, html_names::kLinkTag) && link_is_webbundle_;
-  }
-
   bool ShouldPreloadLink(absl::optional<ResourceType>& type) const {
     if (link_is_style_sheet_) {
       return type_attribute_value_.IsEmpty() ||
@@ -777,7 +727,6 @@
   bool link_is_preconnect_ = false;
   bool link_is_preload_ = false;
   bool link_is_modulepreload_ = false;
-  bool link_is_webbundle_ = false;
   bool matched_ = true;
   bool input_is_image_ = false;
   String img_src_url_;
@@ -1087,13 +1036,6 @@
           &document_parameters_->disabled_image_types);
       scanner.ProcessAttributes(token.Attributes());
 
-      if (scanner.MaybeUpdateExclusionInfo(predicted_base_element_url_,
-                                           document_url_, exclusion_info_)) {
-        // This means the tag is <link rel=webbundle>. We don't preload the
-        // web bundle request.
-        return;
-      }
-
       if (in_picture_ && media_values_->Width())
         scanner.HandlePictureSourceURL(picture_data_);
       std::unique_ptr<PreloadRequest> request = scanner.CreatePreloadRequest(
diff --git a/third_party/blink/renderer/core/html/rel_list.cc b/third_party/blink/renderer/core/html/rel_list.cc
index b788f41e..62a052a 100644
--- a/third_party/blink/renderer/core/html/rel_list.cc
+++ b/third_party/blink/renderer/core/html/rel_list.cc
@@ -7,7 +7,6 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
-#include "third_party/blink/renderer/core/html/link_web_bundle.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
@@ -63,10 +62,6 @@
         token_value == "allowed-alt-sxg") {
       return true;
     }
-    if (LinkWebBundle::IsFeatureEnabled(GetElement().GetExecutionContext()) &&
-        token_value == "webbundle") {
-      return true;
-    }
   } else if ((GetElement().HasTagName(html_names::kATag) ||
               GetElement().HasTagName(html_names::kAreaTag)) &&
              SupportedTokensAnchorAndArea().Contains(token_value)) {
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 77f3044..513bee6 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -238,7 +238,7 @@
 void DevToolsSession::DidStartProvisionalLoad(LocalFrame* frame) {
   if (v8_session_ && agent_->inspected_frames_->Root() == frame) {
     v8_session_->setSkipAllPauses(true);
-    v8_session_->resume(true /* terminate on resume */);
+    v8_session_->resume();
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index 339899e..3b11d28 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -609,11 +609,11 @@
       if (available_outer_space < LayoutUnit()) {
         // We're past the end of the outer fragmentainer (typically due to a
         // margin). Nothing will fit here, not even zero-size content. If we
-        // haven't produced any fragments yet, we'll retry in the next outer
-        // fragmentainer. Otherwise, we need to continue (once we have started
-        // laying out, we cannot skip any fragmentainers) with no available
-        // size.
-        if (!IsResumingLayout(BreakToken()))
+        // haven't produced any fragments yet, and aborting is allowed, we'll
+        // retry in the next outer fragmentainer. Otherwise, we need to continue
+        // (once we have started laying out, we cannot skip any fragmentainers)
+        // with no available size.
+        if (!IsResumingLayout(BreakToken()) && MayAbortOnInsufficientSpace())
           return nullptr;
         available_outer_space = LayoutUnit();
       }
@@ -771,17 +771,18 @@
                      result->BreakAppeal());
 
         // Avoid creating rows that are too short to hold monolithic content.
-        // Bail, discarding all columns. Note that this is safe to do even if
-        // we're column-balancing, because we attempt to make room for all
-        // monolithic content already in the initial column balancing pass (and
-        // if that fails, there's no way it's going to fit), by checking
-        // TallestUnbreakableBlockSize() from the layout results.
+        // Bail if possible, discarding all columns. Note that this is safe to
+        // do even if we're column-balancing, because we attempt to make room
+        // for all monolithic content already in the initial column balancing
+        // pass (and if that fails, there's no way it's going to fit), by
+        // checking TallestUnbreakableBlockSize() from the layout results.
         if (NGBoxFragment(ConstraintSpace().GetWritingDirection(), column)
                 .HasBlockLayoutOverflow()) {
           if (ConstraintSpace().IsInsideBalancedColumns() &&
               !container_builder_.IsInitialColumnBalancingPass())
             container_builder_.PropagateSpaceShortage(minimal_space_shortage);
-          return nullptr;
+          if (MayAbortOnInsufficientSpace())
+            return nullptr;
         }
       }
       allow_discard_start_margin = true;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
index 768c216..feaef20 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.h
@@ -100,6 +100,18 @@
       LayoutUnit block_offset) const;
   NGConstraintSpace CreateConstraintSpaceForMinMax() const;
 
+  // If this is a nested multicol container, and there's no room for anything in
+  // the current outer fragmentainer, we're normally allowed to abort (typically
+  // with NGLayoutResult::kOutOfFragmentainerSpace), and retry in the next outer
+  // fragmentainer. This is not the case for out-of-flow positioned multicol
+  // containers, though, as we're not allowed to insert a soft break before an
+  // out-of-flow positioned node. Our implementation requires that an OOF start
+  // in the fragmentainer where it would "naturally" occur.
+  bool MayAbortOnInsufficientSpace() const {
+    DCHECK(is_constrained_by_outer_fragmentation_context_);
+    return !Node().IsOutOfFlowPositioned();
+  }
+
   int used_column_count_;
   LayoutUnit column_inline_size_;
   LayoutUnit column_inline_progression_;
diff --git a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle.cc b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle.cc
index 1ce89b7..d439ac19 100644
--- a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle.cc
+++ b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle.cc
@@ -44,11 +44,11 @@
 ScriptWebBundle::CreateOrReuseInline(ScriptElementBase& element,
                                      const String& source_text) {
   Document& document = element.GetDocument();
-  auto rule = ScriptWebBundleRule::ParseJson(source_text, document.BaseURL());
-  if (!rule) {
-    return ScriptWebBundleError(ScriptWebBundleError::Type::kParseError,
-                                "Failed to parse web bundle: invalid JSON");
-  }
+  auto rule_or_error = ScriptWebBundleRule::ParseJson(
+      source_text, document.BaseURL(), document.GetExecutionContext());
+  if (absl::holds_alternative<ScriptWebBundleError>(rule_or_error))
+    return absl::get<ScriptWebBundleError>(rule_or_error);
+  auto& rule = absl::get<ScriptWebBundleRule>(rule_or_error);
 
   ResourceFetcher* resource_fetcher = document.Fetcher();
   if (!resource_fetcher) {
@@ -57,14 +57,14 @@
   }
   SubresourceWebBundleList* active_bundles =
       resource_fetcher->GetOrCreateSubresourceWebBundleList();
-  if (active_bundles->GetMatchingBundle(rule->source_url())) {
+  if (active_bundles->GetMatchingBundle(rule.source_url())) {
     ExecutionContext* context = document.GetExecutionContext();
     if (context) {
       context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
           mojom::blink::ConsoleMessageSource::kOther,
           mojom::blink::ConsoleMessageLevel::kWarning,
           "A nested bundle is not supported: " +
-              rule->source_url().ElidedString()));
+              rule.source_url().ElidedString()));
     }
     return ScriptWebBundleError(ScriptWebBundleError::Type::kSystemError,
                                 "A nested bundle is not supported.");
@@ -72,15 +72,15 @@
 
   if (SubresourceWebBundle* found =
           active_bundles->FindSubresourceWebBundleWhichWillBeReleased(
-              rule->source_url(), rule->credentials_mode())) {
+              rule.source_url(), rule.credentials_mode())) {
     // Re-use the ScriptWebBundle if it has the same bundle URL and is being
     // released.
     DCHECK(found->IsScriptWebBundle());
     ScriptWebBundle* reused_script_web_bundle = To<ScriptWebBundle>(found);
-    reused_script_web_bundle->ReusedWith(element, std::move(*rule));
+    reused_script_web_bundle->ReusedWith(element, std::move(rule));
     return reused_script_web_bundle;
   }
-  return MakeGarbageCollected<ScriptWebBundle>(element, document, *rule);
+  return MakeGarbageCollected<ScriptWebBundle>(element, document, rule);
 }
 
 ScriptWebBundle::ScriptWebBundle(ScriptElementBase& element,
diff --git a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_error.h b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_error.h
index 1c37de1b..571af95 100644
--- a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_error.h
+++ b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_error.h
@@ -30,6 +30,7 @@
 
   v8::Local<v8::Value> ToV8(ScriptState* script_state);
   Type GetType();
+  const String& GetMessage() { return message_; }
 
  private:
   Type type_;
diff --git a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.cc b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.cc
index cc3b453..2854887 100644
--- a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.cc
+++ b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.h"
 
+#include "base/containers/contains.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/platform/json/json_parser.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
 
@@ -11,6 +13,13 @@
 
 namespace {
 
+const char kSourceKey[] = "source";
+const char kCredentialsKey[] = "credentials";
+const char kScopesKey[] = "scopes";
+const char kResourcesKey[] = "resources";
+const char* const kKnownKeys[] = {kSourceKey, kCredentialsKey, kScopesKey,
+                                  kResourcesKey};
+
 HashSet<KURL> ParseJSONArrayAsURLs(JSONArray* array, const KURL& base_url) {
   HashSet<KURL> urls;
   if (!array)
@@ -40,39 +49,75 @@
 
 }  // namespace
 
-absl::optional<ScriptWebBundleRule> ScriptWebBundleRule::ParseJson(
-    const String& inline_text,
-    const KURL& base_url) {
-  // TODO(crbug.com/1245166): Emit a user friendly parse error message.
-
+absl::variant<ScriptWebBundleRule, ScriptWebBundleError>
+ScriptWebBundleRule::ParseJson(const String& inline_text,
+                               const KURL& base_url,
+                               ConsoleLogger* logger) {
   std::unique_ptr<JSONValue> json = ParseJSON(inline_text);
-  if (!json)
-    return absl::nullopt;
+  if (!json) {
+    return ScriptWebBundleError(
+        ScriptWebBundleError::Type::kParseError,
+        "Failed to parse web bundle rule: invalid JSON.");
+  }
   std::unique_ptr<JSONObject> json_obj = JSONObject::From(std::move(json));
-  if (!json_obj)
-    return absl::nullopt;
+  if (!json_obj) {
+    return ScriptWebBundleError(
+        ScriptWebBundleError::Type::kParseError,
+        "Failed to parse web bundle rule: not an object.");
+  }
+
+  // Emit console warning for unknown keys.
+  if (logger) {
+    for (wtf_size_t i = 0; i < json_obj->size(); ++i) {
+      JSONObject::Entry entry = json_obj->at(i);
+      if (!base::Contains(kKnownKeys, entry.first)) {
+        logger->AddConsoleMessage(
+            mojom::blink::ConsoleMessageSource::kOther,
+            mojom::blink::ConsoleMessageLevel::kWarning,
+            "Invalid top-level key \"" + entry.first + "\" in WebBundle rule.");
+      }
+    }
+  }
 
   String source;
-  if (!json_obj->GetString("source", &source))
-    return absl::nullopt;
+  if (!json_obj->GetString(kSourceKey, &source)) {
+    return ScriptWebBundleError(ScriptWebBundleError::Type::kParseError,
+                                "Failed to parse web bundle rule: \"source\" "
+                                "top-level key must be a string.");
+  }
   KURL source_url(base_url, source);
-  if (!source_url.IsValid())
-    return absl::nullopt;
+  if (!source_url.IsValid()) {
+    return ScriptWebBundleError(ScriptWebBundleError::Type::kParseError,
+                                "Failed to parse web bundle rule: \"source\" "
+                                "is not parsable as a URL.");
+  }
 
   network::mojom::CredentialsMode credentials_mode;
   String credentials;
-  if (json_obj->GetString("credentials", &credentials)) {
+  if (json_obj->GetString(kCredentialsKey, &credentials)) {
     credentials_mode = ParseCredentials(credentials);
   } else {
     // The default is "same-origin".
     credentials_mode = network::mojom::CredentialsMode::kSameOrigin;
   }
 
-  HashSet<KURL> scope_urls =
-      ParseJSONArrayAsURLs(json_obj->GetArray("scopes"), source_url);
+  JSONValue* scopes = json_obj->Get(kScopesKey);
+  if (scopes && scopes->GetType() != JSONValue::kTypeArray) {
+    return ScriptWebBundleError(
+        ScriptWebBundleError::Type::kParseError,
+        "Failed to parse web bundle rule: \"scopes\" must be an array.");
+  }
+  JSONValue* resources = json_obj->Get(kResourcesKey);
+  if (resources && resources->GetType() != JSONValue::kTypeArray) {
+    return ScriptWebBundleError(
+        ScriptWebBundleError::Type::kParseError,
+        "Failed to parse web bundle rule: \"resources\" must be an array.");
+  }
 
+  HashSet<KURL> scope_urls =
+      ParseJSONArrayAsURLs(JSONArray::Cast(scopes), source_url);
   HashSet<KURL> resource_urls =
-      ParseJSONArrayAsURLs(json_obj->GetArray("resources"), source_url);
+      ParseJSONArrayAsURLs(JSONArray::Cast(resources), source_url);
 
   return ScriptWebBundleRule(source_url, credentials_mode,
                              std::move(scope_urls), std::move(resource_urls));
diff --git a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.h b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.h
index cd06dee..c7618eaf 100644
--- a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.h
+++ b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule.h
@@ -5,9 +5,11 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_WEB_BUNDLE_SCRIPT_WEB_BUNDLE_RULE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_WEB_BUNDLE_SCRIPT_WEB_BUNDLE_RULE_H_
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/console_logger.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
@@ -19,9 +21,10 @@
 // https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md
 class CORE_EXPORT ScriptWebBundleRule final {
  public:
-  static absl::optional<ScriptWebBundleRule> ParseJson(
+  static absl::variant<ScriptWebBundleRule, ScriptWebBundleError> ParseJson(
       const String& inline_text,
-      const KURL& base_url);
+      const KURL& base_url,
+      ConsoleLogger* logger);
 
   ScriptWebBundleRule(const KURL& source_url,
                       network::mojom::CredentialsMode credentialsl_mode,
diff --git a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule_test.cc b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule_test.cc
index 054940b..9cc6c74 100644
--- a/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule_test.cc
+++ b/third_party/blink/renderer/core/loader/web_bundle/script_web_bundle_rule_test.cc
@@ -9,9 +9,34 @@
 
 namespace blink {
 
+namespace {
+
+class MockConsoleLogger final : public GarbageCollected<MockConsoleLogger>,
+                                public ConsoleLogger {
+ public:
+  const String& Message() const { return message_; }
+
+ private:
+  void AddConsoleMessageImpl(
+      mojom::ConsoleMessageSource,
+      mojom::ConsoleMessageLevel,
+      const String& message,
+      bool discard_duplicates,
+      absl::optional<mojom::ConsoleMessageCategory>) override {
+    message_ = message;
+  }
+  String message_;
+};
+
+}  // namespace
+
 TEST(ScriptWebBundleRuleTest, Empty) {
-  EXPECT_FALSE(
-      ScriptWebBundleRule::ParseJson("", KURL("https://example.com/")));
+  auto result =
+      ScriptWebBundleRule::ParseJson("", KURL("https://example.com/"), nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: invalid JSON.");
 }
 
 TEST(ScriptWebBundleRuleTest, Basic) {
@@ -22,9 +47,9 @@
         "scopes": ["js"],
         "resources": ["dir/a.css", "dir/b.css"]
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_THAT(rule.scope_urls(),
               testing::UnorderedElementsAre("https://example.com/js"));
@@ -39,26 +64,9 @@
       R"({
         "source": "foo.wbn"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
-  EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
-  EXPECT_TRUE(rule.scope_urls().IsEmpty());
-  EXPECT_TRUE(rule.resource_urls().IsEmpty());
-}
-
-TEST(ScriptWebBundleRuleTest, InvalidType) {
-  const KURL base_url("https://example.com/");
-  // `scopes` and `resources` should be JSON array.
-  auto result = ScriptWebBundleRule::ParseJson(
-      R"({
-        "source": "foo.wbn",
-        "scopes": "js",
-        "resources":  { "a": "hello" }
-      })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_TRUE(rule.scope_urls().IsEmpty());
   EXPECT_TRUE(rule.resource_urls().IsEmpty());
@@ -71,9 +79,9 @@
         "source": "hello/foo.wbn",
         "resources": ["dir/a.css"]
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/hello/foo.wbn");
   EXPECT_THAT(rule.resource_urls(), testing::UnorderedElementsAre(
                                         "https://example.com/hello/dir/a.css"));
@@ -86,9 +94,9 @@
         "source": "hello/foo.wbn",
         "scopes": ["js"]
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/hello/foo.wbn");
   EXPECT_THAT(rule.scope_urls(),
               testing::UnorderedElementsAre("https://example.com/hello/js"));
@@ -100,9 +108,9 @@
       R"({
         "source": "foo.wbn"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(),
             network::mojom::CredentialsMode::kSameOrigin);
@@ -115,9 +123,9 @@
         "source": "foo.wbn",
         "credentials": "same-origin"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(),
             network::mojom::CredentialsMode::kSameOrigin);
@@ -130,9 +138,9 @@
         "source": "foo.wbn",
         "credentials": "include"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(), network::mojom::CredentialsMode::kInclude);
 }
@@ -144,9 +152,9 @@
         "source": "foo.wbn",
         "credentials": "omit"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(), network::mojom::CredentialsMode::kOmit);
 }
@@ -158,9 +166,9 @@
         "source": "foo.wbn",
         "credentials": "invalid-value"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(),
             network::mojom::CredentialsMode::kSameOrigin);
@@ -173,9 +181,9 @@
         "source": "foo.wbn",
         "credentials": " include"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(),
             network::mojom::CredentialsMode::kSameOrigin);
@@ -188,14 +196,110 @@
         "source": "foo.wbn",
         "credentials": "INCLUDE"
       })",
-      base_url);
-  ASSERT_TRUE(result);
-  ScriptWebBundleRule& rule = *result;
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
   EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
   EXPECT_EQ(rule.credentials_mode(),
             network::mojom::CredentialsMode::kSameOrigin);
 }
 
-// TODO(crbug.com/1245166): Add more tests.
+TEST(ScriptWebBundleRuleTest, TopLevelIsNotAnObject) {
+  const KURL base_url("https://example.com/");
+  auto result = ScriptWebBundleRule::ParseJson("[]", base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: not an object.");
+}
+
+TEST(ScriptWebBundleRuleTest, MissingSource) {
+  const KURL base_url("https://example.com/");
+  auto result = ScriptWebBundleRule::ParseJson("{}", base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: \"source\" "
+            "top-level key must be a string.");
+}
+
+TEST(ScriptWebBundleRuleTest, WrongSourceType) {
+  const KURL base_url("https://example.com/");
+  auto result =
+      ScriptWebBundleRule::ParseJson(R"({"source": 123})", base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: \"source\" "
+            "top-level key must be a string.");
+}
+
+TEST(ScriptWebBundleRuleTest, BadSourceURL) {
+  const KURL base_url("https://example.com/");
+  auto result = ScriptWebBundleRule::ParseJson(R"({"source": "http://"})",
+                                               base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: \"source\" "
+            "is not parsable as a URL.");
+}
+
+TEST(ScriptWebBundleRuleTest, NoScopesNorResources) {
+  const KURL base_url("https://example.com/");
+  auto result = ScriptWebBundleRule::ParseJson(R"({"source": "http://"})",
+                                               base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: \"source\" "
+            "is not parsable as a URL.");
+}
+
+TEST(ScriptWebBundleRuleTest, InvalidScopesType) {
+  const KURL base_url("https://example.com/");
+  auto result = ScriptWebBundleRule::ParseJson(
+      R"({
+        "source": "foo.wbn",
+        "scopes": "js"
+      })",
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: \"scopes\" must be an array.");
+}
+
+TEST(ScriptWebBundleRuleTest, InvalidResourcesType) {
+  const KURL base_url("https://example.com/");
+  auto result = ScriptWebBundleRule::ParseJson(
+      R"({
+        "source": "foo.wbn",
+        "resources":  { "a": "hello" }
+      })",
+      base_url, nullptr);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleError>(result));
+  auto& error = absl::get<ScriptWebBundleError>(result);
+  EXPECT_EQ(error.GetMessage(),
+            "Failed to parse web bundle rule: \"resources\" must be an array.");
+}
+
+TEST(ScriptWebBundleRuleTest, UnknownKey) {
+  const KURL base_url("https://example.com/");
+  MockConsoleLogger logger;
+  auto result = ScriptWebBundleRule::ParseJson(
+      R"({
+        "source": "foo.wbn",
+        "unknown": []
+      })",
+      base_url, &logger);
+  ASSERT_TRUE(absl::holds_alternative<ScriptWebBundleRule>(result));
+  auto& rule = absl::get<ScriptWebBundleRule>(result);
+  EXPECT_EQ(rule.source_url(), "https://example.com/foo.wbn");
+  EXPECT_TRUE(rule.scope_urls().IsEmpty());
+  EXPECT_TRUE(rule.resource_urls().IsEmpty());
+  EXPECT_EQ(logger.Message(),
+            "Invalid top-level key \"unknown\" in WebBundle rule.");
+}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/css_mask_painter.cc b/third_party/blink/renderer/core/paint/css_mask_painter.cc
index 49955a5..08f27e0 100644
--- a/third_party/blink/renderer/core/paint/css_mask_painter.cc
+++ b/third_party/blink/renderer/core/paint/css_mask_painter.cc
@@ -8,11 +8,10 @@
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
 #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
-#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace blink {
 
-absl::optional<gfx::Rect> CSSMaskPainter::MaskBoundingBox(
+absl::optional<gfx::RectF> CSSMaskPainter::MaskBoundingBox(
     const LayoutObject& object,
     const PhysicalOffset& paint_offset) {
   if (!object.IsBoxModelObject() && !object.IsSVGChild())
@@ -28,8 +27,7 @@
             SVGResources::ReferenceBoxForEffects(object);
         const float reference_box_zoom =
             object.IsSVGForeignObject() ? object.StyleRef().EffectiveZoom() : 1;
-        return gfx::ToEnclosingRect(
-            masker->ResourceBoundingBox(reference_box, reference_box_zoom));
+        return masker->ResourceBoundingBox(reference_box, reference_box_zoom);
       }
     }
   }
@@ -56,7 +54,7 @@
   if (style.HasMaskBoxImageOutsets())
     maximum_mask_region.Expand(style.MaskBoxImageOutsets());
   maximum_mask_region.offset += paint_offset;
-  return ToPixelSnappedRect(maximum_mask_region);
+  return gfx::RectF(maximum_mask_region);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/css_mask_painter.h b/third_party/blink/renderer/core/paint/css_mask_painter.h
index 63c4623..2af1ccb 100644
--- a/third_party/blink/renderer/core/paint/css_mask_painter.h
+++ b/third_party/blink/renderer/core/paint/css_mask_painter.h
@@ -8,7 +8,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace blink {
 
@@ -22,7 +22,7 @@
   // Returns the bounding box of the computed mask, which could be
   // smaller or bigger than the reference box. Returns nullopt if the
   // there is no mask or the mask is invalid.
-  static absl::optional<gfx::Rect> MaskBoundingBox(
+  static absl::optional<gfx::RectF> MaskBoundingBox(
       const LayoutObject&,
       const PhysicalOffset& paint_offset);
 };
diff --git a/third_party/blink/renderer/core/paint/css_mask_painter_test.cc b/third_party/blink/renderer/core/paint/css_mask_painter_test.cc
index afc8e45..bdc5c31f 100644
--- a/third_party/blink/renderer/core/paint/css_mask_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/css_mask_painter_test.cc
@@ -25,10 +25,10 @@
     </svg>
   )HTML");
   auto& masked = *GetLayoutObjectByElementId("masked");
-  absl::optional<gfx::Rect> mask_bounding_box =
+  absl::optional<gfx::RectF> mask_bounding_box =
       CSSMaskPainter::MaskBoundingBox(masked, PhysicalOffset());
   ASSERT_TRUE(mask_bounding_box.has_value());
-  EXPECT_EQ(gfx::Rect(35, 35, 180, 180), *mask_bounding_box);
+  EXPECT_EQ(gfx::RectF(35, 35, 180, 180), *mask_bounding_box);
 }
 
 TEST_F(CSSMaskPainterTest, MaskBoundingBoxCSSBlock) {
@@ -37,10 +37,15 @@
                             width:300px; height:200px;"></div>
   )HTML");
   auto& masked = *GetLayoutObjectByElementId("masked");
-  absl::optional<gfx::Rect> mask_bounding_box =
+  absl::optional<gfx::RectF> mask_bounding_box =
       CSSMaskPainter::MaskBoundingBox(masked, PhysicalOffset(8, 8));
   ASSERT_TRUE(mask_bounding_box.has_value());
-  EXPECT_EQ(gfx::Rect(8, 8, 300, 200), *mask_bounding_box);
+  EXPECT_EQ(gfx::RectF(8, 8, 300, 200), *mask_bounding_box);
+
+  mask_bounding_box = CSSMaskPainter::MaskBoundingBox(
+      masked, PhysicalOffset(LayoutUnit(8.25f), LayoutUnit(8.75f)));
+  ASSERT_TRUE(mask_bounding_box.has_value());
+  EXPECT_EQ(gfx::RectF(8.25, 8.75, 300, 200), *mask_bounding_box);
 }
 
 TEST_F(CSSMaskPainterTest, MaskBoundingBoxCSSMaskBoxImageOutset) {
@@ -50,10 +55,10 @@
         -webkit-mask-box-image-outset:10px; width:300px; height:200px;"></div>
   )HTML");
   auto& masked = *GetLayoutObjectByElementId("masked");
-  absl::optional<gfx::Rect> mask_bounding_box =
+  absl::optional<gfx::RectF> mask_bounding_box =
       CSSMaskPainter::MaskBoundingBox(masked, PhysicalOffset(8, 8));
   ASSERT_TRUE(mask_bounding_box.has_value());
-  EXPECT_EQ(gfx::Rect(-2, -2, 320, 220), *mask_bounding_box);
+  EXPECT_EQ(gfx::RectF(-2, -2, 320, 220), *mask_bounding_box);
 }
 
 TEST_F(CSSMaskPainterTest, MaskBoundingBoxCSSInline) {
@@ -68,10 +73,10 @@
     </div>
   )HTML");
   auto& masked = *GetLayoutObjectByElementId("masked");
-  absl::optional<gfx::Rect> mask_bounding_box =
+  absl::optional<gfx::RectF> mask_bounding_box =
       CSSMaskPainter::MaskBoundingBox(masked, PhysicalOffset(8, 8));
   ASSERT_TRUE(mask_bounding_box.has_value());
-  EXPECT_EQ(gfx::Rect(8, 8, 260, 20), *mask_bounding_box);
+  EXPECT_EQ(gfx::RectF(8, 8, 260, 20), *mask_bounding_box);
 }
 
 }  // unnamed namespace
diff --git a/third_party/blink/renderer/core/paint/fragment_data.cc b/third_party/blink/renderer/core/paint/fragment_data.cc
index 30eda97..35cd32b 100644
--- a/third_party/blink/renderer/core/paint/fragment_data.cc
+++ b/third_party/blink/renderer/core/paint/fragment_data.cc
@@ -166,7 +166,7 @@
   rare_data_->clip_path_path = nullptr;
 }
 
-void FragmentData::SetClipPathCache(const gfx::Rect& bounding_box,
+void FragmentData::SetClipPathCache(const gfx::RectF& bounding_box,
                                     scoped_refptr<const RefCountedPath> path) {
   EnsureRareData().is_clip_path_cache_valid = true;
   rare_data_->clip_path_bounding_box = bounding_box;
diff --git a/third_party/blink/renderer/core/paint/fragment_data.h b/third_party/blink/renderer/core/paint/fragment_data.h
index fb41503b..f217fa6 100644
--- a/third_party/blink/renderer/core/paint/fragment_data.h
+++ b/third_party/blink/renderer/core/paint/fragment_data.h
@@ -102,7 +102,7 @@
   }
   void InvalidateClipPathCache();
 
-  absl::optional<gfx::Rect> ClipPathBoundingBox() const {
+  absl::optional<gfx::RectF> ClipPathBoundingBox() const {
     DCHECK(IsClipPathCacheValid());
     return rare_data_ ? rare_data_->clip_path_bounding_box : absl::nullopt;
   }
@@ -110,7 +110,7 @@
     DCHECK(IsClipPathCacheValid());
     return rare_data_ ? rare_data_->clip_path_path.get() : nullptr;
   }
-  void SetClipPathCache(const gfx::Rect& bounding_box,
+  void SetClipPathCache(const gfx::RectF& bounding_box,
                         scoped_refptr<const RefCountedPath>);
   void ClearClipPathCache() {
     if (rare_data_) {
@@ -248,7 +248,7 @@
     std::unique_ptr<ObjectPaintProperties> paint_properties;
     std::unique_ptr<RefCountedPropertyTreeState> local_border_box_properties;
     bool is_clip_path_cache_valid = false;
-    absl::optional<gfx::Rect> clip_path_bounding_box;
+    absl::optional<gfx::RectF> clip_path_bounding_box;
     scoped_refptr<const RefCountedPath> clip_path_path;
     CullRect cull_rect_;
     CullRect contents_cull_rect_;
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 3d01d659..5a526783 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -1166,13 +1166,13 @@
 
   if (NeedsPaintPropertyUpdate()) {
     if (NeedsEffect(object_, full_context_.direct_compositing_reasons)) {
-      absl::optional<gfx::Rect> mask_clip = CSSMaskPainter::MaskBoundingBox(
+      absl::optional<gfx::RectF> mask_clip = CSSMaskPainter::MaskBoundingBox(
           object_, context_.current.paint_offset);
       bool has_clip_path =
           style.HasClipPath() && fragment_data_.ClipPathBoundingBox();
       bool has_mask_based_clip_path =
           has_clip_path && !fragment_data_.ClipPathPath();
-      absl::optional<gfx::Rect> clip_path_clip;
+      absl::optional<gfx::RectF> clip_path_clip;
       if (has_mask_based_clip_path)
         clip_path_clip = fragment_data_.ClipPathBoundingBox();
 
@@ -1181,18 +1181,15 @@
                                     : nullptr;
 
       if (mask_clip || clip_path_clip) {
-        gfx::Rect combined_clip = mask_clip ? *mask_clip : *clip_path_clip;
+        gfx::RectF combined_clip = mask_clip ? *mask_clip : *clip_path_clip;
         if (mask_clip && clip_path_clip)
           combined_clip.Intersect(*clip_path_clip);
 
-        // TODO(crbug.com/1248598): We use pixel-snapped mask clip and clip path
-        // clip as the layout clip rect, which may cause wrong result when
-        // mapping in layout coordinates.
         OnUpdate(properties_->UpdateMaskClip(
             *context_.current.clip,
-            ClipPaintPropertyNode::State(context_.current.transform,
-                                         gfx::RectF(combined_clip),
-                                         FloatRoundedRect(combined_clip))));
+            ClipPaintPropertyNode::State(
+                context_.current.transform, combined_clip,
+                FloatRoundedRect(gfx::ToEnclosingRect(combined_clip)))));
         output_clip = properties_->MaskClip();
       } else {
         OnClear(properties_->ClearMaskClip());
@@ -1585,13 +1582,10 @@
     if (!NeedsClipPathClip(object_, fragment_data_)) {
       OnClear(properties_->ClearClipPathClip());
     } else {
-      // TODO(crbug.com/1248598): We use pixel-snapped clip path clip as the
-      // layout clip rect, which may cause wrong result when mapping in layout
-      // coordinates.
       ClipPaintPropertyNode::State state(
-          context_.current.transform,
-          gfx::RectF(*fragment_data_.ClipPathBoundingBox()),
-          FloatRoundedRect(*fragment_data_.ClipPathBoundingBox()));
+          context_.current.transform, *fragment_data_.ClipPathBoundingBox(),
+          FloatRoundedRect(
+              gfx::ToEnclosingRect(*fragment_data_.ClipPathBoundingBox())));
       state.clip_path = fragment_data_.ClipPathPath();
       OnUpdate(properties_->UpdateClipPathClip(*context_.current.clip,
                                                std::move(state)));
@@ -2688,7 +2682,7 @@
   if (path)
     path->Translate(gfx::Vector2dF(fragment_data_.PaintOffset()));
   fragment_data_.SetClipPathCache(
-      gfx::ToEnclosingRect(*bounding_box),
+      *bounding_box,
       path ? AdoptRef(new RefCountedPath(std::move(*path))) : nullptr);
 }
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 994c916..28162da 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -5154,9 +5154,9 @@
   EXPECT_EQ(output_clip,
             &target->FirstFragment().LocalBorderBoxProperties().Clip());
   EXPECT_EQ(DocContentClip(), output_clip->Parent());
-  // For now we always pixel-snap both LayoutClipRect and PaintClipRect for
-  // mask clip.
-  EXPECT_CLIP_RECT(FloatRoundedRect(8, 8, 300, 201), output_clip);
+  EXPECT_EQ(FloatClipRect(gfx::RectF(8, 8, 300, 200.5)),
+            output_clip->LayoutClipRect());
+  EXPECT_EQ(FloatRoundedRect(8, 8, 300, 201), output_clip->PaintClipRect());
 
   EXPECT_EQ(properties->Effect(),
             &target->FirstFragment().LocalBorderBoxProperties().Effect());
@@ -5771,6 +5771,42 @@
   }
 }
 
+TEST_P(PaintPropertyTreeBuilderTest, EmptyClipPathSubpixelOffset) {
+  SetBodyInnerHTML(R"HTML(
+    <style>body { margin: 0; }</style>
+    <div id="target"
+         style="clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 0 0);
+                position: relative; top: 0.75px; left: 0.25px; width: 0">
+    </div>
+  )HTML");
+
+  const auto* target = GetLayoutObjectByElementId("target");
+  ASSERT_TRUE(target->FirstFragment().PaintProperties());
+  const auto* clip_path_clip =
+      target->FirstFragment().PaintProperties()->ClipPathClip();
+  ASSERT_TRUE(clip_path_clip);
+  EXPECT_EQ(gfx::RectF(0.25, 0.75, 0, 0),
+            clip_path_clip->LayoutClipRect().Rect());
+  EXPECT_EQ(FloatRoundedRect(), clip_path_clip->PaintClipRect());
+}
+
+TEST_P(PaintPropertyTreeBuilderTest, EmptyMaskSubpixelOffset) {
+  SetBodyInnerHTML(R"HTML(
+    <style>body { margin: 0; }</style>
+    <div id="target"
+         style="-webkit-mask: linear-gradient(blue, white);
+                position: relative; top: 0.75px; left: 0.25px; width: 0">
+    </div>
+  )HTML");
+
+  const auto* target = GetLayoutObjectByElementId("target");
+  ASSERT_TRUE(target->FirstFragment().PaintProperties());
+  const auto* mask_clip = target->FirstFragment().PaintProperties()->MaskClip();
+  ASSERT_TRUE(mask_clip);
+  EXPECT_EQ(gfx::RectF(0.25, 0.75, 0, 0), mask_clip->LayoutClipRect().Rect());
+  EXPECT_EQ(FloatRoundedRect(), mask_clip->PaintClipRect());
+}
+
 TEST_P(PaintPropertyTreeBuilderTest, RootHasCompositedScrolling) {
   SetBodyInnerHTML(R"HTML(
     <div id='forceScroll' style='height: 2000px'></div>
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 1188c51..c406a8c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -227,24 +227,41 @@
 blink::KeyboardEvent* CreateKeyboardEvent(
     blink::LocalDOMWindow* local_dom_window,
     blink::WebInputEvent::Type type,
-    AXAction action) {
+    AXAction action,
+    blink::AccessibilityOrientation orientation,
+    ax::mojom::blink::WritingDirection text_direction) {
   blink::WebKeyboardEvent key(type,
                               blink::WebInputEvent::Modifiers::kNoModifiers,
                               base::TimeTicks::Now());
 
-  // TODO(crbug.com/1099069): Fire different arrow events depending on
-  // orientation and dir (RTL/LTR)
-  switch (action) {
-    case AXAction::kActionIncrement:
+  if (action == AXAction::kActionIncrement) {
+    if (orientation == blink::kAccessibilityOrientationVertical) {
       key.dom_key = ui::DomKey::ARROW_UP;
       key.dom_code = static_cast<int>(ui::DomCode::ARROW_UP);
       key.native_key_code = key.windows_key_code = blink::VKEY_UP;
-      break;
-    case AXAction::kActionDecrement:
+    } else if (text_direction == ax::mojom::blink::WritingDirection::kRtl) {
+      key.dom_key = ui::DomKey::ARROW_LEFT;
+      key.dom_code = static_cast<int>(ui::DomCode::ARROW_LEFT);
+      key.native_key_code = key.windows_key_code = blink::VKEY_LEFT;
+    } else {  // horizontal and left to right
+      key.dom_key = ui::DomKey::ARROW_RIGHT;
+      key.dom_code = static_cast<int>(ui::DomCode::ARROW_RIGHT);
+      key.native_key_code = key.windows_key_code = blink::VKEY_RIGHT;
+    }
+  } else if (action == AXAction::kActionDecrement) {
+    if (orientation == blink::kAccessibilityOrientationVertical) {
       key.dom_key = ui::DomKey::ARROW_DOWN;
       key.dom_code = static_cast<int>(ui::DomCode::ARROW_DOWN);
       key.native_key_code = key.windows_key_code = blink::VKEY_DOWN;
-      break;
+    } else if (text_direction == ax::mojom::blink::WritingDirection::kRtl) {
+      key.dom_key = ui::DomKey::ARROW_RIGHT;
+      key.dom_code = static_cast<int>(ui::DomCode::ARROW_RIGHT);
+      key.native_key_code = key.windows_key_code = blink::VKEY_RIGHT;
+    } else {  // horizontal and left to right
+      key.dom_key = ui::DomKey::ARROW_LEFT;
+      key.dom_code = static_cast<int>(ui::DomCode::ARROW_LEFT);
+      key.native_key_code = key.windows_key_code = blink::VKEY_LEFT;
+    }
   }
 
   return blink::KeyboardEvent::Create(key, local_dom_window, true);
@@ -365,9 +382,12 @@
   AXAction action =
       increase ? AXAction::kActionIncrement : AXAction::kActionDecrement;
   LocalDOMWindow* local_dom_window = GetDocument()->domWindow();
+  AccessibilityOrientation orientation = Orientation();
+  ax::mojom::blink::WritingDirection text_direction = GetTextDirection();
 
-  KeyboardEvent* keydown = CreateKeyboardEvent(
-      local_dom_window, WebInputEvent::Type::kRawKeyDown, action);
+  KeyboardEvent* keydown =
+      CreateKeyboardEvent(local_dom_window, WebInputEvent::Type::kRawKeyDown,
+                          action, orientation, text_direction);
   GetNode()->DispatchEvent(*keydown);
 
   // TODO(crbug.com/1099069): add a brief pause between keydown and keyup?
@@ -377,8 +397,9 @@
   if (!GetNode())
     return;
 
-  KeyboardEvent* keyup = CreateKeyboardEvent(
-      local_dom_window, WebInputEvent::Type::kKeyUp, action);
+  KeyboardEvent* keyup =
+      CreateKeyboardEvent(local_dom_window, WebInputEvent::Type::kKeyUp, action,
+                          orientation, text_direction);
   GetNode()->DispatchEvent(*keyup);
 }
 
diff --git a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
index bc4c08b..8e3ab42 100644
--- a/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl2_rendering_context_base.cc
@@ -1222,11 +1222,21 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(),
-                                 kTexImage2D, target, level, internalformat,
-                                 format, type, 0, 0, 0, image,
-                                 GetTextureSourceSubRectangle(width, height), 1,
-                                 unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = 1,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(), params,
+                                 image, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage2D(ExecutionContext* execution_context,
@@ -1272,11 +1282,19 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLVideoElement(
-      execution_context->GetSecurityOrigin(), kTexImage2D, target, level,
-      internalformat, format, type, 0, 0, 0, video,
-      GetTextureSourceSubRectangle(width, height), 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(), params,
+                                 video, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage2D(ExecutionContext* execution_context,
@@ -1297,11 +1315,19 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), kTexImage2D,
-                           target, level, internalformat, format, type, 0, 0, 0,
-                           frame, GetTextureSourceSubRectangle(width, height),
-                           1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), params,
+                           frame, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage2D(GLenum target,
@@ -1554,11 +1580,21 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLImageElement(
-      execution_context->GetSecurityOrigin(), kTexSubImage2D, target, level, 0,
-      format, type, xoffset, yoffset, 0, image,
-      GetTextureSourceSubRectangle(width, height), 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .width = width,
+      .height = height,
+      .depth = 1,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(), params,
+                                 image, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage2D(
@@ -1606,11 +1642,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLVideoElement(
-      execution_context->GetSecurityOrigin(), kTexSubImage2D, target, level, 0,
-      format, type, xoffset, yoffset, 0, video,
-      GetTextureSourceSubRectangle(width, height), 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .width = width,
+      .height = height,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(), params,
+                                 video, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage2D(
@@ -1632,11 +1677,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperVideoFrame(
-      execution_context->GetSecurityOrigin(), kTexSubImage2D, target, level, 0,
-      format, type, xoffset, yoffset, 0, frame,
-      GetTextureSourceSubRectangle(width, height), 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .width = width,
+      .height = height,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), params,
+                           frame, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage2D(
@@ -1949,12 +2003,21 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(),
-                                 kTexImage3D, target, level, internalformat,
-                                 format, type, 0, 0, 0, image,
-                                 GetTextureSourceSubRectangle(width, height),
-                                 depth, unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage3D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(), params,
+                                 image, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage3D(ExecutionContext* execution_context,
@@ -2003,12 +2066,21 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(),
-                                 kTexImage3D, target, level, internalformat,
-                                 format, type, 0, 0, 0, video,
-                                 GetTextureSourceSubRectangle(width, height),
-                                 depth, unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage3D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(), params,
+                                 video, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage3D(ExecutionContext* execution_context,
@@ -2030,11 +2102,20 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), kTexImage3D,
-                           target, level, internalformat, format, type, 0, 0, 0,
-                           frame, GetTextureSourceSubRectangle(width, height),
-                           depth, unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage3D,
+      .target = target,
+      .level = level,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .border = 0,  // See https://crbug.com/1313604
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), params,
+                           frame, exception_state);
 }
 
 void WebGL2RenderingContextBase::texImage3D(GLenum target,
@@ -2199,12 +2280,22 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(),
-                                 kTexSubImage3D, target, level, 0, format, type,
-                                 xoffset, yoffset, zoffset, image,
-                                 GetTextureSourceSubRectangle(width, height),
-                                 depth, unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage3D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .zoffset = zoffset,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(), params,
+                                 image, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage3D(
@@ -2257,12 +2348,22 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(),
-                                 kTexSubImage3D, target, level, 0, format, type,
-                                 xoffset, yoffset, zoffset, video,
-                                 GetTextureSourceSubRectangle(width, height),
-                                 depth, unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage3D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .zoffset = zoffset,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(), params,
+                                 video, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage3D(
@@ -2286,12 +2387,22 @@
                       "a buffer is bound to PIXEL_UNPACK_BUFFER");
     return;
   }
-
-  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(),
-                           kTexSubImage3D, target, level, 0, format, type,
-                           xoffset, yoffset, zoffset, frame,
-                           GetTextureSourceSubRectangle(width, height), depth,
-                           unpack_image_height_, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage3D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .zoffset = zoffset,
+      .width = width,
+      .height = height,
+      .depth = depth,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), params,
+                           frame, exception_state);
 }
 
 void WebGL2RenderingContextBase::texSubImage3D(
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 5e83d81..4416920a 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -1861,8 +1861,11 @@
                                     exception_state)) {
     return;
   }
-  NOTIMPLEMENTED();
+  if (drawing_buffer_color_space_ == color_space)
+    return;
   drawing_buffer_color_space_ = color_space;
+  if (GetDrawingBuffer())
+    GetDrawingBuffer()->SetColorSpace(drawing_buffer_color_space_);
 }
 
 V8PredefinedColorSpace WebGLRenderingContextBase::unpackColorSpace() const {
@@ -5509,21 +5512,10 @@
 
 void WebGLRenderingContextBase::TexImageHelperHTMLImageElement(
     const SecurityOrigin* security_origin,
-    TexImageFunctionID function_id,
-    GLenum target,
-    GLint level,
-    GLint internalformat,
-    GLenum format,
-    GLenum type,
-    GLint xoffset,
-    GLint yoffset,
-    GLint zoffset,
+    const TexImageParams& params,
     HTMLImageElement* image,
-    const absl::optional<gfx::Rect>& source_image_rect,
-    GLsizei depth,
-    GLint unpack_image_height,
     ExceptionState& exception_state) {
-  const char* func_name = GetTexImageFunctionName(function_id);
+  const char* func_name = GetTexImageFunctionName(params.function_id);
   if (isContextLost())
     return;
 
@@ -5533,7 +5525,7 @@
   if (!ValidateHTMLImageElement(security_origin, func_name, image,
                                 exception_state))
     return;
-  if (!ValidateTexImageBinding(func_name, function_id, target))
+  if (!ValidateTexImageBinding(func_name, params.function_id, params.target))
     return;
 
   scoped_refptr<Image> image_for_render = image->CachedImage()->GetImage();
@@ -5549,21 +5541,23 @@
   }
 
   TexImageFunctionType function_type;
-  if (function_id == kTexImage2D || function_id == kTexImage3D)
+  if (params.function_id == kTexImage2D || params.function_id == kTexImage3D)
     function_type = kTexImage;
   else
     function_type = kTexSubImage;
   if (!image_for_render ||
       !ValidateTexFunc(func_name, function_type, kSourceHTMLImageElement,
-                       target, level, internalformat, image_for_render->width(),
-                       image_for_render->height(), depth, 0, format, type,
-                       xoffset, yoffset, zoffset))
+                       params.target, params.level, params.internalformat,
+                       image_for_render->width(), image_for_render->height(),
+                       params.depth.value_or(1), params.border, params.format,
+                       params.type, params.xoffset, params.yoffset,
+                       params.zoffset)) {
     return;
+  }
 
-  TexImageImpl(function_id, target, level, internalformat, xoffset, yoffset,
-               zoffset, format, type, image_for_render.get(),
-               WebGLImageConversion::kHtmlDomImage, /*source_has_flip_y=*/false,
-               source_image_rect, depth, unpack_image_height);
+  TexImageImpl(params, image_for_render.get(),
+               WebGLImageConversion::kHtmlDomImage,
+               /*image_has_flip_y=*/false);
 }
 
 void WebGLRenderingContextBase::texImage2D(ExecutionContext* execution_context,
@@ -5574,10 +5568,17 @@
                                            GLenum type,
                                            HTMLImageElement* image,
                                            ExceptionState& exception_state) {
-  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(),
-                                 kTexImage2D, target, level, internalformat,
-                                 format, type, 0, 0, 0, image, absl::nullopt, 1,
-                                 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(), params,
+                                 image, exception_state);
 }
 
 bool WebGLRenderingContextBase::CanUseTexImageViaGPU(GLenum format,
@@ -5864,21 +5865,10 @@
 
 void WebGLRenderingContextBase::TexImageHelperHTMLVideoElement(
     const SecurityOrigin* security_origin,
-    TexImageFunctionID function_id,
-    GLenum target,
-    GLint level,
-    GLint internalformat,
-    GLenum format,
-    GLenum type,
-    GLint xoffset,
-    GLint yoffset,
-    GLint zoffset,
+    TexImageParams params,
     HTMLVideoElement* video,
-    const absl::optional<gfx::Rect>& source_image_rect,
-    GLsizei depth,
-    GLint unpack_image_height,
     ExceptionState& exception_state) {
-  const char* func_name = GetTexImageFunctionName(function_id);
+  const char* func_name = GetTexImageFunctionName(params.function_id);
   if (isContextLost())
     return;
 
@@ -5891,19 +5881,20 @@
   }
 
   WebGLTexture* texture =
-      ValidateTexImageBinding(func_name, function_id, target);
+      ValidateTexImageBinding(func_name, params.function_id, params.target);
   if (!texture)
     return;
 
   TexImageFunctionType function_type;
-  if (function_id == kTexImage2D || function_id == kTexImage3D)
+  if (params.function_id == kTexImage2D || params.function_id == kTexImage3D)
     function_type = kTexImage;
   else
     function_type = kTexSubImage;
   if (!ValidateTexFunc(func_name, function_type, kSourceHTMLVideoElement,
-                       target, level, internalformat, video->videoWidth(),
-                       video->videoHeight(), 1, 0, format, type, xoffset,
-                       yoffset, zoffset)) {
+                       params.target, params.level, params.internalformat,
+                       video->videoWidth(), video->videoHeight(),
+                       params.depth.value_or(1), 0, params.format, params.type,
+                       params.xoffset, params.yoffset, params.zoffset)) {
     return;
   }
 
@@ -5919,29 +5910,16 @@
 
   // This is enforced by ValidateHTMLVideoElement(), but DCHECK to be sure.
   DCHECK(!WouldTaintOrigin(video));
-  TexImageHelperMediaVideoFrame(
-      function_id, target, level, internalformat, format, type, xoffset,
-      yoffset, zoffset, source_image_rect, depth, unpack_image_height, texture,
-      std::move(media_video_frame), video_renderer);
+  TexImageHelperMediaVideoFrame(params, texture, std::move(media_video_frame),
+                                video_renderer);
 }
 
 void WebGLRenderingContextBase::TexImageHelperVideoFrame(
     const SecurityOrigin* security_origin,
-    TexImageFunctionID function_id,
-    GLenum target,
-    GLint level,
-    GLint internalformat,
-    GLenum format,
-    GLenum type,
-    GLint xoffset,
-    GLint yoffset,
-    GLint zoffset,
+    TexImageParams params,
     VideoFrame* frame,
-    const absl::optional<gfx::Rect>& source_image_rect,
-    GLsizei depth,
-    GLint unpack_image_height,
     ExceptionState& exception_state) {
-  const char* func_name = GetTexImageFunctionName(function_id);
+  const char* func_name = GetTexImageFunctionName(params.function_id);
   if (isContextLost())
     return;
 
@@ -5949,12 +5927,12 @@
   // by consolidating on CanvasImageSource::GetSourceImageForCanvas().
 
   WebGLTexture* texture =
-      ValidateTexImageBinding(func_name, function_id, target);
+      ValidateTexImageBinding(func_name, params.function_id, params.target);
   if (!texture)
     return;
 
   TexImageFunctionType function_type;
-  if (function_id == kTexImage2D || function_id == kTexImage3D)
+  if (params.function_id == kTexImage2D || params.function_id == kTexImage3D)
     function_type = kTexImage;
   else
     function_type = kTexSubImage;
@@ -5967,10 +5945,11 @@
   }
 
   const auto natural_size = local_handle->frame()->natural_size();
-  if (!ValidateTexFunc(func_name, function_type, kSourceVideoFrame, target,
-                       level, internalformat, natural_size.width(),
-                       natural_size.height(), 1, 0, format, type, xoffset,
-                       yoffset, zoffset)) {
+  if (!ValidateTexFunc(func_name, function_type, kSourceVideoFrame,
+                       params.target, params.level, params.internalformat,
+                       natural_size.width(), natural_size.height(), 1, 0,
+                       params.format, params.type, params.xoffset,
+                       params.yoffset, params.zoffset)) {
     return;
   }
 
@@ -5979,7 +5958,7 @@
   if (auto sk_img = local_handle->sk_image()) {
     DCHECK(!sk_img->isTextureBacked());
     const GLint adjusted_internalformat =
-        ConvertTexInternalFormat(internalformat, type);
+        ConvertTexInternalFormat(params.internalformat, params.type);
 
     // For WebGL last-uploaded-frame-metadata API. https://crbug.com/639174
     auto metadata = WebGLVideoTexture::CreateVideoFrameUploadMetadata(
@@ -5989,35 +5968,20 @@
       return;
     }
     auto image = UnacceleratedStaticBitmapImage::Create(std::move(sk_img));
-    TexImageImpl(function_id, target, level, adjusted_internalformat, xoffset,
-                 yoffset, zoffset, format, type, image.get(),
-                 // Note: kHtmlDomVideo means alpha won't be unmultiplied.
-                 WebGLImageConversion::kHtmlDomVideo,
-                 /*source_has_flip_y=*/false, source_image_rect, depth,
-                 unpack_image_height);
+    // Note: kHtmlDomVideo means alpha won't be unmultiplied.
+    params.internalformat = adjusted_internalformat;
+    TexImageImpl(params, image.get(), WebGLImageConversion::kHtmlDomVideo,
+                 /*image_has_flip_y=*/false);
     texture->UpdateLastUploadedFrame(metadata);
     return;
   }
 
-  TexImageHelperMediaVideoFrame(function_id, target, level, internalformat,
-                                format, type, xoffset, yoffset, zoffset,
-                                source_image_rect, depth, unpack_image_height,
-                                texture, local_handle->frame(), nullptr);
+  TexImageHelperMediaVideoFrame(params, texture, local_handle->frame(),
+                                nullptr);
 }
 
 void WebGLRenderingContextBase::TexImageHelperMediaVideoFrame(
-    TexImageFunctionID function_id,
-    GLenum target,
-    GLint level,
-    GLint internalformat,
-    GLenum format,
-    GLenum type,
-    GLint xoffset,
-    GLint yoffset,
-    GLint zoffset,
-    const absl::optional<gfx::Rect>& source_image_rect,
-    GLsizei depth,
-    GLint unpack_image_height,
+    TexImageParams params,
     WebGLTexture* texture,
     scoped_refptr<media::VideoFrame> media_video_frame,
     media::PaintCanvasVideoRenderer* video_renderer) {
@@ -6037,21 +6001,28 @@
   const auto transform = media_video_frame->metadata().transformation.value_or(
       media::kNoTransformation);
 
+  absl::optional<gfx::Rect> source_image_rect;
+  if (params.width && params.height) {
+    source_image_rect =
+        gfx::Rect(params.unpack_skip_pixels, params.unpack_skip_rows,
+                  *params.width, *params.height);
+  }
+
   const GLint adjusted_internalformat =
-      ConvertTexInternalFormat(internalformat, type);
+      ConvertTexInternalFormat(params.internalformat, params.type);
   const bool source_image_rect_is_default =
       !source_image_rect ||
       *source_image_rect == gfx::Rect(media_video_frame->natural_size());
   const auto& caps = GetDrawingBuffer()->ContextProvider()->GetCapabilities();
   const bool may_need_image_external_essl3 =
       caps.egl_image_external &&
-      Extensions3DUtil::CopyTextureCHROMIUMNeedsESSL3(internalformat);
+      Extensions3DUtil::CopyTextureCHROMIUMNeedsESSL3(params.internalformat);
   const bool have_image_external_essl3 = caps.egl_image_external_essl3;
   const bool use_copy_texture_chromium =
-      function_id == kTexImage2D && source_image_rect_is_default &&
-      depth == 1 && GL_TEXTURE_2D == target &&
+      params.function_id == kTexImage2D && source_image_rect_is_default &&
+      params.depth.value_or(1) == 1 && GL_TEXTURE_2D == params.target &&
       (have_image_external_essl3 || !may_need_image_external_essl3) &&
-      CanUseTexImageViaGPU(format, type) &&
+      CanUseTexImageViaGPU(params.format, params.type) &&
       transform == media::kNoTransformation;
 
 #if BUILDFLAG(IS_WIN)
@@ -6070,16 +6041,16 @@
     video_renderer = local_video_renderer.get();
   }
 
-  // Format of source VideoFrame may be 16-bit format, e.g. Y16 format.
-  // glCopyTextureCHROMIUM requires the source texture to be in 8-bit format.
-  // Converting 16-bits formatted source texture to 8-bits formatted texture
-  // will cause precision lost. So, uploading such video texture to half float
-  // or float texture can not use GPU-GPU path.
+  // Format of source VideoFrame may be 16-bit format, e.g. Y16
+  // format. glCopyTextureCHROMIUM requires the source texture to be in
+  // 8-bit format. Converting 16-bits formatted source texture to 8-bits
+  // formatted texture will cause precision lost. So, uploading such video
+  // texture to half float or float texture can not use GPU-GPU path.
   if (use_copy_texture_chromium) {
-    DCHECK(Extensions3DUtil::CanUseCopyTextureCHROMIUM(target));
-    DCHECK_EQ(xoffset, 0);
-    DCHECK_EQ(yoffset, 0);
-    DCHECK_EQ(zoffset, 0);
+    DCHECK(Extensions3DUtil::CanUseCopyTextureCHROMIUM(params.target));
+    DCHECK_EQ(params.xoffset, 0);
+    DCHECK_EQ(params.yoffset, 0);
+    DCHECK_EQ(params.zoffset, 0);
 
     viz::RasterContextProvider* raster_context_provider = nullptr;
     if (auto wrapper = SharedGpuContext::ContextProviderWrapper()) {
@@ -6093,9 +6064,10 @@
 
     if (media_video_frame->HasTextures() &&
         video_renderer->CopyVideoFrameTexturesToGLTexture(
-            raster_context_provider, ContextGL(), media_video_frame, target,
-            texture->Object(), adjusted_internalformat, format, type, level,
-            unpack_premultiply_alpha_, unpack_flip_y_)) {
+            raster_context_provider, ContextGL(), media_video_frame,
+            params.target, texture->Object(), adjusted_internalformat,
+            params.format, params.type, params.level, unpack_premultiply_alpha_,
+            unpack_flip_y_)) {
       texture->UpdateLastUploadedFrame(metadata);
       return;
     }
@@ -6109,9 +6081,10 @@
     if (!media_video_frame->HasTextures() &&
         media::IsOpaque(media_video_frame->format()) && !gpu_teximage_is_slow &&
         video_renderer->CopyVideoFrameYUVDataToGLTexture(
-            raster_context_provider, ContextGL(), media_video_frame, target,
-            texture->Object(), adjusted_internalformat, format, type, level,
-            unpack_premultiply_alpha_, unpack_flip_y_)) {
+            raster_context_provider, ContextGL(), media_video_frame,
+            params.target, texture->Object(), adjusted_internalformat,
+            params.format, params.type, params.level, unpack_premultiply_alpha_,
+            unpack_flip_y_)) {
       texture->UpdateLastUploadedFrame(metadata);
       return;
     }
@@ -6127,17 +6100,18 @@
     const bool premultiply_alpha =
         unpack_premultiply_alpha_ && unpack_colorspace_conversion_ == GL_NONE;
 
-    if (function_id == kTexImage2D &&
+    if (params.function_id == kTexImage2D &&
         media::PaintCanvasVideoRenderer::TexImage2D(
-            target, texture->Object(), ContextGL(), caps,
-            media_video_frame.get(), level, adjusted_internalformat, format,
-            type, unpack_flip_y_, premultiply_alpha)) {
+            params.target, texture->Object(), ContextGL(), caps,
+            media_video_frame.get(), params.level, adjusted_internalformat,
+            params.format, params.type, unpack_flip_y_, premultiply_alpha)) {
       texture->UpdateLastUploadedFrame(metadata);
       return;
-    } else if (function_id == kTexSubImage2D &&
+    } else if (params.function_id == kTexSubImage2D &&
                media::PaintCanvasVideoRenderer::TexSubImage2D(
-                   target, ContextGL(), media_video_frame.get(), level, format,
-                   type, xoffset, yoffset, unpack_flip_y_, premultiply_alpha)) {
+                   params.target, ContextGL(), media_video_frame.get(),
+                   params.level, params.format, params.type, params.xoffset,
+                   params.yoffset, unpack_flip_y_, premultiply_alpha)) {
       texture->UpdateLastUploadedFrame(metadata);
       return;
     }
@@ -6169,14 +6143,15 @@
   // Linux when used with ShMem GpuMemoryBuffer backed frames. We don't have a
   // way to differentiate between true texture backed frames and ShMem GMBs, so
   // for now limit GPU texturing to TexImage2D.
-  const bool function_supports_gpu_teximage = function_id == kTexImage2D;
+  const bool function_supports_gpu_teximage = params.function_id == kTexImage2D;
 #else
   const bool function_supports_gpu_teximage =
-      function_id == kTexImage2D || function_id == kTexSubImage2D;
+      params.function_id == kTexImage2D || params.function_id == kTexSubImage2D;
 #endif
 
   const bool can_upload_via_gpu =
-      function_supports_gpu_teximage && CanUseTexImageViaGPU(format, type) &&
+      function_supports_gpu_teximage &&
+      CanUseTexImageViaGPU(params.format, params.type) &&
       source_image_rect_is_default && !gpu_teximage_is_slow;
 
   // If we can upload via GPU, try to to use an accelerated resource provider
@@ -6206,25 +6181,25 @@
         source_image_rect.value_or(GetTextureSourceSize(image.get()));
 
     auto* accel_image = static_cast<AcceleratedStaticBitmapImage*>(image.get());
-    if (function_id == kTexImage2D) {
-      TexImage2DBase(
-          target, level, internalformat, adjusted_source_image_rect.width(),
-          adjusted_source_image_rect.height(), 0, format, type, nullptr);
-      TexImageViaGPU(function_id, texture, target, level, 0, 0, 0, accel_image,
-                     nullptr, adjusted_source_image_rect,
+    if (params.function_id == kTexImage2D) {
+      TexImage2DBase(params.target, params.level, params.internalformat,
+                     adjusted_source_image_rect.width(),
+                     adjusted_source_image_rect.height(), 0, params.format,
+                     params.type, nullptr);
+      TexImageViaGPU(params.function_id, texture, params.target, params.level,
+                     0, 0, 0, accel_image, nullptr, adjusted_source_image_rect,
                      unpack_premultiply_alpha_, unpack_flip_y_);
     } else {
-      TexImageViaGPU(function_id, texture, target, level, xoffset, yoffset, 0,
-                     accel_image, nullptr, adjusted_source_image_rect,
-                     unpack_premultiply_alpha_, unpack_flip_y_);
+      TexImageViaGPU(params.function_id, texture, params.target, params.level,
+                     params.xoffset, params.yoffset, 0, accel_image, nullptr,
+                     adjusted_source_image_rect, unpack_premultiply_alpha_,
+                     unpack_flip_y_);
     }
   } else {
-    TexImageImpl(function_id, target, level, adjusted_internalformat, xoffset,
-                 yoffset, zoffset, format, type, image.get(),
-                 // Note: kHtmlDomVideo means alpha won't be unmultiplied.
-                 WebGLImageConversion::kHtmlDomVideo,
-                 /*source_has_flip_y=*/false, source_image_rect, depth,
-                 unpack_image_height);
+    params.internalformat = adjusted_internalformat;
+    // Note: kHtmlDomVideo means alpha won't be unmultiplied.
+    TexImageImpl(params, image.get(), WebGLImageConversion::kHtmlDomVideo,
+                 /*image_has_flip_y=*/false);
   }
 
   texture->UpdateLastUploadedFrame(metadata);
@@ -6238,10 +6213,17 @@
                                            GLenum type,
                                            HTMLVideoElement* video,
                                            ExceptionState& exception_state) {
-  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(),
-                                 kTexImage2D, target, level, internalformat,
-                                 format, type, 0, 0, 0, video, absl::nullopt, 1,
-                                 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(), params,
+                                 video, exception_state);
 }
 
 void WebGLRenderingContextBase::texImage2D(ExecutionContext* execution_context,
@@ -6252,9 +6234,17 @@
                                            GLenum type,
                                            VideoFrame* frame,
                                            ExceptionState& exception_state) {
-  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), kTexImage2D,
-                           target, level, internalformat, format, type, 0, 0, 0,
-                           frame, absl::nullopt, 1, 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexImage2D,
+      .target = target,
+      .level = level,
+      .internalformat = internalformat,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), params,
+                           frame, exception_state);
 }
 
 void WebGLRenderingContextBase::TexImageHelperImageBitmap(
@@ -6548,10 +6538,18 @@
     GLenum type,
     HTMLImageElement* image,
     ExceptionState& exception_state) {
-  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(),
-                                 kTexSubImage2D, target, level, 0, format, type,
-                                 xoffset, yoffset, 0, image, absl::nullopt, 1,
-                                 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLImageElement(execution_context->GetSecurityOrigin(), params,
+                                 image, exception_state);
 }
 
 void WebGLRenderingContextBase::texSubImage2D(
@@ -6580,10 +6578,18 @@
     GLenum type,
     HTMLVideoElement* video,
     ExceptionState& exception_state) {
-  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(),
-                                 kTexSubImage2D, target, level, 0, format, type,
-                                 xoffset, yoffset, 0, video, absl::nullopt, 1,
-                                 0, exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperHTMLVideoElement(execution_context->GetSecurityOrigin(), params,
+                                 video, exception_state);
 }
 
 void WebGLRenderingContextBase::texSubImage2D(
@@ -6596,10 +6602,18 @@
     GLenum type,
     VideoFrame* frame,
     ExceptionState& exception_state) {
-  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(),
-                           kTexSubImage2D, target, level, 0, format, type,
-                           xoffset, yoffset, 0, frame, absl::nullopt, 1, 0,
-                           exception_state);
+  TexImageParams params = {
+      .function_id = kTexSubImage2D,
+      .target = target,
+      .level = level,
+      .xoffset = xoffset,
+      .yoffset = yoffset,
+      .format = format,
+      .type = type,
+  };
+  GetCurrentUnpackState(params);
+  TexImageHelperVideoFrame(execution_context->GetSecurityOrigin(), params,
+                           frame, exception_state);
 }
 
 void WebGLRenderingContextBase::texSubImage2D(GLenum target,
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 4558aaef..700f25f 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -1795,19 +1795,8 @@
   void TexImageHelperImageData(TexImageParams, ImageData*);
 
   void TexImageHelperHTMLImageElement(const SecurityOrigin*,
-                                      TexImageFunctionID,
-                                      GLenum,
-                                      GLint,
-                                      GLint,
-                                      GLenum,
-                                      GLenum,
-                                      GLint,
-                                      GLint,
-                                      GLint,
+                                      const TexImageParams& params,
                                       HTMLImageElement*,
-                                      const absl::optional<gfx::Rect>&,
-                                      GLsizei,
-                                      GLint,
                                       ExceptionState&);
 
   void TexImageHelperCanvasRenderingContextHost(const SecurityOrigin*,
@@ -1827,35 +1816,13 @@
                                                 ExceptionState&);
 
   void TexImageHelperHTMLVideoElement(const SecurityOrigin*,
-                                      TexImageFunctionID,
-                                      GLenum,
-                                      GLint,
-                                      GLint,
-                                      GLenum,
-                                      GLenum,
-                                      GLint,
-                                      GLint,
-                                      GLint,
+                                      TexImageParams,
                                       HTMLVideoElement*,
-                                      const absl::optional<gfx::Rect>&,
-                                      GLsizei,
-                                      GLint,
                                       ExceptionState&);
 
   void TexImageHelperVideoFrame(const SecurityOrigin*,
-                                TexImageFunctionID,
-                                GLenum,
-                                GLint,
-                                GLint,
-                                GLenum,
-                                GLenum,
-                                GLint,
-                                GLint,
-                                GLint,
+                                TexImageParams,
                                 VideoFrame*,
-                                const absl::optional<gfx::Rect>&,
-                                GLsizei,
-                                GLint,
                                 ExceptionState&);
 
   void TexImageHelperImageBitmap(TexImageParams params,
@@ -1894,19 +1861,8 @@
                                 Platform::GraphicsInfo* graphics_info);
 
   void TexImageHelperMediaVideoFrame(
-      TexImageFunctionID function_id,
-      GLenum target,
-      GLint level,
-      GLint internalformat,
-      GLenum format,
-      GLenum type,
-      GLint xoffset,
-      GLint yoffset,
-      GLint zoffset,
-      const absl::optional<gfx::Rect>& source_image_rect,
-      GLsizei depth,
-      GLint unpack_image_height,
-      WebGLTexture* texture,
+      TexImageParams,
+      WebGLTexture*,
       scoped_refptr<media::VideoFrame> media_video_frame,
       media::PaintCanvasVideoRenderer* video_renderer);
 
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 70b98b1e..d52f67f 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -597,6 +597,8 @@
     "fonts/font_performance.h",
     "fonts/font_platform_data.cc",
     "fonts/font_platform_data.h",
+    "fonts/font_platform_data_cache.cc",
+    "fonts/font_platform_data_cache.h",
     "fonts/font_selection_algorithm.cc",
     "fonts/font_selection_algorithm.h",
     "fonts/font_selection_types.cc",
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc
index 55070db4..f528c30 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -34,7 +34,6 @@
 
 #include "base/debug/alias.h"
 #include "base/feature_list.h"
-#include "base/memory/ptr_util.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
@@ -43,23 +42,17 @@
 #include "third_party/blink/renderer/platform/font_family_names.h"
 #include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
-#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
 #include "third_party/blink/renderer/platform/fonts/font_data_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_map.h"
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 #include "third_party/blink/renderer/platform/fonts/font_performance.h"
-#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
-#include "third_party/blink/renderer/platform/fonts/font_smoothing_mode.h"
+#include "third_party/blink/renderer/platform/fonts/font_platform_data_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
-#include "third_party/blink/renderer/platform/fonts/text_rendering_mode.h"
+#include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
-#include "third_party/blink/renderer/platform/text/layout_locale.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -71,11 +64,6 @@
 
 namespace blink {
 
-namespace {
-const base::Feature kFontCacheNoSizeInKey{"FontCacheNoSizeInKey",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-}
-
 const base::Feature kAsyncFontAccess{"AsyncFontAccess",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -102,7 +90,9 @@
   return FontGlobalContext::GetFontCache();
 }
 
-FontCache::FontCache() : font_manager_(sk_ref_sp(static_font_manager_)) {
+FontCache::FontCache()
+    : font_manager_(sk_ref_sp(static_font_manager_)),
+      font_platform_data_cache_(FontPlatformDataCache::Create()) {
 #if BUILDFLAG(IS_WIN)
   if (!font_manager_ || should_use_test_font_mgr) {
     // This code path is only for unit tests. This SkFontMgr does not work in
@@ -124,6 +114,8 @@
 #endif
 }
 
+FontCache::~FontCache() = default;
+
 #if !BUILDFLAG(IS_MAC)
 FontPlatformData* FontCache::SystemFontPlatformData(
     const FontDescription& font_description) {
@@ -157,95 +149,8 @@
   }
 #endif
 
-  return font_platform_data_cache_.Add(this, font_description, creation_params,
-                                       alternate_font_name);
-}
-
-FontPlatformData* FontCache::FontPlatformDataCache::Add(
-    FontCache* font_cache,
-    const FontDescription& font_description,
-    const FontFaceCreationParams& creation_params,
-    AlternateFontName alternate_font_name) {
-  const bool is_unique_match =
-      alternate_font_name == AlternateFontName::kLocalUniqueFace;
-  FontCacheKey key =
-      font_description.CacheKey(creation_params, is_unique_match);
-  DCHECK(!key.IsHashTableDeletedValue());
-
-  if (no_size_in_key_) {
-    // Clear font size from they key. Size is not required in the primary key
-    // because per-size FontPlatformData are held in a nested map.
-    key.ClearFontSize();
-  }
-
-  const float size =
-      std::min(font_description.EffectiveFontSize(), font_size_limit_);
-
-  const unsigned rounded_size = size * FontCacheKey::PrecisionMultiplier();
-
-  // Assert that the computed hash map key rounded_size value does not hit
-  // the empty (max()) or deleted (max()-1) sentinel values of the hash map,
-  // compare UnsignedWithZeroKeyHashTraits() in hash_traits.h.
-  DCHECK_LT(rounded_size, std::numeric_limits<unsigned>::max() - 1);
-
-  // Assert that rounded_size was not reset to 0 due to an integer overflow,
-  // i.e. if size was non-zero, rounded_size can't be zero, but if size was 0,
-  // it may be 0.
-  DCHECK_EQ(!!size, !!rounded_size);
-
-  // Remove the font size from the cache key, and handle the font size
-  // separately in the inner HashMap. So that different size of FontPlatformData
-  // can share underlying SkTypeface.
-  {
-    // addResult's scope must end before we recurse for alternate family names
-    // below, to avoid triggering its dtor hash-changed asserts.
-    SizedFontPlatformDataSet* const sized_fonts = GetOrNewEntry(key);
-    const bool was_empty = sized_fonts->IsEmpty();
-
-    // Take a different size instance of the same font before adding an entry to
-    // |sizedFont|.
-    FontPlatformData* const another_size =
-        was_empty ? nullptr : sized_fonts->begin()->value.get();
-    const auto add_result = sized_fonts->insert(rounded_size, nullptr);
-    std::unique_ptr<FontPlatformData>* found = &add_result.stored_value->value;
-    if (!add_result.is_new_entry)
-      return found->get();
-    if (was_empty) {
-      *found = font_cache->CreateFontPlatformData(
-          font_description, creation_params, size, alternate_font_name);
-    } else if (another_size) {
-      *found = font_cache->ScaleFontPlatformData(
-          *another_size, font_description, creation_params, size);
-    }
-    if (auto* result = found->get())
-      return result;
-  }
-
-  if (alternate_font_name != AlternateFontName::kAllowAlternate ||
-      creation_params.CreationType() != kCreateFontByFamily)
-    return nullptr;
-
-  // We were unable to find a font. We have a small set of fonts that we alias
-  // to other names, e.g., Arial/Helvetica, Courier/Courier New, etc. Try
-  // looking up the font under the aliased name.
-  const AtomicString& alternate_name =
-      AlternateFamilyName(creation_params.Family());
-  if (alternate_name.IsEmpty())
-    return nullptr;
-
-  FontFaceCreationParams create_by_alternate_family(alternate_name);
-  FontPlatformData* const result =
-      Add(font_cache, font_description, create_by_alternate_family,
-          AlternateFontName::kNoAlternate);
-  if (!result)
-    return nullptr;
-
-  // "accessibility/font-changed.html" reaches here.
-
-  // Cache the result under the old name.
-  GetOrNewEntry(key)->Set(rounded_size,
-                          std::make_unique<FontPlatformData>(*result));
-  return result;
+  return font_platform_data_cache_->GetOrCreateFontPlatformData(
+      this, font_description, creation_params, alternate_font_name);
 }
 
 std::unique_ptr<FontPlatformData> FontCache::ScaleFontPlatformData(
@@ -379,26 +284,7 @@
 
 void FontCache::PurgePlatformFontDataCache() {
   TRACE_EVENT0("fonts,ui", "FontCache::PurgePlatformFontDataCache");
-  font_platform_data_cache_.Purge(font_data_cache_);
-}
-
-void FontCache::FontPlatformDataCache::Purge(
-    const FontDataCache& font_data_cache) {
-  Vector<FontCacheKey> keys_to_remove;
-  keys_to_remove.ReserveInitialCapacity(map_.size());
-  for (auto& sized_fonts : map_) {
-    Vector<unsigned> sizes_to_remove;
-    sizes_to_remove.ReserveInitialCapacity(sized_fonts.value.size());
-    for (const auto& platform_data : sized_fonts.value) {
-      if (platform_data.value &&
-          !font_data_cache.Contains(platform_data.value.get()))
-        sizes_to_remove.push_back(platform_data.key);
-    }
-    sized_fonts.value.RemoveAll(sizes_to_remove);
-    if (sized_fonts.value.IsEmpty())
-      keys_to_remove.push_back(sized_fonts.key);
-  }
-  map_.RemoveAll(keys_to_remove);
+  font_platform_data_cache_->Purge(font_data_cache_);
 }
 
 void FontCache::PurgeFallbackListShaperCache() {
@@ -441,7 +327,7 @@
 
 void FontCache::Invalidate() {
   TRACE_EVENT0("fonts,ui", "FontCache::Invalidate");
-  font_platform_data_cache_.Clear();
+  font_platform_data_cache_->Clear();
   generation_++;
 
   if (font_cache_clients_) {
@@ -487,7 +373,7 @@
   DCHECK(IsMainThread());
   base::trace_event::MemoryAllocatorDump* dump =
       memory_dump->CreateAllocatorDump("font_caches/font_platform_data_cache");
-  dump->AddScalar("size", "bytes", font_platform_data_cache_.ByteSize());
+  dump->AddScalar("size", "bytes", font_platform_data_cache_->ByteSize());
   memory_dump->AddSuballocation(dump->guid(),
                                 WTF::Partitions::kAllocatedObjectPoolName);
 }
@@ -551,26 +437,4 @@
   return *font_fallback_map_;
 }
 
-FontCache::FontPlatformDataCache::FontPlatformDataCache()
-    : font_size_limit_(std::nextafter(
-          (static_cast<float>(std::numeric_limits<unsigned>::max()) - 2.f) /
-              static_cast<float>(blink::FontCacheKey::PrecisionMultiplier()),
-          0.f)),
-      no_size_in_key_(base::FeatureList::IsEnabled(kFontCacheNoSizeInKey)) {}
-
-FontCache::FontPlatformDataCache::~FontPlatformDataCache() = default;
-
-FontCache::FontPlatformDataCache::SizedFontPlatformDataSet*
-FontCache::FontPlatformDataCache::GetOrNewEntry(FontCacheKey key) {
-  return &map_.insert(key, SizedFontPlatformDataSet()).stored_value->value;
-}
-
-size_t FontCache::FontPlatformDataCache::ByteSize() const {
-  return map_.size() * sizeof(*this);
-}
-
-void FontCache::FontPlatformDataCache::Clear() {
-  map_.clear();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.h b/third_party/blink/renderer/platform/fonts/font_cache.h
index 21777c1c..887c8da6 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.h
+++ b/third_party/blink/renderer/platform/fonts/font_cache.h
@@ -41,11 +41,9 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache_client.h"
-#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
 #include "third_party/blink/renderer/platform/fonts/font_data_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
-#include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
@@ -84,6 +82,8 @@
 class FontFaceCreationParams;
 class FontFallbackMap;
 class FontGlobalContext;
+class FontPlatformData;
+class FontPlatformDataCache;
 class SimpleFontData;
 class WebFontPrewarmer;
 
@@ -290,50 +290,9 @@
 
   FontCache(const FontCache&) = delete;
   FontCache& operator=(const FontCache&) = delete;
-  ~FontCache() = default;
+  ~FontCache();
 
  private:
-  class FontPlatformDataCache final {
-   public:
-    FontPlatformDataCache();
-    ~FontPlatformDataCache();
-
-    FontPlatformData* Add(FontCache* font_cache,
-                          const FontDescription& font_description,
-                          const FontFaceCreationParams& creation_params,
-                          AlternateFontName alternate_font_name);
-    size_t ByteSize() const;
-    void Clear();
-    void Purge(const FontDataCache& font_data_cache);
-
-   private:
-    using SizedFontPlatformDataSet =
-        HashMap<unsigned,
-                std::unique_ptr<FontPlatformData>,
-                WTF::IntHash<unsigned>,
-                WTF::UnsignedWithZeroKeyHashTraits<unsigned>>;
-
-    SizedFontPlatformDataSet* GetOrNewEntry(FontCacheKey key);
-
-    HashMap<FontCacheKey, SizedFontPlatformDataSet> map_;
-
-    // A maximum float value to which we limit incoming font sizes. This is the
-    // smallest float so that multiplying it by
-    // FontCacheKey::PrecisionMultiplier() is still smaller than
-    // std::numeric_limits<unsigned>::max() - 1 in order to avoid hitting
-    // HashMap sentinel values (placed at std::numeric_limits<unsigned>::max()
-    // and std::numeric_limits<unsigned>::max() - 1) for
-    // SizedFontPlatformDataSet and FontPlatformDataCache.
-    const float font_size_limit_;
-
-    // When true, the font size is removed from primary keys in |map_|.
-    // The font size is not necessary in the primary key, because per-size
-    // FontPlatformData are held in a nested map.
-    // This is controlled by a base::Feature to assess impact with an
-    // experiment.
-    const bool no_size_in_key_;
-  };
-
   // BCP47 list used when requesting fallback font for a character.
   // inlineCapacity is set to 4: the array vector not need to hold more than 4
   // elements.
@@ -437,7 +396,7 @@
   uint16_t generation_ = 0;
   bool platform_init_ = false;
   Persistent<HeapHashSet<WeakMember<FontCacheClient>>> font_cache_clients_;
-  FontPlatformDataCache font_platform_data_cache_;
+  std::unique_ptr<FontPlatformDataCache> font_platform_data_cache_;
   FallbackListShaperCache fallback_list_shaper_cache_;
   FontDataCache font_data_cache_;
 
@@ -448,6 +407,7 @@
 
   friend class SimpleFontData;  // For fontDataFromFontPlatformData
   friend class FontFallbackList;
+  friend class FontPlatformDataCache;
   FRIEND_TEST_ALL_PREFIXES(FontCacheAndroidTest, LocaleSpecificTypeface);
 };
 
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc b/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc
new file mode 100644
index 0000000..a9234481
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/fonts/font_platform_data_cache.h"
+
+#include <algorithm>
+#include <cmath>
+#include "base/feature_list.h"
+#include "third_party/blink/renderer/platform/fonts/alternate_font_family.h"
+#include "third_party/blink/renderer/platform/fonts/font_cache.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+namespace {
+const base::Feature kFontCacheNoSizeInKey{"FontCacheNoSizeInKey",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+}
+
+// static
+std::unique_ptr<FontPlatformDataCache> FontPlatformDataCache::Create() {
+  return std::make_unique<FontPlatformDataCache>();
+}
+
+FontPlatformDataCache::FontPlatformDataCache()
+    : font_size_limit_(std::nextafter(
+          (static_cast<float>(std::numeric_limits<unsigned>::max()) - 2.f) /
+              static_cast<float>(blink::FontCacheKey::PrecisionMultiplier()),
+          0.f)),
+      no_size_in_key_(base::FeatureList::IsEnabled(kFontCacheNoSizeInKey)) {}
+
+FontPlatformDataCache::~FontPlatformDataCache() = default;
+
+FontPlatformData* FontPlatformDataCache::GetOrCreateFontPlatformData(
+    FontCache* font_cache,
+    const FontDescription& font_description,
+    const FontFaceCreationParams& creation_params,
+    AlternateFontName alternate_font_name) {
+  const bool is_unique_match =
+      alternate_font_name == AlternateFontName::kLocalUniqueFace;
+  FontCacheKey key =
+      font_description.CacheKey(creation_params, is_unique_match);
+  DCHECK(!key.IsHashTableDeletedValue());
+
+  if (no_size_in_key_) {
+    // Clear font size from they key. Size is not required in the primary key
+    // because per-size FontPlatformData are held in a nested map.
+    key.ClearFontSize();
+  }
+
+  const float size =
+      std::min(font_description.EffectiveFontSize(), font_size_limit_);
+
+  const unsigned rounded_size = size * FontCacheKey::PrecisionMultiplier();
+
+  // Assert that the computed hash map key rounded_size value does not hit
+  // the empty (max()) or deleted (max()-1) sentinel values of the hash map,
+  // compare UnsignedWithZeroKeyHashTraits() in hash_traits.h.
+  DCHECK_LT(rounded_size, std::numeric_limits<unsigned>::max() - 1);
+
+  // Assert that rounded_size was not reset to 0 due to an integer overflow,
+  // i.e. if size was non-zero, rounded_size can't be zero, but if size was 0,
+  // it may be 0.
+  DCHECK_EQ(!!size, !!rounded_size);
+
+  // Remove the font size from the cache key, and handle the font size
+  // separately in the inner map. So that different size of `FontPlatformData`
+  // can share underlying SkTypeface.
+  SizedFontPlatformDataSet& sized_fonts = GetOrCreateSizeMap(key);
+
+  if (auto* result = sized_fonts.GetOrCreateFontPlatformData(
+          font_cache, font_description, creation_params, size,
+          alternate_font_name, rounded_size))
+    return result;
+
+  if (alternate_font_name != AlternateFontName::kAllowAlternate ||
+      creation_params.CreationType() != kCreateFontByFamily)
+    return nullptr;
+
+  // We were unable to find a font. We have a small set of fonts that we alias
+  // to other names, e.g., Arial/Helvetica, Courier/Courier New, etc. Try
+  // looking up the font under the aliased name.
+  const AtomicString& alternate_name =
+      AlternateFamilyName(creation_params.Family());
+  if (alternate_name.IsEmpty())
+    return nullptr;
+
+  FontFaceCreationParams create_by_alternate_family(alternate_name);
+  FontPlatformData* const platform_data = GetOrCreateFontPlatformData(
+      font_cache, font_description, create_by_alternate_family,
+      AlternateFontName::kNoAlternate);
+  if (!platform_data)
+    return nullptr;
+
+  // "accessibility/font-changed.html" reaches here.
+
+  // Cache the platform_data under the old name.
+  sized_fonts.Set(rounded_size, platform_data);
+  return platform_data;
+}
+
+size_t FontPlatformDataCache::ByteSize() const {
+  return map_.size() * sizeof(SizedFontPlatformDataSet);
+}
+
+void FontPlatformDataCache::Clear() {
+  map_.clear();
+}
+
+void FontPlatformDataCache::Purge(const FontDataCache& font_data_cache) {
+  Vector<FontCacheKey> keys_to_remove;
+  keys_to_remove.ReserveInitialCapacity(map_.size());
+  for (auto& entry : map_) {
+    if (entry.value->Purge(font_data_cache))
+      keys_to_remove.push_back(entry.key);
+  }
+  map_.RemoveAll(keys_to_remove);
+}
+
+FontPlatformDataCache::SizedFontPlatformDataSet&
+FontPlatformDataCache::GetOrCreateSizeMap(const FontCacheKey& key) {
+  auto result = map_.insert(key, nullptr);
+  if (result.is_new_entry)
+    result.stored_value->value = SizedFontPlatformDataSet::Create();
+  return *result.stored_value->value;
+}
+
+// --
+
+// static
+scoped_refptr<FontPlatformDataCache::SizedFontPlatformDataSet>
+FontPlatformDataCache::SizedFontPlatformDataSet::Create() {
+  return base::AdoptRef(new SizedFontPlatformDataSet());
+}
+
+FontPlatformDataCache::SizedFontPlatformDataSet::SizedFontPlatformDataSet() =
+    default;
+
+FontPlatformDataCache::SizedFontPlatformDataSet::~SizedFontPlatformDataSet() =
+    default;
+
+FontPlatformData*
+FontPlatformDataCache::SizedFontPlatformDataSet::GetOrCreateFontPlatformData(
+    FontCache* font_cache,
+    const FontDescription& font_description,
+    const FontFaceCreationParams& creation_params,
+    float size,
+    AlternateFontName alternate_font_name,
+    unsigned rounded_size) {
+  // Take a different size instance of the same font before adding an entry to
+  // `size_to_data_map`.
+  FontPlatformData* const another_size =
+      size_to_data_map_.IsEmpty() ? nullptr
+                                  : size_to_data_map_.begin()->value.get();
+  const auto add_result = size_to_data_map_.insert(rounded_size, nullptr);
+  std::unique_ptr<FontPlatformData>* found = &add_result.stored_value->value;
+  if (!add_result.is_new_entry)
+    return found->get();
+
+  if (!another_size) {
+    *found = font_cache->CreateFontPlatformData(
+        font_description, creation_params, size, alternate_font_name);
+    return found->get();
+  }
+
+  *found = font_cache->ScaleFontPlatformData(*another_size, font_description,
+                                             creation_params, size);
+  return found->get();
+}
+
+bool FontPlatformDataCache::SizedFontPlatformDataSet::Purge(
+    const FontDataCache& font_data_cache) {
+  Vector<unsigned> sizes_to_remove;
+  sizes_to_remove.ReserveInitialCapacity(size_to_data_map_.size());
+  for (const auto& entry : size_to_data_map_) {
+    if (entry.value && !font_data_cache.Contains(entry.value.get()))
+      sizes_to_remove.push_back(entry.key);
+  }
+  size_to_data_map_.RemoveAll(sizes_to_remove);
+  return size_to_data_map_.IsEmpty();
+}
+
+void FontPlatformDataCache::SizedFontPlatformDataSet::Set(
+    unsigned rounded_size,
+    FontPlatformData* platform_data) {
+  size_to_data_map_.insert(rounded_size,
+                           std::make_unique<FontPlatformData>(*platform_data));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data_cache.h b/third_party/blink/renderer/platform/fonts/font_platform_data_cache.h
new file mode 100644
index 0000000..165d211
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data_cache.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2007-2008 Torch Mobile, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_PLATFORM_DATA_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_PLATFORM_DATA_CACHE_H_
+
+#include "third_party/blink/renderer/platform/fonts/font_cache_key.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+
+namespace blink {
+
+enum class AlternateFontName;
+class FontCache;
+class FontDataCache;
+class FontDescription;
+class FontFaceCreationParams;
+class FontPlatformData;
+
+// `FontPlatformDataCache` is the shared cache mapping from `FontDescription`
+// to `FontPlatformData`.
+class FontPlatformDataCache final {
+ public:
+  static std::unique_ptr<FontPlatformDataCache> Create();
+
+  FontPlatformDataCache();
+  ~FontPlatformDataCache();
+
+  FontPlatformDataCache(const FontPlatformDataCache&) = delete;
+  FontPlatformDataCache(FontPlatformDataCache&&) = delete;
+
+  FontPlatformDataCache operator=(const FontPlatformDataCache&) = delete;
+  FontPlatformDataCache operator=(FontPlatformDataCache&&) = delete;
+
+  FontPlatformData* GetOrCreateFontPlatformData(
+      FontCache* font_cache,
+      const FontDescription& font_description,
+      const FontFaceCreationParams& creation_params,
+      AlternateFontName alternate_font_name);
+
+  size_t ByteSize() const;
+  void Clear();
+  void Purge(const FontDataCache& font_data_cache);
+
+ private:
+  // `SizedFontPlatformDataSet` maps rounded font size to `FontPlatformData`.
+  class SizedFontPlatformDataSet final
+      : public ThreadSafeRefCounted<SizedFontPlatformDataSet> {
+   public:
+    static scoped_refptr<SizedFontPlatformDataSet> Create();
+
+    ~SizedFontPlatformDataSet();
+
+    SizedFontPlatformDataSet(const SizedFontPlatformDataSet&) = delete;
+    SizedFontPlatformDataSet(SizedFontPlatformDataSet&&) = delete;
+
+    SizedFontPlatformDataSet& operator=(const SizedFontPlatformDataSet&) =
+        delete;
+    SizedFontPlatformDataSet operator=(SizedFontPlatformDataSet&&) = delete;
+
+    FontPlatformData* GetOrCreateFontPlatformData(
+        FontCache* font_cache,
+        const FontDescription& font_description,
+        const FontFaceCreationParams& creation_params,
+        float size,
+        AlternateFontName alternate_font_name,
+        unsigned rounded_size);
+
+    // Returns true if `map_` is empty.
+    bool Purge(const FontDataCache& font_data_cache);
+
+    void Set(unsigned rounded_size, FontPlatformData* platform_data);
+
+   private:
+    using SizeToDataMap = HashMap<unsigned,
+                                  std::unique_ptr<FontPlatformData>,
+                                  WTF::IntHash<unsigned>,
+                                  WTF::UnsignedWithZeroKeyHashTraits<unsigned>>;
+
+    SizedFontPlatformDataSet();
+
+    SizeToDataMap size_to_data_map_;
+  };
+
+  SizedFontPlatformDataSet& GetOrCreateSizeMap(const FontCacheKey& key);
+
+  HashMap<FontCacheKey, scoped_refptr<SizedFontPlatformDataSet>> map_;
+
+  // A maximum float value to which we limit incoming font sizes. This is the
+  // smallest float so that multiplying it by
+  // FontCacheKey::PrecisionMultiplier() is still smaller than
+  // std::numeric_limits<unsigned>::max() - 1 in order to avoid hitting
+  // HashMap sentinel values (placed at std::numeric_limits<unsigned>::max()
+  // and std::numeric_limits<unsigned>::max() - 1) for
+  // SizedFontPlatformDataSet and FontPlatformDataCache.
+  const float font_size_limit_;
+
+  // When true, the font size is removed from primary keys in |map_|.
+  // The font size is not necessary in the primary key, because per-size
+  // FontPlatformData are held in a nested map.
+  // This is controlled by a base::Feature to assess impact with an
+  // experiment.
+  const bool no_size_in_key_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_PLATFORM_DATA_CACHE_H_
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 003562ca..aaca3b5 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -241,7 +241,7 @@
       using_swap_chain_(using_swap_chain),
       want_depth_(want_depth),
       want_stencil_(want_stencil),
-      storage_color_space_(PredefinedColorSpaceToGfxColorSpace(color_space)),
+      color_space_(PredefinedColorSpaceToGfxColorSpace(color_space)),
       use_half_float_storage_(pixel_format == CanvasPixelFormat::kF16),
       filter_quality_(filter_quality),
       chromium_image_usage_(chromium_image_usage),
@@ -486,7 +486,7 @@
 
   *out_resource = viz::TransferableResource::MakeSoftware(
       registered.bitmap->id(), size_, viz::RGBA_8888);
-  out_resource->color_space = storage_color_space_;
+  out_resource->color_space = back_color_buffer_->color_space;
 
   // This holds a ref on the DrawingBuffer that will keep it alive until the
   // mailbox is released (and while the release callback is running). It also
@@ -590,7 +590,7 @@
         color_buffer_for_mailbox->mailbox, GL_LINEAR, texture_target_,
         color_buffer_for_mailbox->produce_sync_token, size_,
         is_overlay_candidate);
-    out_resource->color_space = storage_color_space_;
+    out_resource->color_space = color_buffer_for_mailbox->color_space;
     out_resource->format = color_buffer_for_mailbox->format;
     // This holds a ref on the DrawingBuffer that will keep it alive until the
     // mailbox is released (and while the release callback is running).
@@ -631,6 +631,7 @@
     front_color_buffer_ = nullptr;
 
   if (destruction_in_progress_ || color_buffer->size != size_ ||
+      color_buffer->color_space != color_space_ ||
       gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lost_resource ||
       is_hidden_) {
     return;
@@ -726,6 +727,7 @@
     if (recycled->receive_sync_token.HasData())
       gl_->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
     DCHECK(recycled->size == size_);
+    DCHECK(recycled->color_space == color_space_);
     gl_->BeginSharedImageAccessDirectCHROMIUM(
         recycled->texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
     return recycled;
@@ -823,6 +825,7 @@
 DrawingBuffer::ColorBuffer::ColorBuffer(
     base::WeakPtr<DrawingBuffer> drawing_buffer,
     const gfx::Size& size,
+    const gfx::ColorSpace& color_space,
     viz::ResourceFormat format,
     GLuint texture_id,
     std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
@@ -830,6 +833,7 @@
     : owning_thread_ref(base::PlatformThread::CurrentRef()),
       drawing_buffer(std::move(drawing_buffer)),
       size(size),
+      color_space(color_space),
       format(format),
       texture_id(texture_id),
       gpu_memory_buffer(std::move(gpu_memory_buffer)),
@@ -1029,11 +1033,13 @@
   GLuint texture_id_to_restore_access = 0;
   viz::ResourceFormat format;
   gfx::Size size;
+  gfx::ColorSpace color_space;
   if (src_buffer == kFrontBuffer && front_color_buffer_) {
     mailbox = front_color_buffer_->mailbox;
     produce_sync_token = front_color_buffer_->produce_sync_token;
     format = front_color_buffer_->format;
     size = front_color_buffer_->size;
+    color_space = front_color_buffer_->color_space;
   } else {
     GLuint texture_id = 0;
     if (premultiplied_alpha_false_texture_) {
@@ -1046,6 +1052,7 @@
     }
     format = back_color_buffer_->format;
     size = back_color_buffer_->size;
+    color_space = back_color_buffer_->color_space;
     src_gl->EndSharedImageAccessDirectCHROMIUM(texture_id);
     src_gl->GenUnverifiedSyncTokenCHROMIUM(produce_sync_token.GetData());
     texture_id_to_restore_access = texture_id;
@@ -1061,7 +1068,7 @@
   // Use an empty sync token for `mailbox_holder` because we have already waited
   // on the required sync tokens above.
   gpu::MailboxHolder mailbox_holder(mailbox, gpu::SyncToken(), texture_target_);
-  copy_function(mailbox_holder, format, size);
+  copy_function(mailbox_holder, format, size, color_space);
 
   gpu::SyncToken sync_token;
   dst_interface->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
@@ -1094,7 +1101,7 @@
     unpack_premultiply_alpha_needed = GL_TRUE;
 
   auto copy_function = [&](const gpu::MailboxHolder& src_mailbox,
-                           viz::ResourceFormat, gfx::Size) {
+                           viz::ResourceFormat, gfx::Size, gfx::ColorSpace) {
     GLuint src_texture = dst_gl->CreateAndTexStorage2DSharedImageCHROMIUM(
         src_mailbox.mailbox.name);
     dst_gl->BeginSharedImageAccessDirectCHROMIUM(
@@ -1124,7 +1131,8 @@
     unpack_premultiply_alpha_needed = GL_TRUE;
 
   auto copy_function = [&](const gpu::MailboxHolder& src_mailbox,
-                           viz::ResourceFormat, const gfx::Size&) {
+                           viz::ResourceFormat, const gfx::Size&,
+                           const gfx::ColorSpace&) {
     dst_raster_interface->CopySubTexture(
         src_mailbox.mailbox, dst_mailbox, dst_texture_target,
         dst_texture_offset.x(), dst_texture_offset.y(), src_sub_rectangle.x(),
@@ -1142,13 +1150,13 @@
     bool src_origin_is_top_left,
     const gfx::ColorSpace& dst_color_space,
     WebGraphicsContext3DVideoFramePool::FrameReadyCallback& callback) {
-  const gfx::ColorSpace src_color_space = storage_color_space_;
   const GrSurfaceOrigin src_surface_origin = src_origin_is_top_left
                                                  ? kTopLeft_GrSurfaceOrigin
                                                  : kBottomLeft_GrSurfaceOrigin;
   auto copy_function = [&](const gpu::MailboxHolder& src_mailbox,
                            viz::ResourceFormat src_format,
-                           const gfx::Size& src_size) {
+                           const gfx::Size& src_size,
+                           const gfx::ColorSpace src_color_space) {
     frame_pool->CopyRGBATextureToVideoFrame(
         src_format, src_size, src_color_space, src_surface_origin, src_mailbox,
         dst_color_space, std::move(callback));
@@ -1242,9 +1250,10 @@
   client_ = nullptr;
 }
 
-bool DrawingBuffer::ResizeDefaultFramebuffer(const gfx::Size& size) {
+bool DrawingBuffer::ReallocateDefaultFramebuffer(const gfx::Size& size,
+                                                 bool only_reallocate_color) {
   DCHECK(state_restorer_);
-  // Recreate m_backColorBuffer.
+  // Recreate back_color_buffer_.
   back_color_buffer_ = CreateColorBuffer(size);
 
   // Most OS compositors assume GpuMemoryBuffers contain premultiplied-alpha
@@ -1272,8 +1281,8 @@
                                  ? kTopLeft_GrSurfaceOrigin
                                  : kBottomLeft_GrSurfaceOrigin;
     premultiplied_alpha_false_mailbox_ = sii->CreateSharedImage(
-        back_color_buffer_->format, size, storage_color_space_, origin,
-        kUnpremul_SkAlphaType,
+        back_color_buffer_->format, size, back_color_buffer_->color_space,
+        origin, kUnpremul_SkAlphaType,
         gpu::SHARED_IMAGE_USAGE_GLES2 |
             gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT |
             gpu::SHARED_IMAGE_USAGE_RASTER,
@@ -1296,7 +1305,7 @@
     }
   }
 
-  if (WantDepthOrStencil()) {
+  if (WantDepthOrStencil() && !only_reallocate_color) {
     state_restorer_->SetFramebufferBindingDirty();
     state_restorer_->SetRenderbufferBindingDirty();
     gl_->BindFramebuffer(GL_FRAMEBUFFER,
@@ -1421,7 +1430,8 @@
 
   if (adjusted_size != size_) {
     do {
-      if (!ResizeDefaultFramebuffer(adjusted_size)) {
+      if (!ReallocateDefaultFramebuffer(adjusted_size,
+                                        /*only_reallocate_color=*/false)) {
         adjusted_size =
             gfx::ScaleToFlooredSize(adjusted_size, kResourceAdjustedRatio);
         continue;
@@ -1443,6 +1453,28 @@
   return true;
 }
 
+void DrawingBuffer::SetColorSpace(PredefinedColorSpace predefined_color_space) {
+  // Color space changes that are no-ops should not reach this point.
+  const gfx::ColorSpace color_space =
+      PredefinedColorSpaceToGfxColorSpace(predefined_color_space);
+  DCHECK_NE(color_space, color_space_);
+  color_space_ = color_space;
+
+  ScopedStateRestorer scoped_state_restorer(this);
+
+  // Free all mailboxes, because they are now of the wrong color space.
+  recycled_color_buffer_queue_.clear();
+  recycled_bitmaps_.clear();
+
+  if (!ReallocateDefaultFramebuffer(size_, /*only_reallocate_color=*/true)) {
+    // TODO(https://crbug.com/1208480): What is the correct behavior is we fail
+    // to re-allocate the buffer.
+    DLOG(ERROR) << "Failed to allocate color buffer with new color space.";
+  }
+
+  ClearNewlyAllocatedFramebuffers(kClearAllFBOs);
+}
+
 bool DrawingBuffer::ResolveAndBindForReadAndDraw() {
   {
     ScopedStateRestorer scoped_state_restorer(this);
@@ -1806,10 +1838,9 @@
     DCHECK(!use_half_float_storage_);
     format = viz::RGBX_8888;
   }
-
   if (UsingSwapChain()) {
     gpu::SharedImageInterface::SwapChainMailboxes mailboxes =
-        sii->CreateSwapChain(format, size, storage_color_space_, origin,
+        sii->CreateSwapChain(format, size, color_space_, origin,
                              kPremul_SkAlphaType,
                              usage | gpu::SHARED_IMAGE_USAGE_SCANOUT);
     back_buffer_mailbox = mailboxes.back_buffer;
@@ -1845,9 +1876,8 @@
 
       if (gpu_memory_buffer) {
         back_buffer_mailbox = sii->CreateSharedImage(
-            gpu_memory_buffer.get(), gpu_memory_buffer_manager,
-            storage_color_space_, origin, kPremul_SkAlphaType,
-            usage | additional_usage_flags);
+            gpu_memory_buffer.get(), gpu_memory_buffer_manager, color_space_,
+            origin, kPremul_SkAlphaType, usage | additional_usage_flags);
       }
     }
 
@@ -1863,8 +1893,8 @@
         alpha_type = kUnpremul_SkAlphaType;
 
       back_buffer_mailbox =
-          sii->CreateSharedImage(format, size, storage_color_space_, origin,
-                                 alpha_type, usage, gpu::kNullSurfaceHandle);
+          sii->CreateSharedImage(format, size, color_space_, origin, alpha_type,
+                                 usage, gpu::kNullSurfaceHandle);
     }
   }
 
@@ -1876,8 +1906,8 @@
     texture_id = gl_->CreateAndTexStorage2DSharedImageCHROMIUM(
         front_buffer_mailbox.name);
     front_color_buffer_ = base::MakeRefCounted<ColorBuffer>(
-        weak_factory_.GetWeakPtr(), size, format, texture_id, nullptr,
-        front_buffer_mailbox);
+        weak_factory_.GetWeakPtr(), size, color_space_, format, texture_id,
+        nullptr, front_buffer_mailbox);
   }
   // Import the backbuffer of swap chain or allocated SharedImage into GL.
   texture_id =
@@ -1905,7 +1935,7 @@
   }
 
   return base::MakeRefCounted<ColorBuffer>(
-      weak_factory_.GetWeakPtr(), size, format, texture_id,
+      weak_factory_.GetWeakPtr(), size, color_space_, format, texture_id,
       std::move(gpu_memory_buffer), back_buffer_mailbox);
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
index b7a3924..afc436b 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h
@@ -181,6 +181,10 @@
   // framebuffer. Returns whether the operation was successful.
   bool Resize(const gfx::Size&);
 
+  // Set the color space of the default draw buffer. This will destroy the
+  // contents of the drawing buffer.
+  void SetColorSpace(PredefinedColorSpace color_space);
+
   // Bind the default framebuffer to |target|. |target| must be
   // GL_FRAMEBUFFER, GL_READ_FRAMEBUFFER, or GL_DRAW_FRAMEBUFFER.
   void Bind(GLenum target);
@@ -407,6 +411,7 @@
   struct ColorBuffer : public base::RefCountedThreadSafe<ColorBuffer> {
     ColorBuffer(base::WeakPtr<DrawingBuffer> drawing_buffer,
                 const gfx::Size&,
+                const gfx::ColorSpace& color_space,
                 viz::ResourceFormat,
                 GLuint texture_id,
                 std::unique_ptr<gfx::GpuMemoryBuffer>,
@@ -424,6 +429,7 @@
     // ColorBuffers.
     base::WeakPtr<DrawingBuffer> drawing_buffer;
     const gfx::Size size;
+    const gfx::ColorSpace color_space;
     const viz::ResourceFormat format;
     const GLuint texture_id = 0;
     std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer;
@@ -512,9 +518,12 @@
                                const gpu::SyncToken&,
                                bool lost_resource);
 
-  // Attempts to allocator storage for, or resize all buffers. Returns whether
-  // the operation was successful.
-  bool ResizeDefaultFramebuffer(const gfx::Size&);
+  // Reallocates the storage for all buffers. This is called due to a change in
+  // the properties of the buffer (e.g, its size or color space). If
+  // `only_reallocate_color` is true, then do not reallocate the depth stencil
+  // buffer.
+  bool ReallocateDefaultFramebuffer(const gfx::Size&,
+                                    bool only_reallocate_color);
 
   void ClearCcLayer();
 
@@ -658,8 +667,8 @@
   const bool want_depth_;
   const bool want_stencil_;
 
-  // The color space of this buffer's storage.
-  const gfx::ColorSpace storage_color_space_;
+  // The color space of this buffer.
+  gfx::ColorSpace color_space_;
 
   AntialiasingMode anti_aliasing_mode_ = kAntialiasingModeNone;
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7434844..07f92561 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3147,8 +3147,7 @@
 crbug.com/1024156 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-cascade-002.html [ Pass ]
 crbug.com/1024156 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-paired-cascade-003.html [ Pass ]
 crbug.com/1024156 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-paired-cascade-006.html [ Pass ]
-crbug.com/1295264 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-computed-001.html [ Failure ]
-crbug.com/1295264 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-inheritance-computed-001.html [ Failure ]
+crbug.com/1295264 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-inheritance-computed-001.html [ Pass ]
 crbug.com/1295264 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-pseudos-currentcolor-visited-computed-001.html [ Failure ]
 crbug.com/1024156 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-styling-002.html [ Pass ]
 crbug.com/1024156 virtual/css-highlight-inheritance/external/wpt/css/css-pseudo/highlight-styling-004.html [ Pass ]
diff --git a/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html b/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
index c2141cb..93a3bd94 100644
--- a/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
+++ b/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
@@ -3,6 +3,11 @@
 <head>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
+<style>
+  .rtl {
+    direction: rtl;
+  }
+</style>
 </head>
 <body>
   <div id="slider"
@@ -23,6 +28,16 @@
       assert_equals(event.key, "ArrowDown", "event.key on " + event.type);
       assert_equals(event.keyCode, 40, "event.keyCode on " + event.type);
       assert_equals(event.which, 40, "event.which on " + event.type);
+    } else if (expected_key == "ArrowLeft") {
+      assert_equals(event.code, "ArrowLeft", "event.code on " + event.type);
+      assert_equals(event.key, "ArrowLeft", "event.key on " + event.type);
+      assert_equals(event.keyCode, 37, "event.keyCode on " + event.type);
+      assert_equals(event.which, 37, "event.which on " + event.type);
+    } else if (expected_key == "ArrowRight") {
+      assert_equals(event.code, "ArrowRight", "event.code on " + event.type);
+      assert_equals(event.key, "ArrowRight", "event.key on " + event.type);
+      assert_equals(event.keyCode, 39, "event.keyCode on " + event.type);
+      assert_equals(event.which, 39, "event.which on " + event.type);
     }
 
     assert_true(event.isTrusted, "event.isTrusted on " + event.type);
@@ -33,39 +48,126 @@
     assert_false(event.defaultPrevented, "event.defaultPrevented on " + event.type);
   }
 
-  async_test(function(t) {
-    var slider = document.getElementById("slider");
-    var axSlider = accessibilityController.accessibleElementById("slider");
+  var slider = document.getElementById("slider");
+  var axSlider = accessibilityController.accessibleElementById("slider");
 
-    slider.addEventListener("keydown", t.step_func((event) => {
+  // Tests for horizontal, left-to-right slider
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending an increment event to an ARIA slider generates a right arrow keydown event");
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending an increment event to an ARIA slider generates a right arrow keyup event");
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending a decrement event to an ARIA slider generates a left arrow keydown event");
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending a decrement event to an ARIA slider generates a left arrow keyup event");
+
+  // Tests for horizontal, right-to-left slider
+
+  promise_test(function(t) {
+    slider.classList.toggle("rtl");
+
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending an increment event to a RTL ARIA slider generates a left arrow keydown event");
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
+      checkEvent(event, "ArrowLeft");
+    });
+  }, "check that sending an increment event to a RTL ARIA slider generates a left arrow keyup event");
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending a decrement event to a RTL ARIA slider generates a right arrow keydown event");
+
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowRight");
+    });
+  }, "check that sending a decrement event to a RTL ARIA slider generates a right arrow keyup event");
+
+  // Tests for vertical slider
+
+  promise_test(function(t) {
+    slider.setAttribute('aria-orientation', 'vertical');
+
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.increment();
+    }).then(event => {
       checkEvent(event, "ArrowUp");
-    }), { once: true });
+    });
+  }, "check that sending an increment event to a vertical ARIA slider generates an up arrow keydown event");
 
-    slider.addEventListener("keyup", t.step_func((event) => {
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.increment();
+    }).then(event => {
       checkEvent(event, "ArrowUp");
+    });
+  }, "check that sending an increment event to a vertical ARIA slider generates an up arrow keyup event");
 
-      slider.addEventListener("keydown", t.step_func((event) => {
-        checkEvent(event, "ArrowDown");
-      }), { once: true });
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keydown", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowDown");
+    });
+  }, "check that sending a decrement event to a vertical ARIA slider generates a down arrow keydown event");
 
-      slider.addEventListener("keyup", t.step_func_done((event) => {
-        checkEvent(event, "ArrowDown");
-
-        t.done();
-      }), { once: true });
-
-      window.setTimeout(() => {
-        axSlider.decrement();
-      }, 0);
-    }), { once: true });
-
-    window.setTimeout(t.step_func(() => {
-      assert(false, "didn't get all key events within 1000ms");
-      t.done();
-    }), 1000);
-
-    axSlider.increment();
-  }, "check that sending an increment event to an ARIA slider generates an up arrow key event");
+  promise_test(function(t) {
+    return new Promise(resolve => {
+      slider.addEventListener("keyup", resolve);
+      axSlider.decrement();
+    }).then(event => {
+      checkEvent(event, "ArrowDown");
+    });
+  }, "check that sending a decrement event to a vertical ARIA slider generates a down arrow keyup event");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index ba027e8..5d5e73e 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: fe3c6d583713c0da55254088217bb823c8e2c9d5
+Version: 0d792625664243da144f345130ced901c1e95c37
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 5fa4811..def9ff8 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
@@ -690,6 +690,13 @@
          {}
         ]
        ],
+       "chrome-quotes-crash.html": [
+        "363f96fd02e1bc63d70ccda56dff2b9f935448ed",
+        [
+         null,
+         {}
+        ]
+       ],
        "columns-in-table-001-crash.html": [
         "fe421500dade86b6fedbbf6289a668dcc5174f45",
         [
@@ -1561,6 +1568,13 @@
         {}
        ]
       ],
+      "nested-as-nested-balanced-legend.html": [
+       "0575193c215c1e666fe1e8ee133c5d09699e2bea",
+       [
+        null,
+        {}
+       ]
+      ],
       "nested-multicol-and-float-with-tall-padding-before-float.html": [
        "2a901d1f582f7a17864f9a0b0b0d6afc495dcd21",
        [
@@ -1589,6 +1603,13 @@
         {}
        ]
       ],
+      "nested-with-fragmented-oof-negative-top-offset.html": [
+       "38f62888a6383d4045ae2c13f66e98c2657ad531",
+       [
+        null,
+        {}
+       ]
+      ],
       "nested-with-oof-inside-fixed-width.html": [
        "d53286ebe90bd784e6f0c0e2d02a6bf77548eaf3",
        [
@@ -9273,7 +9294,7 @@
       ]
      ],
      "cursor-image-015.html": [
-      "43229c597cc9684b22b8716d9d5c106b2652ecd3",
+      "108bb2a137bc296d3139ac9742c50fe28b16cac0",
       [
        null,
        {}
@@ -80924,6 +80945,32 @@
         {}
        ]
       ],
+      "multi-line-column-flex-fragmentation-046.html": [
+       "f009b1a3e796f73f9d3472b6c79bc291b15a437c",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-047.html": [
+       "a09eaea6b5ca6ce59ee26678d08227f208f170e7",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "multi-line-row-flex-fragmentation-001.html": [
        "ba6b0103e447994a2778cbd9ea356a490f62b8fb",
        [
@@ -81639,6 +81686,32 @@
         {}
        ]
       ],
+      "multi-line-row-flex-fragmentation-056.html": [
+       "0b71c13cce9bb60261cacdcfcc483ffe9eebc744",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-row-flex-fragmentation-057.html": [
+       "c20e684b45290d0d69dd3cbd3a6e1386f36d3a29",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "single-line-column-flex-fragmentation-001.html": [
        "d1411f9a16a14585b945408b162182ed343419d2",
        [
@@ -82198,6 +82271,32 @@
         {}
        ]
       ],
+      "single-line-column-flex-fragmentation-044.html": [
+       "2ef4c988ec52549cf01e9b244b50e004ddc0d795",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-column-flex-fragmentation-045.html": [
+       "f2d4d745fa2a76bd290b24194d8ae4f7b057073c",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "single-line-row-flex-fragmentation-001.html": [
        "379327a4aff584192f076d3f9d8f3ead232c0f5c",
        [
@@ -82574,6 +82673,32 @@
         ],
         {}
        ]
+      ],
+      "single-line-row-flex-fragmentation-030.html": [
+       "a70ad1473eaf277a4f8ce14b7f84efe363858a7b",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-row-flex-fragmentation-031.html": [
+       "1bda683312ee4228588b1950695b1672fdb23780",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
       ]
      },
      "float-000.html": [
@@ -287523,6 +287648,10 @@
       []
      ],
      "parsing": {
+      "cursor-valid-expected.txt": [
+       "c14f52fc219e5668f1d29b0f3dc0536b84e7029f",
+       []
+      ],
       "outline-color-valid-optional-expected.txt": [
        "ddbde423753384c7fa6f12e96141480222d71ee0",
        []
@@ -385531,6 +385660,13 @@
        {}
       ]
      ],
+     "multicol-fill-balance-022.html": [
+      "3dd530c7e27bed3af993b123c22f30baede77c73",
+      [
+       null,
+       {}
+      ]
+     ],
      "multicol-gap-animation-001.html": [
       "6a3a8d33780fe048528e1e3cd6a26707f2df4b70",
       [
@@ -397473,7 +397609,7 @@
        ]
       ],
       "cursor-valid.html": [
-       "285c16a4ee513b898d9d1c2852ae40897bb12545",
+       "8b3c478391b917d7d1233c8a7b49b816100ed1f4",
        [
         null,
         {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-media-toggle.html b/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-media-toggle.html
new file mode 100644
index 0000000..83a037a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/layer-media-toggle.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<title>CSS Cascade Layers: Tests against a Chrome bug that modifying a sheet affects existing layers</title>
+<link rel="help" href="https://drafts.csswg.org/css-cascade-5/#layering">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1313357">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="match" href="reference/ref-filled-green-100px-square.xht">
+
+<style>
+@layer foo, bar;
+@layer bar {
+  #target { background-color: green; }
+}
+@layer foo {
+  #target { background-color: red; }
+}
+</style>
+<style media="print" id="toggle">
+#target {
+  width: 100px;
+  height: 100px;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="target"></div>
+
+<script>
+document.body.offsetWidth; // Force style calculation
+document.getElementById('toggle').media = 'all';
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-oof-multicol-with-monolithic-child.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-oof-multicol-with-monolithic-child.html
new file mode 100644
index 0000000..bf7aa42
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-oof-multicol-with-monolithic-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1306582">
+<div style="columns:4; column-fill:auto; height:100px;">
+  <div style="position:relative;">
+    <div style="height:90px;"></div>
+    <div style="position:absolute; columns:1; column-fill:auto; width:10px; height:95px;">
+      <div style="height:95px; contain:size;"></div>
+    </div>
+    <div style="position:absolute; width:1px; height:100px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-oof-multicol-with-padding.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-oof-multicol-with-padding.html
new file mode 100644
index 0000000..e4797581
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/crashtests/nested-oof-multicol-with-padding.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1306582">
+<div style="columns:2; column-fill:auto; height:100px;">
+  <div style="position:relative;">
+    <div style="height:90px;"></div>
+    <div style="position:absolute; columns:2; padding-top:40px;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/cursor-image-015.html b/third_party/blink/web_tests/external/wpt/css/css-ui/cursor-image-015.html
index 43229c59..108bb2a1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/cursor-image-015.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/cursor-image-015.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <title>CSS Basic User Interface Test: Cursor property, image-set() value</title>
 <link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
-<link rel="help" href="http://www.w3.org/TR/css3-ui/#cursor">
-<meta name="flags" content="interact image may">
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#cursor">
+<meta name="flags" content="interact image">
 <meta charset="UTF-8">
-<meta name="assert" content="For cursors, UAs may support image-set(), which is part of <image> value type">
+<meta name="assert" content="For cursors, UAs must support image-set() containing simple url() based images">
 
 <style>
 div.test{
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid-expected.txt
new file mode 100644
index 0000000..c14f52fc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid-expected.txt
@@ -0,0 +1,45 @@
+This is a testharness.js-based test.
+PASS e.style['cursor'] = "auto" should set the property value
+PASS e.style['cursor'] = "default" should set the property value
+PASS e.style['cursor'] = "none" should set the property value
+PASS e.style['cursor'] = "context-menu" should set the property value
+PASS e.style['cursor'] = "help" should set the property value
+PASS e.style['cursor'] = "pointer" should set the property value
+PASS e.style['cursor'] = "progress" should set the property value
+PASS e.style['cursor'] = "wait" should set the property value
+PASS e.style['cursor'] = "cell" should set the property value
+PASS e.style['cursor'] = "crosshair" should set the property value
+PASS e.style['cursor'] = "text" should set the property value
+PASS e.style['cursor'] = "vertical-text" should set the property value
+PASS e.style['cursor'] = "alias" should set the property value
+PASS e.style['cursor'] = "copy" should set the property value
+PASS e.style['cursor'] = "move" should set the property value
+PASS e.style['cursor'] = "no-drop" should set the property value
+PASS e.style['cursor'] = "not-allowed" should set the property value
+PASS e.style['cursor'] = "grab" should set the property value
+PASS e.style['cursor'] = "grabbing" should set the property value
+PASS e.style['cursor'] = "e-resize" should set the property value
+PASS e.style['cursor'] = "n-resize" should set the property value
+PASS e.style['cursor'] = "ne-resize" should set the property value
+PASS e.style['cursor'] = "nw-resize" should set the property value
+PASS e.style['cursor'] = "s-resize" should set the property value
+PASS e.style['cursor'] = "se-resize" should set the property value
+PASS e.style['cursor'] = "sw-resize" should set the property value
+PASS e.style['cursor'] = "w-resize" should set the property value
+PASS e.style['cursor'] = "ew-resize" should set the property value
+PASS e.style['cursor'] = "ns-resize" should set the property value
+PASS e.style['cursor'] = "nesw-resize" should set the property value
+PASS e.style['cursor'] = "nwse-resize" should set the property value
+PASS e.style['cursor'] = "col-resize" should set the property value
+PASS e.style['cursor'] = "row-resize" should set the property value
+PASS e.style['cursor'] = "all-scroll" should set the property value
+PASS e.style['cursor'] = "zoom-in" should set the property value
+PASS e.style['cursor'] = "zoom-out" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\"), alias" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\") 1 calc(2 + 0), copy" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\"), url(\"https://example.com/\") 3 -4, move" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\") 5 6, grab" should set the property value
+FAIL e.style['cursor'] = "image-set(\"https://example.com/\" 1x) 5 6, grab" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['cursor'] = "image-set(\"https://example.com/\" 1x, \"https://example.com/highres\" 2x) 5 6, grab" should set the property value assert_not_equals: property should be set got disallowed value ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid.html b/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid.html
index 285c16a4e..8b3c4783 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/parsing/cursor-valid.html
@@ -2,10 +2,10 @@
 <html>
 <head>
 <meta charset="utf-8">
-<title>CSS UI Level 3: parsing cursor with valid values</title>
+<title>CSS UI Level 4: parsing cursor with valid values</title>
 <link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
-<link rel="help" href="https://drafts.csswg.org/css-ui-3/#cursor">
-<meta name="assert" content="cursor supports the full grammar.">
+<link rel="help" href="https://drafts.csswg.org/css-ui-4/#cursor">
+<meta name="assert" content="cursor supports the full required grammar.">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
@@ -53,6 +53,9 @@
 test_valid_value("cursor", 'url("https://example.com/") 1 calc(2 + 0), copy', ['url("https://example.com/") 1 calc(2), copy', 'url("https://example.com/") 1 2, copy', 'url(https://example.com/) 1 2, copy']);
 test_valid_value("cursor", 'url("https://example.com/"), url("https://example.com/") 3 -4, move', ['url("https://example.com/"), url("https://example.com/") 3 -4, move', 'url(https://example.com/), url(https://example.com/) 3 -4, move']);
 test_valid_value("cursor", 'url("https://example.com/") 5 6, grab', ['url("https://example.com/") 5 6, grab', 'url(https://example.com/) 5 6, grab']);
+
+test_valid_value("cursor", 'image-set("https://example.com/" 1x) 5 6, grab', ['image-set("https://example.com/" 1x) 5 6, grab', 'image-set("https://example.com/" 1x) 5 6, grab']);
+test_valid_value("cursor", 'image-set("https://example.com/" 1x, "https://example.com/highres" 2x) 5 6, grab', ['image-set("https://example.com/" 1x, "https://example.com/highres" 2x) 5 6, grab', 'image-set("https://example.com/" 1x, "https://example.com/highres" 2x) 5 6, grab']);
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html
new file mode 100644
index 0000000..05a60034
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<img src="resources/image.png">
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html
new file mode 100644
index 0000000..bc3714b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+<link rel="help" href="https://crbug.com/1308299">
+<link rel="match" href="image-loading-lazy-clip-path-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+img {
+  clip-path: polygon(10% 0, 90% 0, 90% 90%, 0 90%, 0 10%);
+  vertical-align: middle;
+}
+</style>
+<img id=target loading="lazy"
+      src="resources/image.png"
+      style="vertical-align: middle; clip-path: polygon(0 0, 110% 0, 110% 110%, 0 110%, 0 0)">
+<script>
+  target.onload = takeScreenshot;
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-mask.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-mask.html
new file mode 100644
index 0000000..937ed0f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-mask.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes">
+<link rel="help" href="https://crbug.com/1308299">
+<link rel="match" href="image-loading-lazy-clip-path-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<style>
+img {
+  mask: url(#mask);
+  vertical-align: middle;
+}
+</style>
+<svg style="display: none">
+  <mask id="mask">
+    <rect width="1000" height="1000" fill="white"/>
+  </mask>
+</svg>
+<img id=target loading="lazy"
+      src="resources/image.png"
+      style="vertical-align: middle; clip-path: polygon(0 0, 110% 0, 110% 110%, 0 110%, 0 0)">
+<script>
+  target.onload = takeScreenshot;
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-allowed.https.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-allowed.https.tentative.html
deleted file mode 100644
index f7c726b..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-allowed.https.tentative.html
+++ /dev/null
@@ -1,78 +0,0 @@
-<!DOCTYPE html>
-<title>CSP for subresource WebBundle (allowed cases)</title>
-<link
-  rel="help"
-  href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md"
-/>
-<meta
-  http-equiv="Content-Security-Policy"
-  content="
-    script-src
-      https://web-platform.test:8444/web-bundle/resources/wbn/uuid-in-package.wbn
-      https://web-platform.test:8444/resources/testharness.js
-      https://web-platform.test:8444/resources/testharnessreport.js
-      'unsafe-inline';
-    img-src
-      https://web-platform.test:8444/web-bundle/resources/wbn/pass.png;
-    frame-src
-      https://web-platform.test:8444/web-bundle/resources/wbn/uuid-in-package.wbn"
->
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body>
-<link rel="webbundle" href="../resources/wbn/subresource.wbn"
-      resources="https://web-platform.test:8444/web-bundle/resources/wbn/pass.png" />
-<link rel="webbundle" href="../resources/wbn/uuid-in-package.wbn"
-      resources="uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720
-                 uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae" />
-<script>
-  promise_test(() => {
-    return new Promise((resolve, reject) => {
-      const img = document.createElement('img');
-      img.src = 'https://web-platform.test:8444/web-bundle/resources/wbn/pass.png';
-      img.onload = resolve;
-      img.onerror = reject;
-      document.body.appendChild(img);
-    });
-  }, 'URL matching of CSP should be done based on the subresource URL ' +
-     'when the subresource URL is HTTPS URL.');
-
-  promise_test(async () => {
-    const result = await new Promise((resolve) => {
-      // This function will be called from the script.
-      window.report_result = resolve;
-      const script = document.createElement('script');
-      script.src = 'uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720';
-      document.body.appendChild(script);
-    });
-    assert_equals(result, 'OK');
-  }, 'URL matching of script-src CSP should be done based on the bundle URL ' +
-     'when the subresource URL is uuid-in-package: URL.');
-
-  promise_test(async () => {
-    const frame_url = 'uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae';
-    const iframe = document.createElement('iframe');
-    iframe.src = frame_url;
-    const load_promise = new Promise((resolve) => {
-      iframe.addEventListener('load', resolve);
-    });
-    document.body.appendChild(iframe);
-    await load_promise;
-    assert_equals(
-        await evalInIframe(iframe, 'location.href'),
-        frame_url);
-  }, 'URL matching of frame-src CSP should be done based on the bundle URL ' +
-     'when the frame URL is uuid-in-package: URL.');
-
-  async function evalInIframe(iframe, code) {
-    const message_promise = new Promise((resolve) => {
-        window.addEventListener(
-            'message',
-            (e) => { resolve(e.data); },
-            { once : true });
-      });
-    iframe.contentWindow.postMessage(code,'*');
-    return message_promise;
-  }
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-blocked.https.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-blocked.https.tentative.html
deleted file mode 100644
index a6cb5af..0000000
--- a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-blocked.https.tentative.html
+++ /dev/null
@@ -1,146 +0,0 @@
-<!DOCTYPE html>
-<title>CSP for subresource WebBundle (blocked cases)</title>
-<link
-  rel="help"
-  href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md"
-/>
-<meta
-  http-equiv="Content-Security-Policy"
-  content="
-    script-src
-      urn:
-      https://web-platform.test:8444/resources/testharness.js
-      https://web-platform.test:8444/resources/testharnessreport.js
-      'unsafe-inline';
-    img-src
-      https://web-platform.test:8444/web-bundle/resources/wbn/subresource.wbn;
-    frame-src
-      urn:;
-    report-to
-      csp-group"
->
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<body>
-<link rel="webbundle" href="../resources/wbn/subresource.wbn"
-      resources="https://web-platform.test:8444/web-bundle/resources/wbn/fail.png" />
-<link rel="webbundle" href="../resources/wbn/uuid-in-package.wbn"
-      resources="uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720
-                 uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae" />
-<script>
-  const uuid_bundle_url = 'https://web-platform.test:8444/web-bundle/resources/wbn/uuid-in-package.wbn';
-
-  function expect_violation() {
-    return new Promise(resolve => {
-      document.addEventListener('securitypolicyviolation', (e) => {
-        e.stopPropagation();
-        resolve(e);
-      }, {once: true});
-    });
-  }
-
-  function getReportID() {
-    const cookies = document.cookie.split(';');
-    for (var i = 0; i < cookies.length; i++) {
-      const name_value = cookies[i].split('=');
-      const cookieName = name_value[0].trim();
-      if (cookieName === 'csp-blocked-report-id') {
-        return name_value[1].trim();
-      }
-    }
-  }
-
-  function sortReportsByEffectiveDirective(reports) {
-    reports.sort((report1, report2) =>
-      report1.body.effectiveDirective.localeCompare(report2.body.effectiveDirective)
-          || report1.body.blockedURL.localeCompare(report2.body.blockedURL)
-    );
-  }
-
-  promise_test(async () => {
-    const p = expect_violation();
-    const img = document.createElement('img');
-    const error_promise = new Promise(resolve => {
-      img.onerror = resolve;
-    });
-    img.src = 'https://web-platform.test:8444/web-bundle/resources/wbn/fail.png';
-    document.body.appendChild(img);
-    const e = await p;
-    assert_equals(e.blockedURI, img.src);
-    await error_promise;
-  }, 'URL matching of CSP should be done based on the subresource URL, ' +
-     'not on the bundle URL, when the subresource URL is HTTPS URL.');
-
-  const testCases = [
-    {
-      prefix: 'uuid-in-package:',
-      bundle_url: uuid_bundle_url
-    }
-  ];
-  for (const params of testCases) {
-
-    promise_test(async () => {
-      const urn_uuid = params.prefix + '020111b3-437a-4c5c-ae07-adb6bbffb720';
-      const p = expect_violation();
-      const script = document.createElement('script');
-      script.src = urn_uuid;
-      document.body.appendChild(script);
-      const e = await p;
-      // Currently Chromium is reporting the bundle URL.
-      // TODO(crbug.com/1208659): Consider deeper integration with CSP for
-      // providing the both URLs.
-      assert_equals(e.blockedURI, params.bundle_url);
-      assert_equals(e.violatedDirective, 'script-src-elem');
-    }, 'URL matching of script-src CSP should be done based on the bundle URL ' +
-       `when the subresource URL is ${params.prefix} URL.`);
-
-    promise_test(async () => {
-      const urn_uuid = params.prefix + '429fcc4e-0696-4bad-b099-ee9175f023ae';
-      const p = expect_violation();
-      const iframe = document.createElement('iframe');
-      iframe.src = urn_uuid;
-      const load_promise = new Promise(resolve => {
-        iframe.addEventListener('load', resolve);
-      });
-      document.body.appendChild(iframe);
-      const e = await p;
-      // Currently Chromium is reporting the bundle URL.
-      // TODO(crbug.com/1208659): Consider deeper integration with CSP for
-      // providing the both URLs.
-      assert_equals(e.blockedURI, params.bundle_url);
-      assert_equals(e.violatedDirective, 'frame-src');
-
-      // Make sure that the blocked iframe load is finished.
-      await load_promise;
-
-      // The blocked iframe is cross-origin. So accessing
-      // iframe.contentWindow.location should throw a SecurityError.
-      assert_throws_dom(
-        "SecurityError",
-        () => { iframe.contentWindow.location.href; });
-    }, 'URL matching of frame-src CSP should be done based on the bundle URL ' +
-       `when the frame URL is ${params.prefix} URL.`);
-  }
-
-  promise_test(async () => {
-    const retrieve_report_url =
-      "/reporting/resources/report.py?op=retrieve_report&timeout=3&reportID=" +
-      getReportID();
-    const reports = await (await fetch(retrieve_report_url)).json();
-    sortReportsByEffectiveDirective(reports);
-
-    assert_equals(reports.length, 3, "Report count.");
-
-    assert_equals(reports[0].body.blockedURL, uuid_bundle_url);
-    assert_equals(reports[0].body.effectiveDirective, 'frame-src');
-
-    assert_equals(
-      reports[1].body.blockedURL,
-      'https://web-platform.test:8444/web-bundle/resources/wbn/fail.png');
-    assert_equals(reports[1].body.effectiveDirective, 'img-src',);
-
-    assert_equals(reports[2].body.blockedURL, uuid_bundle_url);
-    assert_equals(reports[2].body.effectiveDirective, 'script-src-elem');
-  }, 'Check the CSP violation reports.');
-</script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-allowed.https.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-allowed.https.tentative.html
new file mode 100644
index 0000000..55498eaa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-allowed.https.tentative.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<title>CSP for subresource WebBundle (allowed cases)</title>
+<link
+  rel="help"
+  href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md"
+/>
+<meta
+  http-equiv="Content-Security-Policy"
+  content="
+    script-src
+      https://web-platform.test:8444/web-bundle/resources/wbn/uuid-in-package.wbn
+      https://web-platform.test:8444/resources/testharness.js
+      https://web-platform.test:8444/resources/testharnessreport.js
+      'unsafe-inline';
+    img-src
+      https://web-platform.test:8444/web-bundle/resources/wbn/pass.png;
+    frame-src
+      https://web-platform.test:8444/web-bundle/resources/wbn/uuid-in-package.wbn"
+/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+  <script type="webbundle">
+    {
+      "source": "../resources/wbn/subresource.wbn",
+      "resources": ["https://web-platform.test:8444/web-bundle/resources/wbn/pass.png"]
+    }
+  </script>
+  <script type="webbundle">
+    {
+      "source": "../resources/wbn/uuid-in-package.wbn",
+      "resources": ["uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720",
+                    "uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae"
+      ]
+    }
+  </script>
+  <script>
+    promise_test(() => {
+      return new Promise((resolve, reject) => {
+        const img = document.createElement("img");
+        img.src =
+          "https://web-platform.test:8444/web-bundle/resources/wbn/pass.png";
+        img.onload = resolve;
+        img.onerror = reject;
+        document.body.appendChild(img);
+      });
+    }, "URL matching of CSP should be done based on the subresource URL " +
+       "when the subresource URL is HTTPS URL.");
+
+    promise_test(async () => {
+      const result = await new Promise((resolve) => {
+        // This function will be called from the script.
+        window.report_result = resolve;
+        const script = document.createElement("script");
+        script.src = "uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720";
+        document.body.appendChild(script);
+      });
+      assert_equals(result, "OK");
+    }, "URL matching of script-src CSP should be done based on the bundle URL " +
+       "when the subresource URL is uuid-in-package: URL.");
+
+    promise_test(async () => {
+      const frame_url = "uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae";
+      const iframe = document.createElement("iframe");
+      iframe.src = frame_url;
+      const load_promise = new Promise((resolve) => {
+        iframe.addEventListener("load", resolve);
+      });
+      document.body.appendChild(iframe);
+      await load_promise;
+      assert_equals(await evalInIframe(iframe, "location.href"), frame_url);
+    }, "URL matching of frame-src CSP should be done based on the bundle URL " +
+       "when the frame URL is uuid-in-package: URL.");
+
+    async function evalInIframe(iframe, code) {
+      const message_promise = new Promise((resolve) => {
+        window.addEventListener(
+          "message",
+          (e) => {
+            resolve(e.data);
+          },
+          { once: true }
+        );
+      });
+      iframe.contentWindow.postMessage(code, "*");
+      return message_promise;
+    }
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-blocked.https.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-blocked.https.tentative.html
new file mode 100644
index 0000000..6700533
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-blocked.https.tentative.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<title>CSP for subresource WebBundle (blocked cases)</title>
+<link
+  rel="help"
+  href="https://github.com/WICG/webpackage/blob/main/explainers/subresource-loading.md"
+/>
+<meta
+  http-equiv="Content-Security-Policy"
+  content="
+    script-src
+      urn:
+      https://web-platform.test:8444/resources/testharness.js
+      https://web-platform.test:8444/resources/testharnessreport.js
+      'unsafe-inline';
+    img-src
+      https://web-platform.test:8444/web-bundle/resources/wbn/subresource.wbn;
+    frame-src
+      urn:;
+    report-to
+      csp-group"
+/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+  <script type="webbundle">
+    {
+      "source": "../resources/wbn/subresource.wbn",
+      "resources": ["https://web-platform.test:8444/web-bundle/resources/wbn/fail.png"]
+    }
+  </script>
+  <script type="webbundle">
+    {
+      "source": "../resources/wbn/uuid-in-package.wbn",
+      "resources": ["uuid-in-package:020111b3-437a-4c5c-ae07-adb6bbffb720",
+                    "uuid-in-package:429fcc4e-0696-4bad-b099-ee9175f023ae"]
+    }
+  </script>
+  <script>
+    const uuid_bundle_url =
+      "https://web-platform.test:8444/web-bundle/resources/wbn/uuid-in-package.wbn";
+
+    function expect_violation() {
+      return new Promise((resolve) => {
+        document.addEventListener(
+          "securitypolicyviolation",
+          (e) => {
+            e.stopPropagation();
+            resolve(e);
+          },
+          { once: true }
+        );
+      });
+    }
+
+    function getReportID() {
+      const cookies = document.cookie.split(";");
+      for (var i = 0; i < cookies.length; i++) {
+        const name_value = cookies[i].split("=");
+        const cookieName = name_value[0].trim();
+        if (cookieName === "csp-blocked-report-id") {
+          return name_value[1].trim();
+        }
+      }
+    }
+
+    function sortReportsByEffectiveDirective(reports) {
+      reports.sort(
+        (report1, report2) =>
+          report1.body.effectiveDirective.localeCompare(
+            report2.body.effectiveDirective
+          ) || report1.body.blockedURL.localeCompare(report2.body.blockedURL)
+      );
+    }
+
+    promise_test(async () => {
+      const p = expect_violation();
+      const img = document.createElement("img");
+      const error_promise = new Promise((resolve) => {
+        img.onerror = resolve;
+      });
+      img.src =
+        "https://web-platform.test:8444/web-bundle/resources/wbn/fail.png";
+      document.body.appendChild(img);
+      const e = await p;
+      assert_equals(e.blockedURI, img.src);
+      await error_promise;
+    }, "URL matching of CSP should be done based on the subresource URL, " +
+       "not on the bundle URL, when the subresource URL is HTTPS URL.");
+
+    const testCases = [
+      {
+        prefix: "uuid-in-package:",
+        bundle_url: uuid_bundle_url,
+      },
+    ];
+    for (const params of testCases) {
+      promise_test(async () => {
+        const urn_uuid = params.prefix + "020111b3-437a-4c5c-ae07-adb6bbffb720";
+        const p = expect_violation();
+        const script = document.createElement("script");
+        script.src = urn_uuid;
+        document.body.appendChild(script);
+        const e = await p;
+        // Currently Chromium is reporting the bundle URL.
+        // TODO(crbug.com/1208659): Consider deeper integration with CSP for
+        // providing the both URLs.
+        assert_equals(e.blockedURI, params.bundle_url);
+        assert_equals(e.violatedDirective, "script-src-elem");
+      }, "URL matching of script-src CSP should be done based on the bundle URL " +
+         `when the subresource URL is ${params.prefix} URL.`);
+
+      promise_test(async () => {
+        const urn_uuid = params.prefix + "429fcc4e-0696-4bad-b099-ee9175f023ae";
+        const p = expect_violation();
+        const iframe = document.createElement("iframe");
+        iframe.src = urn_uuid;
+        const load_promise = new Promise((resolve) => {
+          iframe.addEventListener("load", resolve);
+        });
+        document.body.appendChild(iframe);
+        const e = await p;
+        // Currently Chromium is reporting the bundle URL.
+        // TODO(crbug.com/1208659): Consider deeper integration with CSP for
+        // providing the both URLs.
+        assert_equals(e.blockedURI, params.bundle_url);
+        assert_equals(e.violatedDirective, "frame-src");
+
+        // Make sure that the blocked iframe load is finished.
+        await load_promise;
+
+        // The blocked iframe is cross-origin. So accessing
+        // iframe.contentWindow.location should throw a SecurityError.
+        assert_throws_dom("SecurityError", () => {
+          iframe.contentWindow.location.href;
+        });
+      }, "URL matching of frame-src CSP should be done based on the bundle URL " +
+         `when the frame URL is ${params.prefix} URL.`);
+    }
+
+    promise_test(async () => {
+      const retrieve_report_url =
+        "/reporting/resources/report.py?op=retrieve_report&timeout=3&reportID=" +
+        getReportID();
+      const reports = await (await fetch(retrieve_report_url)).json();
+      sortReportsByEffectiveDirective(reports);
+
+      assert_equals(reports.length, 3, "Report count.");
+
+      assert_equals(reports[0].body.blockedURL, uuid_bundle_url);
+      assert_equals(reports[0].body.effectiveDirective, "frame-src");
+
+      assert_equals(
+        reports[1].body.blockedURL,
+        "https://web-platform.test:8444/web-bundle/resources/wbn/fail.png"
+      );
+      assert_equals(reports[1].body.effectiveDirective, "img-src");
+
+      assert_equals(reports[2].body.blockedURL, uuid_bundle_url);
+      assert_equals(reports[2].body.effectiveDirective, "script-src-elem");
+    }, "Check the CSP violation reports.");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-blocked.https.tentative.html.sub.headers b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-blocked.https.tentative.html.sub.headers
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/link-csp-blocked.https.tentative.html.sub.headers
rename to third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-csp-blocked.https.tentative.html.sub.headers
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-invalid-json.https.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-invalid-json.https.tentative.html
index dfa2577..036de1b 100644
--- a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-invalid-json.https.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-invalid-json.https.tentative.html
@@ -23,10 +23,6 @@
         script.onload = () => reject(script);
         script.onerror = () => reject(script);
         window.onerror = function (message, url, line, col, error) {
-          assert_equals(
-            message,
-            "Uncaught SyntaxError: Failed to parse web bundle: invalid JSON"
-          );
           assert_equals(error.name, "SyntaxError");
           resolve(script);
         };
diff --git a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative.html b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative.html
index 0f4e7ef..81d87bc 100644
--- a/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative.html
@@ -282,7 +282,7 @@
       clearWebBundleFetchCount();
       script1 = createWebBundleElement(
         wbn_url + "?pipe=trickle(d0.1)",
-        resource1
+        [resource1]
       );
       document.body.appendChild(script1);
 
@@ -291,7 +291,7 @@
       script1.remove();
       script2 = createWebBundleElement(
         wbn_url + "?pipe=trickle(d0.1)",
-        resource2
+        [resource2]
       );
       await addElementAndWaitForLoad(script2);
 
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-ui/parsing/cursor-valid-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-ui/parsing/cursor-valid-expected.txt
new file mode 100644
index 0000000..c14f52fc
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/css-ui/parsing/cursor-valid-expected.txt
@@ -0,0 +1,45 @@
+This is a testharness.js-based test.
+PASS e.style['cursor'] = "auto" should set the property value
+PASS e.style['cursor'] = "default" should set the property value
+PASS e.style['cursor'] = "none" should set the property value
+PASS e.style['cursor'] = "context-menu" should set the property value
+PASS e.style['cursor'] = "help" should set the property value
+PASS e.style['cursor'] = "pointer" should set the property value
+PASS e.style['cursor'] = "progress" should set the property value
+PASS e.style['cursor'] = "wait" should set the property value
+PASS e.style['cursor'] = "cell" should set the property value
+PASS e.style['cursor'] = "crosshair" should set the property value
+PASS e.style['cursor'] = "text" should set the property value
+PASS e.style['cursor'] = "vertical-text" should set the property value
+PASS e.style['cursor'] = "alias" should set the property value
+PASS e.style['cursor'] = "copy" should set the property value
+PASS e.style['cursor'] = "move" should set the property value
+PASS e.style['cursor'] = "no-drop" should set the property value
+PASS e.style['cursor'] = "not-allowed" should set the property value
+PASS e.style['cursor'] = "grab" should set the property value
+PASS e.style['cursor'] = "grabbing" should set the property value
+PASS e.style['cursor'] = "e-resize" should set the property value
+PASS e.style['cursor'] = "n-resize" should set the property value
+PASS e.style['cursor'] = "ne-resize" should set the property value
+PASS e.style['cursor'] = "nw-resize" should set the property value
+PASS e.style['cursor'] = "s-resize" should set the property value
+PASS e.style['cursor'] = "se-resize" should set the property value
+PASS e.style['cursor'] = "sw-resize" should set the property value
+PASS e.style['cursor'] = "w-resize" should set the property value
+PASS e.style['cursor'] = "ew-resize" should set the property value
+PASS e.style['cursor'] = "ns-resize" should set the property value
+PASS e.style['cursor'] = "nesw-resize" should set the property value
+PASS e.style['cursor'] = "nwse-resize" should set the property value
+PASS e.style['cursor'] = "col-resize" should set the property value
+PASS e.style['cursor'] = "row-resize" should set the property value
+PASS e.style['cursor'] = "all-scroll" should set the property value
+PASS e.style['cursor'] = "zoom-in" should set the property value
+PASS e.style['cursor'] = "zoom-out" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\"), alias" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\") 1 calc(2 + 0), copy" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\"), url(\"https://example.com/\") 3 -4, move" should set the property value
+PASS e.style['cursor'] = "url(\"https://example.com/\") 5 6, grab" should set the property value
+FAIL e.style['cursor'] = "image-set(\"https://example.com/\" 1x) 5 6, grab" should set the property value assert_not_equals: property should be set got disallowed value ""
+FAIL e.style['cursor'] = "image-set(\"https://example.com/\" 1x, \"https://example.com/highres\" 2x) 5 6, grab" should set the property value assert_not_equals: property should be set got disallowed value ""
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/paint/masks/fieldset-mask-expected.png b/third_party/blink/web_tests/flag-specific/highdpi/paint/masks/fieldset-mask-expected.png
index d29140c8d..beef076 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/paint/masks/fieldset-mask-expected.png
+++ b/third_party/blink/web_tests/flag-specific/highdpi/paint/masks/fieldset-mask-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/virtual/wbn-from-network/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/virtual/wbn-from-network/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative-expected.txt
new file mode 100644
index 0000000..f79dd1f
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/virtual/wbn-from-network/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS A webbundle should be fetched again when new script element is appended.
+PASS 'remove(), then append()' should reuse webbundle resources
+PASS 'remove(), then append()' should reuse webbundle resources and both scripts should fire load events
+PASS 'remove(), then append()' should reuse webbundle resources and both scripts should fire error events
+FAIL Should reuse webbundle resources if a credential mode is same assert_equals: expected 1 but got 0
+FAIL Should not reuse webbundle resources if a credentials mode is different (same-origin vs omit) assert_equals: expected 1 but got 2
+PASS Should not reuse webbundle resources if a credential mode is different (same-origin vs include
+PASS 'remove(), then append()' for the same element should reuse webbundle resources
+PASS Multiple 'remove(), then append()' for the same element should reuse webbundle resources
+PASS 'remove(), then append() in a separate task' should not reuse webbundle resources
+PASS replaceWith() should reuse webbundle resources.
+PASS append() should reuse webbundle resoruces even if the old script was moved to another document.
+PASS Even if the bundle is still loading, we should reuse the resources.
+PASS When reusing the resources with script2, a load event should be fired regardless of if the script1 fired a load
+PASS When reusing the resources with script2, an error event should be fired regardless of if the script1 fired an error
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2-expected.txt
deleted file mode 100644
index 26e6d2184..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.
-
-Setting break on all exceptions.
-Call stack:
-    0) divergingFunctionWithThrow (diverge-without-breakpoint-throw-on-load.html:6)
-    1)  (:1)
-Reloading page...
-Page reloaded.
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2.js
deleted file mode 100644
index 5f6a4d0c..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-2.js
+++ /dev/null
@@ -1,27 +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.
-
-(async function() {
-  TestRunner.addResult(
-      `Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
-  await SourcesTestRunner.startDebuggerTestPromise(true);
-  TestRunner.addResult('Setting break on all exceptions.');
-  TestRunner.DebuggerAgent.setPauseOnExceptions(
-      SDK.DebuggerModel.PauseOnExceptionsState.PauseOnAllExceptions);
-  SourcesTestRunner.waitUntilPaused(onPaused);
-  await TestRunner.navigatePromise(
-      '../../sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html');
-  TestRunner.evaluateInPageWithTimeout(`divergingFunctionWithThrow()`);
-
-  async function onPaused(callFrames) {
-    await SourcesTestRunner.captureStackTrace(callFrames);
-    TestRunner.addResult('Reloading page...');
-    TestRunner.reloadPage(onPageReloaded);
-  }
-
-  function onPageReloaded() {
-    TestRunner.completeTest();
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-expected.txt
deleted file mode 100644
index 999ae44d..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.
-
-
-Running: testFetchBreakpoint
-Waiting for breakpoint.
-Script execution paused.
-Call stack:
-    0) divergingFunction (reload-on-breakpoint.js:11)
-    1)  (:1)
-Reloading page...
-Script execution resumed.
-Page reloaded.
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint.js
deleted file mode 100644
index 605557c..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/reload-on-breakpoint.js
+++ /dev/null
@@ -1,31 +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.
-
-(async function() {
-  TestRunner.addResult(
-      `Tests that reloading while paused at a breakpoint doesn't execute code after the breakpoint.\n`);
-  await TestRunner.loadLegacyModule('sources'); await TestRunner.loadTestModule('sources_test_runner');
-  await TestRunner.evaluateInPagePromise(`
-      function divergingFunction() {
-          debugger;
-          while(true) {};
-      }
-  `);
-
-  SourcesTestRunner.runDebuggerTestSuite([function testFetchBreakpoint(next) {
-    SourcesTestRunner.waitUntilPaused(onPaused);
-    TestRunner.addResult('Waiting for breakpoint.');
-    TestRunner.evaluateInPageWithTimeout('divergingFunction()');
-
-    async function onPaused(callFrames) {
-      await SourcesTestRunner.captureStackTrace(callFrames);
-      TestRunner.addResult('Reloading page...');
-      TestRunner.reloadPage(onPageReloaded);
-    }
-
-    function onPageReloaded() {
-      next();
-    }
-  }]);
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html
deleted file mode 100644
index f0e33df..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/resources/diverge-without-breakpoint-throw-on-load.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<html>
-<head>
-<script>
-function divergingFunctionWithThrow() {
-  try {
-    throw new Error();
-  } catch (e) {}
-  while (true) {};
-}
-</script>
-</head>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/loading/wbn/origin-trial/link-webbundle-origin-trial.https.html b/third_party/blink/web_tests/http/tests/loading/wbn/origin-trial/link-webbundle-origin-trial.https.html
deleted file mode 100644
index 8343a435..0000000
--- a/third_party/blink/web_tests/http/tests/loading/wbn/origin-trial/link-webbundle-origin-trial.https.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!DOCTYPE html>
-<head><title>Subresource Web Bundles Origin Trial</title></head>
-<body>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-promise_test(async (t) => {
-  assert_false(
-    document.createElement('link').relList.supports('webbundle'),
-    'Subresource Web Bundles should not be supported by default.');
-
-  const meta = document.createElement('meta');
-  meta.httpEquiv = "origin-trial";
-  // This Origin Trial token is generated with the command:
-  //   tools/origin_trials/generate_token.py \
-  //     --expire-timestamp=2000000000 \
-  //     --version=3 \
-  //     https://127.0.0.1:8443 SubresourceWebBundles
-  meta.content = "A1nOn5e148yGA6ExfhqzlxQFFC71b03gYEAJEZ1XpUw+Lv8uUA4rpeyhCme0z3bBaRtIy8XkDK8twDUuhVXOegEAAABeeyJvcmlnaW4iOiAiaHR0cHM6Ly8xMjcuMC4wLjE6ODQ0MyIsICJmZWF0dXJlIjogIlN1YnJlc291cmNlV2ViQnVuZGxlcyIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==";
-  document.head.appendChild(meta);
-
-  assert_true(
-    document.createElement('link').relList.supports('webbundle'),
-    'Subresource Web Bundles should be supported.');
-
-  const wbn_url = 'https://localhost:8443/loading/wbn/resources/wbn/wbn-subresource-origin-trial.wbn';
-  const script_url = 'https://localhost:8443/loading/wbn/resources/wbn/server/wbn-subresource-origin-trial/script.js';
-
-  const SubresourceWebBundles = 3446;  // From web_feature.mojom
-  assert_false(window.internals.isUseCounted(document, SubresourceWebBundles));
-
-  const link = document.createElement('link');
-  link.rel = 'webbundle';
-  link.href = wbn_url;
-  link.resources.add(script_url);
-  document.body.appendChild(link);
-
-  assert_true(window.internals.isUseCounted(document, SubresourceWebBundles));
-
-  async function loadScriptAndWaitReport(script_url) {
-    const result_promise = new Promise((resolve) => {
-        // This function will be called from script.js
-        window.report_result = resolve;
-      });
-
-    const script = document.createElement('script');
-    script.src = script_url;
-    document.body.appendChild(script);
-    return result_promise;
-  }
-
-  assert_equals(
-    await loadScriptAndWaitReport(script_url),
-    'from web bundle',
-    'Script should be loaded from the web bundle.');
-  document.body.removeChild(link);
-
-  assert_equals(
-    await loadScriptAndWaitReport(script_url),
-    'from server',
-    'Script should be loaded from the server after removing the link ' +
-    'rel=webbundle.');
-}, 'Subresource Web Bundles Origin Trial');
-</script>
-</body>
diff --git a/third_party/blink/web_tests/http/tests/loading/wbn/origin-trial/link-webbundle-third-party-origin-trial.https.html b/third_party/blink/web_tests/http/tests/loading/wbn/origin-trial/link-webbundle-third-party-origin-trial.https.html
deleted file mode 100644
index c2333e5..0000000
--- a/third_party/blink/web_tests/http/tests/loading/wbn/origin-trial/link-webbundle-third-party-origin-trial.https.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE html>
-<head><title>Subresource Web Bundles Third Party Origin Trial</title></head>
-<body>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script>
-promise_test(async (t) => {
-  assert_false(
-    document.createElement('link').relList.supports('webbundle'),
-    'Subresource Web Bundles should not be supported by default.');
-
-  let resolve_third_party_script_loaded;
-  const third_party_script_loaded = new Promise((resolve) => {
-      resolve_third_party_script_loaded = resolve;
-    });
-  const third_party_script = document.createElement('script');
-  third_party_script.src = 'https://localhost:8443/loading/wbn/resources/wbn-subresource-third-party-origin-trial-script.js';
-  third_party_script.addEventListener('load', () => {
-      resolve_third_party_script_loaded();
-    });
-  document.body.appendChild(third_party_script);
-  await third_party_script_loaded;
-
-  assert_true(
-    document.createElement('link').relList.supports('webbundle'),
-    'Subresource Web Bundles should be supported.');
-
-  const wbn_url = 'https://localhost:8443/loading/wbn/resources/wbn/wbn-subresource-third-party-origin-trial.wbn';
-  const script_url = 'https://localhost:8443/loading/wbn/resources/wbn/server/wbn-subresource-third-party-origin-trial/script.js';
-
-  const SubresourceWebBundles = 3446;  // From web_feature.mojom
-  assert_false(window.internals.isUseCounted(document, SubresourceWebBundles));
-
-  const link = document.createElement('link');
-  link.rel = 'webbundle';
-  link.href = wbn_url;
-  link.resources.add(script_url);
-  document.body.appendChild(link);
-
-  assert_true(window.internals.isUseCounted(document, SubresourceWebBundles));
-
-  const result_promise = new Promise((resolve) => {
-      // This function will be called from script.js
-      window.report_result = resolve;
-    });
-
-  const script = document.createElement('script');
-  script.src = script_url;
-  document.body.appendChild(script);
-
-  assert_equals(
-    await result_promise,
-    'from web bundle',
-    'Script should be loaded from the web bundle.');
-}, 'Subresource Web Bundles Third Party Origin Trial');
-</script>
-</body>
diff --git a/third_party/blink/web_tests/http/tests/loading/wbn/subresource-loading/link-preload-relative-url-with-base.https.html b/third_party/blink/web_tests/http/tests/loading/wbn/subresource-loading/link-preload-relative-url-with-base.https.html
deleted file mode 100644
index a18a364..0000000
--- a/third_party/blink/web_tests/http/tests/loading/wbn/subresource-loading/link-preload-relative-url-with-base.https.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<title>Subresource loading using relative URLs in the 'resources' attribute with a base element</title>
-<base href="../resources/">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<body>
-  <link
-  id="bundle"
-  rel="webbundle"
-  href="/wbn/hello.wbn"
-  resources="hello/script.js"/>
-  <script id="script" src="hello/script.js"></script>
-
-  <script>
-    // When preload scanner encounters a <link> element with a webbundle, it must not
-    // preload a request for the webbundle itself and any subresources mentioned in the
-    // 'resources' attribute.
-    // This test loads a simple hello.wbn bundle and script.js served from the bundle,
-    // and checks that both hello.wbn and script.js were not preloaded.
-    const onLoadPromise = new Promise((resolve) => {
-      window.addEventListener('load', resolve, false);
-    });
-
-    promise_test(async () => {
-      await onLoadPromise;
-
-      if (window.internals) {
-        assert_false(internals.isPreloaded(script.src));
-        assert_false(internals.isPreloaded(bundle.href));
-        assert_equals(script_result, 'loaded from webbundle');
-      }
-    }, "A subresource script.js should be loaded from WebBundle using the relative URL and a base element.");
-  </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/http/tests/loading/wbn/subresource-loading/link-preload-relative-url.https.html b/third_party/blink/web_tests/http/tests/loading/wbn/subresource-loading/link-preload-relative-url.https.html
deleted file mode 100644
index b47fcc9..0000000
--- a/third_party/blink/web_tests/http/tests/loading/wbn/subresource-loading/link-preload-relative-url.https.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE html>
-<title>Subresource loading using relative URLs in the 'resources' attribute</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<body>
-  <link
-  id="bundle"
-  rel="webbundle"
-  href="../resources/wbn/hello.wbn"
-  resources="/loading/wbn/resources/hello/script.js"/>
-  <script id="script" src="/loading/wbn/resources/hello/script.js"></script>
-
-  <script>
-    // When preload scanner encounters a <link> element with a webbundle, it must not
-    // preload a request for the webbundle itself and any subresources mentioned in the
-    // 'resources' attribute.
-    // This test loads a simple hello.wbn bundle and script.js served from the bundle,
-    // and checks that both hello.wbn and script.js were not preloaded.
-    const onLoadPromise = new Promise((resolve) => {
-      window.addEventListener('load', resolve, false);
-    });
-
-    promise_test(async () => {
-      await onLoadPromise;
-
-      if (window.internals) {
-        assert_false(internals.isPreloaded(script.src));
-        assert_false(internals.isPreloaded(bundle.href));
-        assert_equals(script_result, 'loaded from webbundle');
-      }
-    }, "A subresource script.js should be loaded from WebBundle using the relative URL.");
-  </script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 7ac6b69..4e9182d 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -801,9 +801,7 @@
     property referrerPolicy
     property rel
     property relList
-    property resources
     property rev
-    property scopes
     property sheet
     property sizes
     property target
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 156f1af..fc025ef1 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -3824,9 +3824,7 @@
     getter referrerPolicy
     getter rel
     getter relList
-    getter resources
     getter rev
-    getter scopes
     getter sheet
     getter sizes
     getter target
@@ -3847,9 +3845,7 @@
     setter referrerPolicy
     setter rel
     setter relList
-    setter resources
     setter rev
-    setter scopes
     setter sizes
     setter target
     setter type
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index 7bb9882..070343c 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -278,6 +278,7 @@
   AUDIO: 'audio',
   IMAGE: 'image',
   VIDEO: 'video',
+  DOCUMENT: 'document',
 };
 
 /** @enum {string} */
@@ -1450,4 +1451,4 @@
 chrome.fileManagerPrivate.onIOTaskProgressStatus;
 
 /** @type {!ChromeEvent} */
-chrome.fileManagerPrivate.onMountableGuestsChanged;
\ No newline at end of file
+chrome.fileManagerPrivate.onMountableGuestsChanged;
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index cf3c20ca..220c430 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -3,9 +3,9 @@
 URL: https://aomedia.googlesource.com/aom/
 Version: 3.1.2
 CPEPrefix: cpe:/a:aomedia:aomedia:3.1.2
-Date: Wednesday March 30 2022
+Date: Tuesday April 05 2022
 Branch: main
-Commit: cc31d8c4b722cf7ecd68ac351bf3d288de07bc68
+Commit: d48a17ce9848b793ecd774ce41a44bd19359385e
 License: BSD
 License File: source/libaom/LICENSE
 Security Critical: yes
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index 5a793341..6d69f72 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -12,8 +12,8 @@
 #define VERSION_MAJOR 3
 #define VERSION_MINOR 3
 #define VERSION_PATCH 0
-#define VERSION_EXTRA "417-gcc31d8c4b"
+#define VERSION_EXTRA "440-gd48a17ce9"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "3.3.0-417-gcc31d8c4b"
-#define VERSION_STRING " 3.3.0-417-gcc31d8c4b"
+#define VERSION_STRING_NOSP "3.3.0-440-gd48a17ce9"
+#define VERSION_STRING " 3.3.0-440-gd48a17ce9"
diff --git a/third_party/libaom/source/config/ios/arm-neon/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/ios/arm-neon/config/aom_dsp_rtcd.h
index 8448e6d..155c1185 100644
--- a/third_party/libaom/source/config/ios/arm-neon/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/ios/arm-neon/config/aom_dsp_rtcd.h
@@ -1232,6 +1232,13 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_neon
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1262,6 +1269,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_neon
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1292,6 +1306,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_neon
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1322,6 +1343,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_neon
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1352,6 +1380,13 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_neon
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1382,6 +1417,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_neon
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1412,6 +1454,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_neon
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1442,6 +1491,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_neon
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/ios/arm64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/ios/arm64/config/aom_dsp_rtcd.h
index 8448e6d..155c1185 100644
--- a/third_party/libaom/source/config/ios/arm64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/ios/arm64/config/aom_dsp_rtcd.h
@@ -1232,6 +1232,13 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_neon
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1262,6 +1269,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_neon
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1292,6 +1306,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_neon
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1322,6 +1343,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_neon
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1352,6 +1380,13 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_neon
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1382,6 +1417,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_neon
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1412,6 +1454,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_neon
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1442,6 +1491,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_neon
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_dsp_rtcd.h
index e0e6435b..2a73acd6 100644
--- a/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm-neon-cpu-detect/config/aom_dsp_rtcd.h
@@ -1371,6 +1371,13 @@
                                                const uint8_t* limit1,
                                                const uint8_t* thresh1);
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1412,6 +1419,13 @@
                                               const uint8_t* limit1,
                                               const uint8_t* thresh1);
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1453,6 +1467,13 @@
                                               const uint8_t* limit1,
                                               const uint8_t* thresh1);
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1494,6 +1515,13 @@
                                               const uint8_t* limit1,
                                               const uint8_t* thresh1);
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1535,6 +1563,13 @@
                                              const uint8_t* limit1,
                                              const uint8_t* thresh1);
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1576,6 +1611,13 @@
                                             const uint8_t* limit1,
                                             const uint8_t* thresh1);
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1617,6 +1659,13 @@
                                             const uint8_t* limit1,
                                             const uint8_t* thresh1);
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1658,6 +1707,13 @@
                                             const uint8_t* limit1,
                                             const uint8_t* thresh1);
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/arm-neon/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/arm-neon/config/aom_dsp_rtcd.h
index 8448e6d..155c1185 100644
--- a/third_party/libaom/source/config/linux/arm-neon/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm-neon/config/aom_dsp_rtcd.h
@@ -1232,6 +1232,13 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_neon
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1262,6 +1269,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_neon
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1292,6 +1306,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_neon
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1322,6 +1343,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_neon
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1352,6 +1380,13 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_neon
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1382,6 +1417,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_neon
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1412,6 +1454,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_neon
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1442,6 +1491,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_neon
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/arm/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/arm/config/aom_dsp_rtcd.h
index b3cdf9d9..a87855d6 100644
--- a/third_party/libaom/source/config/linux/arm/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm/config/aom_dsp_rtcd.h
@@ -1058,6 +1058,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_c
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1075,6 +1082,13 @@
                                  const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_c
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1092,6 +1106,13 @@
                                  const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_c
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1109,6 +1130,13 @@
                                  const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_c
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1126,6 +1154,13 @@
                                 const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_c
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1143,6 +1178,13 @@
                                const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_c
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1160,6 +1202,13 @@
                                const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_c
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1177,6 +1226,13 @@
                                const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_c
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/arm64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/arm64/config/aom_dsp_rtcd.h
index 8448e6d..155c1185 100644
--- a/third_party/libaom/source/config/linux/arm64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/arm64/config/aom_dsp_rtcd.h
@@ -1232,6 +1232,13 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_neon
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1262,6 +1269,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_neon
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1292,6 +1306,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_neon
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1322,6 +1343,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_neon
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1352,6 +1380,13 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_neon
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1382,6 +1417,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_neon
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1412,6 +1454,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_neon
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1442,6 +1491,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_neon
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/generic/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/generic/config/aom_dsp_rtcd.h
index 58904775..5fbcf9c 100644
--- a/third_party/libaom/source/config/linux/generic/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/generic/config/aom_dsp_rtcd.h
@@ -1058,6 +1058,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_c
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1075,6 +1082,13 @@
                                  const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_c
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1092,6 +1106,13 @@
                                  const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_c
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1109,6 +1130,13 @@
                                  const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_c
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1126,6 +1154,13 @@
                                 const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_c
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1143,6 +1178,13 @@
                                const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_c
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1160,6 +1202,13 @@
                                const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_c
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1177,6 +1226,13 @@
                                const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_c
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h
index aa0ee81..5f5646d 100644
--- a/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/ia32/config/aom_dsp_rtcd.h
@@ -2348,6 +2348,18 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_sse2
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+void aom_lpf_horizontal_14_quad_sse2(uint8_t* s,
+                                     int pitch,
+                                     const uint8_t* blimit0,
+                                     const uint8_t* limit0,
+                                     const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_sse2
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2378,6 +2390,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_sse2
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_4_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_sse2
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2408,6 +2432,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_sse2
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_6_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_sse2
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2438,6 +2474,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_sse2
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_8_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_sse2
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -2468,6 +2516,18 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_sse2
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+void aom_lpf_vertical_14_quad_sse2(uint8_t* s,
+                                   int pitch,
+                                   const uint8_t* blimit0,
+                                   const uint8_t* limit0,
+                                   const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_sse2
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2498,6 +2558,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_sse2
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_4_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_sse2
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2528,6 +2600,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_sse2
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_6_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_sse2
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2558,6 +2642,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_sse2
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_8_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_sse2
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h
index 91b2cc7..cbec629 100644
--- a/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/linux/x64/config/aom_dsp_rtcd.h
@@ -2351,6 +2351,18 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_sse2
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+void aom_lpf_horizontal_14_quad_sse2(uint8_t* s,
+                                     int pitch,
+                                     const uint8_t* blimit0,
+                                     const uint8_t* limit0,
+                                     const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_sse2
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2381,6 +2393,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_sse2
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_4_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_sse2
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2411,6 +2435,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_sse2
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_6_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_sse2
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2441,6 +2477,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_sse2
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_8_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_sse2
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -2471,6 +2519,18 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_sse2
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+void aom_lpf_vertical_14_quad_sse2(uint8_t* s,
+                                   int pitch,
+                                   const uint8_t* blimit0,
+                                   const uint8_t* limit0,
+                                   const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_sse2
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2501,6 +2561,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_sse2
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_4_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_sse2
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2531,6 +2603,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_sse2
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_6_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_sse2
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2561,6 +2645,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_sse2
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_8_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_sse2
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/win/arm64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/win/arm64/config/aom_dsp_rtcd.h
index 8448e6d..155c1185 100644
--- a/third_party/libaom/source/config/win/arm64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/win/arm64/config/aom_dsp_rtcd.h
@@ -1232,6 +1232,13 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_neon
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_c
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1262,6 +1269,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_neon
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_c
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1292,6 +1306,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_neon
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_c
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -1322,6 +1343,13 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_neon
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_c
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -1352,6 +1380,13 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_neon
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_c
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1382,6 +1417,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_neon
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_c
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1412,6 +1454,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_neon
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_c
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -1442,6 +1491,13 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_neon
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_c
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h
index aa0ee81..5f5646d 100644
--- a/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/win/ia32/config/aom_dsp_rtcd.h
@@ -2348,6 +2348,18 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_sse2
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+void aom_lpf_horizontal_14_quad_sse2(uint8_t* s,
+                                     int pitch,
+                                     const uint8_t* blimit0,
+                                     const uint8_t* limit0,
+                                     const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_sse2
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2378,6 +2390,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_sse2
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_4_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_sse2
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2408,6 +2432,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_sse2
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_6_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_sse2
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2438,6 +2474,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_sse2
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_8_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_sse2
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -2468,6 +2516,18 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_sse2
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+void aom_lpf_vertical_14_quad_sse2(uint8_t* s,
+                                   int pitch,
+                                   const uint8_t* blimit0,
+                                   const uint8_t* limit0,
+                                   const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_sse2
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2498,6 +2558,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_sse2
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_4_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_sse2
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2528,6 +2600,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_sse2
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_6_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_sse2
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2558,6 +2642,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_sse2
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_8_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_sse2
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h b/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h
index 91b2cc7..cbec629 100644
--- a/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h
+++ b/third_party/libaom/source/config/win/x64/config/aom_dsp_rtcd.h
@@ -2351,6 +2351,18 @@
                                      const uint8_t* thresh1);
 #define aom_lpf_horizontal_14_dual aom_lpf_horizontal_14_dual_sse2
 
+void aom_lpf_horizontal_14_quad_c(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+void aom_lpf_horizontal_14_quad_sse2(uint8_t* s,
+                                     int pitch,
+                                     const uint8_t* blimit0,
+                                     const uint8_t* limit0,
+                                     const uint8_t* thresh0);
+#define aom_lpf_horizontal_14_quad aom_lpf_horizontal_14_quad_sse2
+
 void aom_lpf_horizontal_4_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2381,6 +2393,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_4_dual aom_lpf_horizontal_4_dual_sse2
 
+void aom_lpf_horizontal_4_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_4_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_4_quad aom_lpf_horizontal_4_quad_sse2
+
 void aom_lpf_horizontal_6_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2411,6 +2435,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_6_dual aom_lpf_horizontal_6_dual_sse2
 
+void aom_lpf_horizontal_6_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_6_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_6_quad aom_lpf_horizontal_6_quad_sse2
+
 void aom_lpf_horizontal_8_c(uint8_t* s,
                             int pitch,
                             const uint8_t* blimit,
@@ -2441,6 +2477,18 @@
                                     const uint8_t* thresh1);
 #define aom_lpf_horizontal_8_dual aom_lpf_horizontal_8_dual_sse2
 
+void aom_lpf_horizontal_8_quad_c(uint8_t* s,
+                                 int pitch,
+                                 const uint8_t* blimit0,
+                                 const uint8_t* limit0,
+                                 const uint8_t* thresh0);
+void aom_lpf_horizontal_8_quad_sse2(uint8_t* s,
+                                    int pitch,
+                                    const uint8_t* blimit0,
+                                    const uint8_t* limit0,
+                                    const uint8_t* thresh0);
+#define aom_lpf_horizontal_8_quad aom_lpf_horizontal_8_quad_sse2
+
 void aom_lpf_vertical_14_c(uint8_t* s,
                            int pitch,
                            const uint8_t* blimit,
@@ -2471,6 +2519,18 @@
                                    const uint8_t* thresh1);
 #define aom_lpf_vertical_14_dual aom_lpf_vertical_14_dual_sse2
 
+void aom_lpf_vertical_14_quad_c(uint8_t* s,
+                                int pitch,
+                                const uint8_t* blimit0,
+                                const uint8_t* limit0,
+                                const uint8_t* thresh0);
+void aom_lpf_vertical_14_quad_sse2(uint8_t* s,
+                                   int pitch,
+                                   const uint8_t* blimit0,
+                                   const uint8_t* limit0,
+                                   const uint8_t* thresh0);
+#define aom_lpf_vertical_14_quad aom_lpf_vertical_14_quad_sse2
+
 void aom_lpf_vertical_4_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2501,6 +2561,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_4_dual aom_lpf_vertical_4_dual_sse2
 
+void aom_lpf_vertical_4_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_4_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_4_quad aom_lpf_vertical_4_quad_sse2
+
 void aom_lpf_vertical_6_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2531,6 +2603,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_6_dual aom_lpf_vertical_6_dual_sse2
 
+void aom_lpf_vertical_6_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_6_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_6_quad aom_lpf_vertical_6_quad_sse2
+
 void aom_lpf_vertical_8_c(uint8_t* s,
                           int pitch,
                           const uint8_t* blimit,
@@ -2561,6 +2645,18 @@
                                   const uint8_t* thresh1);
 #define aom_lpf_vertical_8_dual aom_lpf_vertical_8_dual_sse2
 
+void aom_lpf_vertical_8_quad_c(uint8_t* s,
+                               int pitch,
+                               const uint8_t* blimit0,
+                               const uint8_t* limit0,
+                               const uint8_t* thresh0);
+void aom_lpf_vertical_8_quad_sse2(uint8_t* s,
+                                  int pitch,
+                                  const uint8_t* blimit0,
+                                  const uint8_t* limit0,
+                                  const uint8_t* thresh0);
+#define aom_lpf_vertical_8_quad aom_lpf_vertical_8_quad_sse2
+
 unsigned int aom_masked_sad128x128_c(const uint8_t* src,
                                      int src_stride,
                                      const uint8_t* ref,
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index 02ad12d7..e0c629f 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby-connections
-Version: f4b80ccb59fb7d7a7c45e73e53da61158f985b07
+Version: 6b3402ccd0753a60cd190d3ec70e508d3b6880ff
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 54d27d9..208bbfb 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -538,7 +538,6 @@
       'Fuchsia x64': 'release_bot_fuchsia',
       'Leak Detection Linux': 'release_bot_reclient',
       'Linux Builder (dbg)': 'gpu_tests_debug_bot_reclient',
-      'Linux Builder (dbg)(32)': 'gpu_tests_debug_bot_x86_reclient',
       'Linux Builder': 'gpu_tests_release_bot_do_typecheck_reclient',
       'Linux Builder (Wayland)': 'gpu_tests_wayland_release_bot_reclient',
       'Network Service Linux': 'release_bot_reclient',
@@ -2525,10 +2524,6 @@
       'gpu_tests', 'debug_bot', 'arm64',
     ],
 
-    'gpu_tests_debug_bot_x86_reclient': [
-      'gpu_tests', 'debug_bot_reclient', 'x86',
-    ],
-
     'gpu_tests_debug_bot_x86_no_symbols': [
       'gpu_tests', 'debug_bot', 'x86', 'no_symbols'
     ],
diff --git a/tools/mb/mb_config_expectations/chromium.linux.json b/tools/mb/mb_config_expectations/chromium.linux.json
index 2d50cdd..1296f4e5 100644
--- a/tools/mb/mb_config_expectations/chromium.linux.json
+++ b/tools/mb/mb_config_expectations/chromium.linux.json
@@ -157,18 +157,6 @@
       "use_remoteexec": true
     }
   },
-  "Linux Builder (dbg)(32)": {
-    "gn_args": {
-      "ffmpeg_branding": "Chrome",
-      "is_component_build": true,
-      "is_debug": true,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "target_cpu": "x86",
-      "use_rbe": true,
-      "use_remoteexec": true
-    }
-  },
   "Network Service Linux": {
     "gn_args": {
       "dcheck_always_on": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2c1ad7d..6a315f4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -54596,6 +54596,7 @@
   <int value="-333216449" label="MouseSubframeNoImplicitCapture:enabled"/>
   <int value="-332010491" label="RetailCoupons:enabled"/>
   <int value="-331952590" label="OmniboxTriggerForPrerender2:enabled"/>
+  <int value="-329807250" label="EnableNeuralPalmRejectionBetaModel:disabled"/>
   <int value="-329727402" label="disable-files-quick-view"/>
   <int value="-328361990" label="enable-experimental-extension-apis"/>
   <int value="-327520505" label="RelatedSearchesAlternateUx:enabled"/>
@@ -56378,6 +56379,7 @@
   <int value="898311758" label="ReaderMode:disabled"/>
   <int value="899347105" label="NearbySharingWifiLan:enabled"/>
   <int value="900614020" label="ContentSuggestionsShowSummary:disabled"/>
+  <int value="900784986" label="EnableNeuralPalmRejectionBetaModel:enabled"/>
   <int value="902209599" label="ShelfHotseat:enabled"/>
   <int value="902839593" label="WebContentsForceDark:disabled"/>
   <int value="903267263" label="disable-offline-pages"/>
@@ -91108,6 +91110,18 @@
   <int value="8" label="Missing/Missing (Backup/Local State)"/>
 </enum>
 
+<enum name="UmaCleanExitConsistency3">
+  <int value="0" label="Clean/Clean (Beacon File/Platform Specific)"/>
+  <int value="1" label="Clean/Dirty (Beacon File/Platform Specific)"/>
+  <int value="2" label="Clean/Missing (Beacon File/Platform Specific)"/>
+  <int value="3" label="Dirty/Clean (Beacon File/Platform Specific)"/>
+  <int value="4" label="Dirty/Dirty (Beacon File/Platform Specific)"/>
+  <int value="5" label="Dirty/Missing (Beacon File/Platform Specific)"/>
+  <int value="6" label="Missing/Clean (Beacon File/Platform Specific)"/>
+  <int value="7" label="Missing/Dirty (Beacon File/Platform Specific)"/>
+  <int value="8" label="Missing/Missing (Beacon File/Platform Specific)"/>
+</enum>
+
 <enum name="UmaEntropySourceType">
   <int value="0" label="No entropy source (never hit)"/>
   <int value="1" label="Low Entropy Source"/>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index d1c2593..adc1df40 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -27,6 +27,8 @@
   <variant name="Clipboard" summary="Handler for clipboard"/>
   <variant name="Extension" summary="Handler for extensions"/>
   <variant name="Files" summary="Handler for files"/>
+  <variant name="Lacros" summary="Handler which calls cleanup on Lacros"/>
+  <variant name="LacrosBrowsingData" summary="Handler for files on Lacros"/>
   <variant name="OpenWindows" summary="Handler for open browser windows"/>
   <variant name="PrintJobs" summary="Handler for print jobs"/>
 </variants>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index 92a0b059..c0bd1ac2 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -140,17 +140,34 @@
   <owner>olivierrobin@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
-    Recorded when the CleanExitBeacon is initialized. Reports the combined state
-    of distinct beacons stored in a platform-specific location (e.g. the Windows
-    registry or NSUserDefaults on iOS) and in Local State. They are normally
-    expected to be identical.
+    Recorded when the CleanExitBeacon is initialized on Windows and iOS. Reports
+    the combined state of distinct beacons stored in a platform-specific
+    location (e.g. the Windows registry or NSUserDefaults on iOS) and in Local
+    State. They are normally expected to be identical.
 
-    Unlike it's predecessor, UMA.CleanExitBeaconConsistency, this histogram has
+    Unlike its predecessor, UMA.CleanExitBeaconConsistency, this histogram has
     buckets that account for the situation in which Local State is missing a
     beacon value.
   </summary>
 </histogram>
 
+<histogram name="UMA.CleanExitBeaconConsistency3"
+    enum="UmaCleanExitConsistency3" expires_after="2022-08-01">
+  <owner>caitlinfischer@google.com</owner>
+  <owner>justincohen@chromium.org</owner>
+  <owner>src/base/metrics/OWNERS</owner>
+  <summary>
+    Recorded when the CleanExitBeacon is initialized for clients in the Extended
+    Variations Safe Mode enabled group on Windows and iOS. Reports the combined
+    state of distinct beacons stored in the beacon file and in a
+    platform-specific location (e.g. the Windows registry or NSUserDefaults on
+    iOS). They are normally expected to be identical.
+
+    Unlike UMA.CleanExitBeaconConsistency2, this histogram excludes the Local
+    State beacon.
+  </summary>
+</histogram>
+
 <histogram name="UMA.ClientIdBackupRecoveredWithAge" units="hours"
     expires_after="2022-08-01">
   <owner>asvitkine@chromium.org</owner>
diff --git a/tools/typescript/tsconfig_base.json b/tools/typescript/tsconfig_base.json
index 7cef25d..9766fe35 100644
--- a/tools/typescript/tsconfig_base.json
+++ b/tools/typescript/tsconfig_base.json
@@ -6,6 +6,7 @@
     "noEmitOnError": true,
     "pretty": true,
 
+    "allowUnreachableCode": false,
     "forceConsistentCasingInFileNames": true,
     "noFallthroughCasesInSwitch": true,
     "noImplicitOverride": true,
diff --git a/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.h b/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.h
index d1275e7..d0433b8 100644
--- a/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.h
+++ b/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.h
@@ -63,8 +63,14 @@
       const id target,
       const AXPropertyNode& property_node) const;
 
-  // Returns a parameterized attribute parameter by a property node.
-  AXOptionalNSObject ParamByPropertyNode(const AXPropertyNode&) const;
+  // Returns a parameterized attribute parameter by a property node representing
+  // an attribute call.
+  AXOptionalNSObject ParamFrom(const AXPropertyNode&) const;
+
+  // Returns a parameterized attribute parameter by an attribute and a property
+  // node representing an argument.
+  AXOptionalNSObject ParamFrom(const std::string& attribute,
+                               const AXPropertyNode& argument) const;
 
   // Converts a given property node to NSObject. If not convertible, returns
   // nil.
diff --git a/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.mm b/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.mm
index 7bcca03..1fc47126 100644
--- a/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.mm
+++ b/ui/accessibility/platform/inspect/ax_call_statement_invoker_mac.mm
@@ -177,7 +177,7 @@
     return AXOptionalNSObject::NotNullOrNotApplicable(AXActionNamesOf(target));
   }
   if (property_node.name_or_value == "AXPerformAction") {
-    AXOptionalNSObject param = ParamByPropertyNode(property_node);
+    AXOptionalNSObject param = ParamFrom(property_node);
     if (param.IsNotNull()) {
       PerformAXAction(target, *param);
       return AXOptionalNSObject::Unsupported();
@@ -207,7 +207,7 @@
   // Parameterized attributes.
   for (NSString* attribute : AXParameterizedAttributeNamesOf(target)) {
     if (property_node.IsMatching(base::SysNSStringToUTF8(attribute))) {
-      AXOptionalNSObject param = ParamByPropertyNode(property_node);
+      AXOptionalNSObject param = ParamFrom(property_node);
       if (param.IsNotNull()) {
         return AXOptionalNSObject(
             AXParameterizedAttributeValueOf(target, attribute, *param));
@@ -257,7 +257,30 @@
     BOOL return_value;
     [invocation getReturnValue:&return_value];
     return AXOptionalNSObject([NSNumber numberWithBool:return_value]);
-  } else if (base::StartsWith(property_node.name_or_value, "accessibility")) {
+  }
+
+  // accessibilityAttributeValue
+  if (property_node.name_or_value == "accessibilityAttributeValue") {
+    if (property_node.arguments.size() == 1) {
+      return AXOptionalNSObject(AXAttributeValueOf(
+          target,
+          base::SysUTF8ToNSString(property_node.arguments[0].name_or_value)));
+    }
+    // Parameterized accessibilityAttributeValue.
+    if (property_node.arguments.size() == 2) {
+      const std::string& attribute = property_node.arguments[0].name_or_value;
+      AXOptionalNSObject param =
+          ParamFrom(attribute, property_node.arguments[1]);
+      if (!param.HasValue())
+        return AXOptionalNSObject::Error();
+
+      return AXOptionalNSObject(AXParameterizedAttributeValueOf(
+          target, base::SysUTF8ToNSString(attribute), *param));
+    }
+    return AXOptionalNSObject::Error();
+  }
+
+  if (base::StartsWith(property_node.name_or_value, "accessibility")) {
     if (property_node.arguments.size() == 1) {
       absl::optional<id> optional_id =
           PerformAXSelector(target, property_node.name_or_value,
@@ -372,7 +395,7 @@
   return AXOptionalNSObject::NotNullOrError(dictionary[key]);
 }
 
-AXOptionalNSObject AXCallStatementInvoker::ParamByPropertyNode(
+AXOptionalNSObject AXCallStatementInvoker::ParamFrom(
     const AXPropertyNode& property_node) const {
   // NSAccessibility attributes always take a single parameter.
   if (property_node.arguments.size() != 1) {
@@ -381,49 +404,53 @@
     return AXOptionalNSObject::Error();
   }
 
+  return ParamFrom(property_node.name_or_value, property_node.arguments[0]);
+}
+
+AXOptionalNSObject AXCallStatementInvoker::ParamFrom(
+    const std::string& attribute,
+    const AXPropertyNode& argument) const {
   // Nested attribute case: attempt to invoke an attribute for an argument node.
-  const AXPropertyNode& arg_node = property_node.arguments[0];
-  AXOptionalNSObject subvalue = Invoke(arg_node, /* no_object_parse= */ true);
+  AXOptionalNSObject subvalue = Invoke(argument, /* no_object_parse= */ true);
   if (!subvalue.IsNotApplicable()) {
     return subvalue;
   }
 
   // Otherwise parse argument node value.
-  const std::string& property_name = property_node.name_or_value;
-  if (property_name == "AXLineForIndex" ||
-      property_name == "AXTextMarkerForIndex") {  // Int
-    return AXOptionalNSObject::NotNullOrError(PropertyNodeToInt(arg_node));
+  if (attribute == "AXLineForIndex" ||
+      attribute == "AXTextMarkerForIndex") {  // Int
+    return AXOptionalNSObject::NotNullOrError(PropertyNodeToInt(argument));
   }
-  if (property_name == "AXPerformAction") {
-    return AXOptionalNSObject::NotNullOrError(PropertyNodeToString(arg_node));
+  if (attribute == "AXPerformAction") {
+    return AXOptionalNSObject::NotNullOrError(PropertyNodeToString(argument));
   }
-  if (property_name == "AXCellForColumnAndRow") {  // IntArray
-    return AXOptionalNSObject::NotNullOrError(PropertyNodeToIntArray(arg_node));
+  if (attribute == "AXCellForColumnAndRow") {  // IntArray
+    return AXOptionalNSObject::NotNullOrError(PropertyNodeToIntArray(argument));
   }
-  if (property_name ==
+  if (attribute ==
       "AXTextMarkerRangeForUnorderedTextMarkers") {  // TextMarkerArray
     return AXOptionalNSObject::NotNullOrError(
-        PropertyNodeToTextMarkerArray(arg_node));
+        PropertyNodeToTextMarkerArray(argument));
   }
-  if (property_name == "AXStringForRange") {  // NSRange
-    return AXOptionalNSObject::NotNullOrError(PropertyNodeToRange(arg_node));
+  if (attribute == "AXStringForRange") {  // NSRange
+    return AXOptionalNSObject::NotNullOrError(PropertyNodeToRange(argument));
   }
-  if (property_name == "AXIndexForChildUIElement" ||
-      property_name == "AXTextMarkerRangeForUIElement") {  // UIElement
+  if (attribute == "AXIndexForChildUIElement" ||
+      attribute == "AXTextMarkerRangeForUIElement") {  // UIElement
     return AXOptionalNSObject::NotNullOrError(
-        PropertyNodeToUIElement(arg_node));
+        PropertyNodeToUIElement(argument));
   }
-  if (property_name == "AXIndexForTextMarker" ||
-      property_name == "AXNextWordEndTextMarkerForTextMarker" ||
-      property_name ==
+  if (attribute == "AXIndexForTextMarker" ||
+      attribute == "AXNextWordEndTextMarkerForTextMarker" ||
+      attribute ==
           "AXPreviousWordStartTextMarkerForTextMarker") {  // TextMarker
     return AXOptionalNSObject::NotNullOrError(
-        PropertyNodeToTextMarker(arg_node));
+        PropertyNodeToTextMarker(argument));
   }
-  if (property_name == "AXSelectedTextMarkerRangeAttribute" ||
-      property_name == "AXStringForTextMarkerRange") {  // TextMarkerRange
+  if (attribute == "AXSelectedTextMarkerRangeAttribute" ||
+      attribute == "AXStringForTextMarkerRange") {  // TextMarkerRange
     return AXOptionalNSObject::NotNullOrError(
-        PropertyNodeToTextMarkerRange(arg_node));
+        PropertyNodeToTextMarkerRange(argument));
   }
 
   return AXOptionalNSObject::NotApplicable();
diff --git a/ui/aura/cursor/cursor_loader.cc b/ui/aura/cursor/cursor_loader.cc
index df64fa52..60426ff7 100644
--- a/ui/aura/cursor/cursor_loader.cc
+++ b/ui/aura/cursor/cursor_loader.cc
@@ -113,7 +113,10 @@
     cursor = factory_->GetDefaultCursor(type);
     if (cursor)
       return cursor;
-    LOG(ERROR) << "Failed to load a platform cursor of type " << type;
+    // The cursor may fail to load if the cursor theme has just been reset.
+    // We will be notified when the theme is loaded, but at this time we have to
+    // fall back to the assets.
+    LOG(WARNING) << "Failed to load a platform cursor of type " << type;
   }
 
   // Loads the default Aura cursor bitmap for the cursor type. Falls back on
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index 475397b..a0a20fbb 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -86,6 +86,9 @@
   <message name="IDS_FILE_BROWSER_MEDIA_VIEW_AUDIO_ROOT_LABEL" desc="A label for the 'Audio' root of media views.">
     Audio
   </message>
+  <message name="IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL" desc="A label for the 'Documents' root of media views.">
+    Documents
+  </message>
   <message name="IDS_FILE_BROWSER_RECENT_VIEW_FILTER_ON" desc="Message read by Chromevox/screenreader when curtain filter in Recent become active.">
     <ph name="FILTER_NAME">$1<ex>Audio</ex></ph> filter is on.
   </message>
@@ -1336,6 +1339,9 @@
   <message name="IDS_FILE_BROWSER_PLAIN_TEXT_FILE_TYPE" desc="Plain text file type">
     Plain text
   </message>
+  <message name="IDS_FILE_BROWSER_CSV_TEXT_FILE_TYPE" desc="CSV file type">
+    CSV text
+  </message>
   <message name="IDS_FILE_BROWSER_PDF_DOCUMENT_FILE_TYPE" desc="PDF document file type">
     PDF document
   </message>
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CSV_TEXT_FILE_TYPE.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CSV_TEXT_FILE_TYPE.png.sha1
new file mode 100644
index 0000000..88ac7aa1
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_CSV_TEXT_FILE_TYPE.png.sha1
@@ -0,0 +1 @@
+f6c4505022be308a88f80bebc429b944ca42319a
\ No newline at end of file
diff --git a/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL.png.sha1 b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL.png.sha1
new file mode 100644
index 0000000..7290384
--- /dev/null
+++ b/ui/chromeos/file_manager_strings_grdp/IDS_FILE_BROWSER_MEDIA_VIEW_DOCUMENTS_ROOT_LABEL.png.sha1
@@ -0,0 +1 @@
+ab83649aef33b52c6dfafcbf37bdf3443a6df477
\ No newline at end of file
diff --git a/ui/events/ozone/features.cc b/ui/events/ozone/features.cc
index 0297d76a..193492c 100644
--- a/ui/events/ozone/features.cc
+++ b/ui/events/ozone/features.cc
@@ -15,6 +15,9 @@
 const base::Feature kEnableNeuralPalmAdaptiveHold{
     "EnableNeuralPalmAdaptiveHold", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kEnableNeuralPalmRejectionBetaModel{
+    "EnableNeuralPalmRejectionBetaModel", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kEnableNeuralPalmRejectionModelV2{
     "EnableNeuralPalmRejectionModelV2", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ui/events/ozone/features.h b/ui/events/ozone/features.h
index 6abe3e4f..1697221 100644
--- a/ui/events/ozone/features.h
+++ b/ui/events/ozone/features.h
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial_params.h"
 
 namespace ui {
+
 COMPONENT_EXPORT(EVENTS_OZONE)
 extern const base::Feature kEnableHeuristicPalmDetectionFilter;
 
@@ -20,6 +21,9 @@
 extern const base::Feature kEnableNeuralPalmAdaptiveHold;
 
 COMPONENT_EXPORT(EVENTS_OZONE)
+extern const base::Feature kEnableNeuralPalmRejectionBetaModel;
+
+COMPONENT_EXPORT(EVENTS_OZONE)
 extern const base::Feature kEnableNeuralPalmRejectionModelV2;
 
 COMPONENT_EXPORT(EVENTS_OZONE)
diff --git a/ui/file_manager/base/gn/file_types.json5 b/ui/file_manager/base/gn/file_types.json5
index 0f0cc3f..fed8d6c 100644
--- a/ui/file_manager/base/gn/file_types.json5
+++ b/ui/file_manager/base/gn/file_types.json5
@@ -288,9 +288,16 @@
     "type": "text",
     "translationKey": "PLAIN_TEXT_FILE_TYPE",
     "subtype": "TXT",
-    "extensions": [".txt"],
+    "extensions": [".txt", ".text"],
     "mime": "text/plain"
   },
+  {
+    "type": "text",
+    "translationKey": "CSV_TEXT_FILE_TYPE",
+    "subtype": "CSV",
+    "extensions": [".csv"],
+    "mime": "text/csv"
+  },
 
   // Archive
   {
@@ -399,7 +406,7 @@
     "translationKey": "GDOC_DOCUMENT_FILE_TYPE",
     "subtype": "doc",
     "extensions": [".gdoc"],
-    // "mime": "application/vnd.google-apps.document"
+    "mime": "application/vnd.google-apps.document"
   },
   {
     "type": "hosted",
@@ -407,7 +414,7 @@
     "translationKey": "GSHEET_DOCUMENT_FILE_TYPE",
     "subtype": "sheet",
     "extensions": [".gsheet"],
-    // "mime": "application/vnd.google-apps.spreadsheet"
+    "mime": "application/vnd.google-apps.spreadsheet"
   },
   {
     "type": "hosted",
@@ -415,7 +422,7 @@
     "translationKey": "GSLIDES_DOCUMENT_FILE_TYPE",
     "subtype": "slides",
     "extensions": [".gslides"],
-    // "mime": "application/vnd.google-apps.presentation"
+    "mime": "application/vnd.google-apps.presentation"
   },
   {
     "type": "hosted",
@@ -423,7 +430,7 @@
     "translationKey": "GDRAW_DOCUMENT_FILE_TYPE",
     "subtype": "draw",
     "extensions": [".gdraw"],
-    // "mime": "application/vnd.google-apps.drawing"
+    "mime": "application/vnd.google-apps.drawing"
   },
   {
     "type": "hosted",
@@ -431,7 +438,7 @@
     "translationKey": "GTABLE_DOCUMENT_FILE_TYPE",
     "subtype": "table",
     "extensions": [".gtable"],
-    // "mime": "application/vnd.google-apps.fusiontable"
+    "mime": "application/vnd.google-apps.fusiontable"
   },
   {
     "type": "hosted",
@@ -439,7 +446,7 @@
     "translationKey": "GLINK_DOCUMENT_FILE_TYPE",
     "subtype": "glink",
     "extensions": [".glink"],
-    // "mime": "application/vnd.google-apps.shortcut"
+    "mime": "application/vnd.google-apps.shortcut"
   },
   {
     "type": "hosted",
@@ -447,7 +454,7 @@
     "translationKey": "GFORM_DOCUMENT_FILE_TYPE",
     "subtype": "form",
     "extensions": [".gform"],
-    // "mime": "application/vnd.google-apps.form"
+    "mime": "application/vnd.google-apps.form"
   },
   {
     "type": "hosted",
@@ -455,7 +462,7 @@
     "translationKey": "GMAP_DOCUMENT_FILE_TYPE",
     "subtype": "map",
     "extensions": [".gmap"],
-    // "mime": "application/vnd.google-apps.map"
+    "mime": "application/vnd.google-apps.map"
   },
   {
     "type": "hosted",
@@ -463,7 +470,7 @@
     "translationKey": "GSITE_DOCUMENT_FILE_TYPE",
     "subtype": "site",
     "extensions": [".gsite"],
-    // "mime": "application/vnd.google-apps.site"
+    "mime": "application/vnd.google-apps.site"
   },
 
   // Others
diff --git a/ui/file_manager/base/gn/file_types_data.cc.jinja b/ui/file_manager/base/gn/file_types_data.cc.jinja
index 57078361..8a83c8f 100644
--- a/ui/file_manager/base/gn/file_types_data.cc.jinja
+++ b/ui/file_manager/base/gn/file_types_data.cc.jinja
@@ -19,4 +19,12 @@
 {%- endfor %}
 });
 
+const base::flat_set<std::string> kDocumentMIMETypes({
+{%- for file_type in model.file_types %}
+{%- if file_type['mime'] and file_type['type'] in ['text', 'hosted', 'document'] %}
+  "{{ file_type['mime'] }}",
+{%- endif %}
+{%- endfor %}
+});
+
 }  // namespace files_types_data
diff --git a/ui/file_manager/base/gn/file_types_data.h.jinja b/ui/file_manager/base/gn/file_types_data.h.jinja
index 3131df0d..74a1001 100644
--- a/ui/file_manager/base/gn/file_types_data.h.jinja
+++ b/ui/file_manager/base/gn/file_types_data.h.jinja
@@ -15,6 +15,7 @@
 #include <string>
 
 #include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
 
 namespace file_types_data {
 
@@ -23,6 +24,10 @@
 //   {".jpg", "image/jpeg"},
 extern const base::flat_map<std::string, std::string> kExtensionToMIME;
 
+// A set includes all the MIME types for Documents, e.g.:
+//   {"text/plain", "application/vnd.google-apps.document", "application/pdf"}
+extern const base::flat_set<std::string> kDocumentMIMETypes;
+
 }  // namespace file_types_data
 
 #endif  // {{ header_guard }}
diff --git a/ui/file_manager/file_manager/background/js/mock_volume_manager.js b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
index d0ecaa4..c3106900 100644
--- a/ui/file_manager/file_manager/background/js/mock_volume_manager.js
+++ b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
@@ -193,6 +193,12 @@
       type, volumeId, label, devicePath, providerId, remoteMountPath) {
     const fileSystem = new MockFileSystem(volumeId, 'filesystem:' + volumeId);
 
+    let diskFileSystemType = VolumeManagerCommon.FileSystemType.UNKNOWN;
+    if (devicePath && devicePath.startsWith('fusebox')) {
+      diskFileSystemType =
+          /** @type VolumeManagerCommon.FileSystemType */ ('fusebox');
+    }
+
     // If there's no label set it to volumeId to make it shorter to write
     // tests.
     const volumeInfo = new VolumeInfoImpl(
@@ -209,10 +215,10 @@
         false,                                      // configurable
         false,                                      // watchable
         VolumeManagerCommon.Source.NETWORK,         // source
-        VolumeManagerCommon.FileSystemType.UNKNOWN,  // diskFileSystemType
-        {},                                          // iconSet
-        '',                                          // driveLabel
-        remoteMountPath);                            // remoteMountPath
+        diskFileSystemType,                         // diskFileSystemType
+        {},                                         // iconSet
+        '',                                         // driveLabel
+        remoteMountPath);                           // remoteMountPath
 
     return volumeInfo;
   }
diff --git a/ui/file_manager/file_manager/common/js/BUILD.gn b/ui/file_manager/file_manager/common/js/BUILD.gn
index 339ccb6e..d2ad43c 100644
--- a/ui/file_manager/file_manager/common/js/BUILD.gn
+++ b/ui/file_manager/file_manager/common/js/BUILD.gn
@@ -204,6 +204,16 @@
   ]
 }
 
+js_unittest("filtered_volume_manager_unittest.m") {
+  deps = [
+    ":filtered_volume_manager",
+    "//chrome/test/data/webui:chai_assert",
+    "//ui/file_manager/file_manager/background/js:mock_volume_manager",
+    "//ui/file_manager/file_manager/common/js:volume_manager_types",
+    "//ui/webui/resources/js:load_time_data.m",
+  ]
+}
+
 # These importer files actually belong here. Nothing outside the Files app uses
 # them, so restrict visibility. TODO(tapted): Simplify visibility when
 # everything else moves to //ui/file_manager/base.
@@ -375,6 +385,7 @@
   deps = [
     ":file_type_unittest.m",
     ":files_app_entry_types_unittest.m",
+    ":filtered_volume_manager_unittest.m",
     ":importer_common_unittest.m",
     ":lru_cache_unittest.m",
     ":storage_adapter_unittest.m",
diff --git a/ui/file_manager/file_manager/common/js/filtered_volume_manager.js b/ui/file_manager/file_manager/common/js/filtered_volume_manager.js
index e83e8bb..b3bfd065 100644
--- a/ui/file_manager/file_manager/common/js/filtered_volume_manager.js
+++ b/ui/file_manager/file_manager/common/js/filtered_volume_manager.js
@@ -107,10 +107,11 @@
     this.volumeManagerGetter_ = volumeManagerGetter;
 
     /**
-     * Array of Files app mode dependent volume filter names.
-     * @private @const {!Array<string>}
+     * True if |volumeFilter| contains the 'fusebox-only' filter. SelectFileAsh
+     * (file picker) sets this filter.
+     * @private @const {boolean}
      */
-    this.volumeFilter_ = volumeFilter;
+    this.isFuseBoxOnly_ = volumeFilter.includes('fusebox-only');
 
     /**
      * Tracks async initialization of volume manager.
@@ -126,13 +127,9 @@
    * disallowed for other restrictions. To check if a specific volume is allowed
    * or not, use isAllowedVolume_() instead.
    *
-   * TODO(crbug.com/1292825): The above is cleary confusing. This API is only
-   * used by Drive, to work if the 'drive-connection-changed' event should be
-   * emitted, or blocking access to the drive connection state. Make this API
-   * Drive-specific to remove the confusion.
-   *
    * @param {VolumeManagerCommon.VolumeType} volumeType
    * @return {boolean}
+   * @private
    */
   isAllowedVolumeType_(volumeType) {
     switch (this.allowedPaths_) {
@@ -146,10 +143,22 @@
   }
 
   /**
+   * True if the volume |diskFileSystemType| is a fusebox file system.
+   *
+   * @param {string} diskFileSystemType Volume diskFileSystemType.
+   * @return {boolean}
+   * @private
+   */
+  isFuseBoxFileSystem(diskFileSystemType) {
+    return diskFileSystemType === 'fusebox';
+  }
+
+  /**
    * Checks if a volume is allowed.
    *
    * @param {!VolumeInfo} volumeInfo
    * @return {boolean}
+   * @private
    */
   isAllowedVolume_(volumeInfo) {
     if (!volumeInfo.volumeType) {
@@ -160,9 +169,19 @@
       return false;
     }
 
-    // TODO(crbug.com/1292825): implement fusebox-only filter here.
+    // If the volume type is supported by fusebox, we have to decide whether
+    // to use the fusebox or non-fusebox version in the UI.
 
-    // TODO(crbug.com/1292825): maybe remove isAllowedVolumeType_ use here.
+    if (this.isFuseBoxOnly_) {
+      // SelectFileAsh requires native volumes. Note: DocumentsProvider and
+      // FSPs return false here, until they are implemented in the Fusebox.
+      return this.isFuseBoxFileSystem(volumeInfo.diskFileSystemType) ||
+          VolumeManagerCommon.VolumeType.isNative(volumeInfo.volumeType);
+    } else if (this.isFuseBoxFileSystem(volumeInfo.diskFileSystemType)) {
+      // Normal Files app: remove fusebox volumes.
+      return false;
+    }
+
     if (!this.isAllowedVolumeType_(volumeInfo.volumeType)) {
       return false;
     }
diff --git a/ui/file_manager/file_manager/common/js/filtered_volume_manager_unittest.m.js b/ui/file_manager/file_manager/common/js/filtered_volume_manager_unittest.m.js
new file mode 100644
index 0000000..02bc3a1
--- /dev/null
+++ b/ui/file_manager/file_manager/common/js/filtered_volume_manager_unittest.m.js
@@ -0,0 +1,199 @@
+// 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.
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
+
+import {MockVolumeManager} from '../../background/js/mock_volume_manager.js';
+import {VolumeInfo} from '../../externs/volume_info.js';
+import {VolumeInfoList} from '../../externs/volume_info_list.js';
+
+import {FilteredVolumeManager} from './filtered_volume_manager.js';
+import {AllowedPaths, VolumeManagerCommon} from './volume_manager_types.js';
+
+/**
+ * Setup the test components.
+ */
+export function setUp() {
+  loadTimeData.getString = id => id;
+  loadTimeData.resetForTesting({});
+}
+
+/**
+ * Create a new MockVolumeManager for each test fixture.
+ * @return {!MockVolumeManager}
+ */
+function createMockVolumeManager() {
+  // Create mock volume manager.
+  const volumeManager = new MockVolumeManager();
+
+  // Patch its addEventListener, removeEventListener methods to make them log
+  // (instead of throw) "not implemented", since those throw events break the
+  // FilteredVolumeManager initialization code in tests.
+  volumeManager.addEventListener = (type, handler) => {
+    console.log('MockVolumeManager.addEventListener not implemented');
+  };
+  volumeManager.removeEventListener = (type, handler) => {
+    console.log('MockVolumeManager.removeEventListener not implemented');
+  };
+
+  return volumeManager;
+}
+
+/**
+ * Tests FilteredVolumeManager default volume filter.
+ */
+export function testVolumeDefaultFilter(done) {
+  // Create mock volume manager.
+  const volumeManager = createMockVolumeManager();
+
+  // Get `DRIVE` volume.
+  const driveVolumeInfo = volumeManager.getCurrentProfileVolumeInfo(
+      VolumeManagerCommon.VolumeType.DRIVE);
+  assert(driveVolumeInfo);
+
+  // Get `DOWNLOADS` volume.
+  const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo(
+      VolumeManagerCommon.VolumeType.DOWNLOADS);
+  assert(downloadsVolumeInfo);
+
+  // Add `MTP` volume.
+  const mtpVolumeInfo = MockVolumeManager.createMockVolumeInfo(
+      VolumeManagerCommon.VolumeType.MTP, 'mtpNormalVolumeId',
+      'MTP normal volume', 'mtp-path');
+  volumeManager.volumeInfoList.add(mtpVolumeInfo);
+
+  // Add `MTP` fusebox volume.
+  const mtpFuseBoxVolumeInfo = MockVolumeManager.createMockVolumeInfo(
+      VolumeManagerCommon.VolumeType.MTP, 'mtpFuseBoxVolumeId',
+      'MTP fusebox volume', 'fusebox/mtp-path');
+  volumeManager.volumeInfoList.add(mtpFuseBoxVolumeInfo);
+
+  // Add `DOCUMENTS_PROVIDER` volume.
+  const documentsProviderVolumeInfo = MockVolumeManager.createMockVolumeInfo(
+      VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, 'adpNormalVolumeId',
+      'Documents provider normal volume', 'documents-provider-path');
+  volumeManager.volumeInfoList.add(documentsProviderVolumeInfo);
+
+  // Check: volumeManager.volumeInfoList should have 5 volumes.
+  assertEquals(5, volumeManager.volumeInfoList.length);
+
+  /**
+   * Default volume filter []: FilteredVolumeManager should remove fusebox
+   * volumes from the files app UI.
+   */
+  const filteredVolumeManager = new FilteredVolumeManager(
+      AllowedPaths.ANY_PATH_OR_URL, false, Promise.resolve(volumeManager), []);
+
+  filteredVolumeManager.ensureInitialized(() => {
+    // Check: filteredVolumeManager.volumeInfoList should have 4 volumes.
+    assertEquals(4, filteredVolumeManager.volumeInfoList.length);
+
+    // Get `DRIVE` volume.
+    let info = filteredVolumeManager.volumeInfoList.item(0);
+    assert(info, 'volume[0] DRIVE expected');
+    assertEquals(VolumeManagerCommon.VolumeType.DRIVE, info.volumeType);
+
+    // Get `DOWNLOADS` volume.
+    info = filteredVolumeManager.volumeInfoList.item(1);
+    assert(info, 'volume[1] DOWNLOADS expected');
+    assertEquals(VolumeManagerCommon.VolumeType.DOWNLOADS, info.volumeType);
+
+    // Get `MTP` volume.
+    info = filteredVolumeManager.volumeInfoList.item(2);
+    assert(info, 'volume[2] MTP expected');
+    assertEquals(VolumeManagerCommon.VolumeType.MTP, info.volumeType);
+
+    // Check: the MTP volume should be a normal volume.
+    assertEquals('MTP normal volume', info.label);
+    assertEquals('mtp-path', info.devicePath);
+
+    // Get `DOCUMENTS_PROVIDER` volume.
+    info = filteredVolumeManager.volumeInfoList.item(3);
+    assert(info, 'volume[3] DOCUMENTS_PROVIDER expected');
+    assertEquals(
+        VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, info.volumeType);
+
+    // Check: the DOCUMENTS_PROVIDER volume should be a normal volume.
+    assertEquals('Documents provider normal volume', info.label);
+    assertEquals('documents-provider-path', info.devicePath);
+
+    done();
+  });
+}
+
+/**
+ * Tests FilteredVolumeManager 'fusebox-only' volume filter.
+ */
+export function testVolumeFuseboxOnlyFilter(done) {
+  // Create mock volume manager.
+  const volumeManager = createMockVolumeManager();
+
+  // Get `DRIVE` volume.
+  const driveVolumeInfo = volumeManager.getCurrentProfileVolumeInfo(
+      VolumeManagerCommon.VolumeType.DRIVE);
+  assert(driveVolumeInfo);
+
+  // Get `DOWNLOADS` volume.
+  const downloadsVolumeInfo = volumeManager.getCurrentProfileVolumeInfo(
+      VolumeManagerCommon.VolumeType.DOWNLOADS);
+  assert(downloadsVolumeInfo);
+
+  // Add `MTP` volume.
+  const mtpVolumeInfo = MockVolumeManager.createMockVolumeInfo(
+      VolumeManagerCommon.VolumeType.MTP, 'mtpNormalVolumeId',
+      'MTP normal volume', 'mtp-path');
+  volumeManager.volumeInfoList.add(mtpVolumeInfo);
+
+  // Add `MTP` fusebox volume.
+  const mtpFuseBoxVolumeInfo = MockVolumeManager.createMockVolumeInfo(
+      VolumeManagerCommon.VolumeType.MTP, 'mtpFuseBoxVolumeId',
+      'MTP fusebox volume', 'fusebox/mtp-path');
+  volumeManager.volumeInfoList.add(mtpFuseBoxVolumeInfo);
+
+  // Add `DOCUMENTS_PROVIDER` volume.
+  const documentsProviderVolumeInfo = MockVolumeManager.createMockVolumeInfo(
+      VolumeManagerCommon.VolumeType.DOCUMENTS_PROVIDER, 'adpNormalVolumeId',
+      'Documents provider normal volume', 'documents-provider-path');
+  volumeManager.volumeInfoList.add(documentsProviderVolumeInfo);
+
+  // Check: volumeManager.volumeInfoList should have 5 volumes.
+  assertEquals(5, volumeManager.volumeInfoList.length);
+
+  /**
+   * SelectFileAsh sets ['fusebox-only'] filter: FilteredVolumeManager should
+   * only show native and fusebox volumes in the files app UI.
+   */
+  const filteredVolumeManager = new FilteredVolumeManager(
+      AllowedPaths.ANY_PATH_OR_URL, false, Promise.resolve(volumeManager),
+      ['fusebox-only']);
+
+  filteredVolumeManager.ensureInitialized(() => {
+    // Check: filteredVolumeManager.volumeInfoList should have 3 volumes.
+    assertEquals(3, filteredVolumeManager.volumeInfoList.length);
+
+    // Get `DRIVE` volume.
+    let info = filteredVolumeManager.volumeInfoList.item(0);
+    assert(info, 'volume[0] DRIVE expected');
+    assertEquals(VolumeManagerCommon.VolumeType.DRIVE, info.volumeType);
+
+    // Get `DOWNLOADS` volume.
+    info = filteredVolumeManager.volumeInfoList.item(1);
+    assert(info, 'volume[1] DOWNLOADS expected');
+    assertEquals(VolumeManagerCommon.VolumeType.DOWNLOADS, info.volumeType);
+
+    // Get `MTP` volume.
+    info = filteredVolumeManager.volumeInfoList.item(2);
+    assert(info, 'volume[2] MTP expected');
+    assertEquals(VolumeManagerCommon.VolumeType.MTP, info.volumeType);
+
+    // Check: the MTP volume should be a fusebox volume.
+    assertEquals('MTP fusebox volume', info.label);
+    assertEquals('fusebox/mtp-path', info.devicePath);
+    assertEquals('fusebox', info.diskFileSystemType);
+
+    done();
+  });
+}
diff --git a/ui/file_manager/file_manager/foreground/css/combobutton.css b/ui/file_manager/file_manager/foreground/css/combobutton.css
index 24c5016..c47c53b 100644
--- a/ui/file_manager/file_manager/foreground/css/combobutton.css
+++ b/ui/file_manager/file_manager/foreground/css/combobutton.css
@@ -5,7 +5,6 @@
 .dialog-header button.combobutton {
   align-items: stretch;
   background: transparent;
-  border: none;
   border-radius: 3px;
   cursor: pointer;
   display: flex;
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index e869d919..206db1fe 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -126,7 +126,7 @@
 
 #directory-tree .tree-row > .file-row {
   align-items: center;
-  border: 1px solid transparent;
+  border: 2px solid transparent;
   border-inline-start-width: 0 !important;
   border-radius: 0 20px 20px 0;
   box-sizing: border-box;
@@ -274,7 +274,7 @@
 }
 
 html.focus-outline-visible #directory-tree:focus .tree-row[selected] > .file-row {
-  border: 1px solid var(--cros-focus-ring-color);
+  border: 2px solid var(--cros-focus-ring-color);
 }
 
 #directory-tree .tree-row[active] > .file-row {
@@ -435,7 +435,7 @@
   flex: none;
   flex-direction: row;
   font-size: 14px;
-  height: 56px;
+  height: 57px;
   overflow: hidden;
   transition: background 220ms ease;
 }
@@ -463,7 +463,7 @@
   --ink-color: var(--cros-ripple-color);
   --ripple-opacity: 100%;
   --text-color: currentColor;
-  border: 1px solid transparent;
+  border: 2px solid transparent;
   border-radius: 18px;
   box-sizing: border-box;
   color: var(--cros-text-color-primary);
@@ -508,7 +508,7 @@
 
 html.focus-outline-visible .dialog-header.files-ng cr-button:not(:active):focus,
 html.focus-outline-visible .dialog-header.files-ng button:not(:active):focus {
-  border: 1px solid var(--cros-focus-ring-color);
+  border: 2px solid var(--cros-focus-ring-color);
 }
 
 .dialog-header files-ripple,
@@ -908,13 +908,13 @@
 
 /* TODO(adanilo) document the calc() reason. */
 body.files-ng #cancel-selection-button {
-  border: 1px solid transparent;
+  border: 2px solid transparent;
   margin-inline-start: calc(10px + 1px);
   width: 36px;
 }
 
 html.focus-outline-visible body.files-ng #cancel-selection-button:focus:not([tabindex='-1']):not(:active) {
-  border: 1px solid var(--cros-icon-color-prominent);
+  border: 2px solid var(--cros-icon-color-prominent);
 }
 
 body.files-ng #cancel-selection-button > span#cancel-selection-label {
diff --git a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js
index e352512a..86055ef0a7 100644
--- a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller.js
@@ -43,6 +43,10 @@
         chrome.fileManagerPrivate.RecentFileType.VIDEO,
         'MEDIA_VIEW_VIDEOS_ROOT_LABEL'
       ],
+      [
+        chrome.fileManagerPrivate.RecentFileType.DOCUMENT,
+        'MEDIA_VIEW_DOCUMENTS_ROOT_LABEL',
+      ]
     ]);
 
     /**
@@ -97,6 +101,13 @@
     this.videoFilterButton_ = this.createFilterButton_(
         chrome.fileManagerPrivate.RecentFileType.VIDEO);
 
+    /**
+     * @private {!HTMLElement}
+     * @const
+     */
+    this.documentFilterButton_ = this.createFilterButton_(
+        chrome.fileManagerPrivate.RecentFileType.DOCUMENT);
+
     this.directoryModel_.addEventListener(
         'directory-changed', this.onCurrentDirectoryChanged_.bind(this));
 
@@ -117,10 +128,11 @@
      */
     const FileTypeFiltersForUMA =
         /** @type {!Array<!chrome.fileManagerPrivate.RecentFileType>} */ ([
-          chrome.fileManagerPrivate.RecentFileType.ALL,    // 0
-          chrome.fileManagerPrivate.RecentFileType.AUDIO,  // 1
-          chrome.fileManagerPrivate.RecentFileType.IMAGE,  // 2
-          chrome.fileManagerPrivate.RecentFileType.VIDEO,  // 3
+          chrome.fileManagerPrivate.RecentFileType.ALL,       // 0
+          chrome.fileManagerPrivate.RecentFileType.AUDIO,     // 1
+          chrome.fileManagerPrivate.RecentFileType.IMAGE,     // 2
+          chrome.fileManagerPrivate.RecentFileType.VIDEO,     // 3
+          chrome.fileManagerPrivate.RecentFileType.DOCUMENT,  // 4
         ]);
     Object.freeze(FileTypeFiltersForUMA);
     metrics.recordEnum('Recent.FilterByType', fileType, FileTypeFiltersForUMA);
@@ -252,8 +264,11 @@
   updateButtonActiveStates_() {
     const currentFilter = this.recentEntry_.recentFileType;
     const buttons = [
-      this.allFilterButton_, this.audioFilterButton_, this.imageFilterButton_,
-      this.videoFilterButton_
+      this.allFilterButton_,
+      this.audioFilterButton_,
+      this.imageFilterButton_,
+      this.videoFilterButton_,
+      this.documentFilterButton_,
     ];
     buttons.forEach(button => {
       const fileTypeFilter =
diff --git a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.m.js b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.m.js
index 94cc1a8..e3ce355 100644
--- a/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.m.js
+++ b/ui/file_manager/file_manager/foreground/js/file_type_filters_controller_unittest.m.js
@@ -50,6 +50,8 @@
  */
 metrics.recordEnum = function(name, value, opt_validValues) {};
 
+const TOTAL_FILTER_BUTTON_COUNT = 5;
+
 export function setUp() {
   // Mock loadTimeData strings.
   loadTimeData.resetForTesting({
@@ -57,6 +59,7 @@
     MEDIA_VIEW_AUDIO_ROOT_LABEL: 'Audio',
     MEDIA_VIEW_IMAGES_ROOT_LABEL: 'Images',
     MEDIA_VIEW_VIDEOS_ROOT_LABEL: 'Videos',
+    MEDIA_VIEW_DOCUMENTS_ROOT_LABEL: 'Documents',
     RECENT_VIEW_FILTER_ON: 'on',
     RECENT_VIEW_FILTER_OFF: 'off',
     RECENT_VIEW_FILTER_RESET: 'reset',
@@ -77,6 +80,7 @@
         AUDIO: 'audio',
         IMAGE: 'image',
         VIDEO: 'video',
+        DOCUMENT: 'document',
       },
     },
   };
@@ -138,12 +142,13 @@
  */
 export function testCreatedButtonLabels() {
   const buttons = container.children;
-  assertEquals(buttons.length, 4);
+  assertEquals(buttons.length, TOTAL_FILTER_BUTTON_COUNT);
 
   assertEquals(buttons[0].textContent, 'All');
   assertEquals(buttons[1].textContent, 'Audio');
   assertEquals(buttons[2].textContent, 'Images');
   assertEquals(buttons[3].textContent, 'Videos');
+  assertEquals(buttons[4].textContent, 'Documents');
 }
 
 /**
@@ -152,12 +157,13 @@
  */
 export function testButtonInitialActiveState() {
   const buttons = container.children;
-  assertEquals(buttons.length, 4);
+  assertEquals(buttons.length, TOTAL_FILTER_BUTTON_COUNT);
 
   assertTrue(buttons[0].classList.contains('active'));
   assertFalse(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 }
 
 /**
@@ -167,7 +173,7 @@
  */
 export function testButtonToggleState() {
   const buttons = container.children;
-  assertEquals(buttons.length, 4);
+  assertEquals(buttons.length, TOTAL_FILTER_BUTTON_COUNT);
 
   // State change: inactive -> active -> inactive.
   assertFalse(buttons[1].classList.contains('active'));
@@ -188,7 +194,7 @@
  */
 export function testOnlyOneButtonCanActive() {
   const buttons = container.children;
-  assertEquals(buttons.length, 4);
+  assertEquals(buttons.length, TOTAL_FILTER_BUTTON_COUNT);
 
   assertTrue(buttons[0].classList.contains('active'));
 
@@ -198,24 +204,28 @@
   assertTrue(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 
   buttons[2].click();
   assertFalse(buttons[0].classList.contains('active'));
   assertFalse(buttons[1].classList.contains('active'));
   assertTrue(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 
   buttons[3].click();
   assertFalse(buttons[0].classList.contains('active'));
   assertFalse(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertTrue(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 
   buttons[0].click();
   assertTrue(buttons[0].classList.contains('active'));
   assertFalse(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 }
 
 /**
@@ -236,7 +246,7 @@
  */
 export function testActiveButtonIsResetOnLeavingRecents() {
   const buttons = container.children;
-  assertEquals(buttons.length, 4);
+  assertEquals(buttons.length, TOTAL_FILTER_BUTTON_COUNT);
 
   directoryModel.changeDirectoryEntry(recentEntry);
 
@@ -245,6 +255,7 @@
   assertTrue(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 
   // Changing directory to the same Recent doesn't reset states.
   directoryModel.changeDirectoryEntry(recentEntry);
@@ -252,18 +263,21 @@
   assertTrue(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 
   directoryModel.changeDirectoryEntry(myFilesEntry);
   assertTrue(buttons[0].classList.contains('active'));
   assertFalse(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 
   directoryModel.changeDirectoryEntry(recentEntry);
   assertTrue(buttons[0].classList.contains('active'));
   assertFalse(buttons[1].classList.contains('active'));
   assertFalse(buttons[2].classList.contains('active'));
   assertFalse(buttons[3].classList.contains('active'));
+  assertFalse(buttons[4].classList.contains('active'));
 }
 
 /**
@@ -273,7 +287,7 @@
  */
 export function testAppliedFilters() {
   const buttons = container.children;
-  assertEquals(buttons.length, 4);
+  assertEquals(buttons.length, TOTAL_FILTER_BUTTON_COUNT);
 
   directoryModel.changeDirectoryEntry(recentEntry);
 
@@ -305,6 +319,13 @@
   assertTrue(window.isRescanCalled);
   window.isRescanCalled = false;
 
+  buttons[4].click();
+  assertEquals(
+      recentEntry.recentFileType,
+      chrome.fileManagerPrivate.RecentFileType.DOCUMENT);
+  assertTrue(window.isRescanCalled);
+  window.isRescanCalled = false;
+
   buttons[0].click();
   assertEquals(
       recentEntry.recentFileType, chrome.fileManagerPrivate.RecentFileType.ALL);
diff --git a/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.html b/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.html
index d350432..d521e05b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.html
+++ b/ui/file_manager/file_manager/foreground/js/ui/breadcrumb.html
@@ -38,7 +38,7 @@
   button {
     /* don't use browser's background-color. */
     background-color: unset;
-    border: 1px solid transparent;
+    border: 2px solid transparent;
     border-radius: 4px;
     color: var(--cros-text-color-primary);
     cursor: pointer;
@@ -122,7 +122,7 @@
 
   :host-context(:root.focus-outline-visible) > button:focus {
     background-color: unset;
-    border: 1px solid var(--cros-icon-color-prominent);
+    border: 2px solid var(--cros-icon-color-prominent);
   }
 
   :host([checked]) button[elider] {
diff --git a/ui/file_manager/integration_tests/file_manager/recents.js b/ui/file_manager/integration_tests/file_manager/recents.js
index 21e4e5a4..9eafb5f 100644
--- a/ui/file_manager/integration_tests/file_manager/recents.js
+++ b/ui/file_manager/integration_tests/file_manager/recents.js
@@ -29,6 +29,19 @@
   typeText: 'QuickTime video'
 });
 
+// Test entry for a recently-modified document file.
+const RECENTLY_MODIFIED_DOCUMENT = new TestEntryInfo({
+  type: EntryType.FILE,
+  sourceFileName: 'text.docx',
+  targetPath: 'word.docx',
+  mimeType: 'application/vnd.openxmlformats-officedocument' +
+      '.wordprocessingml.document',
+  lastModifiedTime: 'Jul 4, 2038, 10:35 AM',
+  nameText: 'word.docx',
+  sizeText: '9 KB',
+  typeText: 'Word document'
+});
+
 /**
  * Enum for supported recent filter types.
  * @enum {string}
@@ -38,6 +51,7 @@
   AUDIO: 'audio',
   IMAGE: 'image',
   VIDEO: 'video',
+  DOCUMENT: 'document',
 };
 
 /**
@@ -63,6 +77,7 @@
     [RecentFilterType.AUDIO]: '/Audio',
     [RecentFilterType.IMAGE]: '/Images',
     [RecentFilterType.VIDEO]: '/Videos',
+    [RecentFilterType.DOCUMENT]: '/Documents',
   };
 
   if (await isFiltersInRecentsEnabled()) {
@@ -160,6 +175,18 @@
 }
 
 /**
+ * Opens the Recent Document folder and checks the expected entries are
+ * showing there.
+ *
+ * @param {string} appId Files app windowId.
+ * @param {!Array<!TestEntryInfo>} expectedEntries Expected file entries.
+ */
+async function verifyRecentDocuments(appId, expectedEntries) {
+  await navigateToRecent(appId, RecentFilterType.DOCUMENT);
+  await verifyCurrentEntries(appId, expectedEntries);
+}
+
+/**
  * Verifies the breadcrumb has the expected path.
  *
  * @param {string} appId Files app windowId.
@@ -403,6 +430,32 @@
 };
 
 /**
+ * Tests that the document file entries populated in Downloads folder recently
+ * will be displayed in Recent Document folder.
+ */
+testcase.recentDocumentsDownloads = async () => {
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [RECENTLY_MODIFIED_DOCUMENT], []);
+  await verifyRecentDocuments(appId, [RECENTLY_MODIFIED_DOCUMENT]);
+};
+
+/**
+ * Tests that if the video file entries with MIME type are being populated
+ * in both Downloads folder and My Drive folder, the file entries will be
+ * displayed in Recent Document folder regardless of whether it's from Downloads
+ * or My Drive.
+ */
+testcase.recentDocumentsDownloadsAndDrive = async () => {
+  const appId = await setupAndWaitUntilReady(
+      RootPath.DOWNLOADS, [RECENTLY_MODIFIED_DOCUMENT],
+      [RECENTLY_MODIFIED_DOCUMENT]);
+  // RECENTLY_MODIFIED_DOCUMENT exists in both local and drive folder, the
+  // file will appear twice in the result.
+  await verifyRecentDocuments(
+      appId, [RECENTLY_MODIFIED_DOCUMENT, RECENTLY_MODIFIED_DOCUMENT]);
+};
+
+/**
  * Tests if an active filter button is clicked again, it will become inactive
  * and the "All" filter button will become active and focus.
  */
diff --git a/ui/ozone/platform/x11/x11_window.cc b/ui/ozone/platform/x11/x11_window.cc
index 03db677e..3f696838 100644
--- a/ui/ozone/platform/x11/x11_window.cc
+++ b/ui/ozone/platform/x11/x11_window.cc
@@ -2168,9 +2168,14 @@
 
 void X11Window::OnConfigureEvent(const x11::ConfigureNotifyEvent& configure,
                                  bool send_event) {
-  DCHECK_EQ(xwindow_, configure.window);
   DCHECK_EQ(xwindow_, configure.event);
 
+  // ConfigureNotifyEvent could be received for child windows. Ignore events for
+  // child windows.
+  if (xwindow_ != configure.window) {
+    return;
+  }
+
   if (pending_counter_value_) {
     DCHECK(!configure_counter_value_);
     configure_counter_value_ = pending_counter_value_;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 9da9c749..cb15d61 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -2546,12 +2546,6 @@
       MenuRunner::HAS_MNEMONICS | MenuRunner::CONTEXT_MENU);
 }
 
-void Textfield::InvalidateContextMenu() {
-  // Ensure that the Runner doesn't outlive the Model.
-  context_menu_runner_.reset();
-  context_menu_contents_.reset();
-}
-
 bool Textfield::ImeEditingAllowed() const {
   // Disallow input method editing of password fields.
   ui::TextInputType t = GetTextInputType();
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index 849cf82..46966d4f 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -525,11 +525,6 @@
   // Update the cursor position in the text field.
   void UpdateCursorViewPosition();
 
-  // If there's an existing context menu, invalidate it, maybe closing it if
-  // it's showing. This is required if part of the context menu's model is about
-  // to be destroyed.
-  void InvalidateContextMenu();
-
  private:
   friend class TextfieldTestApi;
 
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts
index 17920f1..fc8723f 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.ts
@@ -13,7 +13,7 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {CrDialogElement} from '../../cr_elements/cr_dialog/cr_dialog.m.js';
-import {assertNotReached} from '../../js/assert.m.js';
+import {assertNotReached} from '../../js/assert_ts.js';
 import {I18nMixin} from '../../js/i18n_mixin.js';
 import {loadTimeData} from '../../js/load_time_data.m.js';
 
@@ -67,9 +67,9 @@
         return getString('certificateManagerDeleteCaTitle');
       case CertificateType.OTHER:
         return getString('certificateManagerDeleteOtherTitle');
+      default:
+        assertNotReached();
     }
-    assertNotReached();
-    return '';
   }
 
   private getDescriptionText_(): string {
@@ -83,9 +83,9 @@
         return getString('certificateManagerDeleteCaDescription');
       case CertificateType.OTHER:
         return '';
+      default:
+        assertNotReached();
     }
-    assertNotReached();
-    return '';
   }
 
   private onCancelTap_() {
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts b/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
index e0e339be..5974094 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.ts
@@ -13,7 +13,7 @@
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assertNotReached} from '../../js/assert.m.js';
+import {assertNotReached} from '../../js/assert_ts.js';
 import {I18nMixin} from '../../js/i18n_mixin.js';
 import {loadTimeData} from '../../js/load_time_data.m.js';
 
@@ -94,9 +94,9 @@
         return this.i18n('certificateManagerAuthoritiesDescription');
       case CertificateType.OTHER:
         return this.i18n('certificateManagerOthersDescription');
+      default:
+        assertNotReached();
     }
-
-    assertNotReached();
   }
 
   private canImport_(): boolean {