diff --git a/.clang-tidy b/.clang-tidy
index a37936e4..431f88a4 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -66,7 +66,9 @@
       value:        base/containers/adapters.h
     # Exclude some third_party headers from modification as file paths are not
     # starting from repository root in replacement suggestion.
+    # 'build/linux/debian' excludes system headers as they don't have
+    # appropriate IWYU pragmas.
     # https://clang.llvm.org/extra/clang-tidy/checks/misc/include-cleaner.html
     - key: misc-include-cleaner.IgnoreHeaders
-      value:        (gmock/gmock|gtest/gtest|third_party).*
+      value:        (gmock/gmock|gtest/gtest|third_party|build/linux/debian).*
 ...
diff --git a/DEPS b/DEPS
index c7dd75f..bc1546c 100644
--- a/DEPS
+++ b/DEPS
@@ -284,7 +284,7 @@
   'screen_ai_windows_386': 'version:124.00',
 
   # siso CIPD package version.
-  'siso_version': 'git_revision:4524544994f4eac131378143f498ee4d0b7d1f36',
+  'siso_version': 'git_revision:df7a992166415f66f6958744f149f724df138879',
 
   # download libaom test data
   'download_libaom_testdata': False,
@@ -312,11 +312,11 @@
   # 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': '9b5453026ea3517aea3a40ae86f229af12a25435',
+  'v8_revision': '3c9fa12db3183a6f4ea53d2675adb66ea1194529',
   # 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': 'd1bb6ed8399dd12e79484f30f9e9ded95c25625a',
+  'angle_revision': '4e887491e14a924c8d22a6419af24b613ceb1fa9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -367,7 +367,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling HarfBuzz
   # and whatever else without interference from each other.
-  'harfbuzz_revision': '155015f4bec434ecc2f94621665844218f05ce51',
+  'harfbuzz_revision': 'bba0c0e27cf22244d6ffcd9dffc8e9ad1f4c1bc6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Emoji Segmenter
   # and whatever else without interference from each other.
@@ -383,7 +383,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
-  'chromium_variations_revision': '3097b9057e203656efcd2f6f04eb24198d0c5ec7',
+  'chromium_variations_revision': '1545704ff52cfb5119f3693c9a9e971594e9cb43',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -427,7 +427,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.
-  'quiche_revision': '690aed3796de57c7f846b8f02dcb4569f0b95f07',
+  'quiche_revision': 'ee237e96f18ef123af9992f74645a8a0ce9ef6ef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -471,7 +471,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.
-  'cros_components_revision': '961957ea3bcc7ee4121c604859bc9b495c352e79',
+  'cros_components_revision': '1985ff9dfd894b5cd958163bf9f4fde8716acbb4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -506,7 +506,7 @@
   'libcxx_revision':       'e3b94d0e5b86883fd77696bf10dc33ba250ba99b',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:b0c2742896b6b9f869dc0eb35ae4785cbf2a4512',
+  'gn_version': 'git_revision:df98b86690c83b81aedc909ded18857296406159',
 
   # ninja CIPD package version.
   # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja
@@ -1003,7 +1003,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'cdaa3f151a155c0c0a3e907ea53fc968343d7c98',
+    'dc4fa1175ee236ab899770bb9edddaecd4c482e1',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1459,13 +1459,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a9b9284faf89cd1a426371237df8307c328cc818',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e1385296c4ab4c7ee0a809676635b52d1df23b87',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'f1404dfce216fea06d8ec5b0380b6cca25704051',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '66623d94d619797383f9872fa1fd0f82e4508e74',
     'condition': 'checkout_src_internal',
   },
 
@@ -2132,7 +2132,7 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@862e523d9e3d530220f34aa2a660d46dc556eda4',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@a06b82c306ee85326c5d02518b88fbee434a34a0',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -2172,7 +2172,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '44bcaa233f372acd2ef70736b602f5364a9fa378',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '76c02aff4c76cc2d47c0a975333789978986e2b9',
+    Var('webrtc_git') + '/src.git' + '@' + '36ecffa1291884ca8fd5f2bc579028e9664f42e6',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -4409,7 +4409,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        'a4bca5f99ed1d6460e444e82d9359ef57826fa0b',
+        'd1a37ce7e9462472f99553f1d25f3f5a45eca986',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index abfd429..ca6e920 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -743,16 +743,6 @@
                 ContentFeatures.PREFETCH_REDIRECTS,
                 "Enables following redirects during speculation rules prefetch."),
         Flag.baseFeature(
-                BlinkFeatures.FIX_GESTURE_SCROLL_QUEUING_BUG,
-                "Queues gesture scrolls that do not hit a blocking handler, "
-                        + "while handling events that hit a blocking handler instantly"
-                        + " as this behaviour was flipped before this fix."),
-        Flag.baseFeature(
-                BlinkFeatures.QUEUE_BLOCKING_GESTURE_SCROLLS,
-                "Queues all gesture scrolls regardless of blocking status on the"
-                        + "compositor for more consistency and scrolling performance"
-                        + "improvement"),
-        Flag.baseFeature(
                 BaseFeatures.PARTITION_ALLOC_MEMORY_RECLAIMER,
                 "Enables PartitionAlloc's MemoryReclaimer, which tries decommitting unused "
                         + "system pages as much as possible so that other applications can "
diff --git a/ash/components/arc/session/BUILD.gn b/ash/components/arc/session/BUILD.gn
index acf5068..b905981 100644
--- a/ash/components/arc/session/BUILD.gn
+++ b/ash/components/arc/session/BUILD.gn
@@ -62,6 +62,7 @@
     "//chromeos/ash/components/dbus/spaced:spaced",
     "//chromeos/ash/components/dbus/upstart",
     "//chromeos/ash/components/memory:memory",
+    "//chromeos/ash/components/mojo_service_manager",
     "//chromeos/ash/components/system",
     "//chromeos/system",
     "//components/prefs:prefs",
@@ -70,6 +71,13 @@
     "//services/accessibility/android/public/mojom",
     "//ui/display/manager",
   ]
+
+  data_deps = [ ":mojo_service_manager_policy" ]
+}
+
+copy("mojo_service_manager_policy") {
+  sources = [ "arc_bridge_policy.jsonc" ]
+  outputs = [ "$root_out_dir/mojo_service_manager/{{source_file_part}}" ]
 }
 
 source_set("connection_holder") {
diff --git a/ash/components/arc/session/DEPS b/ash/components/arc/session/DEPS
index 98ef692..f7bbb717 100644
--- a/ash/components/arc/session/DEPS
+++ b/ash/components/arc/session/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chromeos/ash/components/memory",
+  "+chromeos/ash/components/mojo_service_manager",
   "+chromeos/system",
   "+services/accessibility/android/public/mojom",
 ]
diff --git a/ash/components/arc/session/OWNERS b/ash/components/arc/session/OWNERS
index 72ad4fb..4ffd5501 100644
--- a/ash/components/arc/session/OWNERS
+++ b/ash/components/arc/session/OWNERS
@@ -1,3 +1,5 @@
+per-file arc_bridge_policy.jsonc=sstan@chromium.org
+
 khmel@chromium.org
 lgcheng@google.com
 niwa@chromium.org
diff --git a/ash/components/arc/session/arc_bridge_host_impl.cc b/ash/components/arc/session/arc_bridge_host_impl.cc
index c3a0f3f..139f634 100644
--- a/ash/components/arc/session/arc_bridge_host_impl.cc
+++ b/ash/components/arc/session/arc_bridge_host_impl.cc
@@ -73,23 +73,35 @@
 #include "chromeos/components/payments/mojom/payment_app.mojom.h"
 #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
 #include "services/accessibility/android/public/mojom/accessibility_helper.mojom.h"
+#include "third_party/cros_system_api/mojo/service_constants.h"
 
 namespace arc {
 
-ArcBridgeHostImpl::ArcBridgeHostImpl(
-    ArcBridgeService* arc_bridge_service,
-    mojo::PendingReceiver<mojom::ArcBridgeHost> pending_receiver)
-    : arc_bridge_service_(arc_bridge_service),
-      receiver_(this, std::move(pending_receiver)) {
+ArcBridgeHostImpl::ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service)
+    : arc_bridge_service_(arc_bridge_service) {
   DCHECK(arc_bridge_service_);
-  receiver_.set_disconnect_handler(
-      base::BindOnce(&ArcBridgeHostImpl::OnClosed, base::Unretained(this)));
+  receivers_.set_disconnect_handler(base::BindRepeating(
+      &ArcBridgeHostImpl::OnClosed, base::Unretained(this)));
+
+  // Register ArcBridgeHost to Mojo Service Manager.
+  if (!ash::mojo_service_manager::IsServiceManagerBound()) {
+    LOG(ERROR) << "mojo service manager not bounded";
+    return;
+  }
+  ash::mojo_service_manager::GetServiceManagerProxy()->Register(
+      chromeos::mojo_services::kChromiumArcBridgeHost,
+      provider_receiver_.BindNewPipeAndPassRemote());
 }
 
 ArcBridgeHostImpl::~ArcBridgeHostImpl() {
   OnClosed();
 }
 
+void ArcBridgeHostImpl::AddReceiver(
+    mojo::PendingReceiver<mojom::ArcBridgeHost> pending_receiver) {
+  receivers_.Add(this, std::move(pending_receiver));
+}
+
 void ArcBridgeHostImpl::OnAccessibilityHelperInstanceReady(
     mojo::PendingRemote<ax::android::mojom::AccessibilityHelperInstance>
         accessibility_helper_remote) {
@@ -441,6 +453,13 @@
   return mojo_channels_.size();
 }
 
+void ArcBridgeHostImpl::Request(
+    chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
+    mojo::ScopedMessagePipeHandle receiver) {
+  receivers_.Add(
+      this, mojo::PendingReceiver<mojom::ArcBridgeHost>(std::move(receiver)));
+}
+
 void ArcBridgeHostImpl::OnClosed() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   VLOG(1) << "Mojo connection lost";
@@ -449,7 +468,7 @@
 
   // Close all mojo channels.
   mojo_channels_.clear();
-  receiver_.reset();
+  receivers_.Clear();
 
   arc_bridge_service_->ObserveAfterArcBridgeClosed();
 }
@@ -459,7 +478,7 @@
     ConnectionHolder<InstanceType, HostType>* holder,
     mojo::PendingRemote<InstanceType> remote) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  DCHECK(receiver_.is_bound());
+  DCHECK(receivers_.size());
   DCHECK(remote.is_valid());
 
   // Track |channel|'s lifetime via |mojo_channels_| so that it will be
diff --git a/ash/components/arc/session/arc_bridge_host_impl.h b/ash/components/arc/session/arc_bridge_host_impl.h
index 00068f8..76301d8 100644
--- a/ash/components/arc/session/arc_bridge_host_impl.h
+++ b/ash/components/arc/session/arc_bridge_host_impl.h
@@ -13,8 +13,11 @@
 #include "ash/components/arc/session/connection_holder.h"
 #include "base/memory/raw_ptr.h"
 #include "base/threading/thread_checker.h"
+#include "chromeos/ash/components/mojo_service_manager/connection.h"
+#include "chromeos/ash/components/mojo_service_manager/mojom/mojo_service_manager.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace arc {
@@ -23,6 +26,7 @@
 class MojoChannelBase;
 
 // Implementation of the ArcBridgeHost.
+// The ArcBridgeHost also registers in Mojo Service Manager by default.
 // The lifetime of ArcBridgeHost mojo channel is tied to this instance.
 // Also, any ARC related Mojo channel will be closed if ArcBridgeHost Mojo
 // channel is closed on error.
@@ -31,17 +35,20 @@
 // the raw pointer to the ArcBridgeService so that other services can access
 // to the pointer, and resets it on channel closing.
 // Note that ArcBridgeService must be alive while ArcBridgeHostImpl is alive.
-class ArcBridgeHostImpl : public mojom::ArcBridgeHost {
+class ArcBridgeHostImpl
+    : public mojom::ArcBridgeHost,
+      public chromeos::mojo_service_manager::mojom::ServiceProvider {
  public:
-  ArcBridgeHostImpl(
-      ArcBridgeService* arc_bridge_service,
-      mojo::PendingReceiver<mojom::ArcBridgeHost> pending_receiver);
+  explicit ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service);
 
   ArcBridgeHostImpl(const ArcBridgeHostImpl&) = delete;
   ArcBridgeHostImpl& operator=(const ArcBridgeHostImpl&) = delete;
 
   ~ArcBridgeHostImpl() override;
 
+  void AddReceiver(
+      mojo::PendingReceiver<mojom::ArcBridgeHost> pending_receiver);
+
   // ArcBridgeHost overrides.
   void OnAccessibilityHelperInstanceReady(
       mojo::PendingRemote<ax::android::mojom::AccessibilityHelperInstance>
@@ -189,6 +196,11 @@
   size_t GetNumMojoChannelsForTesting() const;
 
  private:
+  // chromeos::mojo_service_manager::mojom::ServiceProvider overrides.
+  void Request(
+      chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
+      mojo::ScopedMessagePipeHandle receiver) override;
+
   // Called when the bridge channel is closed. This typically only happens when
   // the ARC instance crashes.
   void OnClosed();
@@ -207,7 +219,11 @@
   // Owned by ArcServiceManager.
   const raw_ptr<ArcBridgeService> arc_bridge_service_;
 
-  mojo::Receiver<mojom::ArcBridgeHost> receiver_;
+  // The mojo receiver of service provider.
+  mojo::Receiver<chromeos::mojo_service_manager::mojom::ServiceProvider>
+      provider_receiver_{this};
+  // The mojo receiver set of ArcBridgeHost.
+  mojo::ReceiverSet<mojom::ArcBridgeHost> receivers_;
 
   // Put as a last member to ensure that any callback tied to the elements
   // is not invoked.
diff --git a/ash/components/arc/session/arc_bridge_host_impl_unittest.cc b/ash/components/arc/session/arc_bridge_host_impl_unittest.cc
index 205a5afd..82d30832 100644
--- a/ash/components/arc/session/arc_bridge_host_impl_unittest.cc
+++ b/ash/components/arc/session/arc_bridge_host_impl_unittest.cc
@@ -33,8 +33,9 @@
     mojo::PendingReceiver<mojom::ArcBridgeHost> pending_receiver;
     mojo::PendingRemote<mojom::ArcBridgeHost> pending_remote =
         pending_receiver.InitWithNewPipeAndPassRemote();
-    arc_bridge_host_impl_ = std::make_unique<ArcBridgeHostImpl>(
-        &bridge_service_, std::move(pending_receiver));
+    arc_bridge_host_impl_ =
+        std::make_unique<ArcBridgeHostImpl>(&bridge_service_);
+    arc_bridge_host_impl_->AddReceiver(std::move(pending_receiver));
     remote_.Bind(std::move(pending_remote));
   }
 
diff --git a/ash/components/arc/session/arc_bridge_policy.jsonc b/ash/components/arc/session/arc_bridge_policy.jsonc
new file mode 100644
index 0000000..754513b
--- /dev/null
+++ b/ash/components/arc/session/arc_bridge_policy.jsonc
@@ -0,0 +1,21 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Start with an array of policies.
+// Validate this file in CrOS device: mojo_service_manager --check_policy
+[
+    {
+        "user": "chronos",
+        "own": [
+            "ChromiumArcBridgeHost"
+        ]
+    },
+    {
+        // TODO(b/333309396): Use "user": "arc-mojo-proxy" instead.
+        "identity": "u:r:cros_arcvm_server_proxy:s0",
+        "request": [
+            "ChromiumArcBridgeHost"
+        ]
+    }
+]
diff --git a/ash/components/arc/session/arc_session_impl.cc b/ash/components/arc/session/arc_session_impl.cc
index 2cce2b6..03327771 100644
--- a/ash/components/arc/session/arc_session_impl.cc
+++ b/ash/components/arc/session/arc_session_impl.cc
@@ -194,6 +194,7 @@
   // Called when Mojo connection is established or canceled.
   // In case of cancel or error, |server_pipe| is invalid.
   void OnMojoConnected(ConnectMojoCallback callback,
+                       std::unique_ptr<ArcBridgeHostImpl> host,
                        mojo::ScopedMessagePipeHandle server_pipe);
 
   // Owned by ArcServiceManager.
@@ -237,7 +238,8 @@
       base::BindOnce(&ArcSessionDelegateImpl::ConnectMojoInternal,
                      std::move(socket_fd), std::move(cancel_fd)),
       base::BindOnce(&ArcSessionDelegateImpl::OnMojoConnected,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_factory_.GetWeakPtr(), std::move(callback),
+                     std::make_unique<ArcBridgeHostImpl>(arc_bridge_service_)));
   return return_fd;
 }
 
@@ -376,6 +378,7 @@
 
 void ArcSessionDelegateImpl::OnMojoConnected(
     ConnectMojoCallback callback,
+    std::unique_ptr<ArcBridgeHostImpl> host,
     mojo::ScopedMessagePipeHandle server_pipe) {
   if (!server_pipe.is_valid()) {
     LOG(ERROR) << "Invalid pipe";
@@ -383,9 +386,9 @@
     return;
   }
 
-  std::move(callback).Run(std::make_unique<ArcBridgeHostImpl>(
-      arc_bridge_service_,
-      mojo::PendingReceiver<mojom::ArcBridgeHost>(std::move(server_pipe))));
+  host->AddReceiver(
+      mojo::PendingReceiver<mojom::ArcBridgeHost>(std::move(server_pipe)));
+  std::move(callback).Run(std::move(host));
 }
 
 }  // namespace
diff --git a/ash/picker/views/picker_preview_bubble.cc b/ash/picker/views/picker_preview_bubble.cc
index 64f986d..42ce90bb 100644
--- a/ash/picker/views/picker_preview_bubble.cc
+++ b/ash/picker/views/picker_preview_bubble.cc
@@ -10,9 +10,7 @@
 #include "ash/bubble/bubble_constants.h"
 #include "ash/bubble/bubble_utils.h"
 #include "ash/style/typography.h"
-#include "components/vector_icons/vector_icons.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/base/themed_vector_icon.h"
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
@@ -56,14 +54,9 @@
   SetCanActivate(false);
 
   // Contents of this view.
-  const ui::ImageModel icon = ui::ImageModel::FromVectorIcon(
-      vector_icons::kForwardArrowIcon, ui::kColorAvatarIconIncognito,
-      kPreviewImageSize.height());
   AddChildView(
       views::Builder<views::ImageView>()
-          .SetImage(icon)
-          .SetSize(kPreviewImageSize)
-          .SetPreferredSize(kPreviewImageSize)
+          .SetImageSize(kPreviewImageSize)
           .SetBackground(views::CreateThemedRoundedRectBackground(
               cros_tokens::kCrosSysSeparator, kPreviewBackgroundBorderRadius))
           .Build());
diff --git a/ash/picker/views/picker_view.cc b/ash/picker/views/picker_view.cc
index 4aac1e46..a57dfc3 100644
--- a/ash/picker/views/picker_view.cc
+++ b/ash/picker/views/picker_view.cc
@@ -53,7 +53,6 @@
 namespace ash {
 namespace {
 
-constexpr gfx::Size kPickerSize(320, 340);
 constexpr int kBorderRadius = 12;
 constexpr SystemShadow::Type kShadowType = SystemShadow::Type::kElevation12;
 constexpr ui::ColorId kBackgroundColor =
@@ -161,7 +160,7 @@
   shadow_ =
       SystemShadow::CreateShadowOnNinePatchLayerForView(this, kShadowType);
   shadow_->SetRoundedCornerRadius(kBorderRadius);
-  SetPreferredSize(kPickerSize);
+  SetPreferredSize(kMaxSize);
   SetProperty(views::kElementIdentifierKey, kPickerElementId);
 
   SetLayoutManager(std::make_unique<views::FlexLayout>())
@@ -399,13 +398,13 @@
   zero_state_view_ =
       contents_view_->AddPage(std::make_unique<PickerZeroStateView>(
           this, delegate_->GetAvailableCategories(),
-          delegate_->ShouldShowSuggestedResults(), kPickerSize.width()));
+          delegate_->ShouldShowSuggestedResults(), kMaxSize.width()));
 
   category_view_ = contents_view_->AddPage(std::make_unique<PickerCategoryView>(
-      this, kPickerSize.width(), delegate_->GetAssetFetcher()));
+      this, kMaxSize.width(), delegate_->GetAssetFetcher()));
   search_results_view_ =
       contents_view_->AddPage(std::make_unique<PickerSearchResultsView>(
-          this, kPickerSize.width(), delegate_->GetAssetFetcher()));
+          this, kMaxSize.width(), delegate_->GetAssetFetcher()));
   SetActivePage(zero_state_view_);
 }
 
diff --git a/ash/picker/views/picker_view.h b/ash/picker/views/picker_view.h
index cd91529..cea6dac 100644
--- a/ash/picker/views/picker_view.h
+++ b/ash/picker/views/picker_view.h
@@ -19,6 +19,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget_delegate.h"
 
@@ -60,6 +61,8 @@
   PickerView& operator=(const PickerView&) = delete;
   ~PickerView() override;
 
+  static constexpr auto kMaxSize = gfx::Size(320, 340);
+
   // views::WidgetDelegateView:
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
   std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
diff --git a/ash/picker/views/picker_widget.cc b/ash/picker/views/picker_widget.cc
index 59e8afe..56514bc 100644
--- a/ash/picker/views/picker_widget.cc
+++ b/ash/picker/views/picker_widget.cc
@@ -20,11 +20,9 @@
 namespace ash {
 namespace {
 
-constexpr gfx::Size kPickerSize(320, 340);
-
 // Gets the preferred layout to use given `anchor_bounds` in screen coordinates.
 PickerView::PickerLayoutType GetLayoutType(const gfx::Rect& anchor_bounds) {
-  return anchor_bounds.bottom() + kPickerSize.height() <=
+  return anchor_bounds.bottom() + PickerView::kMaxSize.height() <=
                  display::Screen::GetScreen()
                      ->GetDisplayMatching(anchor_bounds)
                      .work_area()
@@ -40,7 +38,7 @@
   const PickerView::PickerLayoutType layout_type = GetLayoutType(anchor_bounds);
   auto picker_view = std::make_unique<PickerView>(delegate, layout_type,
                                                   trigger_event_timestamp);
-  picker_view->SetSize(kPickerSize);
+  picker_view->SetSize(PickerView::kMaxSize);
 
   views::Widget::InitParams params;
   params.activatable = views::Widget::InitParams::Activatable::kYes;
diff --git a/base/allocator/partition_allocator/src/partition_alloc/in_slot_metadata.h b/base/allocator/partition_allocator/src/partition_alloc/in_slot_metadata.h
index eda1ddb..34a3282 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/in_slot_metadata.h
+++ b/base/allocator/partition_allocator/src/partition_alloc/in_slot_metadata.h
@@ -475,8 +475,6 @@
 static_assert(kAlignment % alignof(InSlotMetadata) == 0,
               "kAlignment must be multiples of alignof(InSlotMetadata).");
 
-static constexpr size_t kInSlotMetadataBufferSize = sizeof(InSlotMetadata);
-
 #if PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS)
 
 #if PA_CONFIG(IN_SLOT_METADATA_CHECK_COOKIE) || \
@@ -561,16 +559,14 @@
   }
 }
 
-static_assert(sizeof(InSlotMetadata) <= kInSlotMetadataBufferSize,
-              "InSlotMetadata should fit into the in-slot buffer.");
-
-#else  // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
-
-static constexpr size_t kInSlotMetadataBufferSize = 0;
-
 #endif  // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
 
-constexpr size_t kInSlotMetadataSizeAdjustment = kInSlotMetadataBufferSize;
+static inline constexpr size_t kInSlotMetadataSizeAdjustment =
+#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
+    sizeof(InSlotMetadata);
+#else
+    0ul;
+#endif
 
 }  // namespace partition_alloc::internal
 
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
index 1c7c1add..05b9e9b5 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
@@ -3794,8 +3794,8 @@
     // C % kAlignment == (slot_size - ExtraAllocSize(allocator)) % kAlignment.
     // C % kAlignment == (-ExtraAllocSize(allocator)) % kAlignment.
     // Since kCookieSize is a multiple of kAlignment,
-    // C % kAlignment == (-kInSlotMetadataBufferSize) % kAlignment
-    // == (kAlignment - kInSlotMetadataBufferSize) % kAlignment.
+    // C % kAlignment == (-sizeof(InSlotMetadata)) % kAlignment
+    // == (kAlignment - sizeof(InSlotMetadata)) % kAlignment.
     EXPECT_EQ(allocator.root()->AllocationCapacityFromSlotStart(slot_start) %
                   fundamental_alignment,
               UseBRPPool()
diff --git a/base/trace_event/base_tracing.h b/base/trace_event/base_tracing.h
index f3ed650a..dad0ae369 100644
--- a/base/trace_event/base_tracing.h
+++ b/base/trace_event/base_tracing.h
@@ -15,21 +15,21 @@
 #if BUILDFLAG(ENABLE_BASE_TRACING)
 // Update the check in //base/PRESUBMIT.py when adding new headers here.
 // TODO(crbug.com/42050015): Switch to perfetto for trace event implementation.
-#include "base/trace_event/heap_profiler.h"               // nogncheck
-#include "base/trace_event/interned_args_helper.h"        // nogncheck
-#include "base/trace_event/memory_allocator_dump_guid.h"  // nogncheck
-#include "base/trace_event/memory_dump_manager.h"         // nogncheck
-#include "base/trace_event/memory_dump_provider.h"        // nogncheck
-#include "base/trace_event/trace_event.h"                 // nogncheck
-#include "base/trace_event/trace_id_helper.h"             // nogncheck
-#include "base/trace_event/traced_value.h"                // nogncheck
-#include "base/trace_event/typed_macros.h"                // nogncheck
-#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"  // nogncheck
-#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"  // nogncheck
-#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"  // nogncheck
-#include "third_party/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h"  // nogncheck
+#include "base/trace_event/heap_profiler.h"  // IWYU pragma: export, nogncheck
+#include "base/trace_event/interned_args_helper.h"  // IWYU pragma: export, nogncheck
+#include "base/trace_event/memory_allocator_dump_guid.h"  // IWYU pragma: export, nogncheck
+#include "base/trace_event/memory_dump_manager.h"  // IWYU pragma: export, nogncheck
+#include "base/trace_event/memory_dump_provider.h"  // IWYU pragma: export, nogncheck
+#include "base/trace_event/trace_event.h"      // IWYU pragma: export, nogncheck
+#include "base/trace_event/trace_id_helper.h"  // IWYU pragma: export, nogncheck
+#include "base/trace_event/traced_value.h"     // IWYU pragma: export, nogncheck
+#include "base/trace_event/typed_macros.h"     // IWYU pragma: export, nogncheck
+#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"  // IWYU pragma: export, nogncheck
+#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"  // IWYU pragma: export, nogncheck
+#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"  // IWYU pragma: export, nogncheck
+#include "third_party/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h"  // IWYU pragma: export, nogncheck
 #else  // BUILDFLAG(ENABLE_BASE_TRACING)
-#include "base/trace_event/trace_event_stub.h"
+#include "base/trace_event/trace_event_stub.h"  // IWYU pragma: export
 #endif  // BUILDFLAG(ENABLE_BASE_TRACING)
 
 #endif  // BASE_TRACE_EVENT_BASE_TRACING_H_
diff --git a/build/util/ide_query b/build/util/ide_query
index ed151118..0e72a729 100755
--- a/build/util/ide_query
+++ b/build/util/ide_query
@@ -93,7 +93,7 @@
     ])
     args.extend(targets)
     env = os.environ.copy()
-    env['SISO_EXPERIMENTS'] = 'no-fast-deps'
+    env['SISO_EXPERIMENTS'] = 'no-fast-deps,prepare-header-only'
     with subprocess.Popen(
         args,
         cwd=repo_root,
diff --git a/chrome/VERSION b/chrome/VERSION
index fed3b04..fe716d1 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=126
 MINOR=0
-BUILD=6476
+BUILD=6477
 PATCH=0
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 4e035b8c..585dfa3 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -330,6 +330,7 @@
   "junit/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorMediatorTest.java",
   "junit/src/org/chromium/chrome/browser/suggestions/SuggestionsImageFetcherTest.java",
   "junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java",
+  "junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java",
   "junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java",
   "junit/src/org/chromium/chrome/browser/supervised_user/ChildAccountServiceTest.java",
   "junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
index 3e47345..883e32a2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -4,9 +4,11 @@
 
 package org.chromium.chrome.browser.suggestions.tile;
 
+import android.annotation.SuppressLint;
 import android.util.SparseArray;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnCreateContextMenuListener;
@@ -16,6 +18,8 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
+import org.chromium.base.TimeUtils;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.native_page.ContextMenuManager;
 import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId;
 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
@@ -104,7 +108,7 @@
         /**
          * Returns a delegate that will handle user interactions with the view created for the tile.
          */
-        TileInteractionDelegate createInteractionDelegate(Tile tile);
+        TileInteractionDelegate createInteractionDelegate(Tile tile, View view);
 
         /**
          * Returns a callback to be invoked when the icon for the provided tile is loaded. It will
@@ -207,8 +211,8 @@
     private final TileSetupDelegate mTileSetupDelegate =
             new TileSetupDelegate() {
                 @Override
-                public TileInteractionDelegate createInteractionDelegate(Tile tile) {
-                    return new TileInteractionDelegateImpl(tile.getData());
+                public TileInteractionDelegate createInteractionDelegate(Tile tile, View view) {
+                    return new TileInteractionDelegateImpl(tile.getData(), view);
                 }
 
                 @Override
@@ -490,17 +494,34 @@
     }
 
     private class TileInteractionDelegateImpl
-            implements TileInteractionDelegate, ContextMenuManager.Delegate {
+            implements TileInteractionDelegate, ContextMenuManager.Delegate, View.OnTouchListener {
         private final SiteSuggestion mSuggestion;
         private Runnable mOnClickRunnable;
         private Runnable mOnRemoveRunnable;
+        private Long mTouchTimer;
 
-        public TileInteractionDelegateImpl(SiteSuggestion suggestion) {
+        private void maybeRecordTouchDuration(boolean taken) {
+            if (mTouchTimer == null) return;
+
+            long duration = TimeUtils.elapsedRealtimeMillis() - mTouchTimer;
+            mTouchTimer = null;
+            RecordHistogram.recordLongTimesHistogram(
+                    taken
+                            ? "Prerender.Experimental.NewTabPage.TouchDuration.Taken"
+                            : "Prerender.Experimental.NewTabPage.TouchDuration.NotTaken",
+                    duration);
+        }
+
+        public TileInteractionDelegateImpl(SiteSuggestion suggestion, View view) {
             mSuggestion = suggestion;
+            view.setOnTouchListener(TileInteractionDelegateImpl.this);
         }
 
         @Override
         public void onClick(View view) {
+            maybeRecordTouchDuration(true);
+            if (mSuggestion == null) return;
+
             Tile tile = findTile(mSuggestion);
             if (tile == null) return;
 
@@ -510,6 +531,19 @@
         }
 
         @Override
+        @SuppressLint("ClickableViewAccessibility")
+        public boolean onTouch(View view, MotionEvent event) {
+            if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                mTouchTimer = TimeUtils.elapsedRealtimeMillis();
+            }
+            if (event.getAction() == MotionEvent.ACTION_CANCEL) {
+                maybeRecordTouchDuration(false);
+            }
+
+            return false;
+        }
+
+        @Override
         public void openItem(int windowDisposition) {
             Tile tile = findTile(mSuggestion);
             if (tile == null) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
index db145ce9..418fbd3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileRenderer.java
@@ -204,7 +204,8 @@
         updateIcon(tile, setupDelegate);
         updateContentDescription(tile, tileView);
 
-        TileGroup.TileInteractionDelegate delegate = setupDelegate.createInteractionDelegate(tile);
+        TileGroup.TileInteractionDelegate delegate =
+                setupDelegate.createInteractionDelegate(tile, tileView);
         if (tile.getSource() == TileSource.HOMEPAGE) {
             delegate.setOnClickRunnable(
                     () -> {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java
index 7cad56a..071de7f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java
@@ -126,6 +126,7 @@
 
     /** Returns the type of the sync error, for syncing users. */
     public static @SyncError int getSyncError(Profile profile) {
+        assert profile != null;
         SyncService syncService = SyncServiceFactory.getForProfile(profile);
         if (syncService == null) {
             return SyncError.NO_ERROR;
@@ -135,7 +136,7 @@
             return SyncError.NO_ERROR;
         }
 
-        @SyncError int error = getSyncErrorFromSyncService(syncService);
+        @SyncError int error = getCommonError(profile);
         if (error != SyncError.NO_ERROR) {
             return error;
         }
@@ -144,12 +145,6 @@
             return SyncError.SYNC_SETUP_INCOMPLETE;
         }
 
-        if (syncService.getSelectedTypes().contains(UserSelectableType.PASSWORDS)
-                && PasswordManagerUtilBridge.isGmsCoreUpdateRequired(
-                        UserPrefs.get(profile), /* isPwdSyncEnabled= */ true)) {
-            return SyncError.UPM_BACKEND_OUTDATED;
-        }
-
         return SyncError.NO_ERROR;
     }
 
@@ -573,8 +568,12 @@
         return canShowFullName ? fullName : accountEmail;
     }
 
-    /** Returns the type of the sync error/identity error for signed-in non-syncing users. */
+    /**
+     * Returns the type of the sync error/identity error for signed-in non-syncing users.
+     * TODO(crbug.com/330290259): Merge this into getSyncError().
+     */
     public static @SyncError int getIdentityError(Profile profile) {
+        assert profile != null;
         SyncService syncService = SyncServiceFactory.getForProfile(profile);
         // TODO(crbug.com/40944114): Consider converting this to an assertion instead.
         if (syncService == null) {
@@ -593,7 +592,7 @@
             return SyncError.NO_ERROR;
         }
 
-        @SyncError int error = getSyncErrorFromSyncService(syncService);
+        @SyncError int error = getCommonError(profile);
         // Do not show identity error for unrecoverable errors, since they are not actionable.
         // TODO(crbug.com/40944114): Remove these unused values after sync-to-signin transition.
         if (error == SyncError.OTHER_ERRORS) {
@@ -602,8 +601,9 @@
         return error;
     }
 
-    /** Returns the type of the sync error from sync service. */
-    private static @SyncError int getSyncErrorFromSyncService(SyncService syncService) {
+    /** Returns the errors common to both getSyncError() and getIdentityError(). */
+    private static @SyncError int getCommonError(Profile profile) {
+        SyncService syncService = SyncServiceFactory.getForProfile(profile);
         assert syncService != null;
 
         if (syncService.getAuthError() == GoogleServiceAuthError.State.INVALID_GAIA_CREDENTIALS) {
@@ -638,6 +638,12 @@
                     : SyncError.TRUSTED_VAULT_RECOVERABILITY_DEGRADED_FOR_PASSWORDS;
         }
 
+        if (syncService.getSelectedTypes().contains(UserSelectableType.PASSWORDS)
+                && PasswordManagerUtilBridge.isGmsCoreUpdateRequired(
+                        UserPrefs.get(profile), /* isPwdSyncEnabled= */ true)) {
+            return SyncError.UPM_BACKEND_OUTDATED;
+        }
+
         return SyncError.NO_ERROR;
     }
 
@@ -678,6 +684,10 @@
                 return new ErrorCardDetails(
                         R.string.identity_error_card_sync_recoverability_degraded_for_passwords,
                         R.string.identity_error_card_button_verify);
+            case SyncError.UPM_BACKEND_OUTDATED:
+                return new ErrorCardDetails(
+                        R.string.sync_error_card_outdated_gms,
+                        R.string.password_manager_outdated_gms_positive_button);
             case SyncError.OTHER_ERRORS:
             case SyncError.SYNC_SETUP_INCOMPLETE:
             case SyncError.NO_ERROR:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java
index b0367689..95bc0c48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarController.java
@@ -8,38 +8,34 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.Token;
-import org.chromium.base.supplier.LazyOneshotSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncFeatures;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarManageable;
 
 import java.util.List;
-import java.util.Set;
+import java.util.Locale;
 
 /**
  * A controller that listens to and visually represents cancelable tab closures.
- *
- * <p>Each time a tab is undoably closed via {@link TabModelObserver#tabPendingClosure(Tab)}, this
- * controller saves that tab id and title to the stack of SnackbarManager. It will then let
+ * <p/>
+ * Each time a tab is undoably closed via {@link TabModelObserver#tabPendingClosure(Tab)},
+ * this controller saves that tab id and title to the stack of SnackbarManager. It will then let
  * SnackbarManager to show a snackbar representing the top entry in of stack. Each added entry
  * resets the timeout that tracks when to commit the undoable actions.
- *
- * <p>When the undo button is clicked, it will cancel the tab closure if any. all pending closing
- * will be committed.
- *
- * <p>This class also responds to external changes to the undo state by monitoring {@link
- * TabModelObserver#tabClosureUndone(Tab)} and {@link TabModelObserver#tabClosureCommitted(Tab)} to
- * properly keep it's internal state in sync with the model.
+ * <p/>
+ * When the undo button is clicked, it will cancel the tab closure if any. all pending closing will
+ * be committed.
+ * <p/>
+ * This class also responds to external changes to the undo state by monitoring
+ * {@link TabModelObserver#tabClosureUndone(Tab)} and
+ * {@link TabModelObserver#tabClosureCommitted(Tab)} to properly keep it's internal state
+ * in sync with the model.
  */
 public class UndoBarController implements SnackbarManager.SnackbarController {
     private final TabModelSelector mTabModelSelector;
@@ -86,7 +82,7 @@
                     @Override
                     public void tabPendingClosure(Tab tab) {
                         if (disableUndo(true)) return;
-                        showUndoBar(List.of(tab), /* isAllTabs= */ false);
+                        showUndoBar(tab.getId(), tab.getTitle());
                     }
 
                     @Override
@@ -116,7 +112,14 @@
                     @Override
                     public void multipleTabsPendingClosure(List<Tab> tabs, boolean isAllTabs) {
                         if (disableUndo(true)) return;
-                        showUndoBar(tabs, isAllTabs);
+
+                        if (tabs.size() == 1) {
+                            tabPendingClosure(tabs.get(0));
+                            return;
+                        }
+
+                        // "Undo close all" bar can be reused for undoing close multiple tabs.
+                        showUndoCloseMultipleBar(tabs, isAllTabs);
                     }
 
                     @Override
@@ -146,98 +149,59 @@
     }
 
     /**
-     * Shows an undo close all bar. Based on user actions, this will cause a call to either {@link
-     * TabModel#commitTabClosure(int)} or {@link TabModel#cancelTabClosure(int)} to be called for
-     * each tab in {@code closedTabIds}. This will happen unless {@code
-     * SnackbarManager#removeFromStackForData(Object)} is called.
+     * Shows an undo bar. Based on user actions, this will cause a call to either
+     * {@link TabModel#commitTabClosure(int)} or {@link TabModel#cancelTabClosure(int)} to be called
+     * for {@code tabId}.
      *
-     * @param closedTabs A list of tabs that were closed.
-     * @param isAllTabs Whether all tabs were closed.
+     * @param tabId The id of the tab.
+     * @param content The title of the tab.
      */
-    private void showUndoBar(List<Tab> closedTabs, boolean isAllTabs) {
-        if (closedTabs.isEmpty()) return;
-
-        boolean singleTab = closedTabs.size() == 1;
-        boolean deletingTabGroup = isDeletingTabGroup(closedTabs);
-
-        String templateText = getTemplateText(singleTab, deletingTabGroup);
-        int umaType = getUmaType(singleTab, deletingTabGroup, isAllTabs);
-
-        Object actionData = singleTab ? closedTabs.get(0).getId() : closedTabs;
-        String content;
-        if (singleTab && !deletingTabGroup) {
-            content = closedTabs.get(0).getTitle();
-        } else {
-            content = Integer.toString(closedTabs.size());
-        }
-
+    private void showUndoBar(int tabId, String content) {
         mSnackbarManagable
                 .getSnackbarManager()
                 .showSnackbar(
-                        Snackbar.make(content, this, Snackbar.TYPE_ACTION, umaType)
-                                .setDuration(
-                                        isAllTabs
-                                                ? SnackbarManager.DEFAULT_SNACKBAR_DURATION_LONG_MS
-                                                : SnackbarManager.DEFAULT_SNACKBAR_DURATION_MS)
-                                .setTemplateText(templateText)
-                                .setAction(mContext.getString(R.string.undo), actionData)
+                        Snackbar.make(
+                                        content,
+                                        this,
+                                        Snackbar.TYPE_ACTION,
+                                        Snackbar.UMA_TAB_CLOSE_UNDO)
+                                .setTemplateText(
+                                        mContext.getString(R.string.undo_bar_close_message))
+                                .setAction(mContext.getString(R.string.undo), tabId)
                                 .setActionAccessibilityAnnouncement(
                                         getUndoneAccessibilityAnnouncement(content, false)));
     }
 
-    private boolean isDeletingTabGroup(List<Tab> closedTabs) {
-        if (closedTabs.isEmpty()) return false;
-
-        assert !closedTabs.get(0).isIncognito();
-
-        TabGroupModelFilter filter =
-                (TabGroupModelFilter)
-                        mTabModelSelector
-                                .getTabModelFilterProvider()
-                                .getTabModelFilter(/* isIncognito= */ false);
-        Profile profile = filter.getTabModel().getProfile();
-        if (profile == null || !profile.isNativeInitialized()) return false;
-
-        if (!TabGroupSyncFeatures.isTabGroupSyncEnabled(profile)) return false;
-
-        LazyOneshotSupplier<Set<Token>> tabGroupIdsInComprehensiveModel =
-                filter.getLazyAllTabGroupIdsInComprehensiveModel(closedTabs);
-        for (Tab tab : closedTabs) {
-            // We are not deleting a tab group if:
-            // 1. Any tabs are not in a tab group.
-            // 2. Any of the tabs are in a group that is hiding.
-            // 3. The comprehensive model still contains tabs with that group ID meaning the tab
-            //    group is not being fully deleted as a result of this event.
-            @Nullable Token tabGroupId = tab.getTabGroupId();
-            if (tabGroupId == null
-                    || filter.isTabGroupHiding(tabGroupId)
-                    || tabGroupIdsInComprehensiveModel.get().contains(tabGroupId)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private String getTemplateText(boolean singleTab, boolean deletingTabGroup) {
-        if (deletingTabGroup) {
-            return singleTab
-                    ? mContext.getString(R.string.undo_bar_delete_single_tab_group_message)
-                    : mContext.getString(R.string.undo_bar_delete_tab_group_message);
-        }
-        return singleTab
-                ? mContext.getString(R.string.undo_bar_close_message)
-                : mContext.getString(R.string.undo_bar_close_all_message);
-    }
-
-    private int getUmaType(boolean singleTab, boolean deletingTabGroup, boolean isAllTabs) {
-        if (deletingTabGroup) {
-            return singleTab
-                    ? Snackbar.UMA_SINGLE_TAB_GROUP_DELETE_UNDO
-                    : Snackbar.UMA_TAB_GROUP_DELETE_UNDO;
-        } else if (isAllTabs) {
-            return Snackbar.UMA_TAB_CLOSE_ALL_UNDO;
-        }
-        return singleTab ? Snackbar.UMA_TAB_CLOSE_UNDO : Snackbar.UMA_TAB_CLOSE_MULTIPLE_UNDO;
+    /**
+     * Shows an undo close all bar. Based on user actions, this will cause a call to either
+     * {@link TabModel#commitTabClosure(int)} or {@link TabModel#cancelTabClosure(int)} to be called
+     * for each tab in {@code closedTabIds}. This will happen unless
+     * {@code SnackbarManager#removeFromStackForData(Object)} is called.
+     *
+     * @param closedTabs A list of tabs that were closed.
+     * @param isAllTabs Whether all tabs were closed.
+     */
+    private void showUndoCloseMultipleBar(List<Tab> closedTabs, boolean isAllTabs) {
+        String content = String.format(Locale.getDefault(), "%d", closedTabs.size());
+        mSnackbarManagable
+                .getSnackbarManager()
+                .showSnackbar(
+                        Snackbar.make(
+                                        content,
+                                        this,
+                                        Snackbar.TYPE_ACTION,
+                                        isAllTabs
+                                                ? Snackbar.UMA_TAB_CLOSE_ALL_UNDO
+                                                : Snackbar.UMA_TAB_CLOSE_MULTIPLE_UNDO)
+                                .setDuration(
+                                        isAllTabs
+                                                ? SnackbarManager.DEFAULT_SNACKBAR_DURATION_LONG_MS
+                                                : SnackbarManager.DEFAULT_SNACKBAR_DURATION_MS)
+                                .setTemplateText(
+                                        mContext.getString(R.string.undo_bar_close_all_message))
+                                .setAction(mContext.getString(R.string.undo), closedTabs)
+                                .setActionAccessibilityAnnouncement(
+                                        getUndoneAccessibilityAnnouncement(content, true)));
     }
 
     private String getUndoneAccessibilityAnnouncement(String content, boolean isMultiple) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
index bfbea7e5..8ea2ce58 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninFirstRunFragmentRenderTest.java
@@ -5,9 +5,11 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
+import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.not;
 import static org.mockito.Mockito.when;
 
@@ -36,9 +38,11 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.firstrun.FirstRunPageDelegate;
 import org.chromium.chrome.browser.firstrun.PolicyLoadListener;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -193,6 +197,7 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     public void testFragmentRotationToLandscapeWithAccount() throws IOException {
         mAccountManagerTestRule.addAccount(TEST_EMAIL1);
         launchActivityWithFragment(Configuration.ORIENTATION_PORTRAIT);
@@ -210,6 +215,27 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    public void testFragmentRotationToLandscapeWithAccount_replaceSyncWithSigninPromosEnabled()
+            throws IOException {
+        mAccountManagerTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_1);
+        launchActivityWithFragment(Configuration.ORIENTATION_PORTRAIT);
+
+        ActivityTestUtils.rotateActivityToOrientation(
+                getActivity(), Configuration.ORIENTATION_LANDSCAPE);
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_account_landscape_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     public void testFragmentRotationToPortraitWithAccount() throws IOException {
         mAccountManagerTestRule.addAccount(TEST_EMAIL1);
         launchActivityWithFragment(Configuration.ORIENTATION_LANDSCAPE);
@@ -227,6 +253,25 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    public void testFragmentRotationToPortraitWithAccount_replaceSyncWithSigninPromosEnabled()
+            throws IOException {
+        mAccountManagerTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_1);
+        launchActivityWithFragment(Configuration.ORIENTATION_LANDSCAPE);
+
+        ActivityTestUtils.rotateActivityToOrientation(
+                getActivity(), Configuration.ORIENTATION_PORTRAIT);
+        ViewUtils.onViewWaiting(
+                allOf(withId(R.id.account_text_secondary), isCompletelyDisplayed()));
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_account_portrait_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWithAccount(boolean nightModeEnabled, int orientation)
             throws IOException {
@@ -244,6 +289,27 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void testFragmentWithAccount_replaceSyncWithSigninPromosEnabled(
+            boolean nightModeEnabled, int orientation) throws IOException {
+        mAccountManagerTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_1);
+
+        launchActivityWithFragment(orientation);
+
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_account_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWithAccountOnManagedDevice(boolean nightModeEnabled, int orientation)
             throws IOException {
@@ -263,6 +329,28 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void testFragmentWithAccountOnManagedDevice_replaceSyncWithSigninPromosEnabled(
+            boolean nightModeEnabled, int orientation) throws IOException {
+        when(mPolicyLoadListenerMock.get()).thenReturn(true);
+        mAccountManagerTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_1);
+
+        launchActivityWithFragment(orientation);
+
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_account_managed_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWithAccountOnManagedDevice_doesNotApplyFREStringVariations(
             boolean nightModeEnabled, int orientation) throws IOException {
@@ -283,6 +371,28 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void
+            testFragmentWithAccountOnManagedDevice_doesNotApplyFREStringVariations_replaceSyncWithSigninPromosEnabled(
+                    boolean nightModeEnabled, int orientation) throws IOException {
+        when(mPolicyLoadListenerMock.get()).thenReturn(true);
+        mAccountManagerTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_1);
+
+        launchActivityWithFragment(orientation);
+
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_account_managed_and_string_variation_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWithAccountWhenSigninIsDisabledByPolicy(
             boolean nightModeEnabled, int orientation) throws IOException {
@@ -340,6 +450,7 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWithChildAccount(boolean nightModeEnabled, int orientation)
             throws IOException {
@@ -357,6 +468,27 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void testFragmentWithChildAccount_replaceSyncWithSigninPromosEnabled(
+            boolean nightModeEnabled, int orientation) throws IOException {
+        mAccountManagerTestRule.addAccount(CHILD_ACCOUNT_NAME);
+
+        launchActivityWithFragment(orientation);
+
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_child_account_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWithChildAccount_doesNotApplyFREStringVariation(
             boolean nightModeEnabled, int orientation) throws IOException {
@@ -376,6 +508,27 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void
+            testFragmentWithChildAccount_doesNotApplyFREStringVariation_replaceSyncWithSigninPromosAnabled(
+                    boolean nightModeEnabled, int orientation) throws IOException {
+        mAccountManagerTestRule.addAccount(CHILD_ACCOUNT_NAME);
+
+        launchActivityWithFragment(orientation);
+
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_with_child_account_and_string_variation_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWhenCannotUseGooglePlayService(
             boolean nightModeEnabled, int orientation) throws IOException {
@@ -409,6 +562,7 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWhenMetricsReportingIsDisabledByPolicyWithAccount(
             boolean nightModeEnabled, int orientation) throws IOException {
@@ -434,6 +588,34 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void
+            testFragmentWhenMetricsReportingIsDisabledByPolicyWithAccount_replaceSyncWithSigninPromosEnabled(
+                    boolean nightModeEnabled, int orientation) throws IOException {
+        when(mPolicyLoadListenerMock.get()).thenReturn(true);
+        when(mPrivacyPreferencesManagerMock.isUsageAndCrashReportingPermittedByPolicy())
+                .thenReturn(false);
+
+        PrivacyPreferencesManagerImpl.setInstanceForTesting(mPrivacyPreferencesManagerMock);
+
+        mAccountManagerTestRule.addAccount(AccountManagerTestRule.TEST_ACCOUNT_1);
+
+        launchActivityWithFragment(orientation);
+
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_when_metrics_reporting_is_disabled_by_policy_with_account_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
     public void testFragmentWhenMetricsReportingIsDisabledByPolicyWithChildAccount(
             boolean nightModeEnabled, int orientation) throws IOException {
@@ -456,10 +638,32 @@
                 "signin_first_run_fragment_when_metrics_reporting_is_disabled_by_policy_with_child_account");
     }
 
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeAndOrientationParameterProvider.class)
+    public void
+            testFragmentWhenMetricsReportingIsDisabledByPolicyWithChildAccount_replaceSyncWithSigninPromosEnabled(
+                    boolean nightModeEnabled, int orientation) throws IOException {
+        when(mPolicyLoadListenerMock.get()).thenReturn(true);
+        when(mPrivacyPreferencesManagerMock.isUsageAndCrashReportingPermittedByPolicy())
+                .thenReturn(false);
 
+        PrivacyPreferencesManagerImpl.setInstanceForTesting(mPrivacyPreferencesManagerMock);
 
+        mAccountManagerTestRule.addAccount(CHILD_ACCOUNT_NAME);
 
+        launchActivityWithFragment(orientation);
 
+        CriteriaHelper.pollUiThread(
+                () -> {
+                    return mFragment.getView().findViewById(R.id.account_text_secondary).isShown();
+                });
+        mRenderTestRule.render(
+                mFragment.getView(),
+                "signin_first_run_fragment_when_metrics_reporting_is_disabled_by_policy_with_child_account_replace_sync_with_signin_promos_enabled");
+    }
 
     @Test
     @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/IdentityErrorCardPreferenceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/IdentityErrorCardPreferenceTest.java
index 400fa70..0a9a5fd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/IdentityErrorCardPreferenceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/IdentityErrorCardPreferenceTest.java
@@ -8,6 +8,10 @@
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
 import android.view.View;
 
 import androidx.test.filters.LargeTest;
@@ -18,12 +22,19 @@
 import org.junit.Test;
 import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.HistogramWatcher;
+import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.password_manager.PasswordManagerUtilBridge;
+import org.chromium.chrome.browser.password_manager.PasswordManagerUtilBridgeJni;
 import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
 import org.chromium.chrome.browser.sync.settings.AccountManagementFragment;
+import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.R;
@@ -62,10 +73,17 @@
                     .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
                     .build();
 
+    @Rule public JniMocker mJniMocker = new JniMocker();
+
+    @Mock private PasswordManagerUtilBridge.Natives mPasswordManagerUtilBridgeJniMock;
+
     private FakeSyncServiceImpl mFakeSyncServiceImpl;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mJniMocker.mock(PasswordManagerUtilBridgeJni.TEST_HOOKS, mPasswordManagerUtilBridgeJniMock);
+
         mActivityTestRule.startMainActivityOnBlankPage();
 
         TestThreadUtils.runOnUiThreadBlocking(
@@ -82,7 +100,12 @@
         mFakeSyncServiceImpl.setAuthError(GoogleServiceAuthError.State.INVALID_GAIA_CREDENTIALS);
         mSigninTestRule.addTestAccountThenSignin();
 
-        mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.AuthError",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(getIdentityErrorCardView(), "identity_error_card_auth_error");
     }
 
@@ -93,7 +116,12 @@
         mFakeSyncServiceImpl.setRequiresClientUpgrade(true);
         mSigninTestRule.addTestAccountThenSignin();
 
-        mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.ClientOutOfDate",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(
                 getIdentityErrorCardView(), "identity_error_card_client_out_of_date");
     }
@@ -106,7 +134,12 @@
         mFakeSyncServiceImpl.setPassphraseRequiredForPreferredDataTypes(true);
         mSigninTestRule.addTestAccountThenSignin();
 
-        mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.PassphraseRequired",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(
                 getIdentityErrorCardView(), "identity_error_card_passphrase_required");
     }
@@ -121,6 +154,12 @@
         mSigninTestRule.addTestAccountThenSignin();
 
         mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.TrustedVaultKeyRequiredForEverything",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(
                 getIdentityErrorCardView(), "identity_error_card_trusted_vault_key_required");
     }
@@ -135,6 +174,12 @@
         mSigninTestRule.addTestAccountThenSignin();
 
         mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.TrustedVaultKeyRequiredForPasswords",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(
                 getIdentityErrorCardView(),
                 "identity_error_card_trusted_vault_key_required_for_passwords");
@@ -151,6 +196,12 @@
         mSigninTestRule.addTestAccountThenSignin();
 
         mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.TrustedVaultRecoverabilityDegradedForEverything",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(
                 getIdentityErrorCardView(),
                 "identity_error_card_trusted_vault_recoverability_degraded_for_everything");
@@ -166,7 +217,12 @@
         mFakeSyncServiceImpl.setEncryptEverythingEnabled(false);
         mSigninTestRule.addTestAccountThenSignin();
 
-        mSettingsActivityTestRule.startSettingsActivity();
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.TrustedVaultRecoverabilityDegradedForPasswords",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
         mRenderTestRule.render(
                 getIdentityErrorCardView(),
                 "identity_error_card_trusted_vault_recoverability_degraded_for_passwords");
@@ -174,6 +230,25 @@
 
     @Test
     @LargeTest
+    @Feature("RenderTest")
+    public void testIdentityErrorCardForUpmBackendOutdated() throws Exception {
+        when(mPasswordManagerUtilBridgeJniMock.isGmsCoreUpdateRequired(any(), eq(true)))
+                .thenReturn(true);
+
+        mSigninTestRule.addTestAccountThenSignin();
+
+        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
+                HistogramWatcher.newSingleRecordWatcher(
+                        "Sync.IdentityErrorCard.UpmBackendOutdated",
+                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
+            mSettingsActivityTestRule.startSettingsActivity();
+        }
+        mRenderTestRule.render(
+                getIdentityErrorCardView(), "identity_error_card_upm_backend_outdated");
+    }
+
+    @Test
+    @LargeTest
     public void testIdentityErrorCardNotShownForUnrecoverableErrors() throws Exception {
         mFakeSyncServiceImpl.setAuthError(GoogleServiceAuthError.State.CONNECTION_FAILED);
         mSigninTestRule.addTestAccountThenSignin();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index 3158be6..bb8838ae 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -1207,6 +1207,7 @@
         verifyUrlKeyedAnonymizedDataCollectionNotSet();
     }
 
+    // TODO(crbug.com/330438265): Extend this test for the identity error card.
     @Test
     @LargeTest
     @Feature({"Sync"})
@@ -1235,6 +1236,7 @@
         Intents.release();
     }
 
+    // TODO(crbug.com/330438265): Extend this test for the identity error card.
     @Test
     @SmallTest
     @Feature({"Sync"})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
index cda0ae6..f778c33 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/undo_tab_close_snackbar/UndoBarControllerTest.java
@@ -16,13 +16,9 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Features.DisableFeatures;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tasks.tab_groups.TabGroupModelFilter;
 import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
@@ -33,7 +29,6 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
-import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 
@@ -46,20 +41,12 @@
 
     private SnackbarManager mSnackbarManager;
     private TabModel mTabModel;
-    private TabGroupModelFilter mTabGroupModelFilter;
 
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
         mSnackbarManager = mActivityTestRule.getActivity().getSnackbarManager();
-        mTabGroupModelFilter =
-                (TabGroupModelFilter)
-                        mActivityTestRule
-                                .getActivity()
-                                .getTabModelSelector()
-                                .getTabModelFilterProvider()
-                                .getTabModelFilter(false);
-        mTabModel = mTabGroupModelFilter.getTabModel();
+        mTabModel = mActivityTestRule.getActivity().getCurrentTabModel();
     }
 
     @Test
@@ -150,240 +137,6 @@
 
     @Test
     @SmallTest
-    @DisableFeatures({ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
-    public void testDeleteTabGroup_Undo_SyncDisabled() throws Exception {
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.mergeListOfTabsToGroup(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            mTabModel.getTabAt(0),
-                            /* notify= */ false);
-                });
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(2, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.closeMultipleTabs(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            /* canUndo= */ true,
-                            /* hideTabGroups= */ false);
-                });
-
-        Snackbar currentSnackbar = getCurrentSnackbar();
-        Assert.assertEquals("2 tabs closed", getSnackbarText());
-        Assert.assertTrue(currentSnackbar.getController() instanceof UndoBarController);
-        Assert.assertEquals(0, mTabModel.getCount());
-
-        clickSnackbar();
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(2, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-    }
-
-    @Test
-    @SmallTest
-    @EnableFeatures({ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
-    public void testDeleteTabGroup_Undo() throws Exception {
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.mergeListOfTabsToGroup(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            mTabModel.getTabAt(0),
-                            /* notify= */ false);
-                });
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(2, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.closeMultipleTabs(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            /* canUndo= */ true,
-                            /* hideTabGroups= */ false);
-                });
-
-        Snackbar currentSnackbar = getCurrentSnackbar();
-        Assert.assertEquals("2 tabs deleted", getSnackbarText());
-        Assert.assertTrue(currentSnackbar.getController() instanceof UndoBarController);
-        Assert.assertEquals(0, mTabModel.getCount());
-
-        clickSnackbar();
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(2, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-    }
-
-    @Test
-    @SmallTest
-    @EnableFeatures({ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
-    public void testDeleteSingleTabGroup_Undo() throws Exception {
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.createSingleTabGroup(
-                            mTabModel.getTabAt(0), /* notify= */ false);
-                });
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(1, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.closeMultipleTabs(
-                            List.of(mTabModel.getTabAt(0)),
-                            /* canUndo= */ true,
-                            /* hideTabGroups= */ false);
-                });
-
-        Snackbar currentSnackbar = getCurrentSnackbar();
-        Assert.assertEquals("1 tab deleted", getSnackbarText());
-        Assert.assertTrue(currentSnackbar.getController() instanceof UndoBarController);
-        Assert.assertEquals(0, mTabModel.getCount());
-
-        clickSnackbar();
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(1, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-    }
-
-    @Test
-    @SmallTest
-    @EnableFeatures({ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
-    public void testDeleteTabGroup_WithOtherTab_Undo() throws Exception {
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.mergeListOfTabsToGroup(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            mTabModel.getTabAt(0),
-                            /* notify= */ false);
-                });
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(3, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.closeMultipleTabs(
-                            List.of(
-                                    mTabModel.getTabAt(0),
-                                    mTabModel.getTabAt(1),
-                                    mTabModel.getTabAt(2)),
-                            /* canUndo= */ true,
-                            /* hideTabGroups= */ false);
-                });
-
-        Snackbar currentSnackbar = getCurrentSnackbar();
-        Assert.assertEquals("3 tabs closed", getSnackbarText());
-        Assert.assertTrue(currentSnackbar.getController() instanceof UndoBarController);
-        Assert.assertEquals(0, mTabModel.getCount());
-
-        clickSnackbar();
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(3, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-    }
-
-    @Test
-    @SmallTest
-    @EnableFeatures({ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
-    public void testPartialDeleteTabGroup_Undo() throws Exception {
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.mergeListOfTabsToGroup(
-                            List.of(
-                                    mTabModel.getTabAt(0),
-                                    mTabModel.getTabAt(1),
-                                    mTabModel.getTabAt(2)),
-                            mTabModel.getTabAt(0),
-                            /* notify= */ false);
-                });
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(3, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.closeMultipleTabs(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            /* canUndo= */ true,
-                            /* hideTabGroups= */ false);
-                });
-
-        Snackbar currentSnackbar = getCurrentSnackbar();
-        Assert.assertEquals("2 tabs closed", getSnackbarText());
-        Assert.assertTrue(currentSnackbar.getController() instanceof UndoBarController);
-        Assert.assertEquals(1, mTabModel.getCount());
-
-        clickSnackbar();
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(3, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-    }
-
-    @Test
-    @SmallTest
-    @EnableFeatures({ChromeFeatureList.TAB_GROUP_SYNC_ANDROID})
-    public void testHideTabGroup_Undo() throws Exception {
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.mergeListOfTabsToGroup(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            mTabModel.getTabAt(0),
-                            /* notify= */ false);
-                });
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(2, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> {
-                    mTabGroupModelFilter.closeMultipleTabs(
-                            List.of(mTabModel.getTabAt(0), mTabModel.getTabAt(1)),
-                            /* canUndo= */ true,
-                            /* hideTabGroups= */ true);
-                });
-
-        Snackbar currentSnackbar = getCurrentSnackbar();
-        Assert.assertEquals("2 tabs closed", getSnackbarText());
-        Assert.assertTrue(currentSnackbar.getController() instanceof UndoBarController);
-        Assert.assertEquals(0, mTabModel.getCount());
-
-        clickSnackbar();
-
-        Assert.assertNull(getCurrentSnackbar());
-        Assert.assertEquals(2, mTabModel.getCount());
-        Assert.assertEquals(1, mTabGroupModelFilter.getTabGroupCount());
-    }
-
-    @Test
-    @SmallTest
     public void testUndoSnackbarDisabled_AccessibilityEnabled() throws Exception {
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java
new file mode 100644
index 0000000..1b92318
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileInteractionDelegateTest.java
@@ -0,0 +1,113 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.suggestions.tile;
+
+import static org.mockito.Mockito.verify;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.HistogramWatcher;
+import org.chromium.chrome.browser.native_page.ContextMenuManager;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.suggestions.SiteSuggestion;
+import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
+import org.chromium.url.GURL;
+
+/** Tests for {@link TileInteractionDelegateTest}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class TileInteractionDelegateTest {
+    @Mock Tile mTile;
+    @Mock SuggestionsTileView mTileView;
+    @Mock SiteSuggestion mData;
+    @Mock SuggestionsUiDelegate mSuggestionsUiDelegate;
+    @Mock ContextMenuManager mContextMenuManager;
+    @Mock TileGroup.Delegate mTileGroupDelegate;
+    @Mock OfflinePageBridge mOfflinePageBridge;
+    @Mock private Runnable mSnapshotTileGridChangedRunnable;
+    @Mock private Runnable mTileCountChangedRunnable;
+    @Mock private TileGroup.Observer mTileGroupObserver;
+    @Mock private TileRenderer mTileRenderer;
+
+    @Captor
+    ArgumentCaptor<View.OnTouchListener> mOnTouchListenerCaptor =
+            ArgumentCaptor.forClass(View.OnTouchListener.class);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testTileInteractionDelegateTaken() {
+        HistogramWatcher.Builder histogramWatcherBuilder = HistogramWatcher.newBuilder();
+
+        histogramWatcherBuilder.expectIntRecord(
+                "Prerender.Experimental.NewTabPage.TouchDuration.Taken", 0);
+        histogramWatcherBuilder.expectNoRecords(
+                "Prerender.Experimental.NewTabPage.TouchDuration.NotTaken");
+
+        HistogramWatcher histogramWatcher = histogramWatcherBuilder.build();
+
+        TileGroup tileGroup =
+                new TileGroup(
+                        mTileRenderer,
+                        mSuggestionsUiDelegate,
+                        mContextMenuManager,
+                        mTileGroupDelegate,
+                        mTileGroupObserver,
+                        mOfflinePageBridge);
+        tileGroup.onIconMadeAvailable(new GURL("https://foo.com"));
+        TileGroup.TileSetupDelegate tileSetupCallback = tileGroup.getTileSetupDelegate();
+        TileGroup.TileInteractionDelegate tileInteractionDelegate =
+                tileSetupCallback.createInteractionDelegate(mTile, mTileView);
+        MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        verify(mTileView).setOnTouchListener(mOnTouchListenerCaptor.capture());
+        mOnTouchListenerCaptor.getValue().onTouch(mTileView, event);
+        tileInteractionDelegate.onClick(mTileView);
+
+        histogramWatcher.assertExpected();
+    }
+
+    @Test
+    public void testTileInteractionDelegateNotTaken() {
+        HistogramWatcher.Builder histogramWatcherBuilder = HistogramWatcher.newBuilder();
+
+        histogramWatcherBuilder.expectIntRecord(
+                "Prerender.Experimental.NewTabPage.TouchDuration.NotTaken", 0);
+        histogramWatcherBuilder.expectNoRecords(
+                "Prerender.Experimental.NewTabPage.TouchDuration.Taken");
+
+        HistogramWatcher histogramWatcher = histogramWatcherBuilder.build();
+
+        TileGroup tileGroup =
+                new TileGroup(
+                        mTileRenderer,
+                        mSuggestionsUiDelegate,
+                        mContextMenuManager,
+                        mTileGroupDelegate,
+                        mTileGroupObserver,
+                        mOfflinePageBridge);
+        tileGroup.onIconMadeAvailable(new GURL("https://foo.com"));
+        TileGroup.TileSetupDelegate tileSetupCallback = tileGroup.getTileSetupDelegate();
+        tileSetupCallback.createInteractionDelegate(mTile, mTileView);
+        MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent cancelEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+        verify(mTileView).setOnTouchListener(mOnTouchListenerCaptor.capture());
+        mOnTouchListenerCaptor.getValue().onTouch(mTileView, event);
+        mOnTouchListenerCaptor.getValue().onTouch(mTileView, cancelEvent);
+
+        histogramWatcher.assertExpected();
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
index 8f7c966f2..47cfb21 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/TileRendererTest.java
@@ -126,7 +126,7 @@
         doReturn(mTileSetupCallback).when(mTileSetupDelegate).createIconLoadCallback(any());
         doReturn(mTileInteractionDelegate)
                 .when(mTileSetupDelegate)
-                .createInteractionDelegate(any());
+                .createInteractionDelegate(any(), any());
         doReturn(mBitmap).when(mIconGenerator).generateIconForUrl(any(GURL.class));
     }
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 981059b..c67ab426 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-126.0.6474.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-126.0.6475.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 494e800..d60a195d 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -932,11 +932,8 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableLacrosForkZygotesAtLoginScreen)) {
-    // If prelaunched at login screen, block waiting for the user to login.
-    MaybeBlockAtLoginScreen(/*after_zygotes_fork=*/true);
-  }
+  // If prelaunched at login screen, block waiting for the user to login.
+  MaybeBlockAtLoginScreen(/*after_zygotes_fork=*/true);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -1507,14 +1504,6 @@
 
   crash_reporter::InitializeCrashKeys();
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableLacrosForkZygotesAtLoginScreen)) {
-    // If prelaunched at login screen, block waiting for the user to login.
-    MaybeBlockAtLoginScreen(/*after_zygotes_fork=*/false);
-  }
-#endif
-
 #if BUILDFLAG(IS_POSIX)
   ChromeCrashReporterClient::Create();
 #endif
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index c4ce02a47..a4bba0d9 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -1386,6 +1386,19 @@
   <message name="IDS_LOGIN_CATEGORIES_SCREEN_SKIP" translateable="false" desc="The label on the button that skips the categories screen">
     Skip
   </message>
+  <!-- string for new recommend apps screen-->
+  <message name="IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_LOADING" translateable="false" desc="The title of the recommended apps screen">
+   Please wait...
+  </message>
+  <message name="IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_TITLE" translateable="false" desc="The title of the recommended apps screen">
+    Top apps for your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph>
+  </message>
+  <message name="IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_SUBTITLE" translateable="false" desc="The subtitle of the recommended apps screen">
+    Installation happens after setup. Find more recommendations later in Explore app.
+  </message>
+  <message name="IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_SKIP" translateable="false" desc="The label on the button that skips the recommended apps screen">
+    Skip
+  </message>
   <!-- Strings for recommend apps screen -->
   <message name="IDS_LOGIN_RECOMMEND_APPS_SCREEN_TITLE" desc="The title of the dialog that recommend apps to user">
     Install apps for your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> from the Google Play Store
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index c0a4f53b..0737d2a 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -1060,6 +1060,9 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC" desc="Description for the profile switch interception bubble">
           A Chromium profile with this account already exists
         </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2" desc="This string is the body text for a dialog that appears when the user is already signed in to Chromium, but they just tried to sign in to a Google service (like Gmail) with a different Google Account that already has its own Chromium profile on this device. The body text is after the 'Switch to existing Chromium profile?' string. The tone should be inviting and helpful.">
+          You're already signed in as <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph> in another Chromium profile
+        </message>
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CONSUMER_BUBBLE_DESC" desc="Body of the web signin interception bubble">
           <ph name="EXISTING_USER">$1<ex>Elisa</ex></ph> is already signed in to this Chromium profile. To keep your browsing separate, Chromium can create your own profile for you.
         </message>
@@ -1075,6 +1078,9 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_SUBTITLE" desc="Subtitle of the Chromium Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
           To save and use your passwords and more on all your devices, sign in to Chromium
         </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER" desc="This string is the body text of a reminder message, which is intended to help users understand that they have been signed in to Chromium. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chromium Signin Bubble intercept; when asked whether the user wanted to sign in to Chromium as well, the user selected yes. After a period of time, this reminder message appears at the top right of Chromium (pointing at the Profile Menu) to remind the user that they made this choice a while ago. This body text string comes after the 'Welcome back, Elisa' string. We are focusing on the benefit that user enjoys, now that they're signed in to Chromium. The tone should be welcoming.">
+          You can use your passwords from your Google Account in Chromium while you're signed in
+        </message>
         <message name="IDS_CHROME_SIGNOUT_CONFIRMATION_PROMPT_VERIFY_BODY" desc="This is the subtitle of an error message dialog that appears when the user opens the Chromium Profile Menu and chooses 'Sign out of Chromium'. The error message appears when the user has saved some data (like a password or payment method) to Chromium, but Chromium wasn't able to save the data to the user's Google Account due to a sign-in error; but, the data has been saved locally to Chromium. This subtitle is after the 'Some data isn’t saved yet' string. The tone should be cautionary, because we want to explain to the user that their data is not saved to their Google Account. This string provides a path to resolution because it explains how the user can properly save their data before they sign out of Chromium.">
           Chromium needs to verify it’s you before some data can be saved in your Google Account and used on all your devices. If you sign out, this data will stay on this device.
         </message>
@@ -1149,9 +1155,6 @@
         Sign in to Chromium
       </message>
       <if expr="not use_titlecase">
-        <message name="IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON" desc="The text label of the Customize Chromium button in the Profile Menu.">
-          Customize your Chromium
-        </message>
         <message name="IDS_PROFILE_MENU_PROFILES_LIST_TITLE" desc="Title in the profile menu for the list of 'other Chromium profiles'.">
           Other Chromium profiles
         </message>
@@ -1164,9 +1167,6 @@
         </message>
       </if>
       <if expr="use_titlecase">
-        <message name="IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON" desc="The text label of the Customize Chromium button in the Profile Menu.">
-          Customize Your Chromium
-        </message>
         <message name="IDS_PROFILE_MENU_PROFILES_LIST_TITLE" desc="Title in the profile menu for the list of 'other Chromium profiles'.">
           Other Chromium Profiles
         </message>
diff --git a/chrome/app/chromium_strings_grd/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1 b/chrome/app/chromium_strings_grd/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1
deleted file mode 100644
index 680ddbb..0000000
--- a/chrome/app/chromium_strings_grd/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5e27ce9074b825d323570593a5fd41d262dcb1ef
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER.png.sha1
new file mode 100644
index 0000000..5ff95a1
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER.png.sha1
@@ -0,0 +1 @@
+820531b0abcb12d4e06f6d1d1194fd62ea56681f
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1 b/chrome/app/chromium_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1
new file mode 100644
index 0000000..ceead8e
--- /dev/null
+++ b/chrome/app/chromium_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1
@@ -0,0 +1 @@
+95c6455dddcdcae64fdd5a7bdc6052e622518ccd
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index e516a30..82ca6ab 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -12896,7 +12896,7 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL" desc="Label of the profile switch button in the web signin interception bubble">
           Ok
         </message>
-        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL_V2" desc="Label of the profile switch button in the web signin interception bubble">
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_CONTINUE_BUTTON_LABEL" desc="This string is the button for a dialog that appears when the user is already signed in to Chrome, but they just tried to sign in to a Google service (like Gmail) with a different Google Account that already has its own Chrome profile on this device. The button is after the 'Switch to existing Chrome profile?' string. When the user clicks this button, they will open a different existing Chrome profile that is already signed in to the other Google Account. The tone should be inviting and helpful.">
           Continue
         </message>
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_ACCEPT_TEXT" desc="Label of the Accept button of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account. It also uses the given name of the user.">
@@ -12908,15 +12908,34 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_ENTERPRISE_BUBBLE_DESC_MANAGED_BY_TOKEN" desc="Body of the web signin interception bubble when the new account is managed via a token">
           You just signed in a managed account, creating a new managed profile will allow you to access some resources linked to that account.
         </message>
-        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2" desc="Description for the profile switch interception bubble">
-          <ph name="NAME">$2<ex>Bob</ex></ph>'s profile is linked to <ph name="EMAIL">$1<ex>bob@gmail.com</ex></ph>
-        </message>
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_MANAGED_DISCLAIMER" desc="Enterprise disclaimer for a managed account in the signin interception bubble.">
           This new profile will be managed by your organization. <ph name="BEGIN_LINK">&lt;a href="$1" target=&quot;_blank&quot;&gt;<ex>https://help.google.com/articleLink</ex></ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
         </message>
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_MANAGED_BY_DISCLAIMER" desc="Enterprise disclaimer for a managed account in the signin interception bubble, with the manager domain name.">
           This new profile will be managed by <ph name="DOMAIN">$1<ex>example.com</ex></ph>. <ph name="BEGIN_LINK">&lt;a href="$2" target=&quot;_blank&quot;&gt;<ex>https://help.google.com/articleLink</ex></ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
         </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN" desc="This string is the headline of a confirmation message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. Now, this confirmation appears at the top right of Chrome (pointing at the Profile Menu) to confirm that their choice was effective. This headline string comes before the 'When you sign in on this device, you get your passwords and more from your Google Account' string. We are focusing on the benefit that user enjoys, now that they're signed in to Chrome. The tone should be welcoming.">
+          Welcome, <ph name="PROFILE_NAME">$1<ex>Elisa</ex></ph>
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN_REMINDER" desc="This string is the headline of a reminder message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. After a period of time, this reminder message appears at the top right of Chrome (pointing at the Profile Menu) to remind the user that they made this choice a while ago. This headline string comes before the 'You’re signed in to Chrome. You’ve got your passwords and more from your Google Account.' string. We are focusing on the benefit that user enjoys, now that they're signed in to Chrome. The tone should be welcoming.">
+          Welcome back, <ph name="PROFILE_NAME">$2<ex>Elisa</ex></ph>
+        </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN" desc="This string is the body text of a confirmation message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. Now, this confirmation appears at the top right of Chrome (pointing at the Profile Menu) to confirm that their choice was effective. This body text string comes after the 'Welcome, Elisa' string. We are focusing on the benefit that user enjoys, now that they're signed in to Chrome. The tone should be welcoming.">
+          When you sign in on this device, you get your passwords and more from your Google Account
+        </message>
+        <if expr="use_titlecase">
+          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_OK_BUTTON" desc="This button appears on a confirmation message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. Now, this confirmation appears at the top right of Chrome (pointing at the Profile Menu) to confirm that their choice was effective. This button string comes after the 'Welcome, Elisa' and 'When you sign in on this device, you get your passwords and more from your Google Account' strings. Users click 'Got it' to convey that they saw the message and want to hide the message.">
+            Got It
+          </message>
+        </if>
+        <if expr="not use_titlecase">
+          <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_OK_BUTTON" desc="This button appears on a confirmation message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. Now, this confirmation appears at the top right of Chrome (pointing at the Profile Menu) to confirm that their choice was effective. This button string comes after the 'Welcome, Elisa' and 'When you sign in on this device, you get your passwords and more from your Google Account' strings. Users click 'Got it' to convey that they saw the message and want to hide the message.">
+            Got it
+          </message>
+        </if>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_SETTINGS_BUTTON" desc="This button appears on a confirmation message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. Now, this confirmation appears at the top right of Chrome (pointing at the Profile Menu) to confirm that their choice was effective. This button string comes after the 'Welcome, Elisa' and 'When you sign in on this device, you get your passwords and more from your Google Account' strings. Users click 'Settings' to convey that they don't want Chrome to follow their choice in the future, so the user wants to go to Chrome's settings in order to change their choice.">
+          Settings
+        </message>
 
         <!-- Chrome Signout Confirmation Prompt -->
         <message name="IDS_CHROME_SIGNOUT_CONFIRMATION_PROMPT_TITLE"  desc="This is the title of an error message dialog that appears when the user opens the Chrome Profile Menu and chooses 'Sign out of Chrome'. The error message appears when the user has saved some data (like a password or payment method) to Chrome, but Chrome wasn't able to save the data to the user's Google Account due to a sign-in error; but, the data has been saved locally to Chrome. This title is before the 'Chrome needs to verify it’s you before some data can be saved in your Google Account and used on all your devices. If you sign out, this data will stay on this device.' string. The tone should be cautionary.">
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_OK_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_OK_BUTTON.png.sha1
new file mode 100644
index 0000000..8afa877
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_OK_BUTTON.png.sha1
@@ -0,0 +1 @@
+ce1ca12f4e901174c564a94c89f0792f80cb36b2
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_SETTINGS_BUTTON.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_SETTINGS_BUTTON.png.sha1
new file mode 100644
index 0000000..83b7ff4
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_SETTINGS_BUTTON.png.sha1
@@ -0,0 +1 @@
+7cdb57dfee0c76cf1bd506cd0bc21bd0c4d7408e
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN.png.sha1
new file mode 100644
index 0000000..c62f8f2f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN.png.sha1
@@ -0,0 +1 @@
+98308b8cc6ba5344397c57da4d6b07c182f27137
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN.png.sha1
new file mode 100644
index 0000000..498f42c
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN.png.sha1
@@ -0,0 +1 @@
+14179dc29d33c02149cdddbe392495e09e15f941
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN_REMINDER.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN_REMINDER.png.sha1
new file mode 100644
index 0000000..1bbf804
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TITLE_SIGNIN_REMINDER.png.sha1
@@ -0,0 +1 @@
+fb96f16aae861c2662fa7569489c1ea76d4b46c1
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL_V2.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL_V2.png.sha1
deleted file mode 100644
index 218ee73..0000000
--- a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL_V2.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7b055dcacda680767b62c358a4fd5ffe2f913014
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_CONTINUE_BUTTON_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_CONTINUE_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..41fec013
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_CONTINUE_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+58c56d4672b30c72f37dd4c48daf6e7e98af8041
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1 b/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1
deleted file mode 100644
index 218ee73..0000000
--- a/chrome/app/generated_resources_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-7b055dcacda680767b62c358a4fd5ffe2f913014
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index bd40972..73c1b10a 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1018,6 +1018,9 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC" desc="Description for the profile switch interception bubble">
           A Chrome profile with this account already exists
         </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2" desc="This string is the body text for a dialog that appears when the user is already signed in to Chrome, but they just tried to sign in to a Google service (like Gmail) with a different Google Account that already has its own Chrome profile on this device. The body text is after the 'Switch to existing Chrome profile?' string. The tone should be inviting and helpful.">
+          You're already signed in as <ph name="USER_EMAIL_ADDRESS">$1<ex>foo@gmail.com</ex></ph> in another Chrome profile
+        </message>
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_CONSUMER_BUBBLE_DESC" desc="Body of the web signin interception bubble">
           <ph name="EXISTING_USER">$1<ex>Elisa</ex></ph> is already signed in to this Chrome profile. To keep your browsing separate, Chrome can create your own profile for you.
         </message>
@@ -1033,6 +1036,9 @@
         <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_SUBTITLE" desc="Subtitle of the Chrome Signin Bubble intercept that is shown when a signed out user signs in on the web with it's first account.">
           To save and use your passwords and more on all your devices, sign in to Chrome
         </message>
+        <message name="IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER" desc="This string is the body text of a reminder message, which is intended to help users understand that they have been signed in to Chrome. The user previously signed in to a Google service (like Gmail or Maps), and saw the Chrome Signin Bubble intercept; when asked whether the user wanted to sign in to Chrome as well, the user selected yes. After a period of time, this reminder message appears at the top right of Chrome (pointing at the Profile Menu) to remind the user that they made this choice a while ago. This body text string comes after the 'Welcome back, Elisa' string. We are focusing on the benefit that user enjoys, now that they're signed in to Chrome. The tone should be welcoming.">
+          You can use your passwords from your Google Account in Chrome while you're signed in
+        </message>
         <message name="IDS_CHROME_SIGNOUT_CONFIRMATION_PROMPT_VERIFY_BODY" desc="This is the subtitle of an error message dialog that appears when the user opens the Chrome Profile Menu and chooses 'Sign out of Chrome'. The error message appears when the user has saved some data (like a password or payment method) to Chrome, but Chrome wasn't able to save the data to the user's Google Account due to a sign-in error; but, the data has been saved locally to Chrome. This subtitle is after the 'Some data isn’t saved yet' string. The tone should be cautionary, because we want to explain to the user that their data is not saved to their Google Account. This string provides a path to resolution because it explains how the user can properly save their data before they sign out of Chrome.">
           Chrome needs to verify it’s you before some data can be saved in your Google Account and used on all your devices. If you sign out, this data will stay on this device.
         </message>
@@ -1119,9 +1125,6 @@
         Sign in to Chrome
       </message>
       <if expr="not use_titlecase">
-        <message name="IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON" desc="The text label of the Customize Chrome button in the Profile Menu.">
-          Customize your Chrome
-        </message>
         <message name="IDS_PROFILE_MENU_PROFILES_LIST_TITLE" desc="Title in the profile menu for the list of 'other Chrome profiles'.">
           Other Chrome profiles
         </message>
@@ -1133,9 +1136,6 @@
         </message>
       </if>
       <if expr="use_titlecase">
-        <message name="IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON" desc="The text label of the Customize Chrome button in the Profile Menu.">
-          Customize Your Chrome
-        </message>
         <message name="IDS_PROFILE_MENU_PROFILES_LIST_TITLE" desc="Title in the profile menu for the list of 'other  profiles'.">
           Other Chrome Profiles
         </message>
diff --git a/chrome/app/google_chrome_strings_grd/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1
deleted file mode 100644
index e639d8e..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-82dca996901b65b567b333acb0ae42ddf7e5b291
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER.png.sha1
new file mode 100644
index 0000000..2e8c34d
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CHROME_SIGNIN_IPH_TEXT_SIGNIN_REMINDER.png.sha1
@@ -0,0 +1 @@
+ff8faea3fddae46cff345856c78c922ef809b703
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1
new file mode 100644
index 0000000..e6d73e9c
--- /dev/null
+++ b/chrome/app/google_chrome_strings_grd/IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2.png.sha1
@@ -0,0 +1 @@
+49b7a9fef3350ef34cc03fe12f8d642ae77eb153
\ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp
index 37f05ab..103232c 100644
--- a/chrome/app/profiles_strings.grdp
+++ b/chrome/app/profiles_strings.grdp
@@ -50,6 +50,9 @@
   <message name="IDS_AVATAR_BUTTON_SCHOOL" desc="Short label for the avatar button when the profile is a school profile." meaning="Using a school profile">
     School
   </message>
+  <message name="IDS_AVATAR_BUTTON_GREETING" desc="Greeting that appears at the top of Chrome after the user signs in. It becomes part of the entrypoint for the Chrome Profile Menu. The greeting is casual and friendly, and it invokes the user's name.">
+    Hi, <ph name="PROFILE_NAME">$1<ex>User</ex></ph>
+  </message>
 
   <!-- Multi-profile -->
   <message name="IDS_PROFILES_MENU_NAME" desc="The name of menu for profiles (Mac and linux) and name for the launcher menu section (Mac).">
@@ -157,6 +160,9 @@
     <message name="IDS_PROFILES_OPEN_SYNC_SETTINGS_BUTTON" desc="Button in the profile menu to open sync settings when sync is on.">
       Sync is on
     </message>
+    <message name="IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON" desc="Button in the avatar menu bubble view for editing the current profile.">
+      Customize profile
+    </message>
   </if>
   <if expr="use_titlecase">
     <message name="IDS_PROFILES_LIST_PROFILES_TITLE" desc="Title in the profile menu for the list of 'other profiles'.">
@@ -183,6 +189,9 @@
     <message name="IDS_PROFILES_OPEN_SYNC_SETTINGS_BUTTON" desc="Button in the profile menu to open sync settings when sync is on.">
       Sync Is On
     </message>
+    <message name="IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON" desc="Button in the avatar menu bubble view for editing the current profile. ">
+      Customize Profile
+    </message>
   </if>
   <message name="IDS_PROFILES_MANAGED_BY" desc="Label shown under the avatar in the avatar menu bubble view stating that the account is managed.">
     managed by <ph name="VALUE">$1<ex>1</ex></ph>
@@ -193,7 +202,7 @@
   <message name="IDS_PROFILES_MANAGE_PROFILES_MANAGED_TOOLTIP" desc="Icon in the avatar menu bubble view explaining why selecting other profiles is disabled.">
     Your administrator has disabled other profiles
   </message>
-  <message name="IDS_PROFILES_CUSTOMIZE_PROFILE_BUTTON_TOOLTIP" desc="Button in the avatar menu bubble view for editing the current profile.">
+  <message name="IDS_PROFILES_CUSTOMIZE_PROFILE_BUTTON_TOOLTIP" desc="Tooltip of the customize profile button in the avatar menu bubble view for editing the current profile. The button itself has no text, it is only the edit icon.">
     Customize profile
   </message>
   <message name="IDS_PROFILES_GUEST_PROFILE_NAME" desc="Name of the guest profile.">
diff --git a/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_GREETING.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_GREETING.png.sha1
new file mode 100644
index 0000000..2e1ea576
--- /dev/null
+++ b/chrome/app/profiles_strings_grdp/IDS_AVATAR_BUTTON_GREETING.png.sha1
@@ -0,0 +1 @@
+a61356768c23f237a3f0bd223e3ed965520e19cb
\ No newline at end of file
diff --git a/chrome/app/profiles_strings_grdp/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1 b/chrome/app/profiles_strings_grdp/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1
new file mode 100644
index 0000000..95ec3856
--- /dev/null
+++ b/chrome/app/profiles_strings_grdp/IDS_PROFILE_MENU_CUSTOMIZE_PROFILE_BUTTON.png.sha1
@@ -0,0 +1 @@
+7f21940ed6d631d719b7854d313d585ed567959a
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3f670b01..0676a9b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -9446,11 +9446,6 @@
      flag_descriptions::kLacrosLaunchAtLoginScreenName,
      flag_descriptions::kLacrosLaunchAtLoginScreenDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(crosapi::browser_util::kLacrosLaunchAtLoginScreen)},
-    {"lacros-fork-zygotes-at-login-screen",
-     flag_descriptions::kLacrosForkZygotesAtLoginScreenName,
-     flag_descriptions::kLacrosForkZygotesAtLoginScreenDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(
-         crosapi::browser_util::kLacrosForkZygotesAtLoginScreen)},
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index ebda0531..0aa463b 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -995,8 +995,6 @@
     "customization/customization_wallpaper_util.h",
     "data_migration/data_migration_factory.cc",
     "data_migration/data_migration_factory.h",
-    "dbus/arc_tracing_service_provider.cc",
-    "dbus/arc_tracing_service_provider.h",
     "dbus/ash_dbus_helper.cc",
     "dbus/ash_dbus_helper.h",
     "dbus/chrome_features_service_provider.cc",
@@ -4638,7 +4636,6 @@
 
 action("dbus_service_files") {
   sources = [
-    "dbus/org.chromium.ArcTracing.conf",
     "dbus/org.chromium.ChromeFeaturesService.conf",
     "dbus/org.chromium.ComponentUpdaterService.conf",
     "dbus/org.chromium.CryptohomeKeyDelegate.conf",
@@ -6497,6 +6494,7 @@
     "//chromeos/ash/components/dbus/smbprovider",
     "//chromeos/ash/components/dbus/smbprovider:proto",
     "//chromeos/ash/components/dbus/spaced",
+    "//chromeos/ash/components/dbus/spaced:spaced_proto",
     "//chromeos/ash/components/dbus/system_clock",
     "//chromeos/ash/components/dbus/system_proxy",
     "//chromeos/ash/components/dbus/system_proxy:system_proxy_proto",
diff --git a/chrome/browser/ash/arc/tracing/overview_tracing_handler.cc b/chrome/browser/ash/arc/tracing/overview_tracing_handler.cc
index 27c5cd6..25ea9d2 100644
--- a/chrome/browser/ash/arc/tracing/overview_tracing_handler.cc
+++ b/chrome/browser/ash/arc/tracing/overview_tracing_handler.cc
@@ -434,10 +434,6 @@
   return active_trace_ != nullptr;
 }
 
-bool OverviewTracingHandler::arc_window_is_active() const {
-  return arc_active_window_ != nullptr;
-}
-
 base::TimeTicks OverviewTracingHandler::SystemTicksNow() {
   return TRACE_TIME_TICKS_NOW();
 }
diff --git a/chrome/browser/ash/arc/tracing/overview_tracing_handler.h b/chrome/browser/ash/arc/tracing/overview_tracing_handler.h
index f0c2b8a..6f00556 100644
--- a/chrome/browser/ash/arc/tracing/overview_tracing_handler.h
+++ b/chrome/browser/ash/arc/tracing/overview_tracing_handler.h
@@ -98,7 +98,6 @@
   void StopTracing();
 
   bool is_tracing() const;
-  bool arc_window_is_active() const;
 
  private:
   GraphicsModelReadyCb graphics_model_ready_;
diff --git a/chrome/browser/ash/chrome_browser_main_parts_ash.cc b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
index b9f61322..87a6cc59 100644
--- a/chrome/browser/ash/chrome_browser_main_parts_ash.cc
+++ b/chrome/browser/ash/chrome_browser_main_parts_ash.cc
@@ -68,7 +68,6 @@
 #include "chrome/browser/ash/crosapi/lacros_availability_policy_observer.h"
 #include "chrome/browser/ash/crosapi/lacros_data_backward_migration_mode_policy_observer.h"
 #include "chrome/browser/ash/crostini/crostini_unsupported_action_notifier.h"
-#include "chrome/browser/ash/dbus/arc_tracing_service_provider.h"
 #include "chrome/browser/ash/dbus/ash_dbus_helper.h"
 #include "chrome/browser/ash/dbus/chrome_features_service_provider.h"
 #include "chrome/browser/ash/dbus/component_updater_service_provider.h"
@@ -180,7 +179,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/startup_data.h"
 #include "chrome/browser/task_manager/task_manager_interface.h"
-#include "chrome/browser/tracing/chrome_tracing_delegate.h"
 #include "chrome/browser/ui/ash/assistant/assistant_browser_delegate_impl.h"
 #include "chrome/browser/ui/ash/assistant/assistant_state_client.h"
 #include "chrome/browser/ui/ash/fwupd_download_client_impl.h"
@@ -522,14 +520,6 @@
             std::make_unique<DlpFilesPolicyServiceProvider>()));
 
     if (arc::IsArcVmEnabled()) {
-      if (ChromeTracingDelegate::IsSystemWideTracingEnabled()) {
-        arc_tracing_service_ = CrosDBusService::Create(
-            system_bus, arc::tracing::kArcTracingServiceName,
-            dbus::ObjectPath(arc::tracing::kArcTracingServicePath),
-            CrosDBusService::CreateServiceProviderList(
-                std::make_unique<ArcTracingServiceProvider>()));
-      }
-
       libvda_service_ = CrosDBusService::Create(
           system_bus, libvda::kLibvdaServiceName,
           dbus::ObjectPath(libvda::kLibvdaServicePath),
@@ -587,7 +577,6 @@
     LoginState::Shutdown();
     NetworkCertLoader::Shutdown();
     TPMTokenLoader::Shutdown();
-    arc_tracing_service_.reset();
     proxy_resolution_service_.reset();
     kiosk_info_service_.reset();
     metrics_event_service_.reset();
@@ -642,7 +631,6 @@
   std::unique_ptr<CrosDBusService> fusebox_service_;
   std::unique_ptr<CrosDBusService> mojo_connection_service_;
   std::unique_ptr<CrosDBusService> dlp_files_policy_service_;
-  std::unique_ptr<CrosDBusService> arc_tracing_service_;
 };
 
 }  // namespace internal
diff --git a/chrome/browser/ash/crosapi/browser_launcher.cc b/chrome/browser/ash/crosapi/browser_launcher.cc
index e008e6e3..549317e 100644
--- a/chrome/browser/ash/crosapi/browser_launcher.cc
+++ b/chrome/browser/ash/crosapi/browser_launcher.cc
@@ -284,9 +284,6 @@
     clear_shared_resource_file = true;
   }
 
-  params.enable_fork_zygotes_at_login_screen = base::FeatureList::IsEnabled(
-      browser_util::kLacrosForkZygotesAtLoginScreen);
-
   // Clear shared resource file cache if it's initial lacros launch after ash
   // reboot. If not, rename shared resource file cache to temporal name on
   // Lacros launch.
@@ -600,11 +597,6 @@
     // ash behavior(clear or move cached shared resource file at lacros launch).
     parameters.command_line.AppendSwitch(switches::kEnableResourcesFileSharing);
   }
-
-  if (params.enable_fork_zygotes_at_login_screen) {
-    parameters.command_line.AppendSwitch(
-        switches::kEnableLacrosForkZygotesAtLoginScreen);
-  }
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/crosapi/browser_launcher.h b/chrome/browser/ash/crosapi/browser_launcher.h
index 081fe1d..90aecbe 100644
--- a/chrome/browser/ash/crosapi/browser_launcher.h
+++ b/chrome/browser/ash/crosapi/browser_launcher.h
@@ -70,9 +70,6 @@
     // Sets true if Lacros uses resource file sharing.
     bool enable_resource_file_sharing = false;
 
-    // Sets true if Lacros forks Zygotes at login screen.
-    bool enable_fork_zygotes_at_login_screen = false;
-
     // Any additional args to start lacros with.
     std::vector<std::string> lacros_additional_args;
   };
diff --git a/chrome/browser/ash/crosapi/browser_launcher_unittest.cc b/chrome/browser/ash/crosapi/browser_launcher_unittest.cc
index e3e3189..5804a6e 100644
--- a/chrome/browser/ash/crosapi/browser_launcher_unittest.cc
+++ b/chrome/browser/ash/crosapi/browser_launcher_unittest.cc
@@ -44,6 +44,7 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
 
 namespace crosapi {
 
@@ -205,8 +206,7 @@
 
   // Add feature and check if it's reflected to `params`.
   base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      browser_util::kLacrosForkZygotesAtLoginScreen);
+  scoped_features.InitAndEnableFeature(features::kLacrosResourcesFileSharing);
 
   BrowserLauncher::LaunchParamsFromBackground params;
   base::test::TestFuture<void> future;
@@ -215,7 +215,7 @@
       /*launching_at_login_screen=*/false, future.GetCallback(), params);
 
   EXPECT_TRUE(future.Wait());
-  EXPECT_TRUE(params.enable_fork_zygotes_at_login_screen);
+  EXPECT_TRUE(params.enable_resource_file_sharing);
 }
 
 TEST_F(BrowserLauncherTest, BackgroundWorkPreLaunchOnLaunchingAtLoginScreen) {
@@ -226,8 +226,7 @@
 
   // Add feature and check if it's reflected to `params`.
   base::test::ScopedFeatureList scoped_features;
-  scoped_features.InitAndEnableFeature(
-      browser_util::kLacrosForkZygotesAtLoginScreen);
+  scoped_features.InitAndEnableFeature(features::kLacrosResourcesFileSharing);
 
   BrowserLauncher::LaunchParamsFromBackground params;
   base::test::TestFuture<void> future;
@@ -236,7 +235,7 @@
       /*launching_at_login_screen=*/true, future.GetCallback(), params);
 
   EXPECT_TRUE(future.Wait());
-  EXPECT_TRUE(params.enable_fork_zygotes_at_login_screen);
+  EXPECT_TRUE(params.enable_resource_file_sharing);
 }
 
 // TODO(elkurin): Add kLacrosChromeAdditionalArgsFile unit test.
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc
index c8d857c1..5126c9a 100644
--- a/chrome/browser/ash/crosapi/browser_util.cc
+++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -62,10 +62,6 @@
              "LacrosLaunchAtLoginScreen",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kLacrosForkZygotesAtLoginScreen,
-             "LacrosForkZygotesAtLoginScreen",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 namespace {
 
 // At session start the value for LacrosAvailability logic is applied and the
diff --git a/chrome/browser/ash/crosapi/browser_util.h b/chrome/browser/ash/crosapi/browser_util.h
index e2322b7..eb07157 100644
--- a/chrome/browser/ash/crosapi/browser_util.h
+++ b/chrome/browser/ash/crosapi/browser_util.h
@@ -50,9 +50,6 @@
 // Enable pre-launching Lacros at login screen.
 BASE_DECLARE_FEATURE(kLacrosLaunchAtLoginScreen);
 
-// Enable forking Zygotes at login screen when prelaunching.
-BASE_DECLARE_FEATURE(kLacrosForkZygotesAtLoginScreen);
-
 // Indicates how the decision for the usage of Lacros has been made.
 enum class LacrosLaunchSwitchSource {
   // It is unknown yet if and how Lacros will be used.
diff --git a/chrome/browser/ash/dbus/DEPS b/chrome/browser/ash/dbus/DEPS
index 0f0183c..218ceba 100644
--- a/chrome/browser/ash/dbus/DEPS
+++ b/chrome/browser/ash/dbus/DEPS
@@ -17,7 +17,6 @@
   "+chrome/browser/ash/app_mode",
   "+chrome/browser/ash/arc/fileapi",
   "+chrome/browser/ash/arc/session",
-  "+chrome/browser/ash/arc/tracing",
   "+chrome/browser/ash/arc/video",
   "+chrome/browser/ash/borealis",
   "+chrome/browser/ash/crostini",
diff --git a/chrome/browser/ash/dbus/arc_tracing_service_provider.cc b/chrome/browser/ash/dbus/arc_tracing_service_provider.cc
deleted file mode 100644
index 5350be9a..0000000
--- a/chrome/browser/ash/dbus/arc_tracing_service_provider.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ash/dbus/arc_tracing_service_provider.h"
-
-#include "base/containers/span.h"
-#include "base/files/file_path.h"
-#include "base/functional/bind.h"
-#include "base/strings/string_util.h"
-#include "base/task/thread_pool.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/ash/arc/tracing/overview_tracing_handler.h"
-#include "dbus/bus.h"
-#include "dbus/message.h"
-#include "third_party/cros_system_api/dbus/service_constants.h"
-
-namespace ash {
-namespace {
-constexpr int kMaxStatusMessagesCount = 20;
-}  // namespace
-
-ArcTracingServiceProvider::ArcTracingServiceProvider() = default;
-
-ArcTracingServiceProvider::~ArcTracingServiceProvider() = default;
-
-void ArcTracingServiceProvider::Start(
-    scoped_refptr<dbus::ExportedObject> exported_object) {
-  exported_object->ExportMethod(
-      arc::tracing::kArcTracingInterfaceName,
-      arc::tracing::kArcTracingStartMethod,
-      base::BindRepeating(&ArcTracingServiceProvider::StartTrace,
-                          weak_ptr_factory_.GetWeakPtr()),
-      base::BindOnce(&ArcTracingServiceProvider::OnExported,
-                     weak_ptr_factory_.GetWeakPtr()));
-  exported_object->ExportMethod(
-      arc::tracing::kArcTracingInterfaceName,
-      arc::tracing::kArcTracingGetStatusMethod,
-      base::BindRepeating(&ArcTracingServiceProvider::GetStatus,
-                          weak_ptr_factory_.GetWeakPtr()),
-      base::BindOnce(&ArcTracingServiceProvider::OnExported,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-void ArcTracingServiceProvider::AddStatusMessage(std::string_view status) {
-  msgs_.emplace_back(status);
-  if (msgs_.size() > kMaxStatusMessagesCount) {
-    msgs_.pop_front();
-  }
-}
-
-void ArcTracingServiceProvider::OnExported(const std::string& interface_name,
-                                           const std::string& method_name,
-                                           bool success) {
-  LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "."
-                          << method_name;
-}
-
-void ArcTracingServiceProvider::OnTraceEnd(
-    std::unique_ptr<arc::OverviewTracingResult> result) {
-  if (result->path.empty()) {
-    AddStatusMessage(result->status);
-  } else {
-    AddStatusMessage(
-        base::StrCat({result->status, ": ", result->path.value()}));
-  }
-  // Do this in a separate task because the handler may still have code to run
-  // after we return.
-  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE,
-      base::BindOnce([](std::unique_ptr<arc::OverviewTracingHandler> handler) {},
-                     std::move(handler_)));
-}
-
-void ArcTracingServiceProvider::StartTrace(
-    dbus::MethodCall* method_call,
-    dbus::ExportedObject::ResponseSender response_sender) {
-  auto response = dbus::Response::FromMethodCall(method_call);
-  dbus::MessageWriter writer(response.get());
-
-  std::string result = StartTraceImpl(method_call);
-  writer.AppendString(result);
-  AddStatusMessage(result);
-  std::move(response_sender).Run(std::move(response));
-}
-
-std::string ArcTracingServiceProvider::StartTraceImpl(
-    dbus::MethodCall* method_call) {
-  if (handler_) {
-    return "Trace already in progress";
-  }
-
-  dbus::MessageReader reader(method_call);
-
-  double max_trace_seconds;
-  if (!reader.PopDouble(&max_trace_seconds)) {
-    return "Expect max trace time as type double in seconds";
-  }
-  auto handler = std::make_unique<arc::OverviewTracingHandler>(
-      arc::OverviewTracingHandler::ArcWindowFocusChangeCb());
-
-  auto max_trace_time = base::Seconds(max_trace_seconds);
-  if (max_trace_time < base::Seconds(1)) {
-    return "Max trace seconds out of range; must be >= 1";
-  }
-
-  if (!handler->arc_window_is_active()) {
-    return "ARC window isn't active";
-  }
-
-  handler_ = std::move(handler);
-  handler_->set_graphics_model_ready_cb(base::BindRepeating(
-      &ArcTracingServiceProvider::OnTraceEnd, weak_ptr_factory_.GetWeakPtr()));
-  handler_->set_start_build_model_cb(
-      base::BindRepeating(&ArcTracingServiceProvider::AddStatusMessage,
-                          weak_ptr_factory_.GetWeakPtr(), "Building model..."));
-  handler_->StartTracing(base::FilePath("/tmp"), max_trace_time);
-
-  return "Trace started";
-}
-
-void ArcTracingServiceProvider::GetStatus(
-    dbus::MethodCall* method_call,
-    dbus::ExportedObject::ResponseSender response_sender) {
-  auto response = dbus::Response::FromMethodCall(method_call);
-  dbus::MessageWriter writer(response.get());
-  for (const auto& msg : msgs_) {
-    writer.AppendString(msg);
-  }
-  std::move(response_sender).Run(std::move(response));
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ash/dbus/arc_tracing_service_provider.h b/chrome/browser/ash/dbus/arc_tracing_service_provider.h
deleted file mode 100644
index fb99ae04..0000000
--- a/chrome/browser/ash/dbus/arc_tracing_service_provider.h
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_DBUS_ARC_TRACING_SERVICE_PROVIDER_H_
-#define CHROME_BROWSER_ASH_DBUS_ARC_TRACING_SERVICE_PROVIDER_H_
-
-#include <deque>
-#include <string>
-
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "chromeos/ash/components/dbus/services/cros_dbus_service.h"
-#include "dbus/exported_object.h"
-#include "mojo/public/cpp/system/handle.h"
-
-namespace dbus {
-class MethodCall;
-}  // namespace dbus
-
-namespace arc {
-class OverviewTracingHandler;
-struct OverviewTracingResult;
-}  // namespace
-
-namespace ash {
-
-// This class exports a D-Bus method that libvda will call to establish a
-// mojo pipe to the VideoAcceleratorFactory interface.
-class ArcTracingServiceProvider
-    : public CrosDBusService::ServiceProviderInterface {
- public:
-  ArcTracingServiceProvider();
-
-  ArcTracingServiceProvider(const ArcTracingServiceProvider&) = delete;
-  ArcTracingServiceProvider& operator=(const ArcTracingServiceProvider&) =
-      delete;
-
-  ~ArcTracingServiceProvider() override;
-
-  // CrosDBusService::ServiceProviderInterface:
-  void Start(scoped_refptr<dbus::ExportedObject> exported_object) override;
-
- private:
-  // Adds a message to the circular log buffer, possibly removing the oldest
-  // entry.
-  void AddStatusMessage(std::string_view status);
-
-  // Called from ExportedObject when a handler is exported as a D-Bus
-  // method or failed to be exported.
-  void OnExported(const std::string& interface_name,
-                  const std::string& method_name,
-                  bool success);
-
-  void OnTraceEnd(std::unique_ptr<arc::OverviewTracingResult> result);
-
-  void StartTrace(dbus::MethodCall* method_call,
-                  dbus::ExportedObject::ResponseSender response_sender);
-
-  // Returns error or success status message.
-  std::string StartTraceImpl(dbus::MethodCall* method_call);
-
-  // Responds with (Gets) the messages in the circular log buffer, oldest first.
-  void GetStatus(dbus::MethodCall* method_call,
-                 dbus::ExportedObject::ResponseSender response_sender);
-
-  // This is only present if a trace is running.
-  std::unique_ptr<arc::OverviewTracingHandler> handler_;
-
-  // The last few status messages.
-  std::deque<std::string> msgs_;
-
-  // Keep this last so that all weak pointers will be invalidated at the
-  // beginning of destruction.
-  base::WeakPtrFactory<ArcTracingServiceProvider> weak_ptr_factory_{this};
-};
-
-}  // namespace ash
-
-#endif  // CHROME_BROWSER_ASH_DBUS_ARC_TRACING_SERVICE_PROVIDER_H_
diff --git a/chrome/browser/ash/dbus/org.chromium.ArcTracing.conf b/chrome/browser/ash/dbus/org.chromium.ArcTracing.conf
deleted file mode 100644
index 46f2fe79..0000000
--- a/chrome/browser/ash/dbus/org.chromium.ArcTracing.conf
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
-  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<!--
-  Copyright 2024 The Chromium Authors
-  Use of this source code is governed by a BSD-style license that can be
-  found in the LICENSE file.
--->
-<busconfig>
-  <policy user="chronos">
-    <allow own="org.chromium.ArcTracing"/>
-    <allow receive_sender="org.chromium.ArcTracing"
-           receive_interface="org.chromium.ArcTracing"/>
-    <allow send_destination="org.chromium.ArcTracing"
-           send_interface="org.chromium.ArcTracing"/>
-  </policy>
-</busconfig>
diff --git a/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc b/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc
index 02bf9dd2..04a9e821 100644
--- a/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/cloud_file_system_unittest.cc
@@ -99,8 +99,11 @@
               RemoveItems,
               (RemovedItemStatsCallback callback),
               (override));
+  MOCK_METHOD(const SizeInfo, GetSize, (), (const override));
+  MOCK_METHOD(void, SetMaxBytesOnDisk, (int64_t), (override));
+  MOCK_METHOD(base::WeakPtr<ContentCache>, GetWeakPtr, (), (override));
 
-  base::WeakPtr<MockContentCache> GetWeakPtr() {
+  base::WeakPtr<MockContentCache> GetMockWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
   }
 
@@ -157,7 +160,7 @@
     std::unique_ptr<MockContentCache> mock_content_cache =
         std::make_unique<MockContentCache>();
     base::WeakPtr<MockContentCache> cache_weak_ptr =
-        mock_content_cache->GetWeakPtr();
+        mock_content_cache->GetMockWeakPtr();
     EXPECT_CALL(*mock_content_cache, SetOnItemEvictedCallback(_)).Times(1);
     EXPECT_CALL(mock_cache_manager_,
                 InitializeForProvider(_, IsNotNullCallback()))
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc
index d43841d..9c18481 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.cc
@@ -40,18 +40,15 @@
 
 }  // namespace
 
-CacheManagerImpl::CacheManagerImpl(const base::FilePath& profile_path,
-                                   bool in_memory_only)
+CacheManagerImpl::CacheManagerImpl(const base::FilePath& profile_path)
     : root_content_cache_directory_(
-          profile_path.Append(kFspContentCacheDirName)),
-      in_memory_only_(in_memory_only) {}
+          profile_path.Append(kFspContentCacheDirName)) {}
 
 CacheManagerImpl::~CacheManagerImpl() = default;
 
 std::unique_ptr<CacheManager> CacheManagerImpl::Create(
-    const base::FilePath& profile_path,
-    bool in_memory_only) {
-  return std::make_unique<CacheManagerImpl>(profile_path, in_memory_only);
+    const base::FilePath& profile_path) {
+  return std::make_unique<CacheManagerImpl>(profile_path);
 }
 
 void CacheManagerImpl::InitializeForProvider(
@@ -65,12 +62,6 @@
     return;
   }
 
-  if (in_memory_only_) {
-    OnProviderDirectoryCreationComplete(
-        std::move(callback), cache_directory_path, base::File::FILE_OK);
-    return;
-  }
-
   blocking_task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE, base::BindOnce(&CreateProviderDirectory, cache_directory_path),
       base::BindOnce(&CacheManagerImpl::OnProviderDirectoryCreationComplete,
@@ -87,20 +78,14 @@
   }
   const base::FilePath base64_encoded_provider_folder_name =
       cache_directory_path.BaseName();
-  if (!initialized_providers_.contains(base64_encoded_provider_folder_name)) {
+  if (!initialized_caches_.contains(base64_encoded_provider_folder_name)) {
     OnUninitializeForProvider(base64_encoded_provider_folder_name,
                               base::File::FILE_ERROR_NOT_FOUND);
     return;
   }
 
-  // Remove the provider from the set.
-  initialized_providers_.erase(base64_encoded_provider_folder_name);
-
-  if (in_memory_only_) {
-    OnUninitializeForProvider(base64_encoded_provider_folder_name,
-                              base::File::FILE_OK);
-    return;
-  }
+  // Remove the provider from the map.
+  initialized_caches_.erase(base64_encoded_provider_folder_name);
 
   // Attempt to delete the cache directory to ensure dead files don't remain
   // on the user's disk as the logic changes in this experimental design phase.
@@ -127,15 +112,15 @@
   }
   const base::FilePath base64_encoded_provider_folder_name =
       cache_directory_path.BaseName();
-  return initialized_providers_.contains(base64_encoded_provider_folder_name);
+  return initialized_caches_.contains(base64_encoded_provider_folder_name);
 }
 
-void CacheManagerImpl::AddObserver(Observer* observer) {
+void CacheManagerImpl::AddObserver(CacheManager::Observer* observer) {
   DCHECK(observer);
   observers_.AddObserver(observer);
 }
 
-void CacheManagerImpl::RemoveObserver(Observer* observer) {
+void CacheManagerImpl::RemoveObserver(CacheManager::Observer* observer) {
   DCHECK(observer);
   observers_.RemoveObserver(observer);
 }
@@ -160,9 +145,7 @@
   db_task_runner->PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&InitializeContextDatabase,
-                     (in_memory_only_)
-                         ? base::FilePath()
-                         : cache_directory_path.Append("context.db")),
+                     cache_directory_path.Append("context.db")),
       base::BindOnce(&CacheManagerImpl::OnProviderContextDatabaseSetup,
                      weak_ptr_factory_.GetWeakPtr(), cache_directory_path,
                      std::move(callback), db_task_runner));
@@ -199,16 +182,37 @@
     std::unique_ptr<ContentCache> content_cache,
     const base::FilePath& base64_encoded_provider_folder_name,
     FileErrorOrContentCacheCallback callback) {
-  initialized_providers_.emplace(base64_encoded_provider_folder_name);
+  SpacedClient* const spaced = ash::SpacedClient::Get();
+  initialized_caches_.emplace(base64_encoded_provider_folder_name,
+                              content_cache->GetWeakPtr());
+
+  GetFreeSpaceAndResizeInitializedCaches();
+  // If we're not already observing spaced then start so the disk space can be
+  // retrieved.
+  if (!spaced_client_.IsObserving() && spaced && spaced->IsConnected()) {
+    spaced_client_.Observe(spaced);
+  }
   OnProviderInitializationComplete(base64_encoded_provider_folder_name,
                                    std::move(callback),
                                    std::move(content_cache));
 }
+
 void CacheManagerImpl::OnUninitializeForProvider(
     const base::FilePath& base64_encoded_provider_folder_name,
     base::File::Error result) {
   LOG_IF(ERROR, result != base::File::FILE_OK)
       << "Failed to uninitialize provider";
+  // In the instance that the provider has uninitialized, the remaining caches
+  // should be resized to take up the free space and if not more caches are
+  // initialized after this one, stop observing the free space.
+  if (initialized_caches_.size() > 0) {
+    GetFreeSpaceAndResizeInitializedCaches();
+  } else {
+    SpacedClient* const spaced = ash::SpacedClient::Get();
+    if (spaced_client_.IsObserving() && spaced && spaced->IsConnected()) {
+      spaced_client_.Reset();
+    }
+  }
   // Notify all observers.
   for (auto& observer : observers_) {
     observer.OnProviderUninitialized(base64_encoded_provider_folder_name,
@@ -245,10 +249,89 @@
       error_or_content_cache.error_or(base::File::FILE_OK);
   std::move(callback).Run(std::move(error_or_content_cache));
 
-  for (Observer& observer : observers_) {
+  for (CacheManager::Observer& observer : observers_) {
     observer.OnProviderInitializationComplete(
         base64_encoded_provider_folder_name, result);
   }
 }
 
+void CacheManagerImpl::GetFreeSpaceAndResizeInitializedCaches() {
+  ash::SpacedClient::Get()->GetFreeDiskSpace(
+      root_content_cache_directory_.value(),
+      base::BindOnce(
+          [](base::WeakPtr<CacheManagerImpl> weak_ptr,
+             std::optional<int64_t> free_space_in_bytes) {
+            LOG_IF(ERROR, !free_space_in_bytes.has_value())
+                << "Failed getting free space";
+            if (weak_ptr && free_space_in_bytes.has_value()) {
+              weak_ptr->OnFreeSpace(free_space_in_bytes.value());
+            }
+          },
+          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CacheManagerImpl::OnFreeSpace(int64_t free_space_in_bytes) {
+  if (initialized_caches_.empty()) {
+    return;
+  }
+  VLOG(1) << "Free space: " << free_space_in_bytes;
+
+  // TODO(b/339540246): The logic below currently just divides the space up by
+  // the number of initialized caches. For now, as ODFS is the only expected
+  // content cache, this is ok. However, this should be smarter and allow for
+  // variable size caches.
+  int64_t total_bytes_of_all_caches = 0;
+  for (auto it = initialized_caches_.begin(); it != initialized_caches_.end();
+       ++it) {
+    base::WeakPtr<ContentCache> cache_weak_ptr = it->second;
+    if (!cache_weak_ptr) {
+      initialized_caches_.erase(it);
+      continue;
+    }
+    total_bytes_of_all_caches += cache_weak_ptr->GetSize().total_bytes_on_disk;
+  }
+
+  // The max size of all caches should not exceed the total size of (free space
+  // + used space of all caches) * 0.6.
+  int64_t max_size_of_all_caches_in_bytes =
+      (total_bytes_of_all_caches + free_space_in_bytes) * 0.6;
+
+  // The max size of each individual cache should never go past 5GB either.
+  int64_t max_bytes_per_cache =
+      std::min(static_cast<int64_t>(max_size_of_all_caches_in_bytes) /
+                   static_cast<int64_t>(initialized_caches_.size()),
+               kMaxAllowedCacheSizeInBytes);
+
+  for (const auto& [base64_encoded_provider_folder_name, content_cache] :
+       initialized_caches_) {
+    if (!content_cache) {
+      continue;
+    }
+    int64_t old_max_size = content_cache->GetSize().max_bytes_on_disk;
+    if (old_max_size == max_bytes_per_cache) {
+      VLOG(2) << "No cache resize needed: "
+              << base64_encoded_provider_folder_name;
+      VLOG(2) << " > Old max bytes: " << old_max_size;
+      VLOG(2) << " > New max bytes: " << max_bytes_per_cache;
+      continue;
+    }
+
+    // TODO(b/330602540): Move these logs into the `ContentCache` once
+    // eviction has been implemented.
+    VLOG(1) << "Potential cache resize event: "
+            << base64_encoded_provider_folder_name;
+    VLOG(1) << " > Old max bytes: " << old_max_size;
+    VLOG(1) << " > New max bytes: " << max_bytes_per_cache;
+    content_cache->SetMaxBytesOnDisk(max_bytes_per_cache);
+  }
+}
+
+void CacheManagerImpl::OnSpaceUpdate(const SpaceEvent& event) {
+  if (event.free_space_bytes() < 0) {
+    LOG(ERROR) << "Free space is negative";
+    return;
+  }
+  OnFreeSpace(event.free_space_bytes());
+}
+
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h
index 2eabd61f..f7c0366 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl.h
@@ -11,8 +11,10 @@
 #include "base/files/file_error_or.h"
 #include "base/files/file_path.h"
 #include "base/functional/callback_forward.h"
+#include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/scoped_observation.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -20,13 +22,17 @@
 #include "chrome/browser/ash/file_system_provider/content_cache/cache_manager.h"
 #include "chrome/browser/ash/file_system_provider/content_cache/content_cache.h"
 #include "chrome/browser/ash/file_system_provider/content_cache/context_database.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 
 namespace ash::file_system_provider {
 
-class CacheManagerImpl : public CacheManager {
+// The max size of each individual content cache in bytes, i.e. 5GB.
+inline constexpr int64_t kMaxAllowedCacheSizeInBytes = int64_t(5) << 30;
+
+class CacheManagerImpl : public CacheManager,
+                         public ash::SpacedClient::Observer {
  public:
-  explicit CacheManagerImpl(const base::FilePath& profile_path,
-                            bool in_memory_only = false);
+  explicit CacheManagerImpl(const base::FilePath& profile_path);
 
   CacheManagerImpl(const CacheManagerImpl&) = delete;
   CacheManagerImpl& operator=(const CacheManagerImpl&) = delete;
@@ -34,8 +40,7 @@
   ~CacheManagerImpl() override;
 
   static std::unique_ptr<CacheManager> Create(
-      const base::FilePath& profile_path,
-      bool in_memory_only = false);
+      const base::FilePath& profile_path);
 
   // Setup the cache directory for the specific FSP.
   void InitializeForProvider(const ProvidedFileSystemInfo& file_system_info,
@@ -47,10 +52,13 @@
   bool IsProviderInitialized(
       const ProvidedFileSystemInfo& file_system_info) override;
 
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
+  void AddObserver(CacheManager::Observer* observer) override;
+  void RemoveObserver(CacheManager::Observer* observer) override;
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(FileSystemProviderCacheManagerImplTest,
+                           OnSpaceUpdate);
+
   // Attempt to initialize the context database if the directory creation was
   // successful of the in_memory_only boolean is true.
   void OnProviderDirectoryCreationComplete(
@@ -86,10 +94,23 @@
       FileErrorOrContentCacheCallback callback,
       FileErrorOrContentCache error_or_content_cache);
 
+  // Retrieves free space (either on-demand or via the spaced observer) and
+  // updates the max available size for every initialized content cache.
+  void GetFreeSpaceAndResizeInitializedCaches();
+  void OnFreeSpace(int64_t free_space_in_bytes);
+
+  // ash::SpacedClient overrides.
+  void OnSpaceUpdate(const SpaceEvent& event) override;
+
   const base::FilePath root_content_cache_directory_;
-  bool in_memory_only_ = false;
-  std::set<base::FilePath> initialized_providers_;
-  base::ObserverList<Observer> observers_;
+
+  // A map (keyed by a base64 encoded provider mount path) of weak pointers to
+  // content caches.
+  std::map<base::FilePath, base::WeakPtr<ContentCache>> initialized_caches_;
+  base::ObserverList<CacheManager::Observer> observers_;
+
+  base::ScopedObservation<ash::SpacedClient, ash::SpacedClient::Observer>
+      spaced_client_{this};
 
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_{
       base::ThreadPool::CreateSequencedTaskRunner(
diff --git a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl_unittest.cc b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl_unittest.cc
index d8f1ab80..d11441a 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl_unittest.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/cache_manager_impl_unittest.cc
@@ -12,17 +12,20 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
+#include "chromeos/ash/components/dbus/spaced/fake_spaced_client.h"
+#include "chromeos/ash/components/dbus/spaced/spaced_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash::file_system_provider {
-namespace {
 
 using base::FilePath;
 using base::test::RunClosure;
 using base::test::TestFuture;
 using FileErrorOrContentCache =
     base::FileErrorOr<std::unique_ptr<ContentCache>>;
+using testing::AllOf;
+using testing::Field;
 using testing::IsFalse;
 using testing::IsTrue;
 using testing::Property;
@@ -60,8 +63,12 @@
   void SetUp() override {
     EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
     profile_dir_ = temp_dir_.GetPath();
+
+    ash::SpacedClient::InitializeFake();
   }
 
+  void TearDown() override { ash::SpacedClient::Shutdown(); }
+
   const base::FilePath GetProviderMountPath(const std::string& fsp_id) {
     return profile_dir_.Append(kFspContentCacheDirName)
         .Append(base::Base64Encode(fsp_id));
@@ -73,17 +80,6 @@
 };
 
 TEST_F(FileSystemProviderCacheManagerImplTest,
-       InMemoryOnlyDoesntCreateFolderOnDisk) {
-  CacheManagerImpl cache_manager(profile_dir_, /*in_memory_only=*/true);
-  TestFuture<FileErrorOrContentCache> future;
-  cache_manager.InitializeForProvider(kFileSystemInfo, future.GetCallback());
-  EXPECT_THAT(future.Get(),
-              Property(&FileErrorOrContentCache::has_value, IsTrue()));
-  EXPECT_FALSE(base::PathExists(GetProviderMountPath("fsp_id")));
-  EXPECT_TRUE(cache_manager.IsProviderInitialized(kFileSystemInfo));
-}
-
-TEST_F(FileSystemProviderCacheManagerImplTest,
        EmptyProviderIdFailsInitialization) {
   CacheManagerImpl cache_manager(profile_dir_);
   TestFuture<FileErrorOrContentCache> future;
@@ -135,5 +131,85 @@
   cache_manager.UninitializeForProvider(kFileSystemInfo);
 }
 
-}  // namespace
+TEST_F(FileSystemProviderCacheManagerImplTest, OnSpaceUpdate) {
+  FakeSpacedClient::Get()->set_connected(true);
+  CacheManagerImpl cache_manager(profile_dir_);
+  MockCacheManagerObserver observer;
+  cache_manager.AddObserver(&observer);
+  TestFuture<FileErrorOrContentCache> first_cache_future;
+  // Expect successful initialization.
+  cache_manager.InitializeForProvider(kFileSystemInfo,
+                                      first_cache_future.GetCallback());
+  EXPECT_THAT(first_cache_future.Get(),
+              Property(&FileErrorOrContentCache::has_value, IsTrue()));
+
+  ash::FakeSpacedClient* spaced_client = ash::FakeSpacedClient::Get();
+  using Size = ContentCache::SizeInfo;
+  ash::SpacedClient::Observer::SpaceEvent event;
+
+  // Initial free space bytes event sets the max bytes on disk for the content
+  // cache, capped up to 5GB.
+  event.set_free_space_bytes(int64_t(10) << 30);
+  cache_manager.OnSpaceUpdate(event);
+  const std::unique_ptr<ContentCache>& first_cache =
+      first_cache_future.Get().value();
+  EXPECT_THAT(first_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, kMaxAllowedCacheSizeInBytes));
+
+  // When the free space drops, the max size is appropriately reduced.
+  event.set_free_space_bytes(int64_t(5) << 30);
+  cache_manager.OnSpaceUpdate(event);
+  EXPECT_THAT(first_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, int64_t(3) << 30));
+
+  // When the free space increases, it will max out at 5GB.
+  event.set_free_space_bytes(int64_t(100) << 30);
+  cache_manager.OnSpaceUpdate(event);
+  EXPECT_THAT(first_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, kMaxAllowedCacheSizeInBytes));
+
+  TestFuture<FileErrorOrContentCache> second_cache_future;
+  auto new_file_system = ProvidedFileSystemInfo(
+      "second_fsp", MountOptions("fsp_id_2", "display name"),
+      base::FilePath("/file_system/fsp_id_2"),
+      /*configurable=*/false,
+      /*watchable=*/true, extensions::SOURCE_FILE, IconSet());
+  spaced_client->set_free_disk_space(int64_t(100) << 30);
+  cache_manager.InitializeForProvider(new_file_system,
+                                      second_cache_future.GetCallback());
+  EXPECT_THAT(second_cache_future.Get(),
+              Property(&FileErrorOrContentCache::has_value, IsTrue()));
+
+  // The last update was 100GB, so we expect the max cache size to be 5GB for
+  // both caches.
+  const std::unique_ptr<ContentCache>& second_cache =
+      second_cache_future.Get().value();
+  EXPECT_THAT(first_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, kMaxAllowedCacheSizeInBytes));
+  EXPECT_THAT(second_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, kMaxAllowedCacheSizeInBytes));
+
+  // Update the free space to 5GB and the free space for both the caches should
+  // be 1.5GB for each.
+  event.set_free_space_bytes(int64_t(5) << 30);
+  cache_manager.OnSpaceUpdate(event);
+  spaced_client->set_free_disk_space(int64_t(5) << 30);
+  EXPECT_THAT(first_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, int64_t(1536) << 20));
+  EXPECT_THAT(second_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, int64_t(1536) << 20));
+
+  // Uninitializing a provider should result in a resize of the existing cache.
+  base::RunLoop run_loop;
+  EXPECT_CALL(observer, OnProviderUninitialized(
+                            base::FilePath(base::Base64Encode("fsp_id")),
+                            base::File::FILE_OK))
+      .WillOnce(RunClosure(run_loop.QuitClosure()));
+  cache_manager.UninitializeForProvider(kFileSystemInfo);
+  run_loop.Run();
+  EXPECT_FALSE(cache_manager.IsProviderInitialized(kFileSystemInfo));
+  EXPECT_THAT(second_cache->GetSize(),
+              Field(&Size::max_bytes_on_disk, int64_t(3) << 30));
+}
+
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache.h b/chrome/browser/ash/file_system_provider/content_cache/content_cache.h
index e7ec1c0..0610f9c3 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache.h
@@ -96,6 +96,19 @@
   // removal is already in progress, the callback will be queued to be called
   // with the current stats of the in progress removal.
   virtual void RemoveItems(RemovedItemStatsCallback callback) = 0;
+
+  // A struct of size information pertaining to this cache instance.
+  struct SizeInfo {
+    int64_t max_bytes_on_disk = 0;
+    int64_t total_bytes_on_disk = 0;
+  };
+
+  // Helper methods to get and set size information.
+  virtual const SizeInfo GetSize() const = 0;
+  virtual void SetMaxBytesOnDisk(int64_t max_bytes_on_disk) = 0;
+
+  // Returns a `base::WeakPtr`.
+  virtual base::WeakPtr<ContentCache> GetWeakPtr() = 0;
 };
 
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc
index d6f4324..080a756 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.cc
@@ -472,6 +472,7 @@
 
   CacheFileContext& ctx = it->second;
   if (result == base::File::FILE_OK) {
+    size_.total_bytes_on_disk += length;
     ctx.set_bytes_on_disk(offset + length);
     ctx.set_accessed_time(base::Time::Now());
 
@@ -599,4 +600,16 @@
   return cached_file_paths;
 }
 
+const ContentCache::SizeInfo ContentCacheImpl::GetSize() const {
+  return size_;
+}
+
+void ContentCacheImpl::SetMaxBytesOnDisk(int64_t max_bytes_on_disk) {
+  size_.max_bytes_on_disk = max_bytes_on_disk;
+}
+
+base::WeakPtr<ContentCache> ContentCacheImpl::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
 }  // namespace ash::file_system_provider
diff --git a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h
index 73753085..9c1585a 100644
--- a/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h
+++ b/chrome/browser/ash/file_system_provider/content_cache/content_cache_impl.h
@@ -65,6 +65,11 @@
 
   void RemoveItems(RemovedItemStatsCallback callback) override;
 
+  const SizeInfo GetSize() const override;
+  void SetMaxBytesOnDisk(int64_t max_bytes_on_disk) override;
+
+  base::WeakPtr<ContentCache> GetWeakPtr() override;
+
  private:
   void OnBytesRead(
       const base::FilePath& file_path,
@@ -148,6 +153,8 @@
   OnItemEvictedCallback on_item_evicted_callback_;
   base::OnceCallbackList<void(RemovedItemStats)> on_removed_callbacks_;
 
+  SizeInfo size_;
+
   base::WeakPtrFactory<ContentCacheImpl> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ash/login/login_pref_names.h b/chrome/browser/ash/login/login_pref_names.h
index aeb62c4..88dbf7b 100644
--- a/chrome/browser/ash/login/login_pref_names.h
+++ b/chrome/browser/ash/login/login_pref_names.h
@@ -103,6 +103,10 @@
 inline constexpr char kOobeDisplaySizeFactorDeferred[] =
     "oobe.display_size_factor_defer";
 
+// List of categories selected from the CategoriesSelection screen.
+// This list is used to filter the apps in the new recommended apps screen.
+inline constexpr char kOobeCategoriesSelected[] = "oobe.categories_selected";
+
 // *************** OOBE LOCAL STATE PREFS ***************
 
 // A boolean pref of the OOBE complete flag (first OOBE part before login).
diff --git a/chrome/browser/ash/login/oobe_metrics_browsertest.cc b/chrome/browser/ash/login/oobe_metrics_browsertest.cc
index b61ae8c..2828697a 100644
--- a/chrome/browser/ash/login/oobe_metrics_browsertest.cc
+++ b/chrome/browser/ash/login/oobe_metrics_browsertest.cc
@@ -173,7 +173,8 @@
   ValidateEventRecorded(page_skipped_event);
 }
 
-IN_PROC_BROWSER_TEST_F(OobeMetricsTest, SignInEvents) {
+// TODO(crbug.com/337379954): Flaky on linux-chromeos-chrome.
+IN_PROC_BROWSER_TEST_F(OobeMetricsTest, DISABLED_SignInEvents) {
   // `login_manager_mixin_.LoginAsNewRegularUser()` can not be used in this test
   // since a simulation of login steps are required to get Sign-in events
   // recorded.
diff --git a/chrome/browser/ash/login/screens/categories_selection_screen.cc b/chrome/browser/ash/login/screens/categories_selection_screen.cc
index fbda21a..758f5d3 100644
--- a/chrome/browser/ash/login/screens/categories_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/categories_selection_screen.cc
@@ -4,8 +4,15 @@
 
 #include "chrome/browser/ash/login/screens/categories_selection_screen.h"
 
+#include "ash/constants/ash_pref_names.h"
+#include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/ash/login/categories_selection_screen_handler.h"
+#include "components/prefs/pref_service.h"
+#include "components/user_manager/user_manager.h"
 
 namespace ash {
 namespace {
@@ -45,6 +52,17 @@
     return true;
   }
 
+  const user_manager::UserManager* user_manager =
+      user_manager::UserManager::Get();
+  Profile* profile = ProfileManager::GetActiveUserProfile();
+
+  bool is_managed_account = profile->GetProfilePolicyConnector()->IsManaged();
+  bool is_child_account = user_manager->IsLoggedInAsChildUser();
+  if (is_managed_account || is_child_account) {
+    exit_callback_.Run(Result::kNotApplicable);
+    return true;
+  }
+
   return false;
 }
 
@@ -59,6 +77,11 @@
 
 void CategoriesSelectionScreen::HideImpl() {}
 
+void CategoriesSelectionScreen::OnSelect(base::Value::List categories) {
+  PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
+  prefs->SetList(prefs::kOobeCategoriesSelected, std::move(categories));
+}
+
 void CategoriesSelectionScreen::OnUserAction(const base::Value::List& args) {
   const std::string& action_id = args[0].GetString();
 
@@ -69,7 +92,7 @@
 
   if (action_id == kUserActionNext) {
     CHECK_EQ(args.size(), 2u);
-    // TODO(b/337674429) : save the selected categories into user preferences.
+    OnSelect(args[1].GetList().Clone());
     exit_callback_.Run(Result::kNext);
     return;
   }
diff --git a/chrome/browser/ash/login/screens/categories_selection_screen.h b/chrome/browser/ash/login/screens/categories_selection_screen.h
index c525a94..08405fc 100644
--- a/chrome/browser/ash/login/screens/categories_selection_screen.h
+++ b/chrome/browser/ash/login/screens/categories_selection_screen.h
@@ -42,6 +42,9 @@
   void HideImpl() override;
   void OnUserAction(const base::Value::List& args) override;
 
+  // Called when the user selects categories on the screen.
+  void OnSelect(base::Value::List screens);
+
   base::WeakPtr<CategoriesSelectionScreenView> view_;
   ScreenExitCallback exit_callback_;
 };
diff --git a/chrome/browser/ash/login/screens/osauth/password_selection_screen_browsertest.cc b/chrome/browser/ash/login/screens/osauth/password_selection_screen_browsertest.cc
index 6786590..4cf2bd0 100644
--- a/chrome/browser/ash/login/screens/osauth/password_selection_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/osauth/password_selection_screen_browsertest.cc
@@ -11,7 +11,6 @@
 #include "ash/constants/ash_features.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
-#include "build/build_config.h"
 #include "chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
 #include "chrome/browser/ash/login/test/oobe_base_test.h"
@@ -211,12 +210,7 @@
 }
 
 // crbug.com/337379954: Managed is excessively flaky on linux-chromeos-chrome.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#define MAYBE_Managed DISABLED_Managed
-#else
-#define MAYBE_Managed Managed
-#endif
-IN_PROC_BROWSER_TEST_F(PasswordSelectionScreenTest, MAYBE_Managed) {
+IN_PROC_BROWSER_TEST_F(PasswordSelectionScreenTest, DISABLED_Managed) {
   StartLogin();
   ProfileManager::GetPrimaryUserProfile()
       ->GetProfilePolicyConnector()
@@ -227,7 +221,8 @@
             PasswordSelectionScreen::Result::GAIA_PASSWORD_ENTERPRISE);
 }
 
-IN_PROC_BROWSER_TEST_F(PasswordSelectionScreenTest, SmartCard) {
+// TODO(crbug.com/337379954): Flaky on linux-chromeos-chrome.
+IN_PROC_BROWSER_TEST_F(PasswordSelectionScreenTest, DISABLED_SmartCard) {
   StartLogin();
   auto user_context = BorrowUserContext();
   user_context->SetAuthFactorsConfiguration(GetFakeAuthFactorConfiguration(
@@ -240,13 +235,8 @@
 }
 
 // TODO(crbug.com/337379954): Flaky on linux-chromeos-chrome.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#define MAYBE_RecoveryLocalPassword DISABLED_RecoveryLocalPassword
-#else
-#define MAYBE_RecoveryLocalPassword RecoveryLocalPassword
-#endif
 IN_PROC_BROWSER_TEST_F(PasswordSelectionScreenTest,
-                       MAYBE_RecoveryLocalPassword) {
+                       DISABLED_RecoveryLocalPassword) {
   StartLogin();
   auto user_context = BorrowUserContext();
   LoginDisplayHost::default_host()
diff --git a/chrome/browser/ash/login/screens/personalized_recommend_apps_screen.cc b/chrome/browser/ash/login/screens/personalized_recommend_apps_screen.cc
index 77b7d0bf..50f48395 100644
--- a/chrome/browser/ash/login/screens/personalized_recommend_apps_screen.cc
+++ b/chrome/browser/ash/login/screens/personalized_recommend_apps_screen.cc
@@ -10,8 +10,8 @@
 namespace ash {
 namespace {
 
-// constexpr const char kUserActionNext[] = "next";
-// constexpr const char kUserActionSkip[] = "skip";
+constexpr const char kUserActionNext[] = "next";
+constexpr const char kUserActionSkip[] = "skip";
 
 }  // namespace
 
@@ -51,6 +51,10 @@
     return;
   }
 
+  // TODO(b/339789465) : get the list of apps and categories
+  // user selected and generate the data for the screen
+  // map[category]: list<Apps>
+
   view_->Show();
 }
 
@@ -58,7 +62,21 @@
 
 void PersonalizedRecommendAppsScreen::OnUserAction(
     const base::Value::List& args) {
-  NOTIMPLEMENTED();
+  const std::string& action_id = args[0].GetString();
+
+  if (action_id == kUserActionSkip) {
+    exit_callback_.Run(Result::kSkip);
+    return;
+  }
+
+  if (action_id == kUserActionNext) {
+    CHECK_EQ(args.size(), 2u);
+    // TODO(b/339789465) : the install logic of the apps.
+    exit_callback_.Run(Result::kNext);
+    return;
+  }
+
+  BaseScreen::OnUserAction(args);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/startup_utils.cc b/chrome/browser/ash/login/startup_utils.cc
index de339b2..06f6fbc 100644
--- a/chrome/browser/ash/login/startup_utils.cc
+++ b/chrome/browser/ash/login/startup_utils.cc
@@ -165,6 +165,10 @@
                                   false);
   }
 
+  if (features::IsOobePersonalizedOnboardingEnabled()) {
+    registry->RegisterListPref(prefs::kOobeCategoriesSelected);
+  }
+
   if (features::IsOobeDisplaySizeEnabled()) {
     registry->RegisterDoublePref(prefs::kOobeDisplaySizeFactorDeferred, 1.0);
   }
diff --git a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
index e345083..5d02ac9 100644
--- a/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
+++ b/chrome/browser/ash/policy/core/device_local_account_browsertest.cc
@@ -2042,7 +2042,9 @@
   VerifyKeyboardLayoutMatchesLocale();
 }
 
-IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest, TermsOfServiceWithLocaleSwitch) {
+// TODO(crbug.com/40923043): Flaky.
+IN_PROC_BROWSER_TEST_F(DeviceLocalAccountTest,
+                       DISABLED_TermsOfServiceWithLocaleSwitch) {
   // Specify Terms of Service URL.
   ASSERT_TRUE(embedded_test_server()->Start());
   device_local_account_policy_.payload().mutable_termsofserviceurl()->set_value(
diff --git a/chrome/browser/component_updater/screen_ai_component_installer.cc b/chrome/browser/component_updater/screen_ai_component_installer.cc
index 2b19d990..ea8f13ab 100644
--- a/chrome/browser/component_updater/screen_ai_component_installer.cc
+++ b/chrome/browser/component_updater/screen_ai_component_installer.cc
@@ -69,23 +69,8 @@
   VLOG(1) << "Screen AI Component ready, version " << version.GetString()
           << " in " << install_dir.value();
 
-  // Verifying library availability requires I/O and hence a blocking thread.
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(&screen_ai::ScreenAIInstallState::VerifyLibraryAvailablity,
-                     install_dir),
-      base::BindOnce(
-          [](base::FilePath install_dir, bool library_available) {
-            auto* state = screen_ai::ScreenAIInstallState::GetInstance();
-            if (library_available) {
-              state->SetComponentFolder(install_dir);
-            } else {
-              state->SetState(
-                  screen_ai::ScreenAIInstallState::State::kDownloadFailed);
-            }
-          },
-          install_dir));
+  screen_ai::ScreenAIInstallState::GetInstance()->SetComponentFolder(
+      install_dir);
 }
 
 bool ScreenAIComponentInstallerPolicy::VerifyInstallation(
diff --git a/chrome/browser/content_settings/host_content_settings_map_unittest.cc b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
index 06627d36..5b3e843d 100644
--- a/chrome/browser/content_settings/host_content_settings_map_unittest.cc
+++ b/chrome/browser/content_settings/host_content_settings_map_unittest.cc
@@ -2563,6 +2563,31 @@
   FastForwardTime(ttl);
 }
 
+TEST_P(IndexedHostContentSettingsMapTest, TrackingProtectionMetrics) {
+  const ContentSettingsType type = ContentSettingsType::TRACKING_PROTECTION;
+  TestingProfile profile;
+  auto* map = HostContentSettingsMapFactory::GetForProfile(&profile);
+  map->SetContentSettingCustomScope(
+      ContentSettingsPattern::Wildcard(),
+      ContentSettingsPattern::FromString("https://example1.com"), type,
+      CONTENT_SETTING_ALLOW);
+  map->SetContentSettingCustomScope(
+      ContentSettingsPattern::Wildcard(),
+      ContentSettingsPattern::FromString("https://example2.com"), type,
+      CONTENT_SETTING_ALLOW);
+  map->SetContentSettingCustomScope(
+      ContentSettingsPattern::Wildcard(),
+      ContentSettingsPattern::FromString("https://example3.com"), type,
+      CONTENT_SETTING_ALLOW);
+
+  base::HistogramTester t;
+  auto map2 = base::MakeRefCounted<HostContentSettingsMap>(
+      profile.GetPrefs(), false, true, true, true);
+  map2->ShutdownOnUIThread();
+  t.ExpectUniqueSample(
+      "ContentSettings.RegularProfile.Exceptions.tracking-protection", 3, 1);
+}
+
 // File access is not implemented on Android. Luckily we don't need it for DevTools.
 #if !BUILDFLAG(IS_ANDROID)
 TEST_P(IndexedHostContentSettingsMapTest, DevToolsFileAccess) {
diff --git a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
index bd1a46e..3e3b96d 100644
--- a/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
+++ b/chrome/browser/devtools/devtools_embedder_message_dispatcher.h
@@ -50,7 +50,8 @@
     virtual void ShowItemInFolder(const std::string& file_system_path) = 0;
     virtual void SaveToFile(const std::string& url,
                             const std::string& content,
-                            bool save_as) = 0;
+                            bool save_as,
+                            bool is_base64) = 0;
     virtual void AppendToFile(const std::string& url,
                               const std::string& content) = 0;
     virtual void RequestFileSystems() = 0;
diff --git a/chrome/browser/devtools/devtools_file_helper.cc b/chrome/browser/devtools/devtools_file_helper.cc
index 3c82a96b..54915848 100644
--- a/chrome/browser/devtools/devtools_file_helper.cc
+++ b/chrome/browser/devtools/devtools_file_helper.cc
@@ -7,6 +7,7 @@
 #include <set>
 #include <vector>
 
+#include "base/base64.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
@@ -127,10 +128,23 @@
   CanceledCallback canceled_callback_;
 };
 
-void WriteToFile(const base::FilePath& path, const std::string& content) {
+void WriteToFile(const base::FilePath& path,
+                 const std::string& content,
+                 bool is_base64) {
   DCHECK(!path.empty());
 
-  base::WriteFile(path, content);
+  if (!is_base64) {
+    base::WriteFile(path, content);
+    return;
+  }
+
+  const std::optional<std::vector<uint8_t>> decoded_content =
+      base::Base64Decode(content);
+  if (decoded_content) {
+    base::WriteFile(path, decoded_content.value());
+  } else {
+    LOG(ERROR) << "Invalid base64. Not writing " << path;
+  }
 }
 
 void AppendToFile(const base::FilePath& path, const std::string& content) {
@@ -236,11 +250,13 @@
 void DevToolsFileHelper::Save(const std::string& url,
                               const std::string& content,
                               bool save_as,
+                              bool is_base64,
                               SaveCallback saveCallback,
                               base::OnceClosure cancelCallback) {
   auto it = saved_files_.find(url);
   if (it != saved_files_.end() && !save_as) {
-    SaveAsFileSelected(url, content, std::move(saveCallback), it->second);
+    SaveAsFileSelected(url, content, is_base64, std::move(saveCallback),
+                       it->second);
     return;
   }
 
@@ -286,12 +302,12 @@
     }
   }
 
-  SelectFileDialog::Show(base::BindOnce(&DevToolsFileHelper::SaveAsFileSelected,
-                                        weak_factory_.GetWeakPtr(), url,
-                                        content, std::move(saveCallback)),
-                         std::move(cancelCallback), web_contents_,
-                         ui::SelectFileDialog::SELECT_SAVEAS_FILE,
-                         initial_path);
+  SelectFileDialog::Show(
+      base::BindOnce(&DevToolsFileHelper::SaveAsFileSelected,
+                     weak_factory_.GetWeakPtr(), url, content, is_base64,
+                     std::move(saveCallback)),
+      std::move(cancelCallback), web_contents_,
+      ui::SelectFileDialog::SELECT_SAVEAS_FILE, initial_path);
 }
 
 void DevToolsFileHelper::Append(const std::string& url,
@@ -307,6 +323,7 @@
 
 void DevToolsFileHelper::SaveAsFileSelected(const std::string& url,
                                             const std::string& content,
+                                            bool is_base64,
                                             SaveCallback callback,
                                             const base::FilePath& path) {
   *g_last_save_path.Pointer() = path;
@@ -317,8 +334,9 @@
   base::Value::Dict& files_map = update.Get();
   files_map.Set(base::MD5String(url), base::FilePathToValue(path));
   std::string file_system_path = path.AsUTF8Unsafe();
-  std::move(callback).Run(file_system_path);
-  file_task_runner_->PostTask(FROM_HERE, BindOnce(&WriteToFile, path, content));
+  file_task_runner_->PostTask(
+      FROM_HERE, BindOnce(&WriteToFile, path, content, is_base64)
+                     .Then(BindOnce(std::move(callback), file_system_path)));
 }
 
 void DevToolsFileHelper::AddFileSystem(
diff --git a/chrome/browser/devtools/devtools_file_helper.h b/chrome/browser/devtools/devtools_file_helper.h
index dff17fc4..17c36e6 100644
--- a/chrome/browser/devtools/devtools_file_helper.h
+++ b/chrome/browser/devtools/devtools_file_helper.h
@@ -78,6 +78,7 @@
   void Save(const std::string& url,
             const std::string& content,
             bool save_as,
+            bool is_base64,
             SaveCallback saveCallback,
             base::OnceClosure cancelCallback);
 
@@ -131,6 +132,7 @@
                           platform_util::OpenOperationResult result);
   void SaveAsFileSelected(const std::string& url,
                           const std::string& content,
+                          bool is_base64,
                           SaveCallback callback,
                           const base::FilePath& path);
   void InnerAddFileSystem(const ShowInfoBarCallback& show_info_bar_callback,
diff --git a/chrome/browser/devtools/devtools_file_helper_unittest.cc b/chrome/browser/devtools/devtools_file_helper_unittest.cc
new file mode 100644
index 0000000..9cd380b
--- /dev/null
+++ b/chrome/browser/devtools/devtools_file_helper_unittest.cc
@@ -0,0 +1,124 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/devtools/devtools_file_helper.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "chrome/browser/download/chrome_download_manager_delegate.h"
+#include "chrome/browser/download/download_core_service.h"
+#include "chrome/browser/download/download_core_service_factory.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/file_system_chooser_test_helpers.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/shell_dialogs/selected_file_info.h"
+
+class DevToolsFileHelperTest : public content::RenderViewHostTestHarness,
+                               public DevToolsFileHelper::Delegate {
+ public:
+  void FileSystemAdded(
+      const std::string& error,
+      const DevToolsFileHelper::FileSystem* file_system) override {}
+  void FileSystemRemoved(const std::string& file_system_path) override {}
+  void FilePathsChanged(
+      const std::vector<std::string>& changed_paths,
+      const std::vector<std::string>& added_paths,
+      const std::vector<std::string>& removed_paths) override {}
+
+ protected:
+  void SetUp() override {
+    content::RenderViewHostTestHarness::SetUp();
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    // Setup bits and bobs so we can instantiate a DevToolsFileHelper. It needs
+    // a WebContents, Profile and permission to show the "Save as" dialog.
+    auto* profile = Profile::FromBrowserContext(browser_context());
+    testing_local_state_ = std::make_unique<ScopedTestingLocalState>(
+        TestingBrowserProcess::GetGlobal());
+    testing_local_state_->Get()->SetBoolean(prefs::kAllowFileSelectionDialogs,
+                                            true);
+    DownloadCoreServiceFactory::GetForBrowserContext(profile)
+        ->SetDownloadManagerDelegateForTesting(
+            std::make_unique<ChromeDownloadManagerDelegate>(profile));
+    DownloadPrefs::FromBrowserContext(browser_context())
+        ->SetDownloadPath(temp_dir_.GetPath());
+    file_helper_ =
+        std::make_unique<DevToolsFileHelper>(web_contents(), profile, this);
+  }
+
+  void TearDown() override {
+    // Release the helper first, otherwise the profile will be a dangling ptr.
+    file_helper_.reset();
+    content::RenderViewHostTestHarness::TearDown();
+  }
+
+  std::unique_ptr<content::BrowserContext> CreateBrowserContext() override {
+    return std::make_unique<TestingProfile>();
+  }
+
+  base::ScopedTempDir temp_dir_;
+  std::unique_ptr<DevToolsFileHelper> file_helper_;
+  std::unique_ptr<ScopedTestingLocalState> testing_local_state_;
+};
+
+TEST_F(DevToolsFileHelperTest, SaveToFileBase64) {
+  base::FilePath path = temp_dir_.GetPath().AppendASCII("test.wasm");
+  ui::SelectFileDialog::SetFactory(
+      std::make_unique<content::FakeSelectFileDialogFactory>(
+          std::vector<base::FilePath>{path}));
+
+  base::RunLoop run_loop;
+  file_helper_->Save(
+      "https://example.com/test.wasm", "AGFzbQEAAAA=", /* save_as */ true,
+      /* is_base64 */ true,
+      base::BindLambdaForTesting([&](const std::string&) { run_loop.Quit(); }),
+      base::DoNothing());
+  run_loop.Run();
+
+  const std::vector<uint8_t> kTestData = {0, 'a', 's', 'm', 1, 0, 0, 0};
+  ASSERT_EQ(base::ReadFileToBytes(path), kTestData);
+}
+
+TEST_F(DevToolsFileHelperTest, SaveToFileInvalidBase64) {
+  base::FilePath path = temp_dir_.GetPath().AppendASCII("test.wasm");
+  ui::SelectFileDialog::SetFactory(
+      std::make_unique<content::FakeSelectFileDialogFactory>(
+          std::vector<base::FilePath>{path}));
+
+  file_helper_->Save(
+      "https://example.com/test.wasm", "~~~~", /* save_as */ true,
+      /* is_base64 */ true, base::DoNothing(), base::DoNothing());
+  base::RunLoop().RunUntilIdle();
+
+  ASSERT_FALSE(base::PathExists(path));
+}
+
+TEST_F(DevToolsFileHelperTest, SaveToFileText) {
+  base::FilePath path = temp_dir_.GetPath().AppendASCII("test.txt");
+  ui::SelectFileDialog::SetFactory(
+      std::make_unique<content::FakeSelectFileDialogFactory>(
+          std::vector<base::FilePath>{path}));
+
+  base::RunLoop run_loop;
+  file_helper_->Save(
+      "https://example.com/test.txt", "some text", /* save_as */ true,
+      /* is_base64 */ false,
+      base::BindLambdaForTesting([&](const std::string&) { run_loop.Quit(); }),
+      base::DoNothing());
+  run_loop.Run();
+
+  std::string content;
+  ASSERT_TRUE(base::ReadFileToString(path, &content));
+  ASSERT_EQ(content, "some text");
+}
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 78d5390..6e0854f 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1182,8 +1182,9 @@
 
 void DevToolsUIBindings::SaveToFile(const std::string& url,
                                     const std::string& content,
-                                    bool save_as) {
-  file_helper_->Save(url, content, save_as,
+                                    bool save_as,
+                                    bool is_base64) {
+  file_helper_->Save(url, content, save_as, is_base64,
                      base::BindOnce(&DevToolsUIBindings::FileSavedAs,
                                     weak_factory_.GetWeakPtr(), url),
                      base::BindOnce(&DevToolsUIBindings::CanceledFileSaveAs,
diff --git a/chrome/browser/devtools/devtools_ui_bindings.h b/chrome/browser/devtools/devtools_ui_bindings.h
index d3794c8..11118b7 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.h
+++ b/chrome/browser/devtools/devtools_ui_bindings.h
@@ -148,7 +148,8 @@
   void ShowItemInFolder(const std::string& file_system_path) override;
   void SaveToFile(const std::string& url,
                   const std::string& content,
-                  bool save_as) override;
+                  bool save_as,
+                  bool is_base64) override;
   void AppendToFile(const std::string& url,
                     const std::string& content) override;
   void RequestFileSystems() override;
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 3f4e489..132a68f 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
@@ -7936,7 +7936,8 @@
 // Test that modifyHeaders rules matched in both onBeforeRequest and
 // onHeadersReceived phases will perform the correct action(s) on the request.
 IN_PROC_BROWSER_TEST_P(DNRMatchResponseHeadersBrowserTest,
-                       ModifyHeaders_SingleExtension) {
+                       // TODO(crbug.com/340136384): Re-enable this test
+                       DISABLED_ModifyHeaders_SingleExtension) {
   std::vector<TestHeaderCondition> blank_header_condition =
       std::vector<TestHeaderCondition>(
           {TestHeaderCondition("nonsense-header", {}, {})});
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 03f65dc..bb656d28 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -7358,12 +7358,6 @@
 const char kLacrosLaunchAtLoginScreenDescription[] =
     "When enabled, it causes Lacros to be pre-launched at login screen.";
 
-const char kLacrosForkZygotesAtLoginScreenName[] =
-    "Fork Zygotes at login screen when pre-launching";
-const char kLacrosForkZygotesAtLoginScreenDescription[] =
-    "When enabled, it causes Lacros to fork the Zygotes at login screen, when "
-    "pre-launching is enabled.";
-
 const char kLauncherGameSearchName[] = "Enable launcher game search";
 const char kLauncherGameSearchDescription[] =
     "Enables cloud game search results in the launcher.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 1ae5d4c..48d7f2f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -4275,9 +4275,6 @@
 extern const char kLacrosLaunchAtLoginScreenName[];
 extern const char kLacrosLaunchAtLoginScreenDescription[];
 
-extern const char kLacrosForkZygotesAtLoginScreenName[];
-extern const char kLacrosForkZygotesAtLoginScreenDescription[];
-
 extern const char kLauncherGameSearchName[];
 extern const char kLauncherGameSearchDescription[];
 
diff --git a/chrome/browser/model_execution/model_manager_impl.cc b/chrome/browser/model_execution/model_manager_impl.cc
index 5bb4387..afe5352 100644
--- a/chrome/browser/model_execution/model_manager_impl.cc
+++ b/chrome/browser/model_execution/model_manager_impl.cc
@@ -7,6 +7,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
+#include "base/notreached.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -57,20 +58,6 @@
 
 void ModelManagerImpl::CanCreateGenericSession(
     CanCreateGenericSessionCallback callback) {
-  // If the `OptimizationGuideKeyedService` cannot be retrieved, return false.
-  // TODO(https://crbug.com/330819915): add the checks after optimization guide
-  // component provide more method to determine if a session could be started.
-  content::BrowserContext* browser_context = browser_context_.get();
-  CHECK(browser_context);
-  if (!OptimizationGuideKeyedServiceFactory::GetForProfile(
-          Profile::FromBrowserContext(browser_context))) {
-    render_frame_host().AddMessageToConsole(
-        blink::mojom::ConsoleMessageLevel::kWarning,
-        "Unable to create generic session because the service is not running.");
-    std::move(callback).Run(/*can_create=*/false);
-    return;
-  }
-
   // If the model path is empty or invalid, return false.
   auto model_path =
       optimization_guide::switches::GetOnDeviceModelExecutionOverride();
@@ -149,6 +136,81 @@
       optimization_guide::features::GetOnDeviceModelDefaultTemperature()));
 }
 
+std::string ConvertOnDeviceModelEligibilityReasonToString(
+    optimization_guide::OnDeviceModelEligibilityReason debug_reason) {
+  switch (debug_reason) {
+    case optimization_guide::OnDeviceModelEligibilityReason::kUnknown:
+      return "The service is unable to create new session.";
+    case optimization_guide::OnDeviceModelEligibilityReason::kFeatureNotEnabled:
+      return "The feature flag gating model execution was disabled.";
+    case optimization_guide::OnDeviceModelEligibilityReason::kModelNotAvailable:
+      return "There was no model available.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kConfigNotAvailableForFeature:
+      return "The model was available but there was not an execution config "
+             "available for the feature.";
+    case optimization_guide::OnDeviceModelEligibilityReason::kGpuBlocked:
+      return "The GPU is blocked.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kTooManyRecentCrashes:
+      return "The model process crashed too many times for this version.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kTooManyRecentTimeouts:
+      return "The model took too long too many times for this version.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kSafetyModelNotAvailable:
+      return "The safety model was required but not available.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kSafetyConfigNotAvailableForFeature:
+      return "The safety model was available but there was not a safety config "
+             "available for the feature.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kLanguageDetectionModelNotAvailable:
+      return "The language detection model was required but not available.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kFeatureExecutionNotEnabled:
+      return "Model execution for this feature was not enabled.";
+    case optimization_guide::OnDeviceModelEligibilityReason::
+        kModelAdaptationNotAvailable:
+      return "Model adaptation was required but not available.";
+    case optimization_guide::OnDeviceModelEligibilityReason::kSuccess:
+      NOTREACHED();
+  }
+  return "";
+}
+
+void ModelManagerImpl::CanOptimizationGuideKeyedServiceCreateGenericSession(
+    CanCreateGenericSessionCallback callback) {
+  content::BrowserContext* browser_context = browser_context_.get();
+  CHECK(browser_context);
+  OptimizationGuideKeyedService* service =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(
+          Profile::FromBrowserContext(browser_context));
+
+  // If the `OptimizationGuideKeyedService` cannot be retrieved, return false.
+  if (!service) {
+    render_frame_host().AddMessageToConsole(
+        blink::mojom::ConsoleMessageLevel::kWarning,
+        "Unable to create generic session because the service is not running.");
+    std::move(callback).Run(/*can_create=*/false);
+    return;
+  }
+
+  // If the `OptimizationGuideKeyedService` cannot create new session, return
+  // false.
+  optimization_guide::OnDeviceModelEligibilityReason debug_reason;
+  if (!service->CanCreateOnDeviceSession(
+          optimization_guide::ModelBasedCapabilityKey::kTest, &debug_reason)) {
+    render_frame_host().AddMessageToConsole(
+        blink::mojom::ConsoleMessageLevel::kWarning,
+        ConvertOnDeviceModelEligibilityReasonToString(debug_reason));
+    std::move(callback).Run(/*can_create=*/false);
+    return;
+  }
+
+  std::move(callback).Run(/*can_create=*/true);
+}
+
 void ModelManagerImpl::OnModelPathValidationComplete(
     CanCreateGenericSessionCallback callback,
     const std::string& model_path,
@@ -159,6 +221,9 @@
         base::StringPrintf("Unable to create generic session because "
                            "the model path ('%s') is invalid.",
                            model_path.c_str()));
+    std::move(callback).Run(false);
+    return;
   }
-  std::move(callback).Run(is_valid_path);
+
+  CanOptimizationGuideKeyedServiceCreateGenericSession(std::move(callback));
 }
diff --git a/chrome/browser/model_execution/model_manager_impl.h b/chrome/browser/model_execution/model_manager_impl.h
index a642f893..b40098e 100644
--- a/chrome/browser/model_execution/model_manager_impl.h
+++ b/chrome/browser/model_execution/model_manager_impl.h
@@ -51,6 +51,9 @@
                                      const std::string& model_path,
                                      bool is_valid_path);
 
+  void CanOptimizationGuideKeyedServiceCreateGenericSession(
+      CanCreateGenericSessionCallback callback);
+
   base::WeakPtr<content::BrowserContext> browser_context_;
   mojo::Receiver<blink::mojom::ModelManager> receiver_{this};
 
diff --git a/chrome/browser/password_manager/android/android_backend_with_double_deletion.cc b/chrome/browser/password_manager/android/android_backend_with_double_deletion.cc
index 128f43f6..2c230846 100644
--- a/chrome/browser/password_manager/android/android_backend_with_double_deletion.cc
+++ b/chrome/browser/password_manager/android/android_backend_with_double_deletion.cc
@@ -53,6 +53,7 @@
 
 void AndroidBackendWithDoubleDeletion::Shutdown(
     base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   auto shutdown_closure =
       base::BarrierClosure(2, std::move(shutdown_completed));
   built_in_backend_->Shutdown(
diff --git a/chrome/browser/password_manager/android/legacy_password_store_backend_migration_decorator.cc b/chrome/browser/password_manager/android/legacy_password_store_backend_migration_decorator.cc
index 0ca07b72..be8b51f 100644
--- a/chrome/browser/password_manager/android/legacy_password_store_backend_migration_decorator.cc
+++ b/chrome/browser/password_manager/android/legacy_password_store_backend_migration_decorator.cc
@@ -136,6 +136,7 @@
 
 void LegacyPasswordStoreBackendMigrationDecorator::Shutdown(
     base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   migrator_.reset();
   built_in_backend_ = nullptr;
   android_backend_ = nullptr;
diff --git a/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc b/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
index be0875d..87b5579 100644
--- a/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
+++ b/chrome/browser/password_manager/android/password_manager_android_util_unittest.cc
@@ -1174,16 +1174,21 @@
     // This block of tests is designed to test the behavior of login database
     // (namely that the profile database file is renamed to be the account
     // database file when using the split stores feature).
-    auto is_profile_db_empty_cb =
-        base::BindPostTaskToCurrentDefault(base::BindRepeating(
-            &password_manager::SetEmptyStorePref, profile->GetPrefs(),
-            password_manager::prefs::kEmptyProfileStoreLoginDatabase));
+    std::unique_ptr<password_manager::LoginDatabase> login_db(
+        password_manager::CreateLoginDatabaseForProfileStorage(
+            profile->GetPath()));
+    password_manager::LoginDatabase* login_db_ptr = login_db.get();
     std::unique_ptr<password_manager::PasswordStoreBackend> profile_backend =
         std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
-            password_manager::CreateLoginDatabaseForProfileStorage(
-                profile->GetPath(), std::move(is_profile_db_empty_cb)),
+            std::move(login_db),
             syncer::WipeModelUponSyncDisabledBehavior::kNever,
             profile->GetPrefs());
+    auto is_db_empty_cb =
+        base::BindPostTaskToCurrentDefault(base::BindRepeating(
+            &password_manager::SetEmptyStorePref, profile->GetPrefs(),
+            profile_backend->AsWeakPtr(),
+            password_manager::prefs::kEmptyProfileStoreLoginDatabase));
+    login_db_ptr->SetIsEmptyCb(std::move(is_db_empty_cb));
     ProfilePasswordStoreFactory::GetInstance()->SetTestingFactory(
         profile,
         base::BindRepeating(
diff --git a/chrome/browser/password_manager/android/password_store_android_account_backend.cc b/chrome/browser/password_manager/android/password_store_android_account_backend.cc
index c74d4a8..13af29df 100644
--- a/chrome/browser/password_manager/android/password_store_android_account_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_android_account_backend.cc
@@ -200,6 +200,7 @@
 
 void PasswordStoreAndroidAccountBackend::Shutdown(
     base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   affiliated_match_helper_ = nullptr;
   sync_service_ = nullptr;
   PasswordStoreAndroidBackend::Shutdown(std::move(shutdown_completed));
diff --git a/chrome/browser/password_manager/android/password_store_android_local_backend.cc b/chrome/browser/password_manager/android/password_store_android_local_backend.cc
index 35c2e3f..703094d4 100644
--- a/chrome/browser/password_manager/android/password_store_android_local_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_android_local_backend.cc
@@ -54,6 +54,7 @@
 
 void PasswordStoreAndroidLocalBackend::Shutdown(
     base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   PasswordStoreAndroidBackend::Shutdown(std::move(shutdown_completed));
 }
 
diff --git a/chrome/browser/password_manager/android/password_store_backend_migration_decorator.cc b/chrome/browser/password_manager/android/password_store_backend_migration_decorator.cc
index 711341f2..b1751cc1 100644
--- a/chrome/browser/password_manager/android/password_store_backend_migration_decorator.cc
+++ b/chrome/browser/password_manager/android/password_store_backend_migration_decorator.cc
@@ -82,6 +82,7 @@
 
 void PasswordStoreBackendMigrationDecorator::Shutdown(
     base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   migrator_.reset();
 
   auto shutdown_closure =
diff --git a/chrome/browser/password_manager/android/password_store_proxy_backend.cc b/chrome/browser/password_manager/android/password_store_proxy_backend.cc
index 723d493..5f30a6b 100644
--- a/chrome/browser/password_manager/android/password_store_proxy_backend.cc
+++ b/chrome/browser/password_manager/android/password_store_proxy_backend.cc
@@ -129,6 +129,7 @@
 }
 
 void PasswordStoreProxyBackend::Shutdown(base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   base::RepeatingClosure pending_shutdown_calls = base::BarrierClosure(
       /*num_closures=*/2, std::move(shutdown_completed));
   android_backend_->Shutdown(pending_shutdown_calls);
diff --git a/chrome/browser/password_manager/password_store_backend_factory.cc b/chrome/browser/password_manager/password_store_backend_factory.cc
index 7fb1f15..0981e1f 100644
--- a/chrome/browser/password_manager/password_store_backend_factory.cc
+++ b/chrome/browser/password_manager/password_store_backend_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/password_manager/password_store_backend_factory.h"
 
+#include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/task/bind_post_task.h"
@@ -38,14 +39,15 @@
 
 namespace {
 
+using ::password_manager::PasswordStoreBackend;
+using ::password_manager::PasswordStoreBuiltInBackend;
 #if !BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
 using password_manager::prefs::UseUpmLocalAndSeparateStoresState;
 
-std::unique_ptr<password_manager::PasswordStoreBackend>
+std::unique_ptr<PasswordStoreBackend>
 CreateProfilePasswordStoreBackendForUpmAndroid(
     PrefService* prefs,
-    std::unique_ptr<password_manager::PasswordStoreBuiltInBackend>
-        built_in_backend,
+    std::unique_ptr<PasswordStoreBuiltInBackend> built_in_backend,
     password_manager::PasswordAffiliationSourceAdapter&
         password_affiliation_adapter) {
   base::UmaHistogramBoolean(
@@ -118,50 +120,46 @@
 #endif  // !BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
 }  // namespace
 
-std::unique_ptr<password_manager::PasswordStoreBackend>
-CreateProfilePasswordStoreBackend(
+std::unique_ptr<PasswordStoreBackend> CreateProfilePasswordStoreBackend(
     const base::FilePath& login_db_directory,
     PrefService* prefs,
     password_manager::PasswordAffiliationSourceAdapter&
         password_affiliation_adapter) {
   TRACE_EVENT0("passwords", "PasswordStoreBackendCreation");
+  std::unique_ptr<password_manager::LoginDatabase> login_db(
+      password_manager::CreateLoginDatabaseForProfileStorage(
+          login_db_directory));
+  password_manager::LoginDatabase* login_db_ptr = login_db.get();
+  std::unique_ptr<PasswordStoreBackend> backend =
+      std::make_unique<PasswordStoreBuiltInBackend>(
+          std::move(login_db),
+          syncer::WipeModelUponSyncDisabledBehavior::kNever, prefs);
+
+#if !BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
+  // This are the absolute minimum requirements to have any version of UPM.
+  if (password_manager_android_util::AreMinUpmRequirementsMet()) {
+    std::unique_ptr<PasswordStoreBuiltInBackend> backend_as_built_in_backend(
+        static_cast<PasswordStoreBuiltInBackend*>(backend.release()));
+    backend = CreateProfilePasswordStoreBackendForUpmAndroid(
+        prefs, std::move(backend_as_built_in_backend),
+        password_affiliation_adapter);
+  }
+#endif  // !BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
 
   auto is_profile_db_empty_cb =
 #if BUILDFLAG(IS_ANDROID)
-      // base::Unretained() is safe, `prefs` outlives all keyed services,
-      // including the PasswordStore (LoginDatabase's owner).
       base::BindPostTaskToCurrentDefault(base::BindRepeating(
-          &password_manager::SetEmptyStorePref, prefs,
+          &password_manager::SetEmptyStorePref, prefs, backend->AsWeakPtr(),
           password_manager::prefs::kEmptyProfileStoreLoginDatabase));
 #else
       base::NullCallback();
 #endif
+  login_db_ptr->SetIsEmptyCb(std::move(is_profile_db_empty_cb));
 
-#if BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
-  return std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
-      password_manager::CreateLoginDatabaseForProfileStorage(
-          login_db_directory, std::move(is_profile_db_empty_cb)),
-      syncer::WipeModelUponSyncDisabledBehavior::kNever, prefs);
-#else  // BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
-  std::unique_ptr<password_manager::LoginDatabase> profile_login_db =
-      password_manager::CreateLoginDatabaseForProfileStorage(
-          login_db_directory, std::move(is_profile_db_empty_cb));
-  auto built_in_backend =
-      std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
-          std::move(profile_login_db),
-          syncer::WipeModelUponSyncDisabledBehavior::kNever, prefs);
-
-  // This are the absolute minimum requirements to have any version of UPM.
-  if (password_manager_android_util::AreMinUpmRequirementsMet()) {
-    return CreateProfilePasswordStoreBackendForUpmAndroid(
-        prefs, std::move(built_in_backend), password_affiliation_adapter);
-  }
-  return built_in_backend;
-#endif
+  return backend;
 }
 
-std::unique_ptr<password_manager::PasswordStoreBackend>
-CreateAccountPasswordStoreBackend(
+std::unique_ptr<PasswordStoreBackend> CreateAccountPasswordStoreBackend(
     const base::FilePath& login_db_directory,
     PrefService* prefs,
     std::unique_ptr<password_manager::UnsyncedCredentialsDeletionNotifier>
@@ -169,14 +167,16 @@
   std::unique_ptr<password_manager::LoginDatabase> login_db(
       password_manager::CreateLoginDatabaseForAccountStorage(
           login_db_directory));
+  std::unique_ptr<PasswordStoreBackend> backend;
+
 #if BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
-  return std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
+  backend = std::make_unique<PasswordStoreBuiltInBackend>(
       std::move(login_db), syncer::WipeModelUponSyncDisabledBehavior::kAlways,
       prefs, std::move(unsynced_deletions_notifier));
 #else  // BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
   if (!password_manager_android_util::AreMinUpmRequirementsMet()) {
     // Can happen if the downstream code is not available.
-    return std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
+    backend = std::make_unique<PasswordStoreBuiltInBackend>(
         std::move(login_db), syncer::WipeModelUponSyncDisabledBehavior::kAlways,
         prefs);
   }
@@ -185,12 +185,13 @@
   // syncs it. As such, it expects local data to be cleared every time when
   // sync is permanently disabled and thus uses
   // WipeModelUponSyncDisabledBehavior::kAlways.
-  return std::make_unique<AndroidBackendWithDoubleDeletion>(
-      std::make_unique<password_manager::PasswordStoreBuiltInBackend>(
+  backend = std::make_unique<AndroidBackendWithDoubleDeletion>(
+      std::make_unique<PasswordStoreBuiltInBackend>(
           std::move(login_db),
           syncer::WipeModelUponSyncDisabledBehavior::kAlways, prefs),
       std::make_unique<password_manager::PasswordStoreAndroidAccountBackend>(
           prefs, /*password_affiliation_adapter=*/nullptr,
           password_manager::kAccountStore));
 #endif
+  return backend;
 }
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
index 360dc99..d252a2d 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc
@@ -6,6 +6,8 @@
 
 #include "base/containers/contains.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_split.h"
 #include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/url_util.h"
@@ -599,17 +601,24 @@
   return url.host() + first_level_path;
 }
 
-LcppStat& GetLcppStatToUpdate(const LoadingPredictorConfig& config,
+// Returns LcppStat from `data`.
+// If LcppMultipleKeyKeyStat is enabled, this function can modify `data`
+// to emplace new LcppStat. `data_updated` is true on the case and
+// the caller should update the stored data.
+// TODO(yoichio): Updating data in "Get" function sounds weird. It could be
+// nice if we could restructure the functions or rename them.
+LcppStat* GetLcppStatToUpdate(const LoadingPredictorConfig& config,
                               const GURL& url,
-                              LcppData& data) {
+                              LcppData& data,
+                              bool& data_updated) {
   if (!IsLcppMultipleKeyKeyStatEnabled()) {
-    return *data.mutable_lcpp_stat();
+    return data.mutable_lcpp_stat();
   }
 
   const std::string first_level_path = GetFirstLevelPath(url);
   if (first_level_path.empty() ||
       !IsKeyLengthValidForMultipleKey(url.host(), first_level_path)) {
-    return *data.mutable_lcpp_stat();
+    return data.mutable_lcpp_stat();
   }
 
   LcppKeyStat& key_stat = *data.mutable_lcpp_key_stat();
@@ -620,11 +629,29 @@
       config.lcpp_multiple_key_histogram_sliding_window_size,
       config.lcpp_multiple_key_max_histogram_buckets, first_level_path,
       *key_stat.mutable_key_frequency_stat(), dropped_entry);
+  // Since UpdateLcppStringFrequencyStatData modifies a part of `data`,
+  // caller should update the stored data if the function is called.
+  data_updated = true;
   if (dropped_entry) {
-    CHECK_NE(*dropped_entry, first_level_path);
-    lcpp_stat_map.erase(*dropped_entry);
+    if (*dropped_entry == first_level_path) {
+      // This means `key_stat` is already full of well-used other
+      // first-level-path entries.
+      // However since the frequency map is updated, we need to update
+      // root `data` too via `data_updated` flag.
+      return nullptr;
+    } else {
+      lcpp_stat_map.erase(*dropped_entry);
+    }
   }
-  return lcpp_stat_map[first_level_path];
+  return &lcpp_stat_map[first_level_path];
+}
+
+bool IsLCPPFontPrefetchExcludedHost(const GURL& url) {
+  static const base::NoDestructor<base::flat_set<std::string>> excluded_hosts(
+      base::SplitString(
+          blink::features::kLCPPFontURLPredictorExcludedHosts.Get(), ",",
+          base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
+  return base::Contains(*excluded_hosts, url.host());
 }
 
 }  // namespace
@@ -833,6 +860,7 @@
     const std::string& new_entry,
     LcppStringFrequencyStatData& lcpp_stat_data,
     std::optional<std::string>& dropped_entry) {
+  dropped_entry = std::nullopt;
   std::unique_ptr<LcppFrequencyStatDataUpdater> updater =
       LcppFrequencyStatDataUpdater::FromLcppStringFrequencyStatData(
           sliding_window_size, max_histogram_buckets, lcpp_stat_data);
@@ -945,15 +973,24 @@
     lcpp_data.mutable_lcpp_key_stat()->Clear();
   }
 
-  LcppStat& lcpp_stat = GetLcppStatToUpdate(config_, url, lcpp_data);
-  if (!IsValidLcppStat(lcpp_stat)) {
-    lcpp_stat.Clear();
-    base::UmaHistogramBoolean("LoadingPredictor.LcppStatCorruptedAtLearnTime",
-                              true);
+  bool data_updated = false;
+  LcppStat* lcpp_stat =
+      GetLcppStatToUpdate(config_, url, lcpp_data, data_updated);
+  if (lcpp_stat) {
+    if (!IsValidLcppStat(*lcpp_stat)) {
+      lcpp_stat->Clear();
+      base::UmaHistogramBoolean("LoadingPredictor.LcppStatCorruptedAtLearnTime",
+                                true);
+    }
+    data_updated |=
+        UpdateLcppStatWithLcppDataInputs(config_, inputs, *lcpp_stat);
+    if (IsLCPPFontPrefetchExcludedHost(url) &&
+        lcpp_stat->has_fetched_font_url_stat()) {
+      lcpp_stat->clear_fetched_font_url_stat();
+      data_updated = true;
+    }
+    DCHECK(IsValidLcppStat(*lcpp_stat));
   }
-  const bool data_updated =
-      UpdateLcppStatWithLcppDataInputs(config_, inputs, lcpp_stat);
-  DCHECK(IsValidLcppStat(lcpp_stat));
   if (data_updated) {
     data_map_.UpdateData(key, lcpp_data);
   }
diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util_unittest.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util_unittest.cc
index deb274a..d201af5e 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util_unittest.cc
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util_unittest.cc
@@ -151,6 +151,38 @@
       << updater.Data();
 }
 
+TEST(UpdateLcppStringFrequencyStatDataTest, AddNewEntryToFullBuckets) {
+  Updater updater(/*sliding_window_size=*/4u,
+                  /*max_histogram_buckets=*/2u);
+
+  std::optional<std::string> dropped_entry;
+  updater.Update("foo", dropped_entry);
+  EXPECT_FALSE(dropped_entry.has_value());
+  updater.Update("foo", dropped_entry);
+  EXPECT_FALSE(dropped_entry.has_value());
+  updater.Update("bar", dropped_entry);
+  EXPECT_FALSE(dropped_entry.has_value());
+  updater.Update("bar", dropped_entry);
+  EXPECT_FALSE(dropped_entry.has_value());
+  EXPECT_EQ(updater.Data(), MakeData({{"foo", 2}, {"bar", 2}}, 0))
+      << updater.Data();
+
+  updater.Update("qux", dropped_entry);
+  EXPECT_EQ(*dropped_entry, "qux");
+  EXPECT_EQ(updater.Data(), MakeData({{"foo", 1.5}, {"bar", 1.5}}, 1))
+      << updater.Data();
+
+  updater.Update("qux", dropped_entry);
+  EXPECT_EQ(*dropped_entry, "qux");
+  EXPECT_EQ(updater.Data(), MakeData({{"foo", 1.125}, {"bar", 1.125}}, 1.75))
+      << updater.Data();
+
+  updater.Update("qux", dropped_entry);
+  EXPECT_EQ(*dropped_entry, "bar");
+  EXPECT_EQ(updater.Data(), MakeData({{"foo", 0.84375}, {"qux", 1}}, 2.15625))
+      << updater.Data();
+}
+
 TEST(IsValidLcppStatTest, Empty) {
   LcppStat lcpp_stat;
   EXPECT_TRUE(IsValidLcppStat(lcpp_stat));
@@ -982,12 +1014,13 @@
   }
 
   static LcppStat MakeLcppStatWithLCPElementLocator(
-      const std::string& lcp_element_locator) {
+      const std::string& lcp_element_locator,
+      double frequency = 1) {
     LcppStat stat;
     LcpElementLocatorBucket& bucket = *stat.mutable_lcp_element_locator_stat()
                                            ->add_lcp_element_locator_buckets();
     bucket.set_lcp_element_locator(lcp_element_locator);
-    bucket.set_frequency(1);
+    bucket.set_frequency(frequency);
     return stat;
   }
 
@@ -1556,4 +1589,39 @@
   EXPECT_EQ(data, mock_tables_->lcpp_table_.data_["a.test"]);
 }
 
+TEST_F(LcppMultipleKeyTestKeyStat, AddNewEntryToFullBucketKeyStat) {
+  LoadingPredictorConfig config;
+  PopulateTestConfig(&config);
+  config.max_hosts_to_track_for_lcpp = 2u;
+  config.lcpp_multiple_key_histogram_sliding_window_size = 4u;
+  config.lcpp_multiple_key_max_histogram_buckets = 2u;
+  InitializeDB(config);
+
+  const std::string host = "http://a.test";
+  const GURL url_1(host + "/foo1");
+  const GURL url_2(host + "/foo2");
+  const GURL url_3(host + "/foo3");
+  LearnElementLocator(url_1, "/#lcp1");
+  LearnElementLocator(url_1, "/#lcp1");
+  LearnElementLocator(url_2, "/#lcp2");
+  LearnElementLocator(url_2, "/#lcp2");
+  EXPECT_EQ(*GetLcppStat(url_1),
+            MakeLcppStatWithLCPElementLocator("/#lcp1", 2));
+  EXPECT_EQ(*GetLcppStat(url_2),
+            MakeLcppStatWithLCPElementLocator("/#lcp2", 2));
+
+  LearnElementLocator(url_3, "/#lcp3");
+  EXPECT_FALSE(GetLcppStat(url_3));
+
+  LearnElementLocator(url_3, "/#lcp3");
+  EXPECT_FALSE(GetLcppStat(url_3));
+
+  LearnElementLocator(url_3, "/#lcp3");
+  EXPECT_EQ(*GetLcppStat(url_3),
+            MakeLcppStatWithLCPElementLocator("/#lcp3", 1));
+  EXPECT_FALSE(GetLcppStat(url_1));
+  EXPECT_EQ(*GetLcppStat(url_2),
+            MakeLcppStatWithLCPElementLocator("/#lcp2", 2));
+}
+
 }  // namespace predictors
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc
index 82142ad..18cb2666 100644
--- a/chrome/browser/predictors/loading_predictor_browsertest.cc
+++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -901,8 +901,12 @@
 class LCPCriticalPathPredictorBrowserTest : public LoadingPredictorBrowserTest {
  public:
   LCPCriticalPathPredictorBrowserTest() {
-    scoped_feature_list_.InitWithFeatures(
-        {blink::features::kLCPCriticalPathPredictor}, {});
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {{blink::features::kLCPCriticalPathPredictor, {}},
+         {blink::features::kLCPPFontURLPredictor,
+          {{blink::features::kLCPPFontURLPredictorExcludedHosts.name,
+            "exclude.test,exclude2.test"}}}},
+        {});
   }
 
   std::vector<std::string> ExpectLcpElementLocatorsPrediction(
@@ -941,6 +945,19 @@
     lcp_element_waiter.Wait();
   }
 
+  std::vector<std::string> GetLCPPFonts(const GURL& url) {
+    auto lcpp_stat =
+        loading_predictor()->resource_prefetch_predictor()->GetLcppStat(url);
+    if (!lcpp_stat) {
+      return std::vector<std::string>();
+    }
+    std::vector<std::string> fonts;
+    for (const auto& it : lcpp_stat->fetched_font_url_stat().main_buckets()) {
+      fonts.push_back(it.first);
+    }
+    return fonts;
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1009,6 +1026,37 @@
   EXPECT_EQ(locator_for_a, locators_6[1]);
 }
 
+IN_PROC_BROWSER_TEST_F(LCPCriticalPathPredictorBrowserTest, LearnLCPPFont) {
+  const GURL kUrlA =
+      embedded_test_server()->GetURL("p.com", "/predictors/lcpp_font.html");
+  const GURL kFontUrlA =
+      embedded_test_server()->GetURL("p.com", "/predictors/font.ttf");
+  const GURL kUrlB = embedded_test_server()->GetURL(
+      "exclude.test", "/predictors/lcpp_font.html");
+  const GURL kUrlC = embedded_test_server()->GetURL(
+      "exclude2.test", "/predictors/lcpp_font.html");
+
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlA));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlB));
+
+  std::vector<std::string> expected;
+  expected.push_back(kFontUrlA.spec());
+  NavigateAndWaitForLcpElement(FROM_HERE, kUrlA);
+  EXPECT_EQ(expected, GetLCPPFonts(kUrlA));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlB));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlC));
+
+  NavigateAndWaitForLcpElement(FROM_HERE, kUrlB);
+  EXPECT_EQ(expected, GetLCPPFonts(kUrlA));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlB));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlC));
+
+  NavigateAndWaitForLcpElement(FROM_HERE, kUrlC);
+  EXPECT_EQ(expected, GetLCPPFonts(kUrlA));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlB));
+  EXPECT_EQ(std::vector<std::string>(), GetLCPPFonts(kUrlC));
+}
+
 enum class NetworkIsolationKeyMode {
   kDisabled,
   kEnabled,
diff --git a/chrome/browser/predictors/loading_test_util.cc b/chrome/browser/predictors/loading_test_util.cc
index 1b628ec5..c52aa0e 100644
--- a/chrome/browser/predictors/loading_test_util.cc
+++ b/chrome/browser/predictors/loading_test_util.cc
@@ -301,7 +301,21 @@
 
 std::ostream& operator<<(std::ostream& os, const LcppData& data) {
   os << "[" << data.host() << "," << data.last_visit_time() << "]" << std::endl;
+  os << "lcpp_stat:" << std::endl;
   os << data.lcpp_stat();
+  os << "lcpp_key_stat:" << std::endl;
+  os << data.lcpp_key_stat();
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os, const LcppKeyStat& key_stat) {
+  os << "\t" << "lcpp_stat_map:" << std::endl;
+  for (const auto& [path, path_key_stat] : key_stat.lcpp_stat_map()) {
+    os << "\t\t" << path << std::endl;
+    os << path_key_stat << std::endl;
+  }
+  os << "\t" << "key_frequency_stat:" << std::endl;
+  os << key_stat.key_frequency_stat();
   return os;
 }
 
diff --git a/chrome/browser/predictors/loading_test_util.h b/chrome/browser/predictors/loading_test_util.h
index 1bfb240..60f697e 100644
--- a/chrome/browser/predictors/loading_test_util.h
+++ b/chrome/browser/predictors/loading_test_util.h
@@ -123,6 +123,7 @@
 std::ostream& operator<<(std::ostream& os,
                          const PreconnectPrediction& prediction);
 std::ostream& operator<<(std::ostream& os, const LcppData& data);
+std::ostream& operator<<(std::ostream& os, const LcppKeyStat& key_stat);
 std::ostream& operator<<(std::ostream& os, const LcppStat& stat);
 std::ostream& operator<<(std::ostream& os,
                          const LcpElementLocatorBucket& bucket);
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index b095a6b2..6e991d9 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -68,7 +68,6 @@
 #include "chrome/browser/signin/signin_util.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/unified_consent/unified_consent_service_factory.h"
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_icons.html b/chrome/browser/resources/chromeos/login/components/oobe_icons.html
index b441b421..13b8950 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_icons.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_icons.html
@@ -325,6 +325,9 @@
       <g id="google-g">
         <path fill-rule="evenodd" clip-rule="evenodd" d="M15.515 18.948v-5.772h14.522c0.217 0.977 0.388 1.893 0.388 3.181 0 8.859 -5.942 15.158 -14.894 15.158C6.951 31.515 0 24.564 0 16S6.951 0.485 15.515 0.485c4.189 0 7.695 1.536 10.38 4.049L21.488 8.816C20.371 7.761 18.432 6.505 15.515 6.505c-5.135 0 -9.325 4.267 -9.325 9.495 0 5.229 4.189 9.495 9.325 9.495 5.942 0 8.13 -4.111 8.533 -6.547H15.515Z" fill="var(--oobe-card-radio-button-icon-color)"></path>
       </g>
+      <g id="perosonalized-apps">
+        <path xmlns="http://www.w3.org/2000/svg" d="M3.3 32C2.36667 32 1.58333 31.6833 0.950001 31.05C0.316667 30.4167 2.38419e-07 29.6333 2.38419e-07 28.7C2.38419e-07 27.7667 0.316667 26.9833 0.950001 26.35C1.58333 25.7167 2.36667 25.4 3.3 25.4C4.23333 25.4 5.01667 25.7167 5.65 26.35C6.28333 26.9833 6.6 27.7667 6.6 28.7C6.6 29.6333 6.28333 30.4167 5.65 31.05C5.01667 31.6833 4.23333 32 3.3 32ZM16 32C15.0667 32 14.2833 31.6833 13.65 31.05C13.0167 30.4167 12.7 29.6333 12.7 28.7C12.7 27.7667 13.0167 26.9833 13.65 26.35C14.2833 25.7167 15.0667 25.4 16 25.4C16.9333 25.4 17.7167 25.7167 18.35 26.35C18.9833 26.9833 19.3 27.7667 19.3 28.7C19.3 29.6333 18.9833 30.4167 18.35 31.05C17.7167 31.6833 16.9333 32 16 32ZM28.7 32C27.7667 32 26.9833 31.6833 26.35 31.05C25.7167 30.4167 25.4 29.6333 25.4 28.7C25.4 27.7667 25.7167 26.9833 26.35 26.35C26.9833 25.7167 27.7667 25.4 28.7 25.4C29.6333 25.4 30.4167 25.7167 31.05 26.35C31.6833 26.9833 32 27.7667 32 28.7C32 29.6333 31.6833 30.4167 31.05 31.05C30.4167 31.6833 29.6333 32 28.7 32ZM3.3 19.3C2.36667 19.3 1.58333 18.9833 0.950001 18.35C0.316667 17.7167 2.38419e-07 16.9333 2.38419e-07 16C2.38419e-07 15.0667 0.316667 14.2833 0.950001 13.65C1.58333 13.0167 2.36667 12.7 3.3 12.7C4.23333 12.7 5.01667 13.0167 5.65 13.65C6.28333 14.2833 6.6 15.0667 6.6 16C6.6 16.9333 6.28333 17.7167 5.65 18.35C5.01667 18.9833 4.23333 19.3 3.3 19.3ZM16 19.3C15.0667 19.3 14.2833 18.9833 13.65 18.35C13.0167 17.7167 12.7 16.9333 12.7 16C12.7 15.0667 13.0167 14.2833 13.65 13.65C14.2833 13.0167 15.0667 12.7 16 12.7C16.9333 12.7 17.7167 13.0167 18.35 13.65C18.9833 14.2833 19.3 15.0667 19.3 16C19.3 16.9333 18.9833 17.7167 18.35 18.35C17.7167 18.9833 16.9333 19.3 16 19.3ZM28.7 19.3C27.7667 19.3 26.9833 18.9833 26.35 18.35C25.7167 17.7167 25.4 16.9333 25.4 16C25.4 15.0667 25.7167 14.2833 26.35 13.65C26.9833 13.0167 27.7667 12.7 28.7 12.7C29.6333 12.7 30.4167 13.0167 31.05 13.65C31.6833 14.2833 32 15.0667 32 16C32 16.9333 31.6833 17.7167 31.05 18.35C30.4167 18.9833 29.6333 19.3 28.7 19.3ZM3.3 6.6C2.36667 6.6 1.58333 6.28333 0.950001 5.65C0.316667 5.01666 2.38419e-07 4.23333 2.38419e-07 3.3C2.38419e-07 2.36666 0.316667 1.58333 0.950001 0.949998C1.58333 0.316665 2.36667 -1.90735e-06 3.3 -1.90735e-06C4.23333 -1.90735e-06 5.01667 0.316665 5.65 0.949998C6.28333 1.58333 6.6 2.36666 6.6 3.3C6.6 4.23333 6.28333 5.01666 5.65 5.65C5.01667 6.28333 4.23333 6.6 3.3 6.6ZM16 6.6C15.0667 6.6 14.2833 6.28333 13.65 5.65C13.0167 5.01666 12.7 4.23333 12.7 3.3C12.7 2.36666 13.0167 1.58333 13.65 0.949998C14.2833 0.316665 15.0667 -1.90735e-06 16 -1.90735e-06C16.9333 -1.90735e-06 17.7167 0.316665 18.35 0.949998C18.9833 1.58333 19.3 2.36666 19.3 3.3C19.3 4.23333 18.9833 5.01666 18.35 5.65C17.7167 6.28333 16.9333 6.6 16 6.6ZM28.7 6.6C27.7667 6.6 26.9833 6.28333 26.35 5.65C25.7167 5.01666 25.4 4.23333 25.4 3.3C25.4 2.36666 25.7167 1.58333 26.35 0.949998C26.9833 0.316665 27.7667 -1.90735e-06 28.7 -1.90735e-06C29.6333 -1.90735e-06 30.4167 0.316665 31.05 0.949998C31.6833 1.58333 32 2.36666 32 3.3C32 4.23333 31.6833 5.01666 31.05 5.65C30.4167 6.28333 29.6333 6.6 28.7 6.6Z"></path>
+      </g>
       <g id="password" viewBox="0 -8 32 32">
         <path fill-rule="evenodd" clip-rule="evenodd" d="M4.772 6.414 3.579 8.491l-1.825 -1.053L2.947 5.362H0.562v-2.105H2.947l-1.193 -2.063L3.579 0.141l1.193 2.063L5.965 0.141l1.825 1.053 -1.193 2.063h2.387v2.105H6.596L7.789 7.438l-1.825 1.053 -1.192 -2.077Zm-2.808 7.761v2.808h28.071v-2.808H1.965Zm11.018 -6.737 1.824 1.053L16 6.414l1.193 2.077 1.824 -1.053 -1.192 -2.077H20.211v-2.105h-2.386l1.192 -2.063 -1.824 -1.053L16 2.204 14.807 0.141l-1.824 1.053 1.192 2.063H11.789v2.105h2.386l-1.192 2.077Zm17.263 -6.245L29.053 3.256h2.386v2.105H29.053l1.193 2.077 -1.825 1.053 -1.193 -2.077 -1.192 2.077 -1.825 -1.053 1.193 -2.077h-2.387v-2.105h2.387l-1.193 -2.063 1.825 -1.053 1.192 2.063L28.421 0.141l1.825 1.053Z" fill="var(--oobe-card-radio-button-icon-color)"></path>
       </g>
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts b/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts
index 65691f9..51074db 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts
+++ b/chrome/browser/resources/chromeos/login/components/oobe_personalized_apps_list.ts
@@ -105,7 +105,7 @@
     if (this.itemRendered === this.appsList.length &&
         this.loadedIconsCount === this.appsList.length) {
       this.dispatchEvent(new CustomEvent(
-          'apss-icons-loaded', {bubbles: true, composed: true}));
+          'apps-icons-loaded', {bubbles: true, composed: true}));
     }
   }
 
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index e6905d8..8e2cf4bf 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -25,6 +25,133 @@
   return qrData;
 }
 
+function createCategoriesAppsData() {
+  const data = {
+    data: {
+      'categorie_1': [
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'Pinterest',
+          subname: 'Music streaming',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'WhatsApp Messenger',
+          subname: 'Office software',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'Clash Royale',
+          subname: 'Messaging',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'Zoom',
+          subname: 'Cloud gaming',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+      ],
+      'categorie_23': [
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'Pinterest',
+          subname: 'Music streaming',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'WhatsApp Messenger',
+          subname: 'Office software',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'Clash Royale',
+          subname: 'Messaging',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+        {
+          AppId: 'screenID1',
+          icon:
+              'https://lh3.googleusercontent.com/dVsv8Hc4TOUeLFAahxR8KANg22W9dj2jBsTW1VHv3CV-5NCZjP9D9i2j5IpfVx2NTB8',
+          name: 'Zoom',
+          subname: 'Cloud gaming',
+          package_name: 'Pinterest',
+          selected: false,
+        },
+      ],
+    },
+  };
+  return data;
+}
+
+function createCategoriesData() {
+  const data = {
+    categories: [
+      {
+        categoryId: 'oobe_business',
+        icon: 'https://meltingpot.googleusercontent.com/oobe/business.svg',
+        title: 'Small Business',
+        subtitle: 'Small business essentials',
+        selected: false,
+      },
+      {
+        categoryId: 'oobe_entertainment',
+        icon: 'https://meltingpot.googleusercontent.com/oobe/entertainment.svg',
+        title: 'Entertainment',
+        subtitle: 'Media, music, video streaming',
+        selected: false,
+      },
+      {
+        categoryId: 'oobe_communication',
+        icon: 'https://meltingpot.googleusercontent.com/oobe/communication.svg',
+        title: 'Communication',
+        subtitle: 'Messaging, video chat, social media',
+        selected: false,
+      },
+      {
+        categoryId: 'oobe_creativity',
+        icon: 'https://meltingpot.googleusercontent.com/oobe/productivity.svg',
+        title: 'Creativity',
+        subtitle: 'Drawing, design and media editing',
+        selected: false,
+      },
+      {
+        categoryId: 'oobe_productivity',
+        icon: 'https://meltingpot.googleusercontent.com/oobe/creativity.svg',
+        title: 'Productivity',
+        subtitle: 'Home office, productivity work',
+        selected: false,
+      },
+    ],
+  };
+  return data;
+}
+
 const createAssistantData = (isMinor) => {
   const data = {};
   data['valuePropTitle'] =
@@ -1802,6 +1929,34 @@
       kind: ScreenKind.NORMAL,
     },
     {
+      id: 'categories-selection',
+      kind: ScreenKind.NORMAL,
+      handledSteps: 'overview',
+      states: [
+        {
+          id: 'overview',
+          trigger: (screen) => {
+            screen.setUIStep('overview');
+            screen.setCategoriesData(createCategoriesData());
+          },
+        },
+      ],
+    },
+    {
+      id: 'personalized-apps',
+      kind: ScreenKind.NORMAL,
+      handledSteps: 'overview',
+      states: [
+        {
+          id: 'overview',
+          trigger: (screen) => {
+            screen.setUIStep('overview');
+            screen.setCategoriesAppsMapData(createCategoriesAppsData());
+          },
+        },
+      ],
+    },
+    {
       id: 'marketing-opt-in',
       kind: ScreenKind.NORMAL,
       handledSteps: 'overview',
diff --git a/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html b/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html
index 2da368a..7aafe2d9 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html
+++ b/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.html
@@ -5,7 +5,36 @@
 -->
 <style include="oobe-dialog-host-styles">
 </style>
-
-<oobe-loading-dialog title-key="[[gettingDeviceReadyTitle]]">
-    <iron-icon slot="icon" icon="oobe-32:googleg"></iron-icon>
-</oobe-loading-dialog>
\ No newline at end of file
+<oobe-loading-dialog id="progressDialog" for-step="loading"
+        title-key="personalizedRecommendedLoading" role="dialog"
+        aria-label$="[[i18nDynamic(locale, 'personalizedRecommendedLoading')]]">
+    <iron-icon slot="icon" icon="oobe-32:perosonalized-apps"></iron-icon>
+</oobe-loading-dialog>
+<oobe-adaptive-dialog id="personalizedRecommendDialog" role="presentation"
+      for-step="overview">
+    <iron-icon slot="icon" icon="oobe-32:perosonalized-apps"></iron-icon>
+    <h1 slot="title" id="recommend-title" aria-live="polite">
+      [[i18nDynamic(locale,
+            'personalizedRecommendedAppsScreenTitle')]]
+    </h1>
+    <div slot="subtitle" id="recommend-subtitle">
+      [[i18nDynamic(locale,
+            'personalizedRecommendedAppsScreenDescription')]]
+    </div>
+    <div slot="content" class="layout vertical">
+        <oobe-personalized-apps-list id="categoriesAppsList"
+            class="focus-on-show"
+            selected-apps-count="{{numberOfSelectedApps}}"
+            on-apps-icons-loaded="onFullyLoaded">
+        </oobe-personalized-apps-list>
+    </div>
+    <div slot="bottom-buttons">
+      <oobe-text-button id="skipButton"
+            text-key="personalizedRecommendedAppsScreenSkip" 
+            on-click="onSkip" border>
+      </oobe-text-button>
+      <oobe-next-button id="nextButton" on-click="onNextClicked"
+        disabled="[[!canProceed(numberOfSelectedApps)]]">
+      </oobe-next-button>
+    </div>
+</oobe-adaptive-dialog>
diff --git a/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.ts b/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.ts
index 57ca4dc..3ab5d9c 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.ts
+++ b/chrome/browser/resources/chromeos/login/screens/common/personalized_recommend_apps.ts
@@ -7,17 +7,38 @@
 
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import '../../components/dialogs/oobe_loading_dialog.js';
+import '../../components/oobe_personalized_apps_list.js';
 
+import {assert} from '//resources/js/assert.js';
 import {PolymerElementProperties} from '//resources/polymer/v3_0/polymer/interfaces.js';
 import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {LoginScreenBehavior, LoginScreenBehaviorInterface} from '../../components/behaviors/login_screen_behavior.js';
 import {MultiStepBehavior, MultiStepBehaviorInterface} from '../../components/behaviors/multi_step_behavior.js';
 import {OobeDialogHostBehavior, OobeDialogHostBehaviorInterface} from '../../components/behaviors/oobe_dialog_host_behavior.js';
+import {OobeUiState} from '../../components/display_manager_types.js';
 import {OobeI18nMixin, OobeI18nMixinInterface} from '../../components/mixins/oobe_i18n_mixin.js';
+import {CategoriesAppsMap, OobePersonalizedAppsList} from '../../components/oobe_personalized_apps_list.js';
 
 import {getTemplate} from './personalized_recommend_apps.html.js';
 
+enum PersonalizedAppsStep {
+  LOADING = 'loading',
+  OVERVIEW = 'overview',
+}
+
+/**
+ * Available user actions.
+ */
+enum UserAction {
+  SKIP = 'skip',
+  NEXT = 'next',
+}
+
+interface RecommendAppsScreenData {
+  data: CategoriesAppsMap;
+}
+
 export const PersonalizedRecommedAppsElementBase =
     mixinBehaviors(
         [LoginScreenBehavior, OobeDialogHostBehavior, MultiStepBehavior],
@@ -38,13 +59,86 @@
   }
 
   static get properties(): PolymerElementProperties {
-    return {};
+    return {
+      numberOfSelectedApps: {
+        type: Number,
+        value: 0,
+      },
+    };
+  }
+
+  private numberOfSelectedApps: number;
+
+  override get UI_STEPS() {
+    return PersonalizedAppsStep;
+  }
+
+  // eslint-disable-next-line @typescript-eslint/naming-convention
+  override defaultUIStep() {
+    return PersonalizedAppsStep.LOADING;
+  }
+
+  override get EXTERNAL_API(): string[] {
+    return [
+      'setCategoriesAppsMapData',
+    ];
+  }
+
+  /**
+   * Returns the control which should receive initial focus.
+   */
+  override get defaultControl(): HTMLElement|null {
+    const categoriesDialog = this.shadowRoot?.querySelector<HTMLElement>(
+        '#personalizedRecommendDialog');
+    if (categoriesDialog instanceof HTMLElement) {
+      return categoriesDialog;
+    }
+    return null;
+  }
+
+  // eslint-disable-next-line @typescript-eslint/naming-convention
+  override getOobeUIInitialState(): OobeUiState {
+    return OobeUiState.ONBOARDING;
+  }
+
+  setCategoriesAppsMapData(categoriesData: RecommendAppsScreenData): void {
+    assert('data' in categoriesData);
+    this.shadowRoot!
+        .querySelector<OobePersonalizedAppsList>('#categoriesAppsList')!.init(
+            categoriesData['data']);
+  }
+
+  /**
+   * Handles event when contents in the webview is generated.
+   */
+  private onFullyLoaded(): void {
+    this.setUIStep(PersonalizedAppsStep.OVERVIEW);
+    const categoriesAppsList =
+        this.shadowRoot?.querySelector<HTMLElement>('#categoriesAppsList');
+    if (categoriesAppsList instanceof HTMLElement) {
+      categoriesAppsList.focus();
+    }
   }
 
   override ready(): void {
     super.ready();
     this.initializeLoginScreen('PersonalizedRecommendAppsScreen');
   }
+
+  private onNextClicked(): void {
+    const appsSelected = this.shadowRoot!
+                             .querySelector<OobePersonalizedAppsList>(
+                                 '#categoriesAppsList')!.getAppsSelected();
+    this.userActed([UserAction.NEXT, appsSelected]);
+  }
+
+  private onSkip(): void {
+    this.userActed(UserAction.SKIP);
+  }
+
+  private canProceed(): boolean {
+    return this.numberOfSelectedApps > 0;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/downloads/item.html b/chrome/browser/resources/downloads/item.html
index 454c12e..0197942 100644
--- a/chrome/browser/resources/downloads/item.html
+++ b/chrome/browser/resources/downloads/item.html
@@ -136,6 +136,10 @@
     opacity: .5;
   }
 
+  #esb-download-row-promo {
+    background-color: var(--cr-hover-background-color);
+  }
+
   #file-icon-wrapper iron-icon[icon-color='light-grey'] {
     color: var(--google-grey-400);
   }
@@ -410,7 +414,7 @@
           </template>
           <template is="dom-if" if="[[showDeepScan_]]" restamp>
             <span role="gridcell">
-              <cr-button on-click="onDeepScanClick_" id="deepScan" 
+              <cr-button on-click="onDeepScanClick_" id="deepScan"
                   class="action-button" focus-row-control focus-type="open">
                 [[computeDeepScanControlText_(data.state)]]
               </cr-button>
@@ -445,7 +449,7 @@
         <div id="safe" class="controls" hidden="[[isDangerous_]]">
           <template is="dom-if" if="[[showDeepScan_]]" restamp>
             <span role="gridcell">
-              <cr-button on-click="onDeepScanClick_" id="deepScan" 
+              <cr-button on-click="onDeepScanClick_" id="deepScan"
                   class="action-button" focus-row-control focus-type="open">
                 [[computeDeepScanControlText_(data.state)]]
               </cr-button>
@@ -459,7 +463,7 @@
 
       <!-- Show the dangerous control buttons in the old UX only. -->
       <template is="dom-if"
-          if="[[computeShowButtonsForDangerous_(isDangerous_, 
+          if="[[computeShowButtonsForDangerous_(isDangerous_,
               improvedDownloadWarningsUx_)]]">
         <div id="dangerous" class="controls">
           <!-- Files that enterprise policies allow to be reviewed. -->
diff --git a/chrome/browser/screen_ai/screen_ai_install_state.cc b/chrome/browser/screen_ai/screen_ai_install_state.cc
index 2c432a2..9a71029 100644
--- a/chrome/browser/screen_ai/screen_ai_install_state.cc
+++ b/chrome/browser/screen_ai/screen_ai_install_state.cc
@@ -8,7 +8,6 @@
 
 #include "base/check_is_test.h"
 #include "base/files/file_path.h"
-#include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
@@ -86,19 +85,6 @@
   return true;
 }
 
-// TODO(b/41489907): Remove this function once it's known why the binary is
-// sometimes not available.
-// static
-bool ScreenAIInstallState::VerifyLibraryAvailablity(
-    const base::FilePath& install_dir) {
-    // TODO(b/41489907): Try adding a browser test for this case.
-    bool binary_available =
-        base::PathExists(install_dir.Append(GetComponentBinaryFileName()));
-    base::UmaHistogramBoolean(
-        "Accessibility.ScreenAI.Component.BinaryAvailable", binary_available);
-    return binary_available;
-}
-
 ScreenAIInstallState::ScreenAIInstallState() {
   CHECK_EQ(g_instance, nullptr);
   g_instance = this;
diff --git a/chrome/browser/screen_ai/screen_ai_install_state.h b/chrome/browser/screen_ai/screen_ai_install_state.h
index 35223c4..fc379cf 100644
--- a/chrome/browser/screen_ai/screen_ai_install_state.h
+++ b/chrome/browser/screen_ai/screen_ai_install_state.h
@@ -54,10 +54,6 @@
   // expected.
   static bool VerifyLibraryVersion(const base::Version& version);
 
-  // Verifies that the library is in the expected folder. On Windows, it is
-  // also checked that the library is loadable.
-  static bool VerifyLibraryAvailablity(const base::FilePath& install_dir);
-
   // Returns true if the library is used recently and we need to keep it on
   // device and updated.
   static bool ShouldInstall(PrefService* local_state);
diff --git a/chrome/browser/screen_ai/screen_ai_service_router.cc b/chrome/browser/screen_ai/screen_ai_service_router.cc
index f62f2ba..a88cea8 100644
--- a/chrome/browser/screen_ai/screen_ai_service_router.cc
+++ b/chrome/browser/screen_ai/screen_ai_service_router.cc
@@ -108,7 +108,6 @@
       files_list_file_name);
 }
 
-// TODO(https://crbug.com/41489907): Remove after the issue is fixed.
 void RecordComponentAvailablity(bool available) {
   base::UmaHistogramBoolean("Accessibility.ScreenAI.Component.Available",
                             available);
diff --git a/chrome/browser/signin/chrome_signin_pref_names.h b/chrome/browser/signin/chrome_signin_pref_names.h
index f461eae49..94d67635 100644
--- a/chrome/browser/signin/chrome_signin_pref_names.h
+++ b/chrome/browser/signin/chrome_signin_pref_names.h
@@ -12,6 +12,15 @@
 inline constexpr char kProfileCreationInterceptionDeclined[] =
     "signin.ProfileCreationInterceptionDeclinedPref";
 
+// Integer pref to store the number of times the password bubble signin promo
+// has been shown per profile while the user is signed out.
+inline constexpr char kPasswordSignInPromoShownCountPerProfile[] =
+    "signin.PasswordSignInPromoShownCount";
+// Integer pref to store the number of times any autofill bubble signin promo
+// has been dismissed per profile while the user is signed out.
+inline constexpr char kAutofillSignInPromoDismissCountPerProfile[] =
+    "signin.AutofillSignInPromoDismissCount";
+
 }  // namespace prefs
 
 #endif  // CHROME_BROWSER_SIGNIN_CHROME_SIGNIN_PREF_NAMES_H_
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc
index 80358e1..cb6f607 100644
--- a/chrome/browser/signin/signin_promo.cc
+++ b/chrome/browser/signin/signin_promo.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/google/google_brand.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "chrome/browser/signin/chrome_signin_pref_names.h"
 #include "chrome/browser/signin/signin_promo_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
@@ -166,6 +167,10 @@
 void RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterIntegerPref(prefs::kDiceSigninUserMenuPromoCount, 0);
+  registry->RegisterIntegerPref(
+      prefs::kAutofillSignInPromoDismissCountPerProfile, 0);
+  registry->RegisterIntegerPref(prefs::kPasswordSignInPromoShownCountPerProfile,
+                                0);
 }
 
 }  // namespace signin
diff --git a/chrome/browser/signin/signin_promo_unittest.cc b/chrome/browser/signin/signin_promo_unittest.cc
index 17e86c66..80f3f9d5 100644
--- a/chrome/browser/signin/signin_promo_unittest.cc
+++ b/chrome/browser/signin/signin_promo_unittest.cc
@@ -6,18 +6,22 @@
 
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/signin/chrome_signin_pref_names.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/signin/signin_promo_util.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/base/signin_prefs.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/sync/base/command_line_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
-
 namespace signin {
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -72,6 +76,57 @@
                               GURL("https://continue_url/")));
 }
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+TEST(SignInPromoVersionTest, SignInPromoVersions) {
+  ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
+  content::BrowserTaskEnvironment task_environment;
+
+  IdentityTestEnvironment identity_test_env;
+  IdentityManager* identity_manager = identity_test_env.identity_manager();
+
+  // No Account present.
+  EXPECT_EQ(SignInAutofillBubbleVersion::kNoAccount,
+            GetSignInPromoVersion(identity_manager));
+
+  // Web signed in.
+  identity_test_env.MakeAccountAvailable("test@email.com",
+                                         {.set_cookie = true});
+  EXPECT_EQ(SignInAutofillBubbleVersion::kWebSignedIn,
+            GetSignInPromoVersion(identity_manager));
+
+  // Syncing.
+  AccountInfo info = identity_test_env.MakePrimaryAccountAvailable(
+      "test@email.com", ConsentLevel::kSync);
+  EXPECT_EQ(SignInAutofillBubbleVersion::kNoPromo,
+            GetSignInPromoVersion(identity_manager));
+
+  // Sync paused state.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      info.account_id, GoogleServiceAuthError(
+                           GoogleServiceAuthError::State::USER_NOT_SIGNED_UP));
+  EXPECT_EQ(SignInAutofillBubbleVersion::kNoPromo,
+            GetSignInPromoVersion(identity_manager));
+
+  // Remove account.
+  identity_test_env.ClearPrimaryAccount();
+  EXPECT_EQ(SignInAutofillBubbleVersion::kNoAccount,
+            GetSignInPromoVersion(identity_manager));
+
+  // Signed in.
+  info = identity_test_env.MakePrimaryAccountAvailable("test@email.com",
+                                                       ConsentLevel::kSignin);
+  EXPECT_EQ(SignInAutofillBubbleVersion::kNoPromo,
+            GetSignInPromoVersion(identity_manager));
+
+  // Sign in pending state.
+  identity_test_env.UpdatePersistentErrorOfRefreshTokenForAccount(
+      info.account_id, GoogleServiceAuthError(
+                           GoogleServiceAuthError::State::USER_NOT_SIGNED_UP));
+  EXPECT_EQ(SignInAutofillBubbleVersion::kSignInPending,
+            GetSignInPromoVersion(identity_manager));
+}
+#endif  // !BUILDFLAG(ENABLE_DICE_SUPPORT)
+
 class ShowPromoTest : public testing::Test {
  public:
   ShowPromoTest() {
@@ -94,26 +149,6 @@
       identity_test_env_adaptor_;
 };
 
-// Tests for ShouldShowPromo.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
-TEST_F(ShowPromoTest, ShowPromoWithNoAccount) {
-  EXPECT_TRUE(ShouldShowPromo(*profile(), ConsentLevel::kSync));
-}
-
-TEST_F(ShowPromoTest, ShowPromoWithSignedInAccount) {
-  MakePrimaryAccountAvailable(identity_manager(), "test@email.com",
-                              ConsentLevel::kSignin);
-  EXPECT_TRUE(ShouldShowPromo(*profile(), ConsentLevel::kSync));
-}
-
-TEST_F(ShowPromoTest, DoNotShowPromoWithSyncingAccount) {
-  MakePrimaryAccountAvailable(identity_manager(), "test@email.com",
-                              ConsentLevel::kSync);
-  EXPECT_FALSE(ShouldShowPromo(*profile(), ConsentLevel::kSync));
-}
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
-
-// Tests for ShouldShowSignInPromo.
 TEST_F(ShowPromoTest, DoNotShowSignInPromoWithoutExplicitBrowserSignin) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(
@@ -123,8 +158,57 @@
                                      SignInAutofillBubblePromoType::Passwords));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+class ShowSyncPromoTest : public ShowPromoTest {
+ protected:
+  void DisableSync() {
+    base::CommandLine::ForCurrentProcess()->AppendSwitch(syncer::kDisableSync);
+  }
+};
+
+// Verifies that ShouldShowSyncPromo returns false if sync is disabled by
+// policy.
+TEST_F(ShowSyncPromoTest, ShouldShowSyncPromoSyncDisabled) {
+  DisableSync();
+  EXPECT_FALSE(ShouldShowSyncPromo(*profile()));
+}
+
+// Verifies that ShouldShowSyncPromo returns true if all conditions to
+// show the promo are met.
+TEST_F(ShowSyncPromoTest, ShouldShowSyncPromoSyncEnabled) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // No sync promo on Ash.
+  EXPECT_FALSE(ShouldShowSyncPromo(*profile()));
+#else
+  EXPECT_TRUE(ShouldShowSyncPromo(*profile()));
+#endif
+}
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(ShowSyncPromoTest, ShowPromoWithSignedInAccount) {
+  MakePrimaryAccountAvailable(identity_manager(), "test@email.com",
+                              ConsentLevel::kSignin);
+  EXPECT_TRUE(ShouldShowSyncPromo(*profile()));
+}
+
+TEST_F(ShowSyncPromoTest, DoNotShowPromoWithSyncingAccount) {
+  MakePrimaryAccountAvailable(identity_manager(), "test@email.com",
+                              ConsentLevel::kSync);
+  EXPECT_FALSE(ShouldShowSyncPromo(*profile()));
+}
+#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 class ShowSigninPromoTestExplicitBrowserSignin : public ShowPromoTest {
+ public:
+  std::string gaia_id() {
+    return identity_manager()
+        ->GetPrimaryAccountInfo(ConsentLevel::kSignin)
+        .gaia;
+  }
+
+ private:
   base::test::ScopedFeatureList feature_list{
       switches::kExplicitBrowserSigninUIOnDesktop};
 };
@@ -170,13 +254,53 @@
 }
 
 TEST_F(ShowSigninPromoTestExplicitBrowserSignin,
+       DoNotShowPromoWithOffTheRecordProfile) {
+  EXPECT_FALSE(ShouldShowSignInPromo(
+      *profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true),
+      SignInAutofillBubblePromoType::Payments));
+}
+
+TEST_F(ShowSigninPromoTestExplicitBrowserSignin,
        DoNotShowPromoAfterFiveTimesShown) {
-  // TODO (crbug.com/319411728): Implement a counter and test it.
+  EXPECT_TRUE(ShouldShowSignInPromo(*profile(),
+                                    SignInAutofillBubblePromoType::Passwords));
+
+  profile()->GetPrefs()->SetInteger(
+      prefs::kPasswordSignInPromoShownCountPerProfile, 5);
+
+  EXPECT_FALSE(ShouldShowSignInPromo(*profile(),
+                                     SignInAutofillBubblePromoType::Passwords));
+  EXPECT_TRUE(ShouldShowSignInPromo(*profile(),
+                                    SignInAutofillBubblePromoType::Addresses));
 }
 
 TEST_F(ShowSigninPromoTestExplicitBrowserSignin,
        DoNotShowPromoAfterTwoTimesDismissed) {
-  // TODO (crbug.com/319411728): Implement a counter and test it.
+  EXPECT_TRUE(ShouldShowSignInPromo(*profile(),
+                                    SignInAutofillBubblePromoType::Passwords));
+  EXPECT_TRUE(ShouldShowSignInPromo(*profile(),
+                                    SignInAutofillBubblePromoType::Addresses));
+
+  profile()->GetPrefs()->SetInteger(
+      prefs::kAutofillSignInPromoDismissCountPerProfile, 2);
+
+  EXPECT_FALSE(ShouldShowSignInPromo(*profile(),
+                                     SignInAutofillBubblePromoType::Passwords));
+  EXPECT_FALSE(ShouldShowSignInPromo(*profile(),
+                                     SignInAutofillBubblePromoType::Addresses));
+}
+
+TEST_F(ShowSigninPromoTestExplicitBrowserSignin,
+       ShowPromoAfterTwoTimesDismissedByDifferentAccounts) {
+  profile()->GetPrefs()->SetInteger(
+      prefs::kAutofillSignInPromoDismissCountPerProfile, 1);
+  SigninPrefs prefs(*profile()->GetPrefs());
+  prefs.IncrementAutofillSigninPromoDismissCount("gaia_id");
+
+  EXPECT_TRUE(ShouldShowSignInPromo(*profile(),
+                                    SignInAutofillBubblePromoType::Passwords));
+  EXPECT_TRUE(ShouldShowSignInPromo(*profile(),
+                                    SignInAutofillBubblePromoType::Addresses));
 }
 #endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
diff --git a/chrome/browser/signin/signin_promo_util.cc b/chrome/browser/signin/signin_promo_util.cc
index 7b0acc0..46a2c465 100644
--- a/chrome/browser/signin/signin_promo_util.cc
+++ b/chrome/browser/signin/signin_promo_util.cc
@@ -7,17 +7,38 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/reauth_result.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/base/signin_pref_names.h"
+#include "components/signin/public/base/signin_prefs.h"
 #include "components/signin/public/base/signin_switches.h"
-#include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
 #include "net/base/network_change_notifier.h"
 
-namespace signin {
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/sync/sync_service_factory.h"
+#include "components/sync/service/sync_prefs.h"
+#endif  // !BUILDFLAG(IS_ANDROID)
 
-bool ShouldShowPromo(Profile& profile, ConsentLevel promo_type) {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+#include "chrome/browser/signin/chrome_signin_pref_names.h"
+#include "chrome/browser/signin/signin_ui_util.h"
+
+namespace {
+constexpr int kSigninPromoShownThreshold = 5;
+constexpr int kSigninPromoDismissedThreshold = 2;
+}  // namespace
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
+#if !BUILDFLAG(IS_ANDROID)
+namespace {
+
+// Performs base checks for whether the sync/sign in promos should be shown.
+// Needs additional checks depending on the type of the promo (see
+// ShouldShowSyncPromo and ShouldShowSignInPromo). |profile| is the profile of
+// the tab the promo would be shown on.
+bool ShouldShowPromoCommon(Profile& profile) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // There's no need to show the sign in promo on cros since cros users are
   // already logged in.
@@ -41,30 +62,136 @@
   if (!original_profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed))
     return false;
 
-  IdentityManager* identity_manager =
+  signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(original_profile);
 
   // No promo if the user is already syncing.
-  if (identity_manager->HasPrimaryAccount(ConsentLevel::kSync)) {
+  if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
     return false;
   }
 
-  // Sync Promos are always shown when the user is not syncing.
-  if (promo_type == ConsentLevel::kSync) {
-    return true;
-  }
-
-  // Signin promo is shown if the user is not signed in or needs to reauth.
-  return !identity_manager->HasPrimaryAccount(ConsentLevel::kSignin) ||
-         identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
-             identity_manager->GetPrimaryAccountId(ConsentLevel::kSignin));
+  // Verified the base checks. Depending on whether the promo should be for sync
+  // or signin, additional checks are necessary.
+  return true;
 #endif
 }
 
-bool ShouldShowSignInPromo(Profile& profile,
-                           SignInAutofillBubblePromoType signin_promo_type) {
-  return ShouldShowPromo(profile, ConsentLevel::kSignin) &&
-         switches::IsExplicitBrowserSigninUIOnDesktopEnabled();
+}  // namespace
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+namespace signin {
+
+#if !BUILDFLAG(IS_ANDROID)
+bool ShouldShowSyncPromo(Profile& profile) {
+  // Don't show the promo if it does not pass the base checks.
+  if (!ShouldShowPromoCommon(profile)) {
+    return false;
+  }
+
+  syncer::SyncPrefs prefs(profile.GetPrefs());
+  // Don't show if sync is not allowed to start or is running in local mode.
+  if (!SyncServiceFactory::IsSyncAllowed(&profile) ||
+      prefs.IsLocalSyncEnabled()) {
+    return false;
+  }
+
+  return true;
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
+
+bool ShouldShowSignInPromo(Profile& profile,
+                           SignInAutofillBubblePromoType promo_type) {
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  // Don't show the promo if it does not pass the base checks.
+  if (!ShouldShowPromoCommon(profile)) {
+    return false;
+  }
+
+  // Don't show the promo if the user is off-the-record.
+  if (profile.IsOffTheRecord()) {
+    return false;
+  }
+
+  SignInAutofillBubbleVersion sign_in_status =
+      GetSignInPromoVersion(IdentityManagerFactory::GetForProfile(&profile));
+
+  // Don't show the promo if the user is already signed in.
+  if (sign_in_status == SignInAutofillBubbleVersion::kNoPromo) {
+    return false;
+  }
+
+  // Always show the promo in sign in pending state.
+  if (sign_in_status == SignInAutofillBubbleVersion::kSignInPending &&
+      switches::IsExplicitBrowserSigninUIOnDesktopEnabled()) {
+    return true;
+  }
+
+  IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(&profile);
+  AccountInfo account =
+      signin_ui_util::GetSingleAccountForPromos(identity_manager);
+
+  // Don't show the promo again after it was dismissed twice, regardless of
+  // autofill bubble promo type.
+  int dismiss_count =
+      account.gaia.empty()
+          ? profile.GetPrefs()->GetInteger(
+                prefs::kAutofillSignInPromoDismissCountPerProfile)
+          : SigninPrefs(*profile.GetPrefs())
+                .GetAutofillSigninPromoDismissCount(account.gaia);
+
+  if (dismiss_count >= kSigninPromoDismissedThreshold) {
+    return false;
+  }
+
+  // Don't show the promo again if it has already been shown 5 times.
+  int show_count = 0;
+  switch (promo_type) {
+    case SignInAutofillBubblePromoType::Addresses:
+    case SignInAutofillBubblePromoType::Payments:
+      break;
+    case SignInAutofillBubblePromoType::Passwords:
+      show_count =
+          account.gaia.empty()
+              ? profile.GetPrefs()->GetInteger(
+                    prefs::kPasswordSignInPromoShownCountPerProfile)
+              : SigninPrefs(*profile.GetPrefs())
+                    .GetPasswordSigninPromoImpressionCount(account.gaia);
+  }
+  if (show_count >= kSigninPromoShownThreshold) {
+    return false;
+  }
+
+  // Only show the promo if explicit browser signin is enabled.
+  return switches::IsExplicitBrowserSigninUIOnDesktopEnabled();
+#else
+  return false;
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+}
+
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+SignInAutofillBubbleVersion GetSignInPromoVersion(
+    IdentityManager* identity_manager) {
+  if (identity_manager->HasPrimaryAccount(ConsentLevel::kSync)) {
+    return SignInAutofillBubbleVersion::kNoPromo;
+  }
+
+  if (identity_manager->HasPrimaryAccount(ConsentLevel::kSignin) &&
+      identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
+          identity_manager->GetPrimaryAccountId(ConsentLevel::kSignin))) {
+    return SignInAutofillBubbleVersion::kSignInPending;
+  }
+
+  if (identity_manager->HasPrimaryAccount(ConsentLevel::kSignin)) {
+    return SignInAutofillBubbleVersion::kNoPromo;
+  }
+
+  if (!signin_ui_util::GetSingleAccountForPromos(identity_manager).IsEmpty()) {
+    return SignInAutofillBubbleVersion::kWebSignedIn;
+  }
+
+  return SignInAutofillBubbleVersion::kNoAccount;
+}
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 
 }  // namespace signin
diff --git a/chrome/browser/signin/signin_promo_util.h b/chrome/browser/signin/signin_promo_util.h
index aef8dba4..80e50b4 100644
--- a/chrome/browser/signin/signin_promo_util.h
+++ b/chrome/browser/signin/signin_promo_util.h
@@ -5,7 +5,8 @@
 #ifndef CHROME_BROWSER_SIGNIN_SIGNIN_PROMO_UTIL_H_
 #define CHROME_BROWSER_SIGNIN_SIGNIN_PROMO_UTIL_H_
 
-#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/base/signin_buildflags.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 
 class Profile;
 
@@ -14,16 +15,29 @@
 // Enumeration of sign in promo types for the autofill bubble.
 enum class SignInAutofillBubblePromoType { Passwords, Addresses, Payments };
 
-// Returns true if the sync/sign in promo should be visible.
-// |profile| is the profile of the tab the promo would be shown on.
-// |promo_type| specifies whether the promo would be for sync or sign in.
-bool ShouldShowPromo(Profile& profile, ConsentLevel promo_type);
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+// Enumeration of possible versions of the autofill sign in promo bubble.
+enum class SignInAutofillBubbleVersion {
+  kNoPromo,
+  kNoAccount,
+  kWebSignedIn,
+  kSignInPending
+};
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
+// Whether we should show the sync promo.
+bool ShouldShowSyncPromo(Profile& profile);
 
 // Whether we should show the sign in promo after data of the type
 // |signin_promo_type| was saved.
 bool ShouldShowSignInPromo(Profile& profile,
                            SignInAutofillBubblePromoType signin_promo_type);
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+// Returns the version of the autofill bubble that should be displayed.
+SignInAutofillBubbleVersion GetSignInPromoVersion(
+    IdentityManager* identity_manager);
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
 }  // namespace signin
 
 #endif  // CHROME_BROWSER_SIGNIN_SIGNIN_PROMO_UTIL_H_
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 5db9491..9351a7ee 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1422,6 +1422,8 @@
       "sad_tab_types.h",
       "safety_hub/abusive_notification_permissions_manager.cc",
       "safety_hub/abusive_notification_permissions_manager.h",
+      "safety_hub/card_data_helper.cc",
+      "safety_hub/card_data_helper.h",
       "safety_hub/extensions_result.cc",
       "safety_hub/extensions_result.h",
       "safety_hub/menu_notification.cc",
@@ -1508,8 +1510,6 @@
       "sync/browser_synced_window_delegates_getter.h",
       "sync/profile_signin_confirmation_helper.cc",
       "sync/profile_signin_confirmation_helper.h",
-      "sync/sync_promo_ui.cc",
-      "sync/sync_promo_ui.h",
       "tab_contents/tab_contents_iterator.cc",
       "tab_contents/tab_contents_iterator.h",
       "tab_modal_confirm_dialog_delegate.cc",
diff --git a/chrome/browser/ui/android/signin/BUILD.gn b/chrome/browser/ui/android/signin/BUILD.gn
index 62d1575..4d31bcb9 100644
--- a/chrome/browser/ui/android/signin/BUILD.gn
+++ b/chrome/browser/ui/android/signin/BUILD.gn
@@ -115,6 +115,7 @@
     "java/res/drawable-xxhdpi/ic_account_child_20dp.png",
     "java/res/drawable-xxxhdpi/ic_account_child_20dp.png",
     "java/res/drawable/chrome_sync_logo.xml",
+    "java/res/drawable/existing_account_row_background.xml",
     "java/res/drawable/history_sync_illustration.xml",
     "java/res/drawable/ic_expand_more_in_circle_24dp.xml",
     "java/res/layout/account_picker_bottom_sheet_continue_button.xml",
diff --git a/chrome/browser/ui/android/signin/java/res/drawable/existing_account_row_background.xml b/chrome/browser/ui/android/signin/java/res/drawable/existing_account_row_background.xml
new file mode 100644
index 0000000..5974520
--- /dev/null
+++ b/chrome/browser/ui/android/signin/java/res/drawable/existing_account_row_background.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2024 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item>
+        <org.chromium.components.browser_ui.widget.SurfaceColorDrawable
+            xmlns:app="http://schemas.android.com/apk/res-auto"
+            android:shape="rectangle"
+            app:surfaceElevation="@dimen/default_elevation_1">
+            <corners android:radius="10dp"/>
+        </org.chromium.components.browser_ui.widget.SurfaceColorDrawable>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/chrome/browser/ui/android/signin/java/res/layout/account_picker_state_collapsed.xml b/chrome/browser/ui/android/signin/java/res/layout/account_picker_state_collapsed.xml
index c0b9de62..5a817836 100644
--- a/chrome/browser/ui/android/signin/java/res/layout/account_picker_state_collapsed.xml
+++ b/chrome/browser/ui/android/signin/java/res/layout/account_picker_state_collapsed.xml
@@ -24,7 +24,7 @@
         android:id="@+id/account_picker_selected_account"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?attr/selectableItemBackground"
+        android:foreground="?attr/selectableItemBackground"
         android:gravity="center_vertical"
         android:orientation="horizontal"
         android:layout_marginTop="8dp"
@@ -36,6 +36,7 @@
         <include layout="@layout/account_row" />
 
         <ImageView
+            android:id="@+id/account_picker_selected_account_expand_icon"
             android:layout_width="24dp"
             android:layout_height="24dp"
             android:layout_marginStart="16dp"
diff --git a/chrome/browser/ui/android/signin/java/res/layout/fullscreen_signin_bottom_group_view.xml b/chrome/browser/ui/android/signin/java/res/layout/fullscreen_signin_bottom_group_view.xml
index fabc96d..9f3ee07 100644
--- a/chrome/browser/ui/android/signin/java/res/layout/fullscreen_signin_bottom_group_view.xml
+++ b/chrome/browser/ui/android/signin/java/res/layout/fullscreen_signin_bottom_group_view.xml
@@ -12,7 +12,7 @@
         android:id="@+id/signin_fre_selected_account"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?attr/selectableItemBackground"
+        android:foreground="?attr/selectableItemBackground"
         android:gravity="center_vertical"
         android:orientation="horizontal"
         android:paddingStart="24dp"
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java
index 03a7f0b..312210a 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetRenderTest.java
@@ -31,8 +31,10 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Features;
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
 import org.chromium.chrome.browser.ui.signin.R;
@@ -170,6 +172,7 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
     public void testCollapsedSheetWithAccountViewForWebSigninEntryPoint(boolean nightModeEnabled)
             throws IOException {
@@ -184,6 +187,24 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void
+            testCollapsedSheetWithAccountViewForWebSigninEntryPoint_replaceSyncWithSigninPromosEnabled(
+                    boolean nightModeEnabled) throws IOException {
+        mAccountManagerTestRule.addAccount(TEST_EMAIL1, FULL_NAME1, GIVEN_NAME1, null);
+        buildAndShowCollapsedBottomSheet();
+        ViewUtils.waitForVisibleView(allOf(withText(TEST_EMAIL1), isDisplayed()));
+        ViewUtils.waitForVisibleView(allOf(withText(FULL_NAME1), isDisplayed()));
+        mRenderTestRule.render(
+                mCoordinator.getBottomSheetViewForTesting(),
+                "collapsed_sheet_with_account_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
     public void testCollapsedSheetWithAccountViewForSendTabToSelfEntryPoint(
             boolean nightModeEnabled) throws IOException {
@@ -200,6 +221,25 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void
+            testCollapsedSheetWithAccountViewForSendTabToSelfEntryPoint_replaceSyncWithSigninPromosEnabled(
+                    boolean nightModeEnabled) throws IOException {
+        mSigninAccessPoint = SigninAccessPoint.SEND_TAB_TO_SELF_PROMO;
+        mAccountManagerTestRule.addAccount(TEST_EMAIL1, FULL_NAME1, GIVEN_NAME1, null);
+        buildAndShowCollapsedBottomSheet();
+        ViewUtils.waitForVisibleView(allOf(withText(TEST_EMAIL1), isDisplayed()));
+        ViewUtils.waitForVisibleView(allOf(withText(FULL_NAME1), isDisplayed()));
+        mRenderTestRule.render(
+                mCoordinator.getBottomSheetViewForTesting(),
+                "collapsed_sheet_with_account_for_send_tab_to_self_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
+    @Features.DisableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
     @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
     public void testCollapsedSheetWithAccountViewForBookmarksEntryPoint(boolean nightModeEnabled)
             throws IOException {
@@ -216,6 +256,24 @@
     @Test
     @MediumTest
     @Feature("RenderTest")
+    @Features.EnableFeatures(ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)
+    @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
+    public void
+            testCollapsedSheetWithAccountViewForBookmarksEntryPoint_replaceSyncWithSigninPromosEnabled(
+                    boolean nightModeEnabled) throws IOException {
+        mSigninAccessPoint = SigninAccessPoint.BOOKMARK_MANAGER;
+        mAccountManagerTestRule.addAccount(TEST_EMAIL1, FULL_NAME1, GIVEN_NAME1, null);
+        buildAndShowCollapsedBottomSheet();
+        ViewUtils.waitForVisibleView(allOf(withText(TEST_EMAIL1), isDisplayed()));
+        ViewUtils.waitForVisibleView(allOf(withText(FULL_NAME1), isDisplayed()));
+        mRenderTestRule.render(
+                mCoordinator.getBottomSheetViewForTesting(),
+                "collapsed_sheet_with_account_for_bookmarks_replace_sync_with_signin_promos_enabled");
+    }
+
+    @Test
+    @MediumTest
+    @Feature("RenderTest")
     @ParameterAnnotations.UseMethodParameter(NightModeTestUtils.NightModeParams.class)
     public void testExpandedSheetViewForWebSigninEntryPoint(boolean nightModeEnabled)
             throws IOException {
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetView.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetView.java
index 30711ad..92e9286 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetView.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/account_picker/AccountPickerBottomSheetView.java
@@ -5,25 +5,35 @@
 package org.chromium.chrome.browser.ui.signin.account_picker;
 
 import android.app.Activity;
+import android.content.Context;
+import android.content.res.ColorStateList;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup.MarginLayoutParams;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.ViewFlipper;
 
 import androidx.annotation.IdRes;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.widget.ImageViewCompat;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.google.android.material.color.MaterialColors;
+
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.signin.services.DisplayableProfileData;
 import org.chromium.chrome.browser.ui.signin.R;
 import org.chromium.chrome.browser.ui.signin.SigninUtils;
 import org.chromium.chrome.browser.ui.signin.account_picker.AccountPickerBottomSheetProperties.ViewState;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
+import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.widget.ButtonCompat;
 import org.chromium.ui.widget.TextViewWithLeading;
 
@@ -168,7 +178,14 @@
         View view = mViewFlipper.getChildAt(ViewState.COLLAPSED_ACCOUNT_LIST);
         ExistingAccountRowViewBinder.bindAccountView(
                 accountProfileData, mSelectedAccountView, /* isCurrentlySelected= */ true);
-
+        // The layout is updated in this place instead of the view's constructor since native is not
+        // guaranteed to be initialized when the view is created (e.g. bottom sheet should be able
+        // to show with a loading state during native initialization in the new sign-in flow), so
+        // we may not be able to check the flag's value there.
+        if (ChromeFeatureList.isEnabled(
+                ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)) {
+            revampSelectedAccountView();
+        }
         ButtonCompat continueButton = view.findViewById(R.id.account_picker_continue_as_button);
         continueButton.setText(
                 SigninUtils.getContinueAsButtonText(view.getContext(), accountProfileData));
@@ -317,4 +334,31 @@
             throw new IllegalArgumentException("Match failed with ViewState:" + viewState);
         }
     }
+
+    // TODO(b/40944124): Move the layout configurations to the xml file after UNO is launched.
+    private void revampSelectedAccountView() {
+        Context context = mSelectedAccountView.getContext();
+        mSelectedAccountView.setBackground(
+                AppCompatResources.getDrawable(
+                        context, R.drawable.existing_account_row_background));
+        int padding = ViewUtils.dpToPx(context, 16);
+        mSelectedAccountView.setPadding(padding, padding, padding, padding);
+        int horizontalMargin = ViewUtils.dpToPx(context, 24);
+        int bottomMargin = ViewUtils.dpToPx(context, 16);
+        MarginLayoutParams params = (MarginLayoutParams) mSelectedAccountView.getLayoutParams();
+        params.setMargins(
+                /* left= */ horizontalMargin,
+                /* top= */ 0,
+                /* right= */ horizontalMargin,
+                /* bottom= */ bottomMargin);
+
+        ImageView expandIcon =
+                mSelectedAccountView.findViewById(R.id.account_picker_selected_account_expand_icon);
+        expandIcon.setImageResource(R.drawable.ic_expand_more_black_24dp);
+        ColorStateList colorStateList =
+                ColorStateList.valueOf(
+                        MaterialColors.getColor(
+                                mSelectedAccountView, R.attr.colorOnSurfaceVariant));
+        ImageViewCompat.setImageTintList(expandIcon, colorStateList);
+    }
 }
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninViewBinder.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninViewBinder.java
index 99b9d36..285d781 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninViewBinder.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fullscreen_signin/FullscreenSigninViewBinder.java
@@ -4,18 +4,28 @@
 
 package org.chromium.chrome.browser.ui.signin.fullscreen_signin;
 
+import android.content.Context;
+import android.content.res.ColorStateList;
 import android.text.method.LinkMovementMethod;
 import android.transition.AutoTransition;
 import android.transition.TransitionManager;
 import android.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.ImageView;
 import android.widget.ProgressBar;
 
 import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.widget.ImageViewCompat;
 
+import com.google.android.material.color.MaterialColors;
+
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.signin.services.DisplayableProfileData;
 import org.chromium.chrome.browser.ui.signin.R;
 import org.chromium.chrome.browser.ui.signin.SigninUtils;
 import org.chromium.chrome.browser.ui.signin.account_picker.ExistingAccountRowViewBinder;
+import org.chromium.ui.base.ViewUtils;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -24,8 +34,8 @@
     static void bind(PropertyModel model, FullscreenSigninView view, PropertyKey propertyKey) {
         if (propertyKey == FullscreenSigninProperties.ON_CONTINUE_AS_CLICKED) {
             view.getContinueButtonView()
-                    .setOnClickListener(model
-                            .get(FullscreenSigninProperties.ON_CONTINUE_AS_CLICKED));
+                    .setOnClickListener(
+                            model.get(FullscreenSigninProperties.ON_CONTINUE_AS_CLICKED));
         } else if (propertyKey == FullscreenSigninProperties.ON_DISMISS_CLICKED) {
             view.getDismissButtonView()
                     .setOnClickListener(model.get(FullscreenSigninProperties.ON_DISMISS_CLICKED));
@@ -57,6 +67,10 @@
                 // this case.
                 initialLoadProgressSpinner.animate().alpha(1.0f).setStartDelay(500);
             } else {
+                // Native needs to be ready before feature flag can be checked. Therefore the flag
+                // guarded layout update is done once the initial loading spinner disappears, since
+                // native is initialized at this point.
+                revampSelectedAccountViewIfNecessary(view.getSelectedAccountView());
                 TransitionManager.beginDelayedTransition(view);
                 initialLoadProgressSpinner.setVisibility(View.GONE);
             }
@@ -201,5 +215,32 @@
         view.getSigninProgressText().setVisibility(showSigningInText ? View.VISIBLE : View.GONE);
     }
 
+    // TODO(b/40944124): Move the layout configurations to the xml file after UNO is launched.
+    public static void revampSelectedAccountViewIfNecessary(View accountPickerView) {
+        if (ChromeFeatureList.isEnabled(
+                ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS)) {
+            Context context = accountPickerView.getContext();
+            accountPickerView.setBackground(
+                    AppCompatResources.getDrawable(
+                            context, R.drawable.existing_account_row_background));
+
+            int padding = ViewUtils.dpToPx(context, 16);
+            accountPickerView.setPadding(padding, padding, padding, padding);
+            int margin = ViewUtils.dpToPx(context, 24);
+            MarginLayoutParams params = (MarginLayoutParams) accountPickerView.getLayoutParams();
+            params.setMargins(
+                    /* left= */ margin, /* top= */ 0, /* right= */ margin, /* bottom= */ margin);
+
+            ImageView moreArrow =
+                    accountPickerView.findViewById(R.id.signin_fre_selected_account_expand_icon);
+            moreArrow.setImageResource(R.drawable.ic_expand_more_black_24dp);
+            ColorStateList colorStateList =
+                    ColorStateList.valueOf(
+                            MaterialColors.getColor(
+                                    accountPickerView, R.attr.colorOnSurfaceVariant));
+            ImageViewCompat.setImageTintList(moreArrow, colorStateList);
+        }
+    }
+
     private FullscreenSigninViewBinder() {}
 }
diff --git a/chrome/browser/ui/android/signin/junit/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinatorTest.java b/chrome/browser/ui/android/signin/junit/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinatorTest.java
index 0c027ac..2e06582 100644
--- a/chrome/browser/ui/android/signin/junit/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinatorTest.java
+++ b/chrome/browser/ui/android/signin/junit/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinatorTest.java
@@ -31,8 +31,10 @@
 import org.chromium.base.Promise;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.signin.services.SigninMetricsUtils;
@@ -53,6 +55,7 @@
 /** Unit tests for {@link SigninAccountPickerCoordinator}. */
 @Batch(Batch.UNIT_TESTS)
 @RunWith(BaseRobolectricTestRunner.class)
+@EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
 public class SigninAccountPickerCoordinatorTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule public JniMocker mJniMocker = new JniMocker();
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 131e924..fc824d5 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -4549,12 +4549,6 @@
       <message name="IDS_UNDO_BAR_CLOSE_ALL_MESSAGE" desc="Message shown when you can undo a close all tabs action.">
         <ph name="TAB_COUNT">%1$s<ex>3</ex></ph> tabs closed
       </message>
-      <message name="IDS_UNDO_BAR_DELETE_TAB_GROUP_MESSAGE" desc="Message shown when you can undo a tab group deletion action.">
-        <ph name="TAB_COUNT">%1$s<ex>3</ex></ph> tabs deleted
-      </message>
-      <message name="IDS_UNDO_BAR_DELETE_SINGLE_TAB_GROUP_MESSAGE" desc="Message shown when you can undo a tab group containing a single tab.">
-        <ph name="TAB_COUNT">%1$s<ex>1</ex></ph> tab deleted
-      </message>
       <message name="IDS_CONFIRM_DELETE_MESSAGE" desc="Message shown or announced to get confirmation on whether a file should be deleted.">
         Do you want to delete <ph name="ITEM_TITLE">%1$s<ex>example.pdf</ex></ph>?
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNDO_BAR_DELETE_SINGLE_TAB_GROUP_MESSAGE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNDO_BAR_DELETE_SINGLE_TAB_GROUP_MESSAGE.png.sha1
deleted file mode 100644
index 25ad69d..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNDO_BAR_DELETE_SINGLE_TAB_GROUP_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f1b6b976a3bf9b43d3f14c0fe8eff7f6ebc50935
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNDO_BAR_DELETE_TAB_GROUP_MESSAGE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNDO_BAR_DELETE_TAB_GROUP_MESSAGE.png.sha1
deleted file mode 100644
index 70450a6..0000000
--- a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_UNDO_BAR_DELETE_TAB_GROUP_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-23b6fcda204e337ab7603c7969cd6865bbc34aec
\ No newline at end of file
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
index 30f53bc..7f611f702 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ui/hats/trust_safety_sentiment_service_factory.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/page_action/page_action_icon_type.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/autofill/core/browser/autofill_client.h"
diff --git a/chrome/browser/ui/extensions/extension_installed_bubble_model.cc b/chrome/browser/ui/extensions/extension_installed_bubble_model.cc
index 00e38986..390e12bc 100644
--- a/chrome/browser/ui/extensions/extension_installed_bubble_model.cc
+++ b/chrome/browser/ui/extensions/extension_installed_bubble_model.cc
@@ -8,8 +8,8 @@
 #include "chrome/browser/extensions/api/commands/command_service.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_promo_util.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -98,7 +98,7 @@
   show_key_binding_ = command.has_value();
 
   show_sign_in_promo_ = extensions::util::ShouldSync(extension, profile) &&
-                        SyncPromoUI::ShouldShowSyncPromo(profile);
+                        signin::ShouldShowSyncPromo(*profile);
 
   if (show_how_to_use_)
     how_to_use_text_ = MakeHowToUseText(action_info, command, keyword);
diff --git a/chrome/browser/ui/hats/trust_safety_sentiment_service.cc b/chrome/browser/ui/hats/trust_safety_sentiment_service.cc
index af70ba0..5790ae01 100644
--- a/chrome/browser/ui/hats/trust_safety_sentiment_service.cc
+++ b/chrome/browser/ui/hats/trust_safety_sentiment_service.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/hats/trust_safety_sentiment_service.h"
 
 #include <map>
+#include <vector>
 
 #include "base/metrics/histogram_functions.h"
 #include "base/rand_util.h"
@@ -13,6 +14,9 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/ui/hats/hats_service.h"
 #include "chrome/browser/ui/hats/hats_service_factory.h"
+#include "chrome/browser/ui/safety_hub/card_data_helper.h"
+#include "chrome/browser/ui/safety_hub/menu_notification_service_factory.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
 #include "chrome/browser/ui/webui/settings/site_settings_helper.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_features.h"
@@ -674,6 +678,46 @@
   TriggerOccurred(FeatureArea::kPasswordProtectionUI, product_specific_data);
 }
 
+std::map<std::string, bool>
+TrustSafetySentimentService::GetSafetyHubProductSpecificData() {
+  std::map<std::string, bool> product_specific_data;
+  auto* notification_service =
+      SafetyHubMenuNotificationServiceFactory::GetForProfile(profile_);
+  std::optional<safety_hub::SafetyHubModuleType> last_module =
+      notification_service->GetLastShownNotificationModule();
+
+  const std::vector<std::pair<safety_hub::SafetyHubModuleType, std::string>>
+      modules = {
+          {safety_hub::SafetyHubModuleType::EXTENSIONS, "extensions"},
+          {safety_hub::SafetyHubModuleType::NOTIFICATION_PERMISSIONS,
+           "notification permissions"},
+          {safety_hub::SafetyHubModuleType::PASSWORDS, "passwords"},
+          {safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS,
+           "unused site permissions"},
+          {safety_hub::SafetyHubModuleType::SAFE_BROWSING, "safe browsing"},
+      };
+  for (const auto& module : modules) {
+    product_specific_data["Is notification module " + std::get<1>(module)] =
+        last_module.has_value() && last_module.value() == std::get<0>(module);
+  }
+
+  safety_hub::SafetyHubCardState card_state =
+      safety_hub::GetOverallState(profile_);
+  const std::vector<std::pair<safety_hub::SafetyHubCardState, std::string>>
+      states = {
+          {safety_hub::SafetyHubCardState::kSafe, "safe"},
+          {safety_hub::SafetyHubCardState::kInfo, "info"},
+          {safety_hub::SafetyHubCardState::kWeak, "weak"},
+          {safety_hub::SafetyHubCardState::kWarning, "warning"},
+      };
+  for (const auto& state : states) {
+    product_specific_data["Global state is " + std::get<1>(state)] =
+        card_state == std::get<0>(state);
+  }
+
+  return product_specific_data;
+}
+
 // static
 bool TrustSafetySentimentService::VersionCheck(FeatureArea feature_area) {
   bool isV2 =
diff --git a/chrome/browser/ui/hats/trust_safety_sentiment_service.h b/chrome/browser/ui/hats/trust_safety_sentiment_service.h
index 33a115e5..05c6dc86 100644
--- a/chrome/browser/ui/hats/trust_safety_sentiment_service.h
+++ b/chrome/browser/ui/hats/trust_safety_sentiment_service.h
@@ -283,6 +283,10 @@
   void MaybeTriggerPasswordProtectionSurvey(PasswordProtectionUIType ui_type,
                                             PasswordProtectionUIAction action);
 
+  // Returns the product specific data related to surveys triggered for Safety
+  // Hub.
+  std::map<std::string, bool> GetSafetyHubProductSpecificData();
+
   const raw_ptr<Profile> profile_;
   std::map<FeatureArea, PendingTrigger> pending_triggers_;
   std::unique_ptr<SettingsWatcher> settings_watcher_;
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
index 03775984..a72b145 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java
@@ -117,8 +117,6 @@
     public static final int UMA_BOOKMARK_MOVED = 62;
     public static final int UMA_CLEAR_BROWSING_DATA = 63;
     public static final int UMA_SIGN_OUT = 64;
-    public static final int UMA_TAB_GROUP_DELETE_UNDO = 65;
-    public static final int UMA_SINGLE_TAB_GROUP_DELETE_UNDO = 66;
 
     private @Nullable SnackbarController mController;
     private CharSequence mText;
diff --git a/chrome/browser/ui/safety_hub/card_data_helper.cc b/chrome/browser/ui/safety_hub/card_data_helper.cc
new file mode 100644
index 0000000..be4753b
--- /dev/null
+++ b/chrome/browser/ui/safety_hub/card_data_helper.cc
@@ -0,0 +1,210 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/safety_hub/card_data_helper.h"
+
+#include <algorithm>
+#include <list>
+#include <map>
+#include <optional>
+#include <vector>
+
+#include "base/values.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/cws_info_service.h"
+#include "chrome/browser/extensions/cws_info_service_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/safety_hub/extensions_result.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h"
+#include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_service.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service_factory.h"
+#include "chrome/browser/ui/webui/settings/safety_hub_handler.h"
+#include "chrome/browser/ui/webui/version/version_ui.h"
+#include "chrome/browser/upgrade_detector/build_state.h"
+#include "chrome/grit/branded_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+base::Value::Dict CardDataToValue(int header_id,
+                                  int subheader_id,
+                                  safety_hub::SafetyHubCardState card_state) {
+  base::Value::Dict card_info;
+
+  card_info.Set(safety_hub::kCardHeaderKey,
+                l10n_util::GetStringUTF16(header_id));
+  card_info.Set(safety_hub::kCardSubheaderKey,
+                l10n_util::GetStringUTF16(subheader_id));
+  card_info.Set(safety_hub::kCardStateKey, static_cast<int>(card_state));
+
+  return card_info;
+}
+
+// Returns the state of Safe Browsing setting.
+SafeBrowsingState GetSafeBrowsingState(PrefService* pref_service) {
+  // TODO(crbug.com/40267370): Use SafeBrowsingResult from Safety Hub instead.
+  if (safe_browsing::IsEnhancedProtectionEnabled(*pref_service)) {
+    return SafeBrowsingState::kEnabledEnhanced;
+  }
+  if (safe_browsing::IsSafeBrowsingEnabled(*pref_service)) {
+    return SafeBrowsingState::kEnabledStandard;
+  }
+  if (safe_browsing::IsSafeBrowsingPolicyManaged(*pref_service)) {
+    return SafeBrowsingState::kDisabledByAdmin;
+  }
+  if (safe_browsing::IsSafeBrowsingExtensionControlled(*pref_service)) {
+    return SafeBrowsingState::kDisabledByExtension;
+  }
+  return SafeBrowsingState::kDisabledByUser;
+}
+
+}  // namespace
+
+namespace safety_hub {
+
+base::Value::Dict GetVersionCardData() {
+  base::Value::Dict result;
+  switch (g_browser_process->GetBuildState()->update_type()) {
+    case BuildState::UpdateType::kNone:
+      result.Set(kCardHeaderKey,
+                 l10n_util::GetStringUTF16(
+                     IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_HEADER_UPDATED));
+      result.Set(kCardSubheaderKey,
+                 VersionUI::GetAnnotatedVersionStringForUi());
+      result.Set(kCardStateKey, static_cast<int>(SafetyHubCardState::kSafe));
+      break;
+    case BuildState::UpdateType::kNormalUpdate:
+    // kEnterpriseRollback and kChannelSwitchRollback are fairly rare state,
+    // they will be handled same as there is waiting updates.
+    case BuildState::UpdateType::kEnterpriseRollback:
+    case BuildState::UpdateType::kChannelSwitchRollback:
+      result = CardDataToValue(
+          IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_HEADER_RESTART,
+          IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_SUBHEADER_RESTART,
+          SafetyHubCardState::kWarning);
+  }
+  return result;
+}
+
+base::Value::Dict GetPasswordCardData(Profile* profile) {
+  PasswordStatusCheckService* service =
+      PasswordStatusCheckServiceFactory::GetForProfile(profile);
+  CHECK(service);
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile);
+  bool signed_in = identity_manager && identity_manager->HasPrimaryAccount(
+                                           signin::ConsentLevel::kSignin);
+
+  return service->GetPasswordCardData(signed_in);
+}
+
+base::Value::Dict GetSafeBrowsingCardData(Profile* profile) {
+  SafeBrowsingState result = GetSafeBrowsingState(profile->GetPrefs());
+
+  base::Value::Dict sb_card_info;
+
+  switch (result) {
+    case SafeBrowsingState::kEnabledEnhanced:
+      sb_card_info =
+          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_HEADER,
+                          IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_SUBHEADER,
+                          SafetyHubCardState::kSafe);
+      break;
+    case SafeBrowsingState::kEnabledStandard:
+      sb_card_info =
+          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_HEADER,
+                          IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_SUBHEADER,
+                          SafetyHubCardState::kSafe);
+      break;
+    case SafeBrowsingState::kDisabledByAdmin:
+      sb_card_info =
+          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER,
+                          IDS_SETTINGS_SAFETY_HUB_SB_OFF_MANAGED_SUBHEADER,
+                          SafetyHubCardState::kInfo);
+      break;
+    case SafeBrowsingState::kDisabledByExtension:
+      sb_card_info =
+          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER,
+                          IDS_SETTINGS_SAFETY_HUB_SB_OFF_EXTENSION_SUBHEADER,
+                          SafetyHubCardState::kInfo);
+      break;
+    default:
+      sb_card_info =
+          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER,
+                          IDS_SETTINGS_SAFETY_HUB_SB_OFF_USER_SUBHEADER,
+                          SafetyHubCardState::kWarning);
+  }
+  return sb_card_info;
+}
+
+SafetyHubCardState GetOverallState(Profile* profile) {
+  // If there are any modules that need to be reviewed, the overall state is
+  // "warning".
+  UnusedSitePermissionsService* usp_service =
+      UnusedSitePermissionsServiceFactory::GetForProfile(profile);
+  std::optional<std::unique_ptr<SafetyHubService::Result>> opt_usp_result =
+      usp_service->GetCachedResult();
+  if (opt_usp_result.has_value()) {
+    auto* result =
+        static_cast<UnusedSitePermissionsService::UnusedSitePermissionsResult*>(
+            opt_usp_result.value().get());
+    if (!result->GetRevokedOrigins().empty()) {
+      return SafetyHubCardState::kWarning;
+    }
+  }
+
+  NotificationPermissionsReviewService* npr_service =
+      NotificationPermissionsReviewServiceFactory::GetForProfile(profile);
+  std::optional<std::unique_ptr<SafetyHubService::Result>> opt_npr_result =
+      npr_service->GetCachedResult();
+  if (opt_npr_result.has_value()) {
+    auto* result = static_cast<
+        NotificationPermissionsReviewService::NotificationPermissionsResult*>(
+        opt_npr_result.value().get());
+    if (!result->GetNotificationPermissions().empty()) {
+      return SafetyHubCardState::kWarning;
+    }
+  }
+
+  extensions::CWSInfoService* extension_info_service =
+      extensions::CWSInfoServiceFactory::GetForProfile(profile);
+  std::optional<std::unique_ptr<SafetyHubService::Result>> opt_ext_result =
+      SafetyHubExtensionsResult::GetResult(extension_info_service, profile,
+                                           true);
+  if (opt_ext_result.has_value()) {
+    auto* result =
+        static_cast<SafetyHubExtensionsResult*>(opt_ext_result.value().get());
+    if (result->GetNumTriggeringExtensions() > 0) {
+      return SafetyHubCardState::kWarning;
+    }
+  }
+
+  // Get the version card data for all remaining modules.
+  std::vector<base::Value::Dict> cards;
+  cards.push_back(GetVersionCardData());
+  cards.push_back(GetPasswordCardData(profile));
+  cards.push_back(GetSafeBrowsingCardData(profile));
+
+  // Return the lowest value, which coincides with the "worst" global state.
+  SafetyHubCardState min_value = SafetyHubCardState::kMaxValue;
+
+  for (const auto& card : cards) {
+    SafetyHubCardState current =
+        static_cast<SafetyHubCardState>(card.FindInt(kCardStateKey).value());
+    if (current < min_value) {
+      min_value = current;
+    }
+  }
+
+  return min_value;
+}
+
+}  // namespace safety_hub
diff --git a/chrome/browser/ui/safety_hub/card_data_helper.h b/chrome/browser/ui/safety_hub/card_data_helper.h
new file mode 100644
index 0000000..e87c7bb
--- /dev/null
+++ b/chrome/browser/ui/safety_hub/card_data_helper.h
@@ -0,0 +1,32 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_SAFETY_HUB_CARD_DATA_HELPER_H_
+#define CHROME_BROWSER_UI_SAFETY_HUB_CARD_DATA_HELPER_H_
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/safety_hub/password_status_check_service.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
+
+namespace safety_hub {
+
+// Fetches data for the version card to return data to the UI.
+base::Value::Dict GetVersionCardData();
+
+// Fetches data for the password card to return data to the UI.
+base::Value::Dict GetPasswordCardData(Profile* profile);
+
+// Fetches data for the Safe Browsing card to return data to the UI.
+base::Value::Dict GetSafeBrowsingCardData(Profile* profile);
+
+// Gets the overall state for the different Safety Hub modules. This will be the
+// "worst" state that any module is in. For instance, a single "warning" and
+// two "safe" states will result in "warning". For modules with a card, the
+// value will reflect that of a card, for the other modules, the state will be
+// in a "warning" state if any item needs to be reviewed.
+SafetyHubCardState GetOverallState(Profile* profile);
+
+}  // namespace safety_hub
+
+#endif  // CHROME_BROWSER_UI_SAFETY_HUB_CARD_DATA_HELPER_H_
diff --git a/chrome/browser/ui/safety_hub/card_data_helper_unittest.cc b/chrome/browser/ui/safety_hub/card_data_helper_unittest.cc
new file mode 100644
index 0000000..d1badbe1
--- /dev/null
+++ b/chrome/browser/ui/safety_hub/card_data_helper_unittest.cc
@@ -0,0 +1,281 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/safety_hub/card_data_helper.h"
+
+#include <memory>
+#include <string>
+
+#include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
+#include "chrome/browser/engagement/site_engagement_service_factory.h"
+#include "chrome/browser/extensions/cws_info_service_factory.h"
+#include "chrome/browser/password_manager/password_manager_test_util.h"
+#include "chrome/browser/permissions/notifications_engagement_service_factory.h"
+#include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h"
+#include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_test_util.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service.h"
+#include "chrome/browser/ui/safety_hub/unused_site_permissions_service_factory.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/password_manager/core/browser/leak_detection/bulk_leak_check_service.h"
+#include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h"
+#include "components/password_manager/core/browser/password_store/test_password_store.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/permissions/constants.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/site_engagement/content/site_engagement_score.h"
+#include "components/site_engagement/content/site_engagement_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/site_engagement/site_engagement.mojom-shared.h"
+
+using password_manager::BulkLeakCheckService;
+using password_manager::TestPasswordStore;
+
+namespace {
+
+constexpr char kEmail[] = "test@example.com";
+
+}  // namespace
+
+class SafetyHubCardDataHelperTest : public testing::Test {
+ public:
+  SafetyHubCardDataHelperTest() = default;
+
+  void SetUp() override {
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(profile_manager_->SetUp());
+    // The profile that we create should use the proper testing factories that
+    // encompass the identity test environment.
+    TestingProfile::TestingFactories factories =
+        IdentityTestEnvironmentProfileAdaptor::
+            GetIdentityTestEnvironmentFactories();
+    profile_ = profile_manager_->CreateTestingProfile(kEmail, factories);
+
+    // Create an adaptor for the identity test environment, as we do not
+    // directly control how the idenity test environment will be used.
+    identity_test_env_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
+    identity_test_env_ = identity_test_env_adaptor_->identity_test_env();
+
+    feature_list_.InitWithFeatures({features::kSafetyHub}, {});
+    identity_test_env()->MakePrimaryAccountAvailable(
+        kEmail, signin::ConsentLevel::kSignin);
+
+    profile_store_ = CreateAndUseTestPasswordStore(profile());
+    account_store_ = CreateAndUseTestAccountPasswordStore(profile());
+    bulk_leak_check_service_ =
+        safety_hub_test_util::CreateAndUseBulkLeakCheckService(
+            identity_test_env_->identity_manager(), profile());
+
+    profile()->GetPrefs()->SetDouble(
+        password_manager::prefs::kLastTimePasswordCheckCompleted,
+        base::Time::Now().InSecondsFSinceUnixEpoch());
+    SetMockCredentialEntry("https://example1.com", false);
+    PasswordStatusCheckService* service =
+        PasswordStatusCheckServiceFactory::GetForProfile(profile());
+    service->UpdateInsecureCredentialCountAsync();
+    RunUntilIdle();
+
+    // Ensure that after the setup, the overall state is safe.
+    ASSERT_EQ(safety_hub::GetOverallState(profile()),
+              safety_hub::SafetyHubCardState::kSafe);
+  }
+
+  std::unique_ptr<KeyedService> SetMockCWSInfoService(
+      content::BrowserContext* context) {
+    return safety_hub_test_util::GetMockCWSInfoService(profile());
+  }
+
+ protected:
+  void CreateMockNotificationPermissionForReview() {
+    const GURL kUrl = GURL("https://www.example.com:443");
+    auto* site_engagement_service =
+        site_engagement::SiteEngagementServiceFactory::GetForProfile(profile());
+
+    // Set a host to have minimum engagement. This should be in review list.
+    hcsm()->SetContentSettingDefaultScope(kUrl, GURL(),
+                                          ContentSettingsType::NOTIFICATIONS,
+                                          CONTENT_SETTING_ALLOW);
+    auto* notifications_engagement_service =
+        NotificationsEngagementServiceFactory::GetForProfile(profile());
+    notifications_engagement_service->RecordNotificationDisplayed(kUrl, 7);
+
+    site_engagement::SiteEngagementScore score =
+        site_engagement_service->CreateEngagementScore(kUrl);
+    score.Reset(0.5, base::Time::Now());
+    score.Commit();
+    EXPECT_EQ(blink::mojom::EngagementLevel::MINIMAL,
+              site_engagement_service->GetEngagementLevel(kUrl));
+  }
+
+  void CreateMockUnusedSitePermissionForReview() {
+    const GURL kUrl = GURL("https://www.example.com:443");
+    base::Value::Dict dict;
+    dict.Set(permissions::kRevokedKey,
+             base::Value::List().Append(
+                 static_cast<int32_t>(ContentSettingsType::GEOLOCATION)));
+    content_settings::ContentSettingConstraints default_constraint(
+        base::Time::Now());
+    default_constraint.set_lifetime(base::Days(60));
+    hcsm()->SetWebsiteSettingCustomScope(
+        ContentSettingsPattern::FromURLNoWildcard(kUrl),
+        ContentSettingsPattern::Wildcard(),
+        ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
+        base::Value(std::move(dict)), default_constraint);
+  }
+
+  void SetMockCredentialEntry(const std::string origin,
+                              bool leaked,
+                              bool weak = false) {
+    // Create a password form and mark it as leaked.
+    const char16_t* password = weak ? u"123456" : u"Un1Qu3Cr3DeNt!aL";
+    password_manager::PasswordForm form =
+        safety_hub_test_util::MakeForm(u"username", password, origin, leaked);
+
+    profile_store().AddLogin(form);
+    account_store().AddLogin(form);
+    RunUntilIdle();
+  }
+
+  void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
+  TestingProfile* profile() { return profile_.get(); }
+  sync_preferences::TestingPrefServiceSyncable* prefs() {
+    return profile()->GetTestingPrefService();
+  }
+  HostContentSettingsMap* hcsm() {
+    return HostContentSettingsMapFactory::GetForProfile(profile());
+  }
+
+  TestPasswordStore& profile_store() { return *profile_store_; }
+  TestPasswordStore& account_store() { return *account_store_; }
+
+  signin::IdentityTestEnvironment* identity_test_env() {
+    return identity_test_env_;
+  }
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_adaptor_;
+
+  raw_ptr<signin::IdentityTestEnvironment> identity_test_env_;
+  std::unique_ptr<TestingProfileManager> profile_manager_;
+  raw_ptr<TestingProfile> profile_;
+  base::test::ScopedFeatureList feature_list_;
+  scoped_refptr<TestPasswordStore> profile_store_;
+  scoped_refptr<TestPasswordStore> account_store_;
+  raw_ptr<BulkLeakCheckService> bulk_leak_check_service_;
+};
+
+TEST_F(SafetyHubCardDataHelperTest, GetOverallState_SafeBrowsingInfo) {
+  // When Safe Browsing is disabled by admin, that card gets into the info
+  // state. The overall state should thus be in the info state.
+  prefs()->SetManagedPref(prefs::kSafeBrowsingEnabled, base::Value(false));
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kInfo);
+}
+
+TEST_F(SafetyHubCardDataHelperTest, GetOverallState_NotificationPermission) {
+  // When there are notification permissions to review, the overall state should
+  // be "warning".
+  auto* notification_service =
+      NotificationPermissionsReviewServiceFactory::GetForProfile(profile());
+  CreateMockNotificationPermissionForReview();
+  safety_hub_test_util::UpdateSafetyHubServiceAsync(notification_service);
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWarning);
+}
+
+TEST_F(SafetyHubCardDataHelperTest, GetOverallState_UnusedSitePermissions) {
+  // When there are unused permissions to review, the overall state should
+  // be "warning".
+  auto* usp_service =
+      UnusedSitePermissionsServiceFactory::GetForProfile(profile());
+  CreateMockUnusedSitePermissionForReview();
+  safety_hub_test_util::UpdateSafetyHubServiceAsync(usp_service);
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWarning);
+}
+
+TEST_F(SafetyHubCardDataHelperTest, GetOverallState_Extension) {
+  // Create mock notifications that will require a review.
+  extensions::CWSInfoServiceFactory::GetInstance()->SetTestingFactory(
+      profile(),
+      base::BindRepeating(&SafetyHubCardDataHelperTest::SetMockCWSInfoService,
+                          base::Unretained(this)));
+  safety_hub_test_util::CreateMockExtensions(profile());
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWarning);
+}
+
+TEST_F(SafetyHubCardDataHelperTest, GetOverallState_Password) {
+  // Setting a weak password should result in a "weak" state.
+  SetMockCredentialEntry("https://example1.com", /*leaked=*/false,
+                         /*weak=*/true);
+  PasswordStatusCheckService* psc_service =
+      PasswordStatusCheckServiceFactory::GetForProfile(profile());
+  psc_service->UpdateInsecureCredentialCountAsync();
+  RunUntilIdle();
+
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWeak);
+
+  // When a leaked password is added, we should get in a "warning" state
+  // instead.
+  SetMockCredentialEntry("https://example2.com", /*leaked=*/true);
+  psc_service->UpdateInsecureCredentialCountAsync();
+  RunUntilIdle();
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWarning);
+}
+
+TEST_F(SafetyHubCardDataHelperTest, GetOverallState_Multi) {
+  // Initially we are in a "safe" state.
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kSafe);
+
+  // Setting a weak password gets us in a "weak" state.
+  SetMockCredentialEntry("https://example1.com", /*leaked=*/false,
+                         /*weak=*/true);
+  PasswordStatusCheckService* psc_service =
+      PasswordStatusCheckServiceFactory::GetForProfile(profile());
+  psc_service->UpdateInsecureCredentialCountAsync();
+  RunUntilIdle();
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWeak);
+
+  // When adding notification permissions to review, we should get into a
+  // "warning" state.
+  auto* notification_service =
+      NotificationPermissionsReviewServiceFactory::GetForProfile(profile());
+  CreateMockNotificationPermissionForReview();
+  safety_hub_test_util::UpdateSafetyHubServiceAsync(notification_service);
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWarning);
+
+  // When a leaked password is added, we should remain in the "warning" state.
+  SetMockCredentialEntry("https://example2.com", /*leaked=*/true);
+  psc_service->UpdateInsecureCredentialCountAsync();
+  RunUntilIdle();
+  EXPECT_EQ(safety_hub::GetOverallState(profile()),
+            safety_hub::SafetyHubCardState::kWarning);
+}
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service.cc b/chrome/browser/ui/safety_hub/menu_notification_service.cc
index 87d27f40..51ce219d 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service.cc
@@ -173,6 +173,7 @@
     (*it)->Dismiss();
   }
   notification_to_show->Show();
+  last_shown_module_ = notification_to_show->GetModuleType();
 
   // The information related to showing the notification needs to be persisted
   // as well.
@@ -275,3 +276,8 @@
     notification->Dismiss();
   }
 }
+
+std::optional<safety_hub::SafetyHubModuleType>
+SafetyHubMenuNotificationService::GetLastShownNotificationModule() const {
+  return last_shown_module_;
+}
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service.h b/chrome/browser/ui/safety_hub/menu_notification_service.h
index d297bbdf..b4b0a2b 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service.h
+++ b/chrome/browser/ui/safety_hub/menu_notification_service.h
@@ -91,6 +91,10 @@
   void DismissActiveNotificationOfModule(
       safety_hub::SafetyHubModuleType module);
 
+  // Returns the module of the notification that was last displayed to the user.
+  std::optional<safety_hub::SafetyHubModuleType>
+  GetLastShownNotificationModule() const;
+
   // Returns the |service_info_map_|. For testing purposes only.
   SafetyHubMenuNotification* GetNotificationForTesting(
       safety_hub::SafetyHubModuleType service_type);
@@ -146,6 +150,9 @@
 
   // Registrar to record the pref changes to Safe Browsing.
   PrefChangeRegistrar registrar_;
+
+  // The module of the last notification that has been shown.
+  std::optional<safety_hub::SafetyHubModuleType> last_shown_module_;
 };
 
 #endif  // CHROME_BROWSER_UI_SAFETY_HUB_MENU_NOTIFICATION_SERVICE_H_
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
index fc7ca1138..31767cf 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
@@ -402,9 +402,18 @@
   prefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
   notification = menu_notification_service()->GetNotificationToShow();
   EXPECT_FALSE(notification.has_value());
+  EXPECT_FALSE(menu_notification_service()
+                   ->GetLastShownNotificationModule()
+                   .has_value());
   AdvanceClockBy(base::Days(1));
   notification = menu_notification_service()->GetNotificationToShow();
   EXPECT_TRUE(notification.has_value());
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::SAFE_BROWSING);
 
   // A leaked password warning should override the safe browsing notification.
   SetMockCredentialEntry(origin, true);
@@ -413,12 +422,27 @@
   ExpectPluralString(
       IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION, 1,
       notification.value().label);
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::PASSWORDS);
 
   // Fixing the leaked password will clear notification. Because the safe
   // browsing notification was dismissed, it will not be shown either.
   SetMockCredentialEntry(origin, false, true);
   notification = menu_notification_service()->GetNotificationToShow();
   EXPECT_FALSE(notification.has_value());
+
+  // The last shown menu notification remains the same even when it has been
+  // dismissed.
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::PASSWORDS);
 }
 
 TEST_F(SafetyHubMenuNotificationServiceTest, PasswordTrigger) {
@@ -447,11 +471,24 @@
   ExpectPluralString(
       IDS_SETTINGS_SAFETY_HUB_UNUSED_SITE_PERMISSIONS_MENU_NOTIFICATION, 1,
       notification.value().label);
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS);
 
-  // When all notifications are dismissed, there should be no more notification.
+  // When all notifications are dismissed, there should be no more notification
+  // but the last shown notification remains the same.
   menu_notification_service()->DismissActiveNotification();
   EXPECT_FALSE(
       menu_notification_service()->GetNotificationToShow().has_value());
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::UNUSED_SITE_PERMISSIONS);
 
   // Create mock password menu notification.
   const std::string& kOrigin = "https://www.example.com";
@@ -461,10 +498,22 @@
   ExpectPluralString(
       IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION, 1,
       notification.value().label);
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::PASSWORDS);
 
   // The notification should no longer appear after it has been dismissed.
   menu_notification_service()->DismissActiveNotificationOfModule(
       safety_hub::SafetyHubModuleType::PASSWORDS);
   notification = menu_notification_service()->GetNotificationToShow();
   EXPECT_FALSE(notification.has_value());
+  EXPECT_TRUE(menu_notification_service()
+                  ->GetLastShownNotificationModule()
+                  .has_value());
+  EXPECT_EQ(
+      menu_notification_service()->GetLastShownNotificationModule().value(),
+      safety_hub::SafetyHubModuleType::PASSWORDS);
 }
diff --git a/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc b/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc
index fc121f09..76f06b35 100644
--- a/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/password_status_check_service_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/safety_hub/password_status_check_result.h"
 #include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
 #include "chrome/browser/ui/safety_hub/safety_hub_prefs.h"
+#include "chrome/browser/ui/safety_hub/safety_hub_test_util.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -52,20 +53,7 @@
 using password_manager::PasswordForm;
 using password_manager::TestPasswordStore;
 using safety_hub::SafetyHubCardState;
-
-BulkLeakCheckService* CreateAndUseBulkLeakCheckService(
-    signin::IdentityManager* identity_manager,
-    Profile* profile) {
-  return static_cast<BulkLeakCheckService*>(
-      BulkLeakCheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
-          profile, base::BindLambdaForTesting([identity_manager](
-                                                  content::BrowserContext*) {
-            return std::unique_ptr<
-                KeyedService>(std::make_unique<BulkLeakCheckService>(
-                identity_manager,
-                base::MakeRefCounted<network::TestSharedURLLoaderFactory>()));
-          })));
-}
+using safety_hub_test_util::MakeForm;
 
 // Mock observer for BulkLeakCheckService for EXPECT_CALL.
 class MockObserver : public BulkLeakCheckService::Observer {
@@ -93,30 +81,8 @@
   raw_ptr<BulkLeakCheckService> leak_check_service_;
 };
 
-PasswordForm MakeForm(std::u16string_view username,
-                      std::u16string_view password,
-                      std::string origin = kOrigin1,
-                      bool is_leaked = false) {
-  PasswordForm form;
-  form.username_value = username;
-  form.password_value = password;
-  form.signon_realm = origin;
-  form.url = GURL(origin);
-
-  if (is_leaked) {
-    // Credential issues for weak and reused are detected automatically and
-    // don't need to be specified explicitly.
-    form.password_issues.insert_or_assign(
-        InsecureType::kLeaked,
-        password_manager::InsecurityMetadata(
-            base::Time::Now(), password_manager::IsMuted(false),
-            password_manager::TriggerBackendNotification(false)));
-  }
-  return form;
-}
-
 PasswordForm WeakForm() {
-  return MakeForm(kUsername1, kWeakPassword);
+  return MakeForm(kUsername1, kWeakPassword, kOrigin1);
 }
 
 PasswordForm LeakedForm() {
@@ -124,7 +90,7 @@
 }
 
 PasswordForm ReusedForm1() {
-  return MakeForm(kUsername3, kPassword2);
+  return MakeForm(kUsername3, kPassword2, kOrigin1);
 }
 
 PasswordForm ReusedForm2() {
@@ -197,8 +163,9 @@
       CreateAndUseTestAccountPasswordStore(&profile_);
 
   raw_ptr<BulkLeakCheckService> bulk_leak_check_service_ =
-      CreateAndUseBulkLeakCheckService(identity_test_env_.identity_manager(),
-                                       &profile_);
+      safety_hub_test_util::CreateAndUseBulkLeakCheckService(
+          identity_test_env_.identity_manager(),
+          &profile_);
 
   std::unique_ptr<PasswordStatusCheckService> service_;
 };
@@ -360,18 +327,18 @@
 
 TEST_P(PasswordStatusCheckServiceParameterizedStoreTest,
        DetectChangingWeakPassword) {
-  password_store().AddLogin(MakeForm(kUsername1, kWeakPassword));
+  password_store().AddLogin(MakeForm(kUsername1, kWeakPassword, kOrigin1));
   RunUntilIdle();
   EXPECT_EQ(service()->weak_credential_count(), 1UL);
 
   // When changing the password for this credential from the weak password to a
   // stronger password, it is no longer counted as weak.
-  password_store().UpdateLogin(MakeForm(kUsername1, kPassword));
+  password_store().UpdateLogin(MakeForm(kUsername1, kPassword, kOrigin1));
   RunUntilIdle();
   EXPECT_EQ(service()->weak_credential_count(), 0UL);
 
   // When the strong password changes to a weak one is is counted as such.
-  password_store().UpdateLogin(MakeForm(kUsername1, kWeakPassword));
+  password_store().UpdateLogin(MakeForm(kUsername1, kWeakPassword, kOrigin1));
   RunUntilIdle();
   EXPECT_EQ(service()->weak_credential_count(), 1UL);
 }
@@ -383,7 +350,7 @@
   EXPECT_EQ(service()->compromised_credential_count(), 1UL);
 
   // When a leaked password is changed it is no longer leaked.
-  password_store().UpdateLogin(MakeForm(kUsername2, kPassword2));
+  password_store().UpdateLogin(MakeForm(kUsername2, kPassword2, kOrigin1));
   RunUntilIdle();
   EXPECT_EQ(service()->compromised_credential_count(), 0UL);
 
@@ -397,13 +364,13 @@
 TEST_P(PasswordStatusCheckServiceParameterizedStoreTest,
        DetectChangingReusedPassword) {
   // Two credentials share the same password. The service counts them as reused.
-  password_store().AddLogin(MakeForm(kUsername3, kPassword));
+  password_store().AddLogin(MakeForm(kUsername3, kPassword, kOrigin1));
   password_store().AddLogin(MakeForm(kUsername4, kPassword, kOrigin2));
   RunUntilIdle();
   EXPECT_EQ(service()->reused_credential_count(), 2UL);
 
   // After changing one the reused passwords, there are now 0.
-  password_store().UpdateLogin(MakeForm(kUsername3, kPassword2));
+  password_store().UpdateLogin(MakeForm(kUsername3, kPassword2, kOrigin1));
   RunUntilIdle();
   EXPECT_EQ(service()->reused_credential_count(), 0UL);
 
@@ -433,7 +400,7 @@
 
 TEST_F(PasswordStatusCheckServiceBaseTest,
        PasswordCheckSignedOutWithPasswords) {
-  profile_store().AddLogin(MakeForm(kUsername1, kPassword));
+  profile_store().AddLogin(MakeForm(kUsername1, kPassword, kOrigin1));
 
   ::testing::StrictMock<MockObserver> observer(bulk_leak_check_service());
 
@@ -448,7 +415,7 @@
   identity_test_env().MakeAccountAvailable(kTestEmail);
 
   // Store credential that has no issue associated with it.
-  profile_store().AddLogin(MakeForm(kUsername1, kPassword));
+  profile_store().AddLogin(MakeForm(kUsername1, kPassword, kOrigin1));
   UpdateInsecureCredentials();
   EXPECT_EQ(service()->compromised_credential_count(), 0UL);
 
@@ -470,7 +437,7 @@
 
 TEST_F(PasswordStatusCheckServiceBaseTest, PasswordCheck_Error) {
   identity_test_env().MakeAccountAvailable(kTestEmail);
-  profile_store().AddLogin(MakeForm(kUsername1, kPassword));
+  profile_store().AddLogin(MakeForm(kUsername1, kPassword, kOrigin1));
 
   UpdateInsecureCredentials();
   EXPECT_EQ(service()->compromised_credential_count(), 0UL);
@@ -646,7 +613,7 @@
         base::Time::Now().InSecondsFSinceUnixEpoch());
   }
   if (include_safe_password()) {
-    profile_store().AddLogin(MakeForm(kUsername1, kPassword));
+    profile_store().AddLogin(MakeForm(kUsername1, kPassword, kOrigin1));
   }
   if (include_weak()) {
     profile_store().AddLogin(WeakForm());
@@ -795,7 +762,7 @@
 
 TEST_F(PasswordStatusCheckServiceBaseTest, PasswordCardCheckTime) {
   // Add a password without issues to reach safe state.
-  profile_store().AddLogin(MakeForm(kUsername1, kPassword));
+  profile_store().AddLogin(MakeForm(kUsername1, kPassword, kOrigin1));
   RunUntilIdle();
 
   SetLastCheckTime(base::TimeDelta(base::Seconds(0)));
diff --git a/chrome/browser/ui/safety_hub/safety_hub_constants.h b/chrome/browser/ui/safety_hub/safety_hub_constants.h
index 30e7079b..4080c69 100644
--- a/chrome/browser/ui/safety_hub/safety_hub_constants.h
+++ b/chrome/browser/ui/safety_hub/safety_hub_constants.h
@@ -38,7 +38,8 @@
 // Key of the revoked chooser permissions in the |UnusedSitePermissions| object.
 extern const char kSafetyHubChooserPermissionsData[];
 
-// State that a top card in the Safety Hub page can be in.
+// State that a top card in the Safety Hub page can be in. This enum should
+// remain sorted from the "worst" state (warning) to the "best" state (safe).
 // Should be kept in sync with the corresponding enum in
 // chrome/browser/resources/settings/safety_hub/safety_hub_browser_proxy.ts
 enum class SafetyHubCardState {
diff --git a/chrome/browser/ui/safety_hub/safety_hub_test_util.cc b/chrome/browser/ui/safety_hub/safety_hub_test_util.cc
index 3da17ad..1a41156 100644
--- a/chrome/browser/ui/safety_hub/safety_hub_test_util.cc
+++ b/chrome/browser/ui/safety_hub/safety_hub_test_util.cc
@@ -7,15 +7,18 @@
 #include <memory>
 
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/run_until.h"
 #include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/crx_file/id_util.h"
+#include "components/password_manager/core/browser/password_store/test_password_store.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/manifest_constants.h"
 #include "extensions/common/mojom/manifest.mojom-shared.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
 
 namespace {
 
@@ -83,6 +86,9 @@
 
 namespace safety_hub_test_util {
 
+using password_manager::BulkLeakCheckService;
+using password_manager::TestPasswordStore;
+
 MockCWSInfoService::MockCWSInfoService(Profile* profile)
     : extensions::CWSInfoService(profile) {}
 MockCWSInfoService::~MockCWSInfoService() = default;
@@ -219,4 +225,40 @@
       name, "ack_safety_check_warning", base::Value(true));
 }
 
+BulkLeakCheckService* CreateAndUseBulkLeakCheckService(
+    signin::IdentityManager* identity_manager,
+    Profile* profile) {
+  return static_cast<BulkLeakCheckService*>(
+      BulkLeakCheckServiceFactory::GetInstance()->SetTestingFactoryAndUse(
+          profile, base::BindLambdaForTesting([identity_manager](
+                                                  content::BrowserContext*) {
+            return std::unique_ptr<
+                KeyedService>(std::make_unique<BulkLeakCheckService>(
+                identity_manager,
+                base::MakeRefCounted<network::TestSharedURLLoaderFactory>()));
+          })));
+}
+
+password_manager::PasswordForm MakeForm(std::u16string_view username,
+                                        std::u16string_view password,
+                                        const std::string origin,
+                                        bool is_leaked) {
+  password_manager::PasswordForm form;
+  form.username_value = username;
+  form.password_value = password;
+  form.signon_realm = origin;
+  form.url = GURL(origin);
+
+  if (is_leaked) {
+    // Credential issues for weak and reused are detected automatically and
+    // don't need to be specified explicitly.
+    form.password_issues.insert_or_assign(
+        password_manager::InsecureType::kLeaked,
+        password_manager::InsecurityMetadata(
+            base::Time::Now(), password_manager::IsMuted(false),
+            password_manager::TriggerBackendNotification(false)));
+  }
+  return form;
+}
+
 }  // namespace safety_hub_test_util
diff --git a/chrome/browser/ui/safety_hub/safety_hub_test_util.h b/chrome/browser/ui/safety_hub/safety_hub_test_util.h
index ffee56b..85ba918b 100644
--- a/chrome/browser/ui/safety_hub/safety_hub_test_util.h
+++ b/chrome/browser/ui/safety_hub/safety_hub_test_util.h
@@ -74,6 +74,19 @@
 void AcknowledgeSafetyCheckExtensions(const std::string& name,
                                       Profile* profile);
 
+// Creates the service used for bulk leak checks.
+password_manager::BulkLeakCheckService* CreateAndUseBulkLeakCheckService(
+    signin::IdentityManager* identity_manager,
+    Profile* profile);
+
+// Creates a form for the password manager with a given username, password and
+// origin. If |is_leaked| is set to |true|, the password will be considered a
+// leaked password.
+password_manager::PasswordForm MakeForm(base::StringPiece16 username,
+                                        base::StringPiece16 password,
+                                        std::string origin,
+                                        bool is_leaked = false);
+
 }  // namespace safety_hub_test_util
 
 #endif  // CHROME_BROWSER_UI_SAFETY_HUB_SAFETY_HUB_TEST_UTIL_H_
diff --git a/chrome/browser/ui/sync/sync_promo_ui.cc b/chrome/browser/ui/sync/sync_promo_ui.cc
deleted file mode 100644
index df5da2ce..0000000
--- a/chrome/browser/ui/sync/sync_promo_ui.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/signin_promo_util.h"
-#include "chrome/browser/sync/sync_service_factory.h"
-#include "components/signin/public/base/consent_level.h"
-#include "components/sync/service/sync_prefs.h"
-
-bool SyncPromoUI::ShouldShowSyncPromo(Profile* profile) {
-  // Don't show sync promo if the sign in promo should not be shown.
-  if (!signin::ShouldShowPromo(*profile, signin::ConsentLevel::kSync)) {
-    return false;
-  }
-
-  syncer::SyncPrefs prefs(profile->GetPrefs());
-  // Don't show if sync is not allowed to start or is running in local mode.
-  if (!SyncServiceFactory::IsSyncAllowed(profile) ||
-      prefs.IsLocalSyncEnabled()) {
-    return false;
-  }
-
-  return true;
-}
diff --git a/chrome/browser/ui/sync/sync_promo_ui.h b/chrome/browser/ui/sync/sync_promo_ui.h
deleted file mode 100644
index 138f6fc2..0000000
--- a/chrome/browser/ui/sync/sync_promo_ui.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_SYNC_SYNC_PROMO_UI_H_
-#define CHROME_BROWSER_UI_SYNC_SYNC_PROMO_UI_H_
-
-class Profile;
-
-// Static helper function useful for sync promos.
-class SyncPromoUI {
- public:
-  // Returns true if the sync promo should be visible.
-  // |profile| is the profile for which the promo would be displayed.
-  static bool ShouldShowSyncPromo(Profile* profile);
-};
-
-#endif  // CHROME_BROWSER_UI_SYNC_SYNC_PROMO_UI_H_
diff --git a/chrome/browser/ui/sync/sync_promo_ui_unittest.cc b/chrome/browser/ui/sync/sync_promo_ui_unittest.cc
deleted file mode 100644
index 2bf9b5d..0000000
--- a/chrome/browser/ui/sync/sync_promo_ui_unittest.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
-
-#include <memory>
-
-#include "base/command_line.h"
-#include "base/compiler_specific.h"
-#include "base/functional/bind.h"
-#include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/sync/base/command_line_switches.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-class SyncPromoUITest : public testing::Test {
- public:
-  SyncPromoUITest() = default;
-
-  SyncPromoUITest(const SyncPromoUITest&) = delete;
-  SyncPromoUITest& operator=(const SyncPromoUITest&) = delete;
-
-  // testing::Test:
-  void SetUp() override {
-    testing::Test::SetUp();
-    TestingProfile::Builder builder;
-    profile_ = builder.Build();
-  }
-
- protected:
-  void DisableSync() {
-    base::CommandLine::ForCurrentProcess()->AppendSwitch(syncer::kDisableSync);
-  }
-
-  content::BrowserTaskEnvironment task_environment_;
-  std::unique_ptr<TestingProfile> profile_;
-};
-
-// Verifies that ShouldShowSyncPromo returns false if sync is disabled by
-// policy.
-TEST_F(SyncPromoUITest, ShouldShowSyncPromoSyncDisabled) {
-  DisableSync();
-  EXPECT_FALSE(SyncPromoUI::ShouldShowSyncPromo(profile_.get()));
-}
-
-// Verifies that ShouldShowSyncPromo returns true if all conditions to
-// show the promo are met.
-TEST_F(SyncPromoUITest, ShouldShowSyncPromoSyncEnabled) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  // No sync promo on CrOS.
-  EXPECT_FALSE(SyncPromoUI::ShouldShowSyncPromo(profile_.get()));
-#else
-  EXPECT_TRUE(SyncPromoUI::ShouldShowSyncPromo(profile_.get()));
-#endif
-}
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index 41a2f48..d8b0572d 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -19,12 +19,12 @@
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_key.h"
+#include "chrome/browser/signin/signin_promo_util.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
 #include "chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/commerce/price_tracking_email_dialog_view.h"
 #include "chrome/browser/ui/views/commerce/price_tracking_view.h"
@@ -453,7 +453,7 @@
                                            bookmark_node)) {
     bubble->SetFootnoteView(
         std::make_unique<commerce::ShoppingCollectionIphView>());
-  } else if (SyncPromoUI::ShouldShowSyncPromo(profile)) {
+  } else if (signin::ShouldShowSyncPromo(*profile)) {
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
     // TODO(pbos): Consider adding model support for footnotes so that this does
     // not need to be tied to views.
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
index 65ef26df..9f51503 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/ui/extensions/extension_installed_waiter.h"
 #include "chrome/browser/ui/signin/bubble_signin_promo_delegate.h"
 #include "chrome/browser/ui/singleton_tabs.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
diff --git a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
index c28a538..b625bda 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
@@ -16,6 +16,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/with_feature_override.h"
 #include "build/build_config.h"
+#include "chrome/browser/signin/chrome_signin_pref_names.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/passwords/manage_passwords_test.h"
@@ -40,7 +41,9 @@
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
+#include "components/signin/public/base/signin_prefs.h"
 #include "components/signin/public/base/signin_switches.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/focus_changed_observer.h"
@@ -521,6 +524,25 @@
       password_manager::metrics_util::CLICKED_ACCEPT, 1);
 }
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+IN_PROC_BROWSER_TEST_P(
+    PasswordBubbleInteractiveUiTestWithExplicitBrowserSigninParam,
+    DismissBubbleBeforeSignInPromoDoesNotIncrementPref) {
+  signin::IdentityTestEnvironment identity_test_env;
+
+  AccountInfo info = identity_test_env.MakeAccountAvailable(
+      "test@email.com", {.set_cookie = true});
+  SetupPendingPassword();
+  ASSERT_TRUE(IsBubbleShowing());
+  PasswordBubbleViewBase::manage_password_bubble()->Cancel();
+
+  EXPECT_EQ(0, browser()->profile()->GetPrefs()->GetInteger(
+                   prefs::kAutofillSignInPromoDismissCountPerProfile));
+  EXPECT_EQ(0, SigninPrefs(*browser()->profile()->GetPrefs())
+                   .GetAutofillSigninPromoDismissCount(info.gaia));
+}
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
 INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
     PasswordBubbleInteractiveUiTestWithExplicitBrowserSigninParam);
 
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.cc b/chrome/browser/ui/views/passwords/password_save_update_view.cc
index 0f481212..d9478701 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.cc
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.cc
@@ -29,7 +29,6 @@
 #include "chrome/grit/theme_resources.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/tracker.h"
-#include "components/signin/public/base/signin_buildflags.h"
 #include "components/user_education/common/feature_promo_specification.h"
 #include "components/user_education/common/help_bubble_params.h"
 #include "content/public/browser/storage_partition.h"
@@ -232,6 +231,18 @@
   return &controller_;
 }
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+bool PasswordSaveUpdateView::OnCloseRequested(
+    views::Widget::ClosedReason close_reason) {
+  if (is_signin_promo_bubble_ &&
+      (close_reason == views::Widget::ClosedReason::kCloseButtonClicked ||
+       close_reason == views::Widget::ClosedReason::kEscKeyPressed)) {
+    AutofillBubbleSignInPromoView::RecordSignInPromoDismissed(web_contents());
+  }
+  return true;
+}
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
 bool PasswordSaveUpdateView::CloseOrReplaceWithPromo() {
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
   // Close the bubble if the sign in promo should not be shown.
@@ -265,6 +276,7 @@
   AddChildView(std::move(sign_in_promo));
   SizeToContents();
 
+  is_signin_promo_bubble_ = true;
   GetBubbleFrameView()->SetProperty(views::kElementIdentifierKey,
                                     kPasswordBubble);
 
diff --git a/chrome/browser/ui/views/passwords/password_save_update_view.h b/chrome/browser/ui/views/passwords/password_save_update_view.h
index 447e9627..bf94696f 100644
--- a/chrome/browser/ui/views/passwords/password_save_update_view.h
+++ b/chrome/browser/ui/views/passwords/password_save_update_view.h
@@ -10,6 +10,8 @@
 #include "base/token.h"
 #include "chrome/browser/ui/passwords/bubble_controllers/save_update_bubble_controller.h"
 #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
+#include "chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.h"
+#include "components/signin/public/base/signin_buildflags.h"
 #include "components/user_education/common/help_bubble.h"
 #include "ui/views/layout/animating_layout_manager.h"
 #include "ui/views/view.h"
@@ -63,6 +65,11 @@
   PasswordBubbleControllerBase* GetController() override;
   const PasswordBubbleControllerBase* GetController() const override;
 
+  // views::WidgetDelegate override.
+#if BUILDFLAG(ENABLE_DICE_SUPPORT)
+  bool OnCloseRequested(views::Widget::ClosedReason close_reason) override;
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)
+
   // If the sign in promo should be shown, it will remove the current contents
   // of the bubble and replace them with the sign in promo. Returns true if the
   // bubble is not replaced with the promo, and therefore closed. Returns false
@@ -117,6 +124,9 @@
   // save bubble.
   const bool is_update_bubble_;
 
+  // True if the bubble is showing the sign in promo. False if not.
+  bool is_signin_promo_bubble_ = false;
+
   raw_ptr<views::Combobox> destination_dropdown_ = nullptr;
 
   // The views for the username and password dropdown elements.
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc b/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
index a1db70a..dd156f0 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/permissions/permission_uma_util.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/color/color_id.h"
 
@@ -30,6 +31,12 @@
 
 using content_settings::SettingSource;
 
+// An upper bound on the maximum number of screens that we can record in
+// metrics. Practically speaking the actual number should never be more than 3
+// but a higher bound allows us to detect via metrics if this happens in the
+// wild.
+constexpr int SCREEN_COUNTER_MAXIMUM = 10;
+
 bool CanGroupVariants(EmbeddedPermissionPrompt::Variant a,
                       EmbeddedPermissionPrompt::Variant b) {
   // Ask and PreviouslyDenied are a special case and can be grouped together.
@@ -303,6 +310,26 @@
       delegate()->Requests(), screen, action, time_to_decision);
 }
 
+void EmbeddedPermissionPrompt::RecordPermissionActionUKM(
+    permissions::ElementAnchoredBubbleAction action) {
+  // There should never be more than SCREEN_COUNTER_MAXIMUM screens. If this is
+  // hit something has gone wrong and we're probably caught in a loop showing
+  // the same screens over and over.
+  DCHECK_LE(prompt_screen_counter_for_metrics_, SCREEN_COUNTER_MAXIMUM);
+
+  permissions::PermissionUmaUtil::RecordElementAnchoredPermissionPromptAction(
+      // This represents all the requests for the entire prompt.
+      delegate_->Requests(),
+      // This only contains the requests for the currently active screen, which
+      // could sometimes be a subset of all requests for the entire prompt.
+      Requests(), action, GetVariant(embedded_prompt_variant_),
+      prompt_screen_counter_for_metrics_, delegate_->GetRequestingOrigin(),
+      delegate_->GetAssociatedWebContents(),
+      delegate_->GetAssociatedWebContents()->GetBrowserContext());
+
+  ++prompt_screen_counter_for_metrics_;
+}
+
 permissions::PermissionPromptDisposition
 EmbeddedPermissionPrompt::GetPromptDisposition() const {
   return permissions::PermissionPromptDisposition::ELEMENT_ANCHORED_BUBBLE;
@@ -369,12 +396,15 @@
 
 void EmbeddedPermissionPrompt::Allow() {
   PrecalculateVariantsForMetrics();
+  RecordPermissionActionUKM(permissions::ElementAnchoredBubbleAction::kGranted);
   delegate_->Accept();
   CloseCurrentViewAndMaybeShowNext(/*first_prompt=*/false);
 }
 
 void EmbeddedPermissionPrompt::AllowThisTime() {
   PrecalculateVariantsForMetrics();
+  RecordPermissionActionUKM(
+      permissions::ElementAnchoredBubbleAction::kGrantedOnce);
   delegate_->AcceptThisTime();
   CloseCurrentViewAndMaybeShowNext(/*first_prompt=*/false);
 }
@@ -383,8 +413,9 @@
   PrecalculateVariantsForMetrics();
   permissions::PermissionUmaUtil::RecordElementAnchoredBubbleDismiss(
       delegate()->Requests(), permissions::DismissedReason::DISMISSED_X_BUTTON);
-
   RecordOsMetrics(permissions::OsScreenAction::DISMISSED_X_BUTTON);
+  RecordPermissionActionUKM(
+      permissions::ElementAnchoredBubbleAction::kDismissedXButton);
 
   delegate_->Dismiss();
   delegate_->FinalizeCurrentRequests();
@@ -393,12 +424,14 @@
 void EmbeddedPermissionPrompt::Acknowledge() {
   // TOOO(crbug.com/1462930): Find how to distinguish between a dismiss and an
   // acknowledge.
+  RecordPermissionActionUKM(permissions::ElementAnchoredBubbleAction::kOk);
   CloseView();
   delegate_->FinalizeCurrentRequests();
 }
 
 void EmbeddedPermissionPrompt::StopAllowing() {
   PrecalculateVariantsForMetrics();
+  RecordPermissionActionUKM(permissions::ElementAnchoredBubbleAction::kDenied);
   delegate_->Deny();
   delegate_->FinalizeCurrentRequests();
 }
@@ -411,7 +444,8 @@
 // better way to handle this scenario.
 #if BUILDFLAG(IS_MAC)
   RecordOsMetrics(permissions::OsScreenAction::SYSTEM_SETTINGS);
-
+  RecordPermissionActionUKM(
+      permissions::ElementAnchoredBubbleAction::kSystemSettings);
   if (requests_[0]->request_type() == permissions::RequestType::kCameraStream) {
     OpenCameraSystemSettingsOnMacOS();
   } else if (requests_[0]->request_type() ==
@@ -435,14 +469,9 @@
 void EmbeddedPermissionPrompt::DismissScrim() {
   permissions::PermissionUmaUtil::RecordElementAnchoredBubbleDismiss(
       delegate()->Requests(), permissions::DismissedReason::DISMISSED_SCRIM);
-
-  if (embedded_prompt_variant_ == Variant::kOsPrompt) {
-    RecordOsMetrics(permissions::OsScreenAction::DISMISSED_SCRIM);
-  }
-
-  if (embedded_prompt_variant_ == Variant::kOsSystemSettings) {
-    RecordOsMetrics(permissions::OsScreenAction::DISMISSED_SCRIM);
-  }
+  RecordOsMetrics(permissions::OsScreenAction::DISMISSED_SCRIM);
+  RecordPermissionActionUKM(
+      permissions::ElementAnchoredBubbleAction::kDismissedScrim);
 
   CloseView();
   PrecalculateVariantsForMetrics();
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt.h b/chrome/browser/ui/views/permissions/embedded_permission_prompt.h
index bfac356..789633b 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt.h
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt.h
@@ -106,6 +106,9 @@
 
   void RecordOsMetrics(permissions::OsScreenAction action);
 
+  void RecordPermissionActionUKM(
+      permissions::ElementAnchoredBubbleAction action);
+
   void PromptForOsPermission();
 
 #if BUILDFLAG(IS_MAC)
@@ -134,6 +137,7 @@
   std::set<ContentSettingsType> prompt_types_;
   std::vector<raw_ptr<permissions::PermissionRequest, VectorExperimental>>
       requests_;
+  int prompt_screen_counter_for_metrics_ = 0;
 
   base::WeakPtrFactory<EmbeddedPermissionPrompt> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc b/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc
index fb01f16..6b84fbc6 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <queue>
 #include <string>
 
-#include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_ask_view.h"
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_base_view.h"
+#include "chrome/browser/ui/views/permissions/embedded_permission_prompt_content_scrim_view.h"
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_previously_denied_view.h"
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_previously_granted_view.h"
 #include "chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h"
@@ -21,18 +22,24 @@
 #include "components/permissions/features.h"
 #include "components/permissions/permission_request_manager.h"
 #include "components/permissions/permission_uma_util.h"
+#include "components/ukm/test_ukm_recorder.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/dns/mock_host_resolver.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/features_generated.h"
 #include "ui/base/interaction/element_identifier.h"
+#include "ui/events/base_event_utils.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/widget/any_widget_observer.h"
 #include "url/origin.h"
 
 namespace {
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsElementId);
 DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kPEPCVisibleEvent);
 DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kDoneVisibleEvent);
+
+using UkmEntry = ukm::builders::Permissions_EmbeddedPromptAction;
 }  // namespace
 
 class EmbeddedPermissionPromptInteractiveTest : public InteractiveBrowserTest {
@@ -65,6 +72,7 @@
     host_resolver()->AddRule("*", "127.0.0.1");
     content::SetupCrossSiteRedirector(https_server());
     https_server()->StartAcceptingConnections();
+    ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
   }
 
   void TearDownOnMainThread() override {
@@ -145,6 +153,42 @@
     }));
   }
 
+  auto CheckNoUkmEntriesSinceLastCheck() {
+    return Steps(Check([this]() {
+      size_t entry_count =
+          ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName).size();
+      ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+      return entry_count == 0U;
+    }));
+  }
+
+  auto CheckEntrySinceLastCheck(
+      permissions::RequestTypeForUma permission,
+      permissions::RequestTypeForUma screen_permission,
+      permissions::ElementAnchoredBubbleAction action,
+      permissions::ElementAnchoredBubbleVariant variant,
+      int screen_counter) {
+    return Steps(Do([=] {
+      auto entries = ukm_recorder_->GetEntriesByName(UkmEntry::kEntryName);
+      CHECK_EQ(entries.size(), 1U);
+
+      ukm_recorder_->ExpectEntryMetric(entries[0],
+                                       UkmEntry::kPermissionTypeName,
+                                       static_cast<int64_t>(permission));
+      ukm_recorder_->ExpectEntryMetric(entries[0],
+                                       UkmEntry::kScreenPermissionTypeName,
+                                       static_cast<int64_t>(screen_permission));
+      ukm_recorder_->ExpectEntryMetric(entries[0], UkmEntry::kActionName,
+                                       static_cast<int64_t>(action));
+      ukm_recorder_->ExpectEntryMetric(entries[0], UkmEntry::kVariantName,
+                                       static_cast<int64_t>(variant));
+      ukm_recorder_->ExpectEntryMetric(entries[0],
+                                       UkmEntry::kPreviousScreensName,
+                                       static_cast<int64_t>(screen_counter));
+      ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
+    }));
+  }
+
   bool DoContentSettingsHaveValue(
       const std::vector<ContentSettingsType>& content_settings_types,
       ContentSetting expected_value) {
@@ -293,6 +337,10 @@
 
  private:
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
+
+  // |ukm_recorder_| needs to be reset after every check so that further check
+  // functions will only check the new data.
+  std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
 };
 
 // Failing on Windows, though manual testing of the same flow does not reproduce
@@ -305,6 +353,7 @@
 #define MAYBE_TestPermissionElementDialogPositioning \
   DISABLED_TestPermissionElementDialogPositioning
 #define MAYBE_TestPepcHistograms DISABLED_TestPepcHistograms
+#define MAYBE_TestPepcUkm DISABLED_TestPepcUkm
 #else
 #define MAYBE_BasicFlowMicrophone BasicFlowMicrophone
 #define MAYBE_BasicFlowCamera BasicFlowCamera
@@ -313,6 +362,7 @@
 #define MAYBE_TestPermissionElementDialogPositioning \
   TestPermissionElementDialogPositioning
 #define MAYBE_TestPepcHistograms TestPepcHistograms
+#define MAYBE_TestPepcUkm TestPepcUkm
 #endif
 IN_PROC_BROWSER_TEST_F(EmbeddedPermissionPromptInteractiveTest,
                        MAYBE_BasicFlowMicrophone) {
@@ -542,6 +592,80 @@
       WaitForStateChange(kWebContentsElementId, done_visible));
 }
 
+IN_PROC_BROWSER_TEST_F(EmbeddedPermissionPromptInteractiveTest,
+                       MAYBE_TestPepcUkm) {
+  views::NamedWidgetShownWaiter waiter(
+      views::test::AnyWidgetTestPasskey{},
+      "EmbeddedPermissionPromptContentScrimWidget");
+  RunTestSequence(
+      InstrumentTab(kWebContentsElementId),
+      NavigateWebContents(kWebContentsElementId, GetURL()),
+      ClickOnPEPCElement("camera-microphone"),
+      InAnyContext(WaitForShow(EmbeddedPermissionPromptBaseView::kMainViewId)),
+      CheckNoUkmEntriesSinceLastCheck(),
+      PushPEPCPromptButton(EmbeddedPermissionPromptAskView::kAllowId),
+      CheckEntrySinceLastCheck(
+          permissions::RequestTypeForUma::MULTIPLE_AUDIO_AND_VIDEO_CAPTURE,
+          permissions::RequestTypeForUma::MULTIPLE_AUDIO_AND_VIDEO_CAPTURE,
+          permissions::ElementAnchoredBubbleAction::kGranted,
+          permissions::ElementAnchoredBubbleVariant::ASK, 0),
+
+      // Now mic+camera are granted.
+      ClickOnPEPCElement("camera"),
+      InAnyContext(WaitForShow(EmbeddedPermissionPromptBaseView::kMainViewId)),
+      PushPEPCPromptButton(
+          EmbeddedPermissionPromptPreviouslyGrantedView::kStopAllowingId),
+      CheckEntrySinceLastCheck(
+          permissions::RequestTypeForUma::PERMISSION_MEDIASTREAM_CAMERA,
+          permissions::RequestTypeForUma::PERMISSION_MEDIASTREAM_CAMERA,
+          permissions::ElementAnchoredBubbleAction::kDenied,
+          permissions::ElementAnchoredBubbleVariant::PREVIOUSLY_GRANTED, 0),
+
+      ClickOnPEPCElement("microphone"),
+      InAnyContext(WaitForShow(EmbeddedPermissionPromptBaseView::kMainViewId)),
+      PushPEPCPromptButton(
+          EmbeddedPermissionPromptPreviouslyGrantedView::kContinueAllowingId),
+      CheckEntrySinceLastCheck(
+          permissions::RequestTypeForUma::PERMISSION_MEDIASTREAM_MIC,
+          permissions::RequestTypeForUma::PERMISSION_MEDIASTREAM_MIC,
+          permissions::ElementAnchoredBubbleAction::kOk,
+          permissions::ElementAnchoredBubbleVariant::PREVIOUSLY_GRANTED, 0),
+
+      // Mic is granted, camera is blocked. Triggering the double permission
+      // prompt will show the screen that is only for camera, while the prompt
+      // is for both.
+      ClickOnPEPCElement("camera-microphone"),
+      InAnyContext(WaitForShow(EmbeddedPermissionPromptBaseView::kMainViewId)),
+      PushPEPCPromptButton(
+          EmbeddedPermissionPromptPreviouslyDeniedView::kAllowThisTimeId),
+      CheckEntrySinceLastCheck(
+          permissions::RequestTypeForUma::MULTIPLE_AUDIO_AND_VIDEO_CAPTURE,
+          permissions::RequestTypeForUma::PERMISSION_MEDIASTREAM_CAMERA,
+          permissions::ElementAnchoredBubbleAction::kGrantedOnce,
+          permissions::ElementAnchoredBubbleVariant::PREVIOUSLY_DENIED, 0),
+
+      // Both permissions are granted. Dismiss the prompt via clicking on the
+      // scrim.
+      ClickOnPEPCElement("camera-microphone"),
+      InAnyContext(WaitForShow(EmbeddedPermissionPromptBaseView::kMainViewId)),
+      FlushEvents(), Do([&]() {
+        auto* scrim_view =
+            static_cast<EmbeddedPermissionPromptContentScrimView*>(
+                waiter.WaitIfNeededAndGet()->GetContentsView());
+        scrim_view->OnMousePressed(
+            ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+        scrim_view->OnMouseReleased(
+            ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
+                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0));
+      }),
+      CheckEntrySinceLastCheck(
+          permissions::RequestTypeForUma::MULTIPLE_AUDIO_AND_VIDEO_CAPTURE,
+          permissions::RequestTypeForUma::MULTIPLE_AUDIO_AND_VIDEO_CAPTURE,
+          permissions::ElementAnchoredBubbleAction::kDismissedScrim,
+          permissions::ElementAnchoredBubbleVariant::PREVIOUSLY_GRANTED, 0));
+}
+
 class EmbeddedPermissionPromptPositioningInteractiveTest
     : public EmbeddedPermissionPromptInteractiveTest {
  public:
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
index abc24f4a..c9a186b5 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -550,6 +550,17 @@
 }
 #endif
 
+class AvatarToolbarButtonBrowserTestWithExplicitBrowserSignin
+    : public base::test::WithFeatureOverride,
+      public AvatarToolbarButtonBrowserTest {
+ public:
+  AvatarToolbarButtonBrowserTestWithExplicitBrowserSignin()
+      : base::test::WithFeatureOverride(
+            switches::kExplicitBrowserSigninUIOnDesktop) {}
+
+  bool is_explicit_browser_signin() const { return IsParamFeatureEnabled(); }
+};
+
 // TODO(crbug/327688158): Flaky on chromium/ci/win-asan. Disable for Windows.
 // TODO(b/331746545): Check windows issues with time duration/delays.
 #if BUILDFLAG(IS_WIN)
@@ -557,7 +568,7 @@
 #else
 #define MAYBE_ShowNameOnSignin_ThenSync ShowNameOnSignin_ThenSync
 #endif
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
+IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonBrowserTestWithExplicitBrowserSignin,
                        MAYBE_ShowNameOnSignin_ThenSync) {
   AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
   // Normal state.
@@ -570,9 +581,12 @@
   // The button is in a waiting for image state, the name is not yet displayed.
   EXPECT_EQ(avatar_button->GetText(), std::u16string());
 
-  // The name will only show when the image is loaded.
+  // The greeting will only show when the image is loaded.
   AddSignedInImage(account_info.account_id);
-  EXPECT_EQ(avatar_button->GetText(), name);
+  EXPECT_EQ(avatar_button->GetText(),
+            is_explicit_browser_signin()
+                ? l10n_util::GetStringFUTF16(IDS_AVATAR_BUTTON_GREETING, name)
+                : name);
 
   observer.WaitForShowNameEnded();
   // Once the name is not shown anymore, we expect no text.
@@ -590,7 +604,8 @@
 #else
 #define MAYBE_ShowNameOnSync ShowNameOnSync
 #endif
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest, MAYBE_ShowNameOnSync) {
+IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonBrowserTestWithExplicitBrowserSignin,
+                       MAYBE_ShowNameOnSync) {
   AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
   // Normal state.
   ASSERT_TRUE(avatar_button->GetText().empty());
@@ -602,9 +617,12 @@
   // The button is in a waiting for image state, the name is not yet displayed.
   EXPECT_EQ(avatar_button->GetText(), std::u16string());
 
-  // The name will only show when the image is loaded.
+  // The greeting will only show when the image is loaded.
   AddSignedInImage(account_info.account_id);
-  EXPECT_EQ(avatar_button->GetText(), name);
+  EXPECT_EQ(avatar_button->GetText(),
+            is_explicit_browser_signin()
+                ? l10n_util::GetStringFUTF16(IDS_AVATAR_BUTTON_GREETING, name)
+                : name);
 
   observer.WaitForShowNameEnded();
   // Once the name is not shown anymore, we expect no text.
@@ -614,7 +632,7 @@
 // Check www.crbug.com/331499330: This test makes sure that no states attempt to
 // request an update during their construction. But rather do so after all the
 // states are created and the view is added to the Widget.
-IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
+IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonBrowserTestWithExplicitBrowserSignin,
                        DISABLED_OpenNewBrowserWhileNameIsShown) {
   AvatarToolbarButton* avatar_button = GetAvatarToolbarButton(browser());
   // Normal state.
@@ -630,9 +648,12 @@
   // The button is in a waiting for image state, the name is not yet displayed.
   EXPECT_EQ(avatar_button->GetText(), std::u16string());
 
-  // The name will only show when the image is loaded.
+  // The greeting will only show when the image is loaded.
   AddSignedInImage(account_info.account_id);
-  EXPECT_EQ(avatar_button->GetText(), name);
+  EXPECT_EQ(avatar_button->GetText(),
+            is_explicit_browser_signin()
+                ? l10n_util::GetStringFUTF16(IDS_AVATAR_BUTTON_GREETING, name)
+                : name);
 
   ASSERT_TRUE(GetIdentityManager()->AreRefreshTokensLoaded());
   // Increase the text duration length to accommodate for the browser creation
@@ -646,6 +667,9 @@
   EXPECT_EQ(new_avatar_button->GetText(), name);
 }
 
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(
+    AvatarToolbarButtonBrowserTestWithExplicitBrowserSignin);
+
 IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonBrowserTest,
                        DISABLED_ShowNameDoesNotAppearOnNewBrowserIfNotShowing) {
   // Name is shown and cleared after waiting.
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
index c509816..af3a91b 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
@@ -1187,7 +1187,10 @@
       break;
     }
     case ButtonState::kShowIdentityName:
-      text = GetShortProfileName();
+      text = switches::IsExplicitBrowserSigninUIOnDesktopEnabled()
+                 ? l10n_util::GetStringFUTF16(IDS_AVATAR_BUTTON_GREETING,
+                                              GetShortProfileName())
+                 : GetShortProfileName();
       break;
     case ButtonState::kExplicitTextShowing: {
       const internal::ExplicitStateProvider* explicit_state =
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 5b9e32d..b156f2b 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -48,7 +48,6 @@
 #include "chrome/browser/ui/profiles/profile_colors_util.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
 #include "chrome/browser/ui/profiles/profile_view_utils.h"
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
diff --git a/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.cc b/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.cc
index d148565f..ff0dbfa 100644
--- a/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.cc
+++ b/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.cc
@@ -11,14 +11,20 @@
 #include "chrome/browser/companion/core/companion_metrics_logger.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "chrome/browser/signin/chrome_signin_pref_names.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/signin_promo_util.h"
+#include "chrome/browser/signin/signin_ui_util.h"
 #include "chrome/browser/ui/autofill/autofill_bubble_signin_promo_controller.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/promos/bubble_signin_promo_view.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_buildflags.h"
 #include "components/signin/public/base/signin_metrics.h"
+#include "components/signin/public/base/signin_prefs.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -72,14 +78,30 @@
       std::make_unique<AutofillBubbleSignInPromoView::DiceSigninPromoDelegate>(
           &controller_);
 
-  int message_resource_id = 0;
+  // Set prefs to record the bubbles appearance and set the text to be shown.
+  // TODO(crbug.com/319411728): Add the correct strings per type.
+  int message_resource_id = IDS_PASSWORD_MANAGER_DICE_PROMO_SIGNIN_MESSAGE;
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile);
+  AccountInfo account =
+      signin_ui_util::GetSingleAccountForPromos(identity_manager);
+
   switch (promo_type_) {
-    // TODO(crbug.com/319411728): Add the correct strings per type.
     case signin::SignInAutofillBubblePromoType::Payments:
     case signin::SignInAutofillBubblePromoType::Addresses:
+      break;
     case signin::SignInAutofillBubblePromoType::Passwords:
-      message_resource_id = IDS_PASSWORD_MANAGER_DICE_PROMO_SIGNIN_MESSAGE;
+      if (account.gaia.empty()) {
+        int show_count = profile->GetPrefs()->GetInteger(
+            prefs::kPasswordSignInPromoShownCountPerProfile);
+        profile->GetPrefs()->SetInteger(
+            prefs::kPasswordSignInPromoShownCountPerProfile, show_count + 1);
+      } else {
+        SigninPrefs(*profile->GetPrefs())
+            .IncrementPasswordSigninPromoImpressionCount(account.gaia);
+      }
   }
+
   AddChildView(new BubbleSignInPromoView(
       profile, dice_sign_in_promo_delegate_.get(),
       signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE,
@@ -87,6 +109,26 @@
       views::style::STYLE_PRIMARY));
 }
 
+void AutofillBubbleSignInPromoView::RecordSignInPromoDismissed(
+    content::WebContents* web_contents) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  AccountInfo account = signin_ui_util::GetSingleAccountForPromos(
+      IdentityManagerFactory::GetForProfile(profile));
+
+  // Count the number of times the promo was dismissed in order to not show it
+  // anymore after 2 dismissals.
+  if (account.gaia.empty()) {
+    int dismiss_count = profile->GetPrefs()->GetInteger(
+        prefs::kAutofillSignInPromoDismissCountPerProfile);
+    profile->GetPrefs()->SetInteger(
+        prefs::kAutofillSignInPromoDismissCountPerProfile, dismiss_count + 1);
+  } else {
+    SigninPrefs(*profile->GetPrefs())
+        .IncrementAutofillSigninPromoDismissCount(account.gaia);
+  }
+}
+
 AutofillBubbleSignInPromoView::~AutofillBubbleSignInPromoView() = default;
 
 BEGIN_METADATA(AutofillBubbleSignInPromoView)
diff --git a/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.h b/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.h
index 23ea590e..e1a7f2bd 100644
--- a/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.h
+++ b/chrome/browser/ui/views/promos/autofill_bubble_signin_promo_view.h
@@ -37,6 +37,9 @@
       const AutofillBubbleSignInPromoView&) = delete;
   ~AutofillBubbleSignInPromoView() override;
 
+  // Records that the bubble has been dismissed.
+  static void RecordSignInPromoDismissed(content::WebContents* web_contents);
+
  private:
   // Delegate for the personalized sign in promo view used when desktop identity
   // consistency is enabled.
diff --git a/chrome/browser/ui/views/promos/bubble_signin_promo_view.cc b/chrome/browser/ui/views/promos/bubble_signin_promo_view.cc
index 9931868..6d3bbcc 100644
--- a/chrome/browser/ui/views/promos/bubble_signin_promo_view.cc
+++ b/chrome/browser/ui/views/promos/bubble_signin_promo_view.cc
@@ -134,7 +134,8 @@
 void BubbleSignInPromoView::SignIn() {
   std::optional<AccountInfo> account = signin_button_view_->account();
   delegate_->OnSignIn(account.value_or(AccountInfo()));
-  GetWidget()->Close();
+  GetWidget()->CloseWithReason(
+      views::Widget::ClosedReason::kAcceptButtonClicked);
 }
 
 BEGIN_METADATA(BubbleSignInPromoView)
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
index 28f6b0b..ede87216 100644
--- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc
+++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -107,6 +107,7 @@
 #include "chrome/browser/ui/webui/ash/login/packaged_license_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/parental_handoff_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/password_selection_screen_handler.h"
+#include "chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/pin_setup_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/recommend_apps_screen_handler.h"
@@ -609,6 +610,8 @@
 
   if (features::IsOobePersonalizedOnboardingEnabled()) {
     AddScreenHandler(std::make_unique<CategoriesSelectionScreenHandler>());
+    AddScreenHandler(
+        std::make_unique<PersonalizedRecommendAppsScreenHandler>());
   }
 
   AddScreenHandler(std::make_unique<AddChildScreenHandler>());
diff --git a/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.cc b/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.cc
index 85b2b3ff..0b4a58f 100644
--- a/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/webui/ash/login/base_screen_handler.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/login/localized_values_builder.h"
+#include "ui/chromeos/devicetype_utils.h"
 
 namespace ash {
 
@@ -20,12 +21,27 @@
     ~PersonalizedRecommendAppsScreenHandler() = default;
 
 void PersonalizedRecommendAppsScreenHandler::DeclareLocalizedValues(
-    ::login::LocalizedValuesBuilder* builder) {}
+    ::login::LocalizedValuesBuilder* builder) {
+  builder->Add("personalizedRecommendedLoading",
+               IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_LOADING);
+  builder->AddF("personalizedRecommendedAppsScreenTitle",
+                IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_TITLE,
+                ui::GetChromeOSDeviceName());
+  builder->Add("personalizedRecommendedAppsScreenDescription",
+               IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_SUBTITLE);
+  builder->Add("personalizedRecommendedAppsScreenSkip",
+               IDS_LOGIN_PERSONALIZED_RECOMMEND_APPS_SCREEN_SCREEN_SKIP);
+}
 
 void PersonalizedRecommendAppsScreenHandler::Show() {
   ShowInWebUI();
 }
 
+void PersonalizedRecommendAppsScreenHandler::SetCategoriesAppsMapData(
+    base::Value::Dict categoriesApps) {
+  CallExternalAPI("setCategoriesAppsMapData", std::move(categoriesApps));
+}
+
 base::WeakPtr<PersonalizedRecommendAppsScreenView>
 PersonalizedRecommendAppsScreenHandler::AsWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
diff --git a/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.h b/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.h
index d01715b7..14cacd7d 100644
--- a/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/personalized_recommend_apps_screen_handler.h
@@ -23,6 +23,8 @@
   // Shows the contents of the screen.
   virtual void Show() = 0;
 
+  virtual void SetCategoriesAppsMapData(base::Value::Dict categoriesApps) = 0;
+
   // Gets a WeakPtr to the instance.
   virtual base::WeakPtr<PersonalizedRecommendAppsScreenView> AsWeakPtr() = 0;
 };
@@ -48,6 +50,7 @@
 
   // PersonalizedRecommendAppsScreenView:
   void Show() override;
+  void SetCategoriesAppsMapData(base::Value::Dict categoriesApps) override;
   base::WeakPtr<PersonalizedRecommendAppsScreenView> AsWeakPtr() override;
 
  private:
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index c04eb03..d49a1915 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -209,7 +209,6 @@
 #endif
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/ui/sync/sync_promo_ui.h"
 #include "chrome/browser/ui/webui/signin/managed_user_profile_notice_ui.h"
 #include "chrome/browser/ui/webui/signin/profile_customization_ui.h"
 #include "chrome/browser/ui/webui/signin/profile_picker_ui.h"
diff --git a/chrome/browser/ui/webui/settings/safety_hub_handler.cc b/chrome/browser/ui/webui/settings/safety_hub_handler.cc
index b2a64e90..14db842 100644
--- a/chrome/browser/ui/webui/settings/safety_hub_handler.cc
+++ b/chrome/browser/ui/webui/settings/safety_hub_handler.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/extensions/cws_info_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/safety_hub/card_data_helper.h"
 #include "chrome/browser/ui/safety_hub/extensions_result.h"
 #include "chrome/browser/ui/safety_hub/menu_notification_service_factory.h"
 #include "chrome/browser/ui/safety_hub/notification_permission_review_service.h"
@@ -113,34 +114,6 @@
   return permissions_data;
 }
 
-// Returns the state of Safe Browsing setting.
-SafeBrowsingState GetSafeBrowsingState(PrefService* pref_service) {
-  // TODO(crbug.com/40267370): Use SafeBrowsingResult from Safety Hub instead.
-  if (safe_browsing::IsEnhancedProtectionEnabled(*pref_service))
-    return SafeBrowsingState::kEnabledEnhanced;
-  if (safe_browsing::IsSafeBrowsingEnabled(*pref_service))
-    return SafeBrowsingState::kEnabledStandard;
-  if (safe_browsing::IsSafeBrowsingPolicyManaged(*pref_service))
-    return SafeBrowsingState::kDisabledByAdmin;
-  if (safe_browsing::IsSafeBrowsingExtensionControlled(*pref_service))
-    return SafeBrowsingState::kDisabledByExtension;
-  return SafeBrowsingState::kDisabledByUser;
-}
-
-base::Value::Dict CardDataToValue(int header_id,
-                                  int subheader_id,
-                                  SafetyHubCardState card_state) {
-  base::Value::Dict sb_card_info;
-
-  sb_card_info.Set(safety_hub::kCardHeaderKey,
-                   l10n_util::GetStringUTF16(header_id));
-  sb_card_info.Set(safety_hub::kCardSubheaderKey,
-                   l10n_util::GetStringUTF16(subheader_id));
-  sb_card_info.Set(safety_hub::kCardStateKey, static_cast<int>(card_state));
-
-  return sb_card_info;
-}
-
 // Returns true if the card dict indicates there is something actionable for the
 // user.
 bool CardHasRecommendations(base::Value::Dict card_data) {
@@ -466,7 +439,8 @@
   CHECK_EQ(1U, args.size());
   const base::Value& callback_id = args[0];
 
-  ResolveJavascriptCallback(callback_id, GetSafeBrowsingCardData());
+  ResolveJavascriptCallback(callback_id,
+                            safety_hub::GetSafeBrowsingCardData(profile_));
 }
 
 void SafetyHubHandler::HandleGetNumberOfExtensionsThatNeedReview(
@@ -477,45 +451,6 @@
                             base::Value(GetNumberOfExtensionsThatNeedReview()));
 }
 
-base::Value::Dict SafetyHubHandler::GetSafeBrowsingCardData() {
-  SafeBrowsingState result = GetSafeBrowsingState(profile_->GetPrefs());
-
-  base::Value::Dict sb_card_info;
-
-  switch (result) {
-    case SafeBrowsingState::kEnabledEnhanced:
-      sb_card_info =
-          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_HEADER,
-                          IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_SUBHEADER,
-                          SafetyHubCardState::kSafe);
-      break;
-    case SafeBrowsingState::kEnabledStandard:
-      sb_card_info =
-          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_HEADER,
-                          IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_SUBHEADER,
-                          SafetyHubCardState::kSafe);
-      break;
-    case SafeBrowsingState::kDisabledByAdmin:
-      sb_card_info =
-          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER,
-                          IDS_SETTINGS_SAFETY_HUB_SB_OFF_MANAGED_SUBHEADER,
-                          SafetyHubCardState::kInfo);
-      break;
-    case SafeBrowsingState::kDisabledByExtension:
-      sb_card_info =
-          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER,
-                          IDS_SETTINGS_SAFETY_HUB_SB_OFF_EXTENSION_SUBHEADER,
-                          SafetyHubCardState::kInfo);
-      break;
-    default:
-      sb_card_info =
-          CardDataToValue(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER,
-                          IDS_SETTINGS_SAFETY_HUB_SB_OFF_USER_SUBHEADER,
-                          SafetyHubCardState::kWarning);
-  }
-  return sb_card_info;
-}
-
 void SafetyHubHandler::HandleGetPasswordCardData(
     const base::Value::List& args) {
   AllowJavascript();
@@ -523,19 +458,8 @@
   CHECK_EQ(1U, args.size());
   const base::Value& callback_id = args[0];
 
-  ResolveJavascriptCallback(callback_id, base::Value(GetPasswordCardData()));
-}
-
-base::Value::Dict SafetyHubHandler::GetPasswordCardData() {
-  PasswordStatusCheckService* service =
-      PasswordStatusCheckServiceFactory::GetForProfile(profile_);
-  CHECK(service);
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile_);
-  bool signed_in = identity_manager && identity_manager->HasPrimaryAccount(
-                                           signin::ConsentLevel::kSignin);
-
-  return service->GetPasswordCardData(signed_in);
+  ResolveJavascriptCallback(
+      callback_id, base::Value(safety_hub::GetPasswordCardData(profile_)));
 }
 
 void SafetyHubHandler::HandleGetVersionCardData(const base::Value::List& args) {
@@ -544,32 +468,8 @@
   CHECK_EQ(1U, args.size());
   const base::Value& callback_id = args[0];
 
-  ResolveJavascriptCallback(callback_id, base::Value(GetVersionCardData()));
-}
-
-base::Value::Dict SafetyHubHandler::GetVersionCardData() {
-  base::Value::Dict result;
-  switch (g_browser_process->GetBuildState()->update_type()) {
-    case BuildState::UpdateType::kNone:
-      result.Set(safety_hub::kCardHeaderKey,
-                 l10n_util::GetStringUTF16(
-                     IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_HEADER_UPDATED));
-      result.Set(safety_hub::kCardSubheaderKey,
-                 VersionUI::GetAnnotatedVersionStringForUi());
-      result.Set(safety_hub::kCardStateKey,
-                 static_cast<int>(SafetyHubCardState::kSafe));
-      break;
-    case BuildState::UpdateType::kNormalUpdate:
-    // kEnterpriseRollback and kChannelSwitchRollback are fairly rare state,
-    // they will be handled same as there is waiting updates.
-    case BuildState::UpdateType::kEnterpriseRollback:
-    case BuildState::UpdateType::kChannelSwitchRollback:
-      result = CardDataToValue(
-          IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_HEADER_RESTART,
-          IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_SUBHEADER_RESTART,
-          SafetyHubCardState::kWarning);
-  }
-  return result;
+  ResolveJavascriptCallback(callback_id,
+                            base::Value(safety_hub::GetVersionCardData()));
 }
 
 void SafetyHubHandler::HandleGetSafetyHubEntryPointData(
@@ -644,15 +544,15 @@
   std::set<SafetyHubModule> modules;
 
   // Passwords module
-  if (CardHasRecommendations(GetPasswordCardData())) {
+  if (CardHasRecommendations(safety_hub::GetPasswordCardData(profile_))) {
     modules.insert(SafetyHubModule::kPasswords);
   }
   // Version module
-  if (CardHasRecommendations(GetVersionCardData())) {
+  if (CardHasRecommendations(safety_hub::GetVersionCardData())) {
     modules.insert(SafetyHubModule::kVersion);
   }
   // SafeBrowsing module
-  if (CardHasRecommendations(GetSafeBrowsingCardData())) {
+  if (CardHasRecommendations(safety_hub::GetSafeBrowsingCardData(profile_))) {
     modules.insert(SafetyHubModule::kSafeBrowsing);
   }
   // Extensions module
diff --git a/chrome/browser/ui/webui/settings/safety_hub_handler.h b/chrome/browser/ui/webui/settings/safety_hub_handler.h
index c6682ef..ba6f6cf 100644
--- a/chrome/browser/ui/webui/settings/safety_hub_handler.h
+++ b/chrome/browser/ui/webui/settings/safety_hub_handler.h
@@ -179,15 +179,9 @@
   // Returns the data for Safe Browsing card.
   void HandleGetSafeBrowsingCardData(const base::Value::List& args);
 
-  // Fetches data for the Safe Browsing card to return data to the UI.
-  base::Value::Dict GetSafeBrowsingCardData();
-
   // Returns the data for the password card.
   void HandleGetPasswordCardData(const base::Value::List& args);
 
-  // Fetches data for the password card to return data to the UI.
-  base::Value::Dict GetPasswordCardData();
-
   // Returns the data for the version card.
   void HandleGetVersionCardData(const base::Value::List& args);
 
@@ -197,6 +191,12 @@
   // Returns the data for Safety Hub entry point.
   void HandleGetSafetyHubEntryPointData(const base::Value::List& args);
 
+  // Returns true if Safety Hub has recommendations.
+  void HandleGetSafetyHubHasRecommendations(const base::Value::List& args);
+
+  // Returns the subheader for Safety Hub entry point in settings.
+  void HandleGetSafetyHubEntryPointSubheader(const base::Value::List& args);
+
   // Sends the list of notification permissions to review to the WebUI.
   void SendNotificationPermissionReviewList();
 
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
index 180d1e9..5af75f08 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
@@ -306,8 +306,12 @@
 std::string DiceWebSigninInterceptHandler::GetBodyText() {
   if (bubble_parameters_.interception_type ==
       WebSigninInterceptor::SigninInterceptionType::kProfileSwitch) {
-    return l10n_util::GetStringUTF8(
-        IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC);
+    return switches::IsExplicitBrowserSigninUIOnDesktopEnabled()
+               ? l10n_util::GetStringFUTF8(
+                     IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC_V2,
+                     base::UTF8ToUTF16(intercepted_account().email))
+               : l10n_util::GetStringUTF8(
+                     IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_DESC);
   }
 
   CHECK(bubble_parameters_.interception_type ==
@@ -360,7 +364,9 @@
   if (bubble_parameters_.interception_type ==
       WebSigninInterceptor::SigninInterceptionType::kProfileSwitch) {
     return l10n_util::GetStringUTF8(
-        IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL);
+        switches::IsExplicitBrowserSigninUIOnDesktopEnabled()
+            ? IDS_SIGNIN_DICE_WEB_INTERCEPT_SWITCH_BUBBLE_CONTINUE_BUTTON_LABEL
+            : IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_CONFIRM_SWITCH_BUTTON_LABEL);
   }
 
   return l10n_util::GetStringUTF8(
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index 9a6dfd7..a6eb0e04 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -48,10 +48,7 @@
 source_set("unit_tests") {
   testonly = true
 
-  sources = [
-    "bookmark_app_util_unittest.cc",
-    "web_app_uninstall_and_replace_job_unittest.cc",
-  ]
+  sources = [ "web_app_uninstall_and_replace_job_unittest.cc" ]
 
   deps = [
     ":extensions",
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.cc b/chrome/browser/web_applications/extensions/bookmark_app_util.cc
index faff67c..28cce5b 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_util.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_util.cc
@@ -9,18 +9,11 @@
 #include <string_view>
 #include <utility>
 
-#include "base/values.h"
-#include "chrome/browser/web_applications/web_app_utils.h"
-#include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "content/public/browser/browser_context.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_icon_set.h"
-#include "extensions/common/manifest_handlers/icons_handler.h"
-#include "url/gurl.h"
 
 namespace extensions {
 namespace {
@@ -49,36 +42,4 @@
   return true;
 }
 
-bool IsInNavigationScopeForLaunchUrl(const GURL& launch_url, const GURL& url) {
-  // Drop any "suffix" components after the path (Resolve "."):
-  const GURL nav_scope = launch_url.GetWithoutFilename();
-
-  const int scope_str_length = nav_scope.spec().size();
-  return std::string_view(nav_scope.spec()) ==
-         std::string_view(url.spec()).substr(0, scope_str_length);
-}
-
-LaunchContainerAndType GetLaunchContainerAndTypeFromDisplayMode(
-    web_app::DisplayMode display_mode) {
-  apps::LaunchContainer apps_launch_container =
-      web_app::ConvertDisplayModeToAppLaunchContainer(display_mode);
-  switch (apps_launch_container) {
-    case apps::LaunchContainer::kLaunchContainerNone:
-      return {apps::LaunchContainer::kLaunchContainerNone,
-              extensions::LaunchType::LAUNCH_TYPE_DEFAULT};
-    case apps::LaunchContainer::kLaunchContainerPanelDeprecated:
-      return {apps::LaunchContainer::kLaunchContainerPanelDeprecated,
-              extensions::LaunchType::LAUNCH_TYPE_REGULAR};
-    case apps::LaunchContainer::kLaunchContainerTab:
-      return {apps::LaunchContainer::kLaunchContainerTab,
-              extensions::LaunchType::LAUNCH_TYPE_REGULAR};
-    case apps::LaunchContainer::kLaunchContainerWindow:
-      return {apps::LaunchContainer::kLaunchContainerTab,
-              display_mode == web_app::DisplayMode::kFullscreen
-                  ? extensions::LaunchType::LAUNCH_TYPE_FULLSCREEN
-                  : extensions::LaunchType::LAUNCH_TYPE_WINDOW};
-  }
-  NOTREACHED();
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util.h b/chrome/browser/web_applications/extensions/bookmark_app_util.h
index cbb93e3..46d6aec 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_util.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_util.h
@@ -5,17 +5,11 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UTIL_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UTIL_H_
 
-#include "chrome/browser/web_applications/web_app_constants.h"
-#include "chrome/browser/web_applications/web_app_install_info.h"
-#include "components/services/app_service/public/cpp/app_launch_util.h"
-#include "extensions/common/constants.h"
 
 namespace content {
 class BrowserContext;
 }
 
-class GURL;
-
 namespace extensions {
 
 class Extension;
@@ -29,18 +23,6 @@
 bool BookmarkAppIsLocallyInstalled(const ExtensionPrefs* prefs,
                                    const Extension* extension);
 
-// Generates a scope based on |launch_url| and checks if the |url| falls under
-// it. https://www.w3.org/TR/appmanifest/#navigation-scope
-bool IsInNavigationScopeForLaunchUrl(const GURL& launch_url, const GURL& url);
-
-struct LaunchContainerAndType {
-  apps::LaunchContainer launch_container;
-  extensions::LaunchType launch_type;
-};
-
-LaunchContainerAndType GetLaunchContainerAndTypeFromDisplayMode(
-    web_app::DisplayMode display_mode);
-
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_UTIL_H_
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_util_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_util_unittest.cc
deleted file mode 100644
index 0ba0e63..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_util_unittest.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace extensions {
-
-// See additional test coverage for edge cases in GURLTest.GetWithoutFilename.
-TEST(BookmarkAppUtil, IsInNavigationScopeForLaunchUrl_UrlArgumentVariations) {
-  const GURL launch_url = GURL("https://mail.google.com/mail/u/0");
-
-  // Not in scope.
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      launch_url, GURL("https://mail.google.com")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      launch_url, GURL("https://mail.google.com/mail/")));
-
-  // The scope itself.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      launch_url, GURL("https://mail.google.com/mail/u/")));
-  // No match if no trailing '/' in path.
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      launch_url, GURL("https://mail.google.com/mail/u")));
-
-  // Regular sub-path.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      launch_url, GURL("https://mail.google.com/mail/u/0/")));
-
-  // With a ref.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      launch_url, GURL("https://mail.google.com/mail/u/0/#inbox")));
-
-  // A launch URL with trailing '/' resolves to itself.
-  const GURL launch_url2 = GURL("https://example.com/path/subpath/");
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      launch_url2, GURL("https://example.com/path/subpath/page.html")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      launch_url2, GURL("https://example.com/path/subpath2/")));
-
-  // With a query.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/subpath"),
-      GURL("https://example.com/path/query?parameter")));
-}
-
-TEST(BookmarkAppUtil, IsInNavigationScopeForLaunchUrl_LaunchUrlVariations) {
-  // With a query.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/query?parameter"),
-      GURL("https://example.com/path/subpath")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/query?parameter"),
-      GURL("https://example.com/query")));
-
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/query/?parameter"),
-      GURL("https://example.com/path/query/")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/query/?parameter"),
-      GURL("https://example.com/path/subpath/")));
-
-  // With a ref.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/#ref"),
-      GURL("https://example.com/path/subpath")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/#ref"), GURL("https://example.com/#ref")));
-
-  // With a ref and query.
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/?query=param#ref"),
-      GURL("https://example.com/path/subpath")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/?query=param#ref"),
-      GURL("https://example.com/subpath")));
-
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/#ref?query=param"),
-      GURL("https://example.com/path/subpath")));
-  EXPECT_FALSE(IsInNavigationScopeForLaunchUrl(
-      GURL("https://example.com/path/#ref?query=param"),
-      GURL("https://example.com/subpath")));
-}
-
-TEST(BookmarkAppUtil, IsInNavigationScopeForLaunchUrl_Extensions) {
-  // The Crosh extension.
-  const GURL extension_launch_url = GURL(
-      "chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/html/crosh.html");
-  EXPECT_TRUE(IsInNavigationScopeForLaunchUrl(
-      extension_launch_url,
-      GURL("chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/html/path")));
-}
-
-}  // namespace extensions
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 4b4e14b..6ef8716 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1715557030-479494ced5371cc841e02a68b4d2ec6fbcc9c61d-c452097d82eb788903c2fa9b242977ff06543f98.profdata
+chrome-android32-main-1715579683-59a5c89ea3199c11b3fdbd9f35bbbfb877dfb178-ddc34a3a91ae02201d68480e44664851d496cbdc.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 86f9175..6dfc077d 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1715526076-e98afbf45707835ade131ec21ae0efc4fa9111ba-5b9e7084bf6b7d1ff10209a99f3074c69bfefc05.profdata
+chrome-android64-main-1715572252-b900d711ceef18ef84c0779bf686e55207ee8f64-6201022f2029b6b8f32ff9177d8e2acf364edfcf.profdata
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt
index dae8f0e..cfc5a640 100644
--- a/chrome/build/lacros64.pgo.txt
+++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-amd64-generic-main-1715500081-af0f79395ad2ecde7a93cc478d60ae3d66604f55-1711cd65cc1938e19e65f00eea236aa04535cedf.profdata
+chrome-chromeos-amd64-generic-main-1715558984-659e4a3e40d1a04c35bd74b3b5dff189d8d4ed3f-d0af0d6ce4a5c00da16c16c013031f83ee3976de.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ecf10bf..d5c8ce0 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1715526076-9176aa747d5295fedc5cc59f606edea5d38ee346-5b9e7084bf6b7d1ff10209a99f3074c69bfefc05.profdata
+chrome-linux-main-1715557030-b87df2c965be309c1c2847e7c9af51ca84173dcf-c452097d82eb788903c2fa9b242977ff06543f98.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 534b5c2..b397602 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1715557030-ff25757f52de357ae56f68716bcdd80d2773cc3e-c452097d82eb788903c2fa9b242977ff06543f98.profdata
+chrome-mac-arm-main-1715594327-134711ca8a25cb91418025b6fa050eaed0fe2470-b4c19f3efcbb376b2be7eeabb6843d9d9470ada5.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index ed28a77..743526cc 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1715557030-dfe5a44f9ef28e2ca14b013d3005500d88b20be8-c452097d82eb788903c2fa9b242977ff06543f98.profdata
+chrome-win-arm64-main-1715579683-c4bd88f80c0f9034d70c7553c68e475f70f83588-ddc34a3a91ae02201d68480e44664851d496cbdc.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index f2773bd..44cfc62 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1715526076-2bbbb00961d4be6fbbe2e37caabf5475ad69c961-5b9e7084bf6b7d1ff10209a99f3074c69bfefc05.profdata
+chrome-win32-main-1715579683-20d3a2bf3d17613f60d928e17661e6478ecca68c-ddc34a3a91ae02201d68480e44664851d496cbdc.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index b7073e9b..f6d2344 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1715526076-88241d6104a82576549e7c5d429dd83ba220495a-5b9e7084bf6b7d1ff10209a99f3074c69bfefc05.profdata
+chrome-win64-main-1715579683-9d9ca4adfd923ef03fa89035a2af13341d2708be-ddc34a3a91ae02201d68480e44664851d496cbdc.profdata
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index b6c63761..95e46886 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -273,13 +273,6 @@
 const char kEnableHangoutServicesExtensionForTesting[] =
     "enable-hangout-services-extension-for-testing";
 
-#if BUILDFLAG(IS_CHROMEOS)
-// Makes Lacros fork the zygotes before blocking when prelaunched at login
-// screen.
-const char kEnableLacrosForkZygotesAtLoginScreen[] =
-    "enable-lacros-fork-zygotes-at-login-screen";
-#endif
-
 // Allows NaCl to run in all contexts (such as open web). Note that
 // kDisableNaCl disables NaCl in all contexts and takes precedence.
 const char kEnableNaCl[] = "enable-nacl";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 726dac8..e1d7246 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -98,9 +98,6 @@
 extern const char kEnableExtensionActivityLogTesting[];
 extern const char kEnableUnsafeExtensionDebugging[];
 extern const char kEnableHangoutServicesExtensionForTesting[];
-#if BUILDFLAG(IS_CHROMEOS)
-extern const char kEnableLacrosForkZygotesAtLoginScreen[];
-#endif
 extern const char kEnableNaCl[];
 extern const char kEnableNetBenchmarking[];
 extern const char kEnablePotentiallyAnnoyingSecurityFeatures[];
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 0c54750..91f6c41 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -529,6 +529,12 @@
     GetMainFrame()->AutofillClient()->DidCompleteFocusChangeInFrame();
   }
 
+  void BlurElement(const std::string& element_id) {
+    std::string script = "document.getElementById('" + element_id + "').blur()";
+    ExecuteJavaScriptForTests(script.c_str());
+    ChangeFocusToNull(GetMainFrame()->GetDocument());
+  }
+
   void ConfigurePasswordSuggestionFiltering(bool enabled) {
     if (enabled) {
       scoped_feature_list_.InitAndEnableFeature(
@@ -811,6 +817,11 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  void CheckSuggestionsNotShown() {
+    EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
+    base::RunLoop().RunUntilIdle();
+  }
+
   void ExpectFieldPropertiesMasks(
       PasswordFormSourceType expected_type,
       const std::map<std::u16string, FieldPropertiesMask>&
@@ -2104,8 +2115,7 @@
   SimulateOnFillPasswordForm(fill_data_);
   SimulateSuggestionChoice(username_element_);
   EXPECT_CALL(fake_driver_, ShowKeyboardReplacingSurface);
-  EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
-  base::RunLoop().RunUntilIdle();
+  CheckSuggestionsNotShown();
 }
 
 TEST_F(PasswordAutofillAgentTest, KeyboardReplacingSurfaceClosed) {
@@ -2537,6 +2547,11 @@
   EXPECT_CALL(fake_driver_, ShowPasswordSuggestions);
   base::RunLoop().RunUntilIdle();
 
+  histogram_tester_.ExpectUniqueSample(
+      "PasswordManager.SuggestionPopupTriggerSource",
+      static_cast<int>(autofill::AutofillSuggestionTriggerSource::
+                           kFormControlElementClicked),
+      1);
   SimulateSuggestionChoice(username_element_);
   CheckSuggestions(kAliceUsername16, true);
 
@@ -2618,8 +2633,7 @@
   // should NOT show up without suggestions.
   ASSERT_TRUE(SimulateElementClick(kPasswordName));
 
-  EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
-  base::RunLoop().RunUntilIdle();
+  CheckSuggestionsNotShown();
 }
 
 // With butter, passwords fields should always trigger the popup so the user can
@@ -2652,8 +2666,7 @@
 
   ASSERT_TRUE(SimulateElementClick(kPasswordName));
 
-  EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
-  base::RunLoop().RunUntilIdle();
+  CheckSuggestionsNotShown();
 }
 
 // Tests the autosuggestions that are given when the element is clicked.
@@ -3191,8 +3204,7 @@
 
   SimulateSuggestionChoiceOfUsernameAndPassword(
       password_element_, std::u16string(), kAlicePassword16);
-  EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
-  base::RunLoop().RunUntilIdle();
+  CheckSuggestionsNotShown();
 }
 
 // Tests with fill-on-account-select enabled that if the username element is
@@ -3355,8 +3367,7 @@
       .Times(NumShowSuggestionsCalls());
   base::RunLoop().RunUntilIdle();
   testing::Mock::VerifyAndClearExpectations(&fake_pw_client_);
-  EXPECT_CALL(fake_driver_, ShowPasswordSuggestions).Times(0);
-  base::RunLoop().RunUntilIdle();
+  CheckSuggestionsNotShown();
 
   // On destruction the state is updated.
   EXPECT_CALL(fake_pw_client_, GenerationElementLostFocus())
@@ -4987,6 +4998,115 @@
       "PasswordManager.TimesReceivedFillDataForForm", 2, 1);
 }
 
+// Tests that if webauthn form was focused before parsing happened, suggestions
+// are shown to the user once the form is parsed.
+TEST_F(PasswordAutofillAgentTest,
+       ShowSuggestionsOnParsingAutofocusedWebAuthnForm) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kShowWebauthnSuggestionsOnAutofocus);
+  LoadHTML(kWebAutnFieldHTML);
+  UpdateUsernameAndPasswordElements();
+  UpdateRendererIDsInFillData();
+
+#if BUILDFLAG(IS_ANDROID)
+  // A fixture needed to properly test suggestions showing on Android.
+  SimulateClosingKeyboardReplacingSurfaceIfAndroid(kUsernameName);
+  // The method above leaves the field focused, which is not needed for this
+  // test.
+  BlurElement(kUsernameName);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  FocusElement(kUsernameName);
+  CheckSuggestionsNotShown();
+
+  // Simulate receiving credentials for filling from the browser and verify that
+  // suggestions are shown to the user.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+  CheckSuggestions(/*typed_username=*/u"", true);
+}
+
+// Tests that if webauthn form is reparsed, suggestions are not shown
+// automatically.
+TEST_F(PasswordAutofillAgentTest,
+       DoNotShowSuggestionsOnParsingFocusedWebAuthnFormSecondTime) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kShowWebauthnSuggestionsOnAutofocus);
+  LoadHTML(kWebAutnFieldHTML);
+  UpdateUsernameAndPasswordElements();
+  UpdateRendererIDsInFillData();
+
+#if BUILDFLAG(IS_ANDROID)
+  // A fixture needed to properly test suggestions showing on Android.
+  SimulateClosingKeyboardReplacingSurfaceIfAndroid(kUsernameName);
+  // The method above leaves the field focused, which is not needed for this
+  // test.
+  BlurElement(kUsernameName);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  // Simulate receiving fill data from the browser and user focusing the field
+  // to see suggestions.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+  SimulateElementClick(kUsernameName);
+  CheckSuggestions(/*typed_username=*/u"", true);
+
+  // Simulate receiving new fill data from the browser and check that
+  // suggestions are not shown.
+  fill_data_.preferred_login.username_value = u"new_username";
+  SimulateOnFillPasswordForm(fill_data_);
+  CheckSuggestionsNotShown();
+}
+
+// Tests that if webauthn form is not focused, suggestions are not shown to the
+// user once the form is parsed.
+TEST_F(PasswordAutofillAgentTest,
+       DoNotShowSuggestionsOnParsingWebAuthnFormWithoutFocus) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kShowWebauthnSuggestionsOnAutofocus);
+
+  LoadHTML(kWebAutnFieldHTML);
+  UpdateUsernameAndPasswordElements();
+  UpdateRendererIDsInFillData();
+
+#if BUILDFLAG(IS_ANDROID)
+  // A fixture needed to properly test suggestions showing on Android.
+  SimulateClosingKeyboardReplacingSurfaceIfAndroid(kUsernameName);
+  // The method above leaves the field focused, which is not needed for this
+  // test.
+  BlurElement(kUsernameName);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  // Simulate receiving credentials for filling from the browser.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+  CheckSuggestionsNotShown();
+}
+
+// Tests that if non-webauthn form was focused before parsing happened,
+// suggestions are not shown to the user once the form is parsed.
+TEST_F(PasswordAutofillAgentTest,
+       DoNotShowSuggestionsOnParsingAutofocusedPasswordForm) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kShowWebauthnSuggestionsOnAutofocus);
+
+#if BUILDFLAG(IS_ANDROID)
+  // A fixture needed to properly test suggestions showing on Android.
+  SimulateClosingKeyboardReplacingSurfaceIfAndroid(kUsernameName);
+  // The method above leaves the field focused, which is not needed for this
+  // test.
+  BlurElement(kUsernameName);
+#endif  // BUILDFLAG(IS_ANDROID)
+
+  FocusElement(kUsernameName);
+  CheckSuggestionsNotShown();
+
+  // Simulate receiving credentials for filling from the browser.
+  fill_data_.wait_for_username = true;
+  SimulateOnFillPasswordForm(fill_data_);
+  CheckSuggestionsNotShown();
+}
+
 #if BUILDFLAG(IS_ANDROID)
 // If a password field is hidden, the field unlikely has an Enter listener. So,
 // trigger a form submission on the username field.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 39afd0c..43f449b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6634,6 +6634,7 @@
       "../browser/ui/exclusive_access/exclusive_access_permission_manager_unittest.cc",
       "../browser/ui/passwords/password_cross_domain_confirmation_popup_controller_impl_unittest.cc",
       "../browser/ui/plus_addresses/plus_address_creation_controller_desktop_unittest.cc",
+      "../browser/ui/safety_hub/card_data_helper_unittest.cc",
       "../browser/ui/safety_hub/menu_notification_service_unittest.cc",
       "../browser/ui/safety_hub/menu_notification_unittest.cc",
       "../browser/ui/safety_hub/notification_permission_review_service_unittest.cc",
@@ -7769,6 +7770,7 @@
       "../browser/device_notifications/device_test_utils.h",
       "../browser/devtools/aida_client_unittest.cc",
       "../browser/devtools/device/android_device_manager_unittest.cc",
+      "../browser/devtools/devtools_file_helper_unittest.cc",
       "../browser/devtools/devtools_file_system_indexer_unittest.cc",
       "../browser/devtools/devtools_file_watcher_unittest.cc",
       "../browser/devtools/devtools_settings_unittest.cc",
@@ -8044,7 +8046,6 @@
       "../browser/ui/side_panel/side_panel_entry_key_unittest.cc",
       "../browser/ui/singleton_tabs_unittest.cc",
       "../browser/ui/startup/launch_mode_recorder_unittest.cc",
-      "../browser/ui/sync/sync_promo_ui_unittest.cc",
       "../browser/ui/tab_contents/chrome_web_contents_menu_helper_unittest.cc",
       "../browser/ui/tab_contents/core_tab_helper_unittest.cc",
       "../browser/ui/tab_contents/tab_contents_iterator_unittest.cc",
diff --git a/chrome/test/data/predictors/lcpp_font.html b/chrome/test/data/predictors/lcpp_font.html
new file mode 100644
index 0000000..54dd342
--- /dev/null
+++ b/chrome/test/data/predictors/lcpp_font.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<style type="text/css">
+@font-face {
+  font-family: 'Test0';
+  src: url('/predictors/font.ttf') format('truetype');
+}
+
+h1 {
+  font-family: 'Test0', sans-serif;
+}
+</style>
+<body>
+<h1>Test0</h1>
+</body>
+</html>
diff --git a/chromeos/profiles/arm-exp.afdo.newest.txt b/chromeos/profiles/arm-exp.afdo.newest.txt
index e57e3ce9..011e77c2 100644
--- a/chromeos/profiles/arm-exp.afdo.newest.txt
+++ b/chromeos/profiles/arm-exp.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-exp-126-6422.19-1714991850-benchmark-126.0.6473.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-exp-126-6422.19-1714991850-benchmark-126.0.6475.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 1e99728..9a5b863f 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-126-6422.10-1714995387-benchmark-126.0.6474.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-126-6422.10-1714995387-benchmark-126.0.6475.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 133cfe8..ef4bd9f 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-126-6422.19-1714991850-benchmark-126.0.6474.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-126-6422.19-1714991850-benchmark-126.0.6475.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 6b5e020..33a30158 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-126-6422.19-1714988737-benchmark-126.0.6474.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-126-6422.19-1714988737-benchmark-126.0.6475.0-r1-redacted.afdo.xz
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 92e5de4..eface3f2 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -171,10 +171,6 @@
 ]
 
 if (!is_official_build) {
-  # b/315373487 the team working on the fix on CrOS side
-  tast_disabled_tests_from_chrome_all +=
-      [ "ui.ChromeCrashLoopV2.crashpad_mock_consent@jacuzzi" ]
-
   # b/322444880 Should be fixed by the next LKGM uprev.
   tast_disabled_tests_from_chrome_all += [ "terminal.SSH@octopus" ]
 
diff --git a/clank b/clank
index cdaa3f1..dc4fa11 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit cdaa3f151a155c0c0a3e907ea53fc968343d7c98
+Subproject commit dc4fa1175ee236ab899770bb9edddaecd4c482e1
diff --git a/components/affiliations/core/browser/affiliation_utils_unittest.cc b/components/affiliations/core/browser/affiliation_utils_unittest.cc
index 669d81b..f61aa6b 100644
--- a/components/affiliations/core/browser/affiliation_utils_unittest.cc
+++ b/components/affiliations/core/browser/affiliation_utils_unittest.cc
@@ -8,11 +8,9 @@
 #include "base/metrics/field_trial.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "base/test/scoped_feature_list.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/url_constants.h"
-#include "url/url_features.h"
 
 namespace affiliations {
 
@@ -320,6 +318,7 @@
         // different url schemas and non tld parts
         MainDomainTestCase{"http://www.twitter.com", "twitter.com"},
         MainDomainTestCase{"https://mobile.twitter.com", "twitter.com"},
+        MainDomainTestCase{"android://blabla@com.twitter.android"},
         // additional URI components, see
         // https://tools.ietf.org/html/rfc3986#section-3
         MainDomainTestCase{"https://facebook.com/", "facebook.com"},
@@ -361,43 +360,6 @@
         MainDomainTestCase{"http://many.many.many.facebook.com",
                            "facebook.com"}));
 
-// Non-special URLs behavior is affected by the
-// StandardCompliantNonSpecialSchemeURLParsing feature.
-// See https://crbug.com/40063064 for details.
-class AffiliationUtilsMainDomainNonSpecialUrlTest
-    : public ::testing::TestWithParam<bool> {
- public:
-  AffiliationUtilsMainDomainNonSpecialUrlTest()
-      : use_standard_compliant_non_special_scheme_url_parsing_(GetParam()) {
-    if (use_standard_compliant_non_special_scheme_url_parsing_) {
-      scoped_feature_list_.InitAndEnableFeature(
-          url::kStandardCompliantNonSpecialSchemeURLParsing);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          url::kStandardCompliantNonSpecialSchemeURLParsing);
-    }
-  }
-
-  bool use_standard_compliant_non_special_scheme_url_parsing_;
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_P(AffiliationUtilsMainDomainNonSpecialUrlTest, ParamTest) {
-  GURL url("android://blabla@com.twitter.android");
-  std::string top_level_domain = GetExtendedTopLevelDomain(url, {});
-  if (use_standard_compliant_non_special_scheme_url_parsing_) {
-    EXPECT_THAT(top_level_domain, testing::Eq("twitter.android"));
-  } else {
-    EXPECT_THAT(top_level_domain, testing::Eq(""));
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         AffiliationUtilsMainDomainNonSpecialUrlTest,
-                         ::testing::Bool());
-
 struct MergeRelatedGroupsTestCase {
   std::vector<std::vector<std::string>> input_groups;
   std::vector<std::vector<std::string>> output_groups;
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index a97851c..f71f05a 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -498,6 +498,14 @@
                               &FormFieldData::IsTextInputElement);
 }
 
+bool IsWebAuthnForm(const FormData* form_data) {
+  auto has_webauthn_attribute = [](const FormFieldData& field) {
+    return field.parsed_autocomplete() && field.parsed_autocomplete()->webauthn;
+  };
+  return form_data &&
+         base::ranges::any_of(form_data->fields, has_webauthn_attribute);
+}
+
 #if BUILDFLAG(IS_ANDROID)
 // Returns a prediction whether the form that contains `username_element` and
 // `password_element` will be ready for submission after filling these two
@@ -1135,20 +1143,12 @@
   std::unique_ptr<FormData> form_data =
       form.IsNull() ? GetFormDataFromUnownedInputElements()
                     : GetFormDataFromWebForm(form);
-  // TODO(crbug.com/40276126): Use FormFieldData::parsed_autocomplete.
-  auto has_webauthn_attribute = [](const FormFieldData& field) {
-    return field.autocomplete_attribute().find(
-               password_manager::constants::kAutocompleteWebAuthn) !=
-           std::string::npos;
-  };
-  bool is_webauthn_form =
-      form_data &&
-      base::ranges::any_of(form_data->fields, has_webauthn_attribute);
+
   GetPasswordManagerDriver().ShowKeyboardReplacingSurface(
       form_data ? CalculateSubmissionReadiness(*form_data, username_element,
                                                password_element)
                 : mojom::SubmissionReadinessState::kNoInformation,
-      is_webauthn_form);
+      IsWebAuthnForm(form_data.get()));
 
   keyboard_replacing_surface_state_ = KeyboardReplacingSurfaceState::kIsShowing;
   return true;
@@ -1448,6 +1448,7 @@
   // until the user types in a valid username.
   if (form_data.wait_for_username) {
     LogFirstFillingResult(form_data, FillingResult::kWaitForUsername);
+    MaybeTriggerSuggestionsOnFocusedElement(username_element, password_element);
     return;
   }
 
@@ -1719,6 +1720,9 @@
     const std::u16string& typed_username,
     const WebInputElement& user_input,
     AutofillSuggestionTriggerSource trigger_source) {
+  base::UmaHistogramEnumeration("PasswordManager.SuggestionPopupTriggerSource",
+                                trigger_source);
+
   username_query_prefix_ = typed_username;
   auto [form, field] =
       form_util::FindFormAndFieldForFormControlElement(
@@ -2256,4 +2260,28 @@
   }
 }
 
+void PasswordAutofillAgent::MaybeTriggerSuggestionsOnFocusedElement(
+    const WebInputElement& username_element,
+    const WebInputElement& password_element) {
+  WebInputElement focused_element;
+  if (!username_element.IsNull() && username_element.Focused()) {
+    focused_element = username_element;
+  } else if (!password_element.IsNull() && password_element.Focused()) {
+    focused_element = password_element;
+  } else {
+    return;
+  }
+
+  auto form_data =
+      GetFormDataFromWebForm(form_util::GetOwningForm(focused_element));
+  if (IsWebAuthnForm(form_data.get()) &&
+      (times_received_fill_data_[form_data->renderer_id] == 1) &&
+      base::FeatureList::IsEnabled(
+          password_manager::features::kShowWebauthnSuggestionsOnAutofocus)) {
+    autofill_agent_->TriggerSuggestions(
+        autofill::form_util::GetFieldRendererId(focused_element),
+        AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField);
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 7a49d37..43ed6fc 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -496,6 +496,12 @@
   void NotifyPasswordManagerAboutFieldModification(
       const blink::WebInputElement& element);
 
+  // Shows suggestions on the focused element if it was focused before the form
+  // was processed by the password manager.
+  void MaybeTriggerSuggestionsOnFocusedElement(
+      const blink::WebInputElement& username_element,
+      const blink::WebInputElement& password_element);
+
   FieldDataManager& field_data_manager() const {
     return autofill_agent_->field_data_manager();
   }
diff --git a/components/autofill/content/renderer/suggestion_properties.cc b/components/autofill/content/renderer/suggestion_properties.cc
index 0d93b37..b84eda4 100644
--- a/components/autofill/content/renderer/suggestion_properties.cc
+++ b/components/autofill/content/renderer/suggestion_properties.cc
@@ -29,6 +29,7 @@
     case AutofillSuggestionTriggerSource::kComposeDelayedProactiveNudge:
     case AutofillSuggestionTriggerSource::kTextareaFocusedWithoutClick:
     case AutofillSuggestionTriggerSource::kContentEditableClicked:
+    case AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField:
       return true;
     case AutofillSuggestionTriggerSource::kTextFieldDidChange:
       return false;
@@ -62,6 +63,7 @@
         kShowPromptAfterDialogClosedNonManualFallback:
     case AutofillSuggestionTriggerSource::kTextFieldDidChange:
     case AutofillSuggestionTriggerSource::kTextFieldDidReceiveKeyDown:
+    case AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField:
       return false;
     case AutofillSuggestionTriggerSource::kShowCardsFromAccount:
     case mojom::AutofillSuggestionTriggerSource::kPasswordManager:
@@ -89,6 +91,7 @@
         kShowPromptAfterDialogClosedNonManualFallback:
     case AutofillSuggestionTriggerSource::kComposeDialogLostFocus:
     case AutofillSuggestionTriggerSource::kComposeDelayedProactiveNudge:
+    case AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField:
       return false;
     // `kShowCardsFromAccount`, `kPasswordManager`, `kAndroidWebView` and `kiOS`
     // are not used in the renderer code. As such, suggestion properties don't
@@ -107,6 +110,7 @@
     const WebFormControlElement& element) {
   switch (trigger_source) {
     case AutofillSuggestionTriggerSource::kFormControlElementClicked:
+    case AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField:
       // Even if the user has not edited an input element, it may still contain
       // a default value filled by the website. In that case, don't elide
       // suggestions that don't have a common prefix with the default value.
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index ad94db54..a04f1172 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -107,6 +107,7 @@
         kShowPromptAfterDialogClosedNonManualFallback:
     case AutofillSuggestionTriggerSource::kComposeDialogLostFocus:
     case AutofillSuggestionTriggerSource::kComposeDelayedProactiveNudge:
+    case AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField:
       // On Android, no popup exists. Instead, the keyboard accessory is used.
 #if BUILDFLAG(IS_ANDROID)
       return AutofillTriggerSource::kKeyboardAccessory;
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index d4ace79..6d9af73 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -541,6 +541,7 @@
     case AutofillSuggestionTriggerSource::kManualFallbackPlusAddresses:
     case AutofillSuggestionTriggerSource::
         kShowPromptAfterDialogClosedNonManualFallback:
+    case AutofillSuggestionTriggerSource::kPasswordManagerProcessedFocusedField:
       return false;
   }
 }
diff --git a/components/autofill/core/common/mojom/autofill_types.mojom b/components/autofill/core/common/mojom/autofill_types.mojom
index 4af0bd3..8ed52ee8 100644
--- a/components/autofill/core/common/mojom/autofill_types.mojom
+++ b/components/autofill/core/common/mojom/autofill_types.mojom
@@ -555,6 +555,8 @@
 // This then invokes `AutofillManager::OnAskForValuesToFill()` in the browser
 // process. In some cases, suggestions get updated. This happens solely in the
 // browser process.
+// Must stay in sync with AutofillSuggestionTriggerSource in
+// histograms/enums.xml. Do not reorder or remove items.
 enum AutofillSuggestionTriggerSource {
   // Used as a default value and cannot be used to trigger suggestions.
   kUnspecified,
@@ -622,4 +624,7 @@
   // The compose proactive nudge is triggered after a delay by the compose
   // manager.
   kComposeDelayedProactiveNudge,
+  // Password manager renderer received password suggestions from the browser
+  // after the field was focused (likely due to autofocus on the page load).
+  kPasswordManagerProcessedFocusedField,
 };
diff --git a/components/compose_strings.grdp b/components/compose_strings.grdp
index 1b6de622..2694685 100644
--- a/components/compose_strings.grdp
+++ b/components/compose_strings.grdp
@@ -26,13 +26,13 @@
   <message name="IDS_COMPOSE_SUGGESTION_AX_MESSAGE_ON_SHOW" desc="The message read out by a screen reader whtn the Compose nudge is shown.">
     Resume Help me write. Tab and press enter to open
   </message>
-  <message name="IDS_COMPOSE_NEVER_SHOW_ON_THIS_SITE_AGAIN_CHILD_SUGGESTION_TEXT" desc="Text a user sees in one of the Autofill popup child suggestions for the Help me write feature. When selected, the Autofill popup will no longer be displayed on the current domain." translateable="false">
+  <message name="IDS_COMPOSE_NEVER_SHOW_ON_THIS_SITE_AGAIN_CHILD_SUGGESTION_TEXT" desc="Text a user sees in one of the Autofill popup child suggestions for the Help me write feature. When selected, the Autofill popup will no longer be displayed on the current domain.">
     Never show on this site
   </message>
-  <message name="IDS_COMPOSE_DISABLE_HELP_ME_WRITE_CHILD_SUGGESTION_TEXT" desc="Text a user sees in one of the Autofill popup child suggestions for the Help me write feature. When selected, the Help me write feature is disabled for all websites." translateable="false">
+  <message name="IDS_COMPOSE_DISABLE_HELP_ME_WRITE_CHILD_SUGGESTION_TEXT" desc="Text a user sees in one of the Autofill popup child suggestions for the Help me write feature. When selected, the Help me write feature is disabled for all websites.">
     Disable Help me write
   </message>
-  <message name="IDS_COMPOSE_GO_TO_SETTINGS_CHILD_SUGGESTION_TEXT" desc="Text a user sees in one of the Autofill popup child suggestions for the Help me write feature. When selected, the user is navigate to a decicated Help me write feature settings page." translateable="false">
+  <message name="IDS_COMPOSE_GO_TO_SETTINGS_CHILD_SUGGESTION_TEXT" desc="Text a user sees in one of the Autofill popup child suggestions for the Help me write feature. When selected, the user is navigate to a decicated Help me write feature settings page.">
     Go to Settings
   </message>
   <!-- FRE dialog -->
diff --git a/components/compose_strings_grdp/IDS_COMPOSE_DISABLE_HELP_ME_WRITE_CHILD_SUGGESTION_TEXT.png.sha1 b/components/compose_strings_grdp/IDS_COMPOSE_DISABLE_HELP_ME_WRITE_CHILD_SUGGESTION_TEXT.png.sha1
index b41ee14..00f5ec7 100644
--- a/components/compose_strings_grdp/IDS_COMPOSE_DISABLE_HELP_ME_WRITE_CHILD_SUGGESTION_TEXT.png.sha1
+++ b/components/compose_strings_grdp/IDS_COMPOSE_DISABLE_HELP_ME_WRITE_CHILD_SUGGESTION_TEXT.png.sha1
@@ -1 +1 @@
-facd4e9545e71803bd26d4c26bfba65858b66bf9
\ No newline at end of file
+e08eed2bac23f1a43e6c2f9e063ce5ef1395db6c
\ No newline at end of file
diff --git a/components/compose_strings_grdp/IDS_COMPOSE_GO_TO_SETTINGS_CHILD_SUGGESTION_TEXT.png.sha1 b/components/compose_strings_grdp/IDS_COMPOSE_GO_TO_SETTINGS_CHILD_SUGGESTION_TEXT.png.sha1
index 7b4fd7e..00f5ec7 100644
--- a/components/compose_strings_grdp/IDS_COMPOSE_GO_TO_SETTINGS_CHILD_SUGGESTION_TEXT.png.sha1
+++ b/components/compose_strings_grdp/IDS_COMPOSE_GO_TO_SETTINGS_CHILD_SUGGESTION_TEXT.png.sha1
@@ -1 +1 @@
-3e21080c59dc479d5b408fb880645ec3d095a786
\ No newline at end of file
+e08eed2bac23f1a43e6c2f9e063ce5ef1395db6c
\ No newline at end of file
diff --git a/components/compose_strings_grdp/IDS_COMPOSE_NEVER_SHOW_ON_THIS_SITE_AGAIN_CHILD_SUGGESTION_TEXT.png.sha1 b/components/compose_strings_grdp/IDS_COMPOSE_NEVER_SHOW_ON_THIS_SITE_AGAIN_CHILD_SUGGESTION_TEXT.png.sha1
index b039248..00f5ec7 100644
--- a/components/compose_strings_grdp/IDS_COMPOSE_NEVER_SHOW_ON_THIS_SITE_AGAIN_CHILD_SUGGESTION_TEXT.png.sha1
+++ b/components/compose_strings_grdp/IDS_COMPOSE_NEVER_SHOW_ON_THIS_SITE_AGAIN_CHILD_SUGGESTION_TEXT.png.sha1
@@ -1 +1 @@
-7a71fa36645be04df6a0f85ff20d21369e426ece
\ No newline at end of file
+e08eed2bac23f1a43e6c2f9e063ce5ef1395db6c
\ No newline at end of file
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc
index 30bdc3c..5d066ff 100644
--- a/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -218,6 +218,7 @@
 // is an affected histogram under the "ContentSetting" suffix.
 bool ShouldCollectFineGrainedExceptionHistograms(ContentSettingsType type) {
   switch (type) {
+    case ContentSettingsType::TRACKING_PROTECTION:
     case ContentSettingsType::COOKIES:
     case ContentSettingsType::POPUPS:
     case ContentSettingsType::ADS:
diff --git a/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java b/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java
index a0f37cb5..8839e05 100644
--- a/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java
+++ b/components/cronet/android/java/src/org/chromium/net/telemetry/CronetLoggerImpl.java
@@ -49,7 +49,7 @@
                 info.cronetInitializationRef,
                 convertToProtoCronetEngineBuilderInitializedAuthor(info.author),
                 info.engineBuilderCreatedLatencyMillis,
-                convertToProtoCronetSource(info.source),
+                convertToProtoCronetEngineBuilderInitializedSource(info.source),
                 OptionalBoolean.fromBoolean(info.creationSuccessful).getValue(),
                 info.apiVersion.getMajorVersion(),
                 info.apiVersion.getMinorVersion(),
@@ -127,7 +127,7 @@
                     version.getMinorVersion(),
                     version.getBuildVersion(),
                     version.getPatchVersion(),
-                    convertToProtoCronetSource(source),
+                    convertToProtoCronetEngineCreatedSource(source),
                     builder.isBrotliEnabled(),
                     builder.isHttp2Enabled(),
                     convertToProtoHttpCacheMode(builder.getHttpCacheMode()),
@@ -241,7 +241,27 @@
         }
     }
 
-    private static int convertToProtoCronetSource(CronetSource source) {
+    private static int convertToProtoCronetEngineBuilderInitializedSource(CronetSource source) {
+        switch (source) {
+            case CRONET_SOURCE_STATICALLY_LINKED:
+                return CronetStatsLog
+                        .CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_EMBEDDED_NATIVE;
+            case CRONET_SOURCE_PLAY_SERVICES:
+                return CronetStatsLog
+                        .CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_GMSCORE_NATIVE;
+            case CRONET_SOURCE_FALLBACK:
+                return CronetStatsLog
+                        .CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_EMBEDDED_JAVA;
+            case CRONET_SOURCE_PLATFORM:
+                return CronetStatsLog
+                        .CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_HTTPENGINE_NATIVE;
+            default:
+                return CronetStatsLog
+                        .CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_UNSPECIFIED;
+        }
+    }
+
+    private static int convertToProtoCronetEngineCreatedSource(CronetSource source) {
         switch (source) {
             case CRONET_SOURCE_STATICALLY_LINKED:
                 return CronetStatsLog
diff --git a/components/cronet/android/java/src/org/chromium/net/telemetry/CronetStatsLog.java b/components/cronet/android/java/src/org/chromium/net/telemetry/CronetStatsLog.java
index bf094b5..4d7f0f6 100644
--- a/components/cronet/android/java/src/org/chromium/net/telemetry/CronetStatsLog.java
+++ b/components/cronet/android/java/src/org/chromium/net/telemetry/CronetStatsLog.java
@@ -362,10 +362,10 @@
             0;
     public static final int
             CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_EMBEDDED_NATIVE = 1;
-    public static final int CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_EMBEDDED_JAVA =
-            2;
     public static final int
-            CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_GMSCORE_NATIVE = 3;
+            CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_GMSCORE_NATIVE = 2;
+    public static final int CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_EMBEDDED_JAVA =
+            3;
     public static final int
             CRONET_ENGINE_BUILDER_INITIALIZED__SOURCE__CRONET_SOURCE_HTTPENGINE_NATIVE = 4;
 
diff --git a/components/gwp_asan/client/thread_local_state.h b/components/gwp_asan/client/thread_local_state.h
index f29961293..707b582 100644
--- a/components/gwp_asan/client/thread_local_state.h
+++ b/components/gwp_asan/client/thread_local_state.h
@@ -14,7 +14,7 @@
 // on a new thread will cause an allocation, leading to infinite recursion.
 // Also, `thread_local` goes through `emutls` on Android, which is slower than
 // `pthread_getspecific`.
-#define USE_PTHREAD_TLS
+#define THREAD_LOCAL_STATE_USES_PARTITION_ALLOC_TLS
 #elif BUILDFLAG(IS_POSIX) && defined(COMPONENT_BUILD)
 // On POSIX platforms when built as component build, use of a `thread_local`
 // variable may or may not cause a call to `free()` depending on an
@@ -28,11 +28,11 @@
 // Fortunately, statically-linked executables (non-component builds) do not hit
 // the problem because they use the "local-exec" model of TLS (i.e. do not call
 // any library function).
-#define USE_PTHREAD_TLS
+#define THREAD_LOCAL_STATE_USES_PARTITION_ALLOC_TLS
 #endif
 
-#if defined(USE_PTHREAD_TLS)
-#include <pthread.h>
+#if defined(THREAD_LOCAL_STATE_USES_PARTITION_ALLOC_TLS)
+#include "partition_alloc/partition_tls.h"
 #endif
 
 namespace gwp_asan::internal {
@@ -43,14 +43,14 @@
 class ThreadLocalState {
  protected:
   static void InitIfNeeded() {
-#if defined(USE_PTHREAD_TLS)
+#if defined(THREAD_LOCAL_STATE_USES_PARTITION_ALLOC_TLS)
     if (!tls_key_) {
-      pthread_key_create(&tls_key_, nullptr);
+      partition_alloc::internal::PartitionTlsCreate(&tls_key_, nullptr);
     }
 #endif
   }
 
-#if !defined(USE_PTHREAD_TLS)
+#if !defined(THREAD_LOCAL_STATE_USES_PARTITION_ALLOC_TLS)
   ALWAYS_INLINE static uintptr_t GetState() { return state_; }
   ALWAYS_INLINE static void SetState(uintptr_t value) { state_ = value; }
 
@@ -60,25 +60,27 @@
 #else
   ALWAYS_INLINE static uintptr_t GetState() {
     DCHECK(tls_key_);
-    return reinterpret_cast<uintptr_t>(pthread_getspecific(tls_key_));
+    return reinterpret_cast<uintptr_t>(
+        partition_alloc::internal::PartitionTlsGet(tls_key_));
   }
 
   ALWAYS_INLINE static void SetState(uintptr_t value) {
     DCHECK(tls_key_);
-    pthread_setspecific(tls_key_, reinterpret_cast<void*>(value));
+    partition_alloc::internal::PartitionTlsSet(tls_key_,
+                                               reinterpret_cast<void*>(value));
   }
 
  private:
-  static pthread_key_t tls_key_;
+  static partition_alloc::internal::PartitionTlsKey tls_key_;
 #endif
 };
 
-#if !defined(USE_PTHREAD_TLS)
+#if !defined(THREAD_LOCAL_STATE_USES_PARTITION_ALLOC_TLS)
 template <typename T>
 thread_local uintptr_t ThreadLocalState<T>::state_ = 0;
 #else
 template <typename T>
-pthread_key_t ThreadLocalState<T>::tls_key_ = 0;
+partition_alloc::internal::PartitionTlsKey ThreadLocalState<T>::tls_key_ = 0;
 #endif
 
 }  // namespace gwp_asan::internal
diff --git a/components/password_manager/core/browser/password_store/fake_password_store_backend.cc b/components/password_manager/core/browser/password_store/fake_password_store_backend.cc
index 2927bef9..88555f4 100644
--- a/components/password_manager/core/browser/password_store/fake_password_store_backend.cc
+++ b/components/password_manager/core/browser/password_store/fake_password_store_backend.cc
@@ -97,6 +97,7 @@
 }
 
 void FakePasswordStoreBackend::Shutdown(base::OnceClosure shutdown_completed) {
+  weak_ptr_factory_.InvalidateWeakPtrs();
   match_helper_ = nullptr;
   // Ensure that the shutdown is only completed after any other backend task on
   // the same task runner concluded. The backend always uses the same runner.
diff --git a/components/password_manager/core/browser/password_store/login_database.cc b/components/password_manager/core/browser/password_store/login_database.cc
index ed0466d..e6ab7b8 100644
--- a/components/password_manager/core/browser/password_store/login_database.cc
+++ b/components/password_manager/core/browser/password_store/login_database.cc
@@ -1003,14 +1003,10 @@
   std::string keychain_identifier;
 };
 
-LoginDatabase::LoginDatabase(
-    const base::FilePath& db_path,
-    IsAccountStore is_account_store,
-    const base::RepeatingCallback<void(LoginDatabaseEmptynessState)>&
-        is_empty_cb)
+LoginDatabase::LoginDatabase(const base::FilePath& db_path,
+                             IsAccountStore is_account_store)
     : db_path_(db_path),
       is_account_store_(is_account_store),
-      is_empty_cb_(is_empty_cb),
       // Set options for a small, private database (based on WebDatabase).
       db_({.page_size = 2048, .cache_size = 32}) {}
 
@@ -1919,6 +1915,10 @@
   return db_.CommitTransaction();
 }
 
+void LoginDatabase::SetIsEmptyCb(IsEmptyCallback is_empty_cb) {
+  is_empty_cb_ = std::move(is_empty_cb);
+}
+
 LoginDatabase::SyncMetadataStore::SyncMetadataStore(sql::Database* db)
     : db_(db) {
   CHECK(db);
diff --git a/components/password_manager/core/browser/password_store/login_database.h b/components/password_manager/core/browser/password_store/login_database.h
index d865af4..3e27a7a 100644
--- a/components/password_manager/core/browser/password_store/login_database.h
+++ b/components/password_manager/core/browser/password_store/login_database.h
@@ -61,16 +61,10 @@
                            const LoginDatabaseEmptynessState&) = default;
   };
 
-  // If a non-null `is_empty_cb` is passed, it's called to signal whether the
-  // database is empty (i.e. without any logins *or* blocklists) and whether
-  // there are any autofillable logins. The call happens when initializing the
-  // database and when adding/removing entries, regardless of success.
-  LoginDatabase(
-      const base::FilePath& db_path,
-      IsAccountStore is_account_store,
-      const base::RepeatingCallback<void(LoginDatabaseEmptynessState)>&
-          is_empty_cb = base::NullCallback());
+  using IsEmptyCallback =
+      base::RepeatingCallback<void(LoginDatabaseEmptynessState)>;
 
+  LoginDatabase(const base::FilePath& db_path, IsAccountStore is_account_store);
   LoginDatabase(const LoginDatabase&) = delete;
   LoginDatabase& operator=(const LoginDatabase&) = delete;
 
@@ -195,6 +189,12 @@
   void RollbackTransaction();
   bool CommitTransaction();
 
+  // `is_empty_cb`is called to signal whether the database is empty (i.e.
+  // without any logins *or* blocklists) and whether there are any autofillable
+  // logins. The call happens when initializing the database and when
+  // adding/removing entries, regardless of success.
+  void SetIsEmptyCb(IsEmptyCallback is_empty_cb);
+
   StatisticsTable& stats_table() { return stats_table_; }
   InsecureCredentialsTable& insecure_credentials_table() {
     return insecure_credentials_table_;
@@ -371,7 +371,7 @@
 
   const base::FilePath db_path_;
   const IsAccountStore is_account_store_;
-  const base::RepeatingCallback<void(LoginDatabaseEmptynessState)> is_empty_cb_;
+  IsEmptyCallback is_empty_cb_ = base::NullCallback();
 
   mutable sql::Database db_;
   sql::MetaTable meta_table_;
diff --git a/components/password_manager/core/browser/password_store/login_database_unittest.cc b/components/password_manager/core/browser/password_store/login_database_unittest.cc
index 9ef2dc2f..7b5f92ec 100644
--- a/components/password_manager/core/browser/password_store/login_database_unittest.cc
+++ b/components/password_manager/core/browser/password_store/login_database_unittest.cc
@@ -281,8 +281,8 @@
     file_ = temp_dir_.GetPath().AppendASCII("TestMetadataStoreMacDatabase");
     OSCryptMocker::SetUp();
 
-    db_ = std::make_unique<LoginDatabase>(file_, IsAccountStore(false),
-                                          is_empty_cb_.Get());
+    db_ = std::make_unique<LoginDatabase>(file_, IsAccountStore(false));
+    db_->SetIsEmptyCb(is_empty_cb_.Get());
     ASSERT_TRUE(db_->Init());
   }
 
@@ -292,9 +292,7 @@
 
   base::ScopedTempDir temp_dir_;
   base::FilePath file_;
-  NiceMock<base::MockCallback<base::RepeatingCallback<void(
-      LoginDatabase::LoginDatabaseEmptynessState)>>>
-      is_empty_cb_;
+  NiceMock<base::MockCallback<LoginDatabase::IsEmptyCallback>> is_empty_cb_;
   std::unique_ptr<LoginDatabase> db_;
   // A full TaskEnvironment is required instead of only
   // SingleThreadTaskEnvironment because on iOS,
@@ -2236,10 +2234,9 @@
       AddDummyLogin("foo3", GURL("https://foo3.com/"),
                     /*should_be_corrupted=*/true, /*blocklisted=*/true);
 
-  NiceMock<base::MockCallback<base::RepeatingCallback<void(
-      LoginDatabase::LoginDatabaseEmptynessState)>>>
-      is_empty_cb;
-  LoginDatabase db(database_path(), IsAccountStore(false), is_empty_cb.Get());
+  LoginDatabase db(database_path(), IsAccountStore(false));
+  NiceMock<base::MockCallback<LoginDatabase::IsEmptyCallback>> is_empty_cb;
+  db.SetIsEmptyCb(is_empty_cb.Get());
   base::HistogramTester histogram_tester;
   ASSERT_TRUE(db.Init());
 
@@ -2806,11 +2803,10 @@
 }
 
 TEST_F(LoginDatabaseTest, IsEmptyCb_InitEmpty) {
-  NiceMock<base::MockCallback<base::RepeatingCallback<void(
-      LoginDatabase::LoginDatabaseEmptynessState)>>>
-      is_empty_cb;
   LoginDatabase db(temp_dir_.GetPath().AppendASCII("DbDirectory"),
-                   IsAccountStore(false), is_empty_cb.Get());
+                   IsAccountStore(false));
+  NiceMock<base::MockCallback<LoginDatabase::IsEmptyCallback>> is_empty_cb;
+  db.SetIsEmptyCb(is_empty_cb.Get());
   EXPECT_CALL(is_empty_cb, Run(LoginDatabase::LoginDatabaseEmptynessState{
                                .no_login_found = true,
                                .autofillable_credentials_exist = false}));
@@ -2821,18 +2817,16 @@
   base::FilePath directory = temp_dir_.GetPath().AppendASCII("DbDirectory");
   {
     // Simulate the DB being populated in a previous startup.
-    auto db = std::make_unique<LoginDatabase>(directory, IsAccountStore(false),
-                                              base::NullCallback());
+    auto db = std::make_unique<LoginDatabase>(directory, IsAccountStore(false));
     db->Init();
     std::ignore =
         db->AddLogin(GenerateExamplePasswordForm(), /*error=*/nullptr);
     db.reset();
   }
 
-  NiceMock<base::MockCallback<base::RepeatingCallback<void(
-      LoginDatabase::LoginDatabaseEmptynessState)>>>
-      is_empty_cb;
-  LoginDatabase db(directory, IsAccountStore(false), is_empty_cb.Get());
+  LoginDatabase db(directory, IsAccountStore(false));
+  NiceMock<base::MockCallback<LoginDatabase::IsEmptyCallback>> is_empty_cb;
+  db.SetIsEmptyCb(is_empty_cb.Get());
   EXPECT_CALL(is_empty_cb, Run(LoginDatabase::LoginDatabaseEmptynessState{
                                .no_login_found = false,
                                .autofillable_credentials_exist = true}));
diff --git a/components/password_manager/core/browser/password_store/password_store_backend.h b/components/password_manager/core/browser/password_store/password_store_backend.h
index f23d188..ff9ee99 100644
--- a/components/password_manager/core/browser/password_store/password_store_backend.h
+++ b/components/password_manager/core/browser/password_store/password_store_backend.h
@@ -61,6 +61,9 @@
 
   // Shuts down the store asynchronously. The callback is run on the main thread
   // after the shutdown has concluded and it is safe to delete the backend.
+  // Please invalidate the weak pointers whenever defining this method.
+  // Otherwise, some prefs might be set after the backend is shut down, leading
+  // to a crash.
   virtual void Shutdown(base::OnceClosure shutdown_completed) = 0;
 
   // Necessary condition to offer saving passwords.
diff --git a/components/password_manager/core/browser/password_store/password_store_built_in_backend.cc b/components/password_manager/core/browser/password_store/password_store_built_in_backend.cc
index 6764fd8..2b82a837 100644
--- a/components/password_manager/core/browser/password_store/password_store_built_in_backend.cc
+++ b/components/password_manager/core/browser/password_store/password_store_built_in_backend.cc
@@ -81,6 +81,7 @@
 void PasswordStoreBuiltInBackend::Shutdown(
     base::OnceClosure shutdown_completed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  weak_ptr_factory_.InvalidateWeakPtrs();
   affiliated_match_helper_ = nullptr;
   if (helper_) {
     background_task_runner_->DeleteSoon(FROM_HERE, std::move(helper_));
diff --git a/components/password_manager/core/browser/password_store_factory_util.cc b/components/password_manager/core/browser/password_store_factory_util.cc
index ea80c13..ccc68d8 100644
--- a/components/password_manager/core/browser/password_store_factory_util.cc
+++ b/components/password_manager/core/browser/password_store_factory_util.cc
@@ -16,6 +16,7 @@
 #include "components/password_manager/core/browser/old_google_credentials_cleaner.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
 #include "components/password_manager/core/browser/password_store/login_database.h"
+#include "components/password_manager/core/browser/password_store/password_store_backend.h"
 #include "components/password_manager/core/browser/password_store/password_store_interface.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_service.h"
@@ -23,13 +24,11 @@
 namespace password_manager {
 
 std::unique_ptr<LoginDatabase> CreateLoginDatabaseForProfileStorage(
-    const base::FilePath& db_directory,
-    const base::RepeatingCallback<
-        void(LoginDatabase::LoginDatabaseEmptynessState)>& is_empty_cb) {
+    const base::FilePath& db_directory) {
   base::FilePath login_db_file_path =
       db_directory.Append(kLoginDataForProfileFileName);
   return std::make_unique<LoginDatabase>(login_db_file_path,
-                                         IsAccountStore(false), is_empty_cb);
+                                         IsAccountStore(false));
 }
 
 std::unique_ptr<LoginDatabase> CreateLoginDatabaseForAccountStorage(
@@ -79,16 +78,30 @@
 }
 
 void SetEmptyStorePref(PrefService* prefs,
+                       base::WeakPtr<PasswordStoreBackend> backend,
                        const std::string& pref,
                        LoginDatabase::LoginDatabaseEmptynessState value) {
-  prefs->SetBoolean(pref, value.no_login_found);
+  // The prefs should not be set after `PasswordStoreBackend::Shutdown()` was
+  // called, because it will lead to a use-after-free failure. When any backend
+  // is shut down, the weak pointers are invalidated.
+  if (backend) {
+    CHECK(prefs);
+    prefs->SetBoolean(pref, value.no_login_found);
+  }
 }
 
 void SetAutofillableCredentialsStorePref(
     PrefService* prefs,
+    base::WeakPtr<PasswordStoreBackend> backend,
     const std::string& pref,
     LoginDatabase::LoginDatabaseEmptynessState value) {
-  prefs->SetBoolean(pref, value.autofillable_credentials_exist);
+  // The prefs should not be set after `PasswordStoreBackend::Shutdown()` was
+  // called, because it will lead to a use-after-free failure. When any backend
+  // is shut down, the weak pointers are invalidated.
+  if (backend) {
+    CHECK(prefs);
+    prefs->SetBoolean(pref, value.autofillable_credentials_exist);
+  }
 }
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store_factory_util.h b/components/password_manager/core/browser/password_store_factory_util.h
index 37f6c60..7e28294 100644
--- a/components/password_manager/core/browser/password_store_factory_util.h
+++ b/components/password_manager/core/browser/password_store_factory_util.h
@@ -9,6 +9,7 @@
 
 #include "base/files/file_path.h"
 #include "base/functional/callback.h"
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "components/password_manager/core/browser/password_store/login_database.h"
 
@@ -21,19 +22,14 @@
 namespace password_manager {
 
 class CredentialsCleanerRunner;
+class PasswordStoreBackend;
 class PasswordStoreInterface;
 
 // Creates a LoginDatabase. Looks in |db_directory| for the database file.
 // Does not call LoginDatabase::Init() -- to avoid UI jank, that needs to be
 // called by PasswordStore::Init() on the background thread.
-// If a non-null `is_empty_cb` is passed, it's called to signal whether the
-// database is empty (i.e. without any logins *or* blocklists) and whether there
-// are any autofillable logins. The call happens when initializing the database
-// and when adding/removing entries, regardless of success.
 std::unique_ptr<LoginDatabase> CreateLoginDatabaseForProfileStorage(
-    const base::FilePath& db_directory,
-    const base::RepeatingCallback<
-        void(LoginDatabase::LoginDatabaseEmptynessState)>& is_empty_cb);
+    const base::FilePath& db_directory);
 std::unique_ptr<LoginDatabase> CreateLoginDatabaseForAccountStorage(
     const base::FilePath& db_directory);
 
@@ -59,6 +55,7 @@
 
 // Extracts `value.no_login_found` and uses it as a value for the pref.
 void SetEmptyStorePref(PrefService* prefs,
+                       base::WeakPtr<PasswordStoreBackend> backend,
                        const std::string& pref,
                        LoginDatabase::LoginDatabaseEmptynessState value);
 
@@ -66,6 +63,7 @@
 // the pref.
 void SetAutofillableCredentialsStorePref(
     PrefService* prefs,
+    base::WeakPtr<PasswordStoreBackend> backend,
     const std::string& pref,
     LoginDatabase::LoginDatabaseEmptynessState value);
 
diff --git a/components/permissions/permission_uma_util.cc b/components/permissions/permission_uma_util.cc
index 9d616e7..028a47c 100644
--- a/components/permissions/permission_uma_util.cc
+++ b/components/permissions/permission_uma_util.cc
@@ -547,6 +547,24 @@
   builder.Record(ukm::UkmRecorder::Get());
 }
 
+void RecordElementAnchoredPermissionPromptActionUkm(
+    RequestTypeForUma permission,
+    RequestTypeForUma screen_permission,
+    ElementAnchoredBubbleAction action,
+    ElementAnchoredBubbleVariant variant,
+    int screen_counter,
+    std::optional<ukm::SourceId> source_id) {
+  ukm::builders::Permissions_EmbeddedPromptAction builder(source_id.value());
+
+  builder.SetVariant(static_cast<int64_t>(variant))
+      .SetAction(static_cast<int64_t>(action))
+      .SetPermissionType(static_cast<int64_t>(permission))
+      .SetScreenPermissionType(static_cast<int64_t>(screen_permission))
+      .SetPreviousScreens(std::min(kPriorCountCap, screen_counter));
+
+  builder.Record(ukm::UkmRecorder::Get());
+}
+
 // |full_version| represented in the format `YYYY.M.D.m`, where m is the
 // minute-of-day. Return int represented in the format `YYYYMMDD`.
 // CrowdDeny versions published before 2020 will be reported as 1.
@@ -2007,4 +2025,28 @@
   return std::nullopt;
 }
 
+// static
+void PermissionUmaUtil::RecordElementAnchoredPermissionPromptAction(
+    const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>& requests,
+    const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>&
+        screen_requests,
+    ElementAnchoredBubbleAction action,
+    ElementAnchoredBubbleVariant variant,
+    int screen_counter,
+    const GURL& requesting_origin,
+    content::WebContents* web_contents,
+    content::BrowserContext* browser_context) {
+  CHECK(requests.size());
+  CHECK(screen_requests.size());
+  auto first_request_type =
+      RequestTypeToContentSettingsType(requests[0]->request_type());
+  PermissionsClient::Get()->GetUkmSourceId(
+      first_request_type.value(), browser_context, web_contents,
+      requesting_origin,
+      base::BindOnce(&RecordElementAnchoredPermissionPromptActionUkm,
+                     GetUmaValueForRequests(requests),
+                     GetUmaValueForRequests(screen_requests), action, variant,
+                     screen_counter));
+}
+
 }  // namespace permissions
diff --git a/components/permissions/permission_uma_util.h b/components/permissions/permission_uma_util.h
index 8f536f8..488f978 100644
--- a/components/permissions/permission_uma_util.h
+++ b/components/permissions/permission_uma_util.h
@@ -496,6 +496,34 @@
   kMaxValue = REMEMBER_CHECKBOX_TOGGLED,
 };
 
+// This enum backs up the 'ElementAnchoredBubbleAction' histograms enum.
+enum class ElementAnchoredBubbleAction {
+  // Site level permission was granted.
+  kGranted = 0,
+
+  // Site level permission was granted once.
+  kGrantedOnce = 1,
+
+  // Site level permission was denied.
+  kDenied = 2,
+
+  // Acknowledging the prompt informing the user a permission is managed by
+  // admin.
+  kOk = 3,
+
+  // The prompt was dismissed by the user clicking on the [X] button.
+  kDismissedXButton = 4,
+
+  // The prompt was dismissed by the user clicking outside of the prompt area.
+  kDismissedScrim = 5,
+
+  // User clicked "Open system settings" to manage OS level permission prompts.
+  kSystemSettings = 6,
+
+  // Always keep at the end.
+  kMaxValue = kSystemSettings,
+};
+
 // The reason the permission action `PermissionAction::IGNORED` was triggered.
 enum class PermissionIgnoredReason {
   // Ignore was triggered due to closure of the browser window
@@ -802,6 +830,22 @@
       base::Time current_time,
       HostContentSettingsMap* hcsm);
 
+  // Records UKM metrics for ContentSettingsTypes that have user facing
+  // permission prompts triggered by the user clicking on the Embedded
+  // Permission Element. The passed in `permission` must be such that
+  // PermissionUtil::IsPermission(permission) returns true.
+  static void RecordElementAnchoredPermissionPromptAction(
+      const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>&
+          requests,
+      const std::vector<raw_ptr<PermissionRequest, VectorExperimental>>&
+          screen_requests,
+      ElementAnchoredBubbleAction action,
+      ElementAnchoredBubbleVariant variant,
+      int screen_counter,
+      const GURL& requesting_origin,
+      content::WebContents* web_contents,
+      content::BrowserContext* browser_context);
+
   // A scoped class that will check the current resolved content setting on
   // construction and report a revocation metric accordingly if the revocation
   // condition is met (from ALLOW to something else).
diff --git a/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.cc b/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.cc
index 77a70fa6..0aefca7f 100644
--- a/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.cc
+++ b/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.cc
@@ -29,16 +29,21 @@
     default;
 
 void PlusAddressAffiliationMatchHelper::GetAffiliatedPlusProfiles(
-    const PlusProfile& plus_profile,
+    const FacetURI& facet,
     AffiliatedPlusProfilesCallback result_callback) {
+  DCHECK(facet.IsValidWebFacetURI());
+
   if (!base::FeatureList::IsEnabled(
           plus_addresses::features::kPlusAddressAffiliations)) {
-    std::move(result_callback).Run({plus_profile});
+    std::vector<PlusProfile> results;
+    if (std::optional<PlusProfile> profile =
+            plus_address_service_->GetPlusProfile(facet)) {
+      results.push_back(std::move(*profile));
+    }
+    std::move(result_callback).Run(std::move(results));
     return;
   }
 
-  const FacetURI& facet = absl::get<FacetURI>(plus_profile.facet);
-  DCHECK(facet.IsValidWebFacetURI());
   // The barrier is used to collect affiliated plus addresses from multiple
   // sources (i.e. grouped affiliations, PSL matches), combine and return them.
   const int kCallsNumber = 2;
diff --git a/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.h b/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.h
index 29ec45d1..9f76659 100644
--- a/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.h
+++ b/components/plus_addresses/affiliations/plus_address_affiliation_match_helper.h
@@ -47,12 +47,10 @@
       const PlusAddressAffiliationMatchHelper&) = delete;
   virtual ~PlusAddressAffiliationMatchHelper();
 
-  // Returns the complete list of plus profiles, including the specified
-  // `plus_profile`, that belong to the same affiliation group based on their
-  // facet value. Only profiles with valid web facets must be passed-in.
-  // TODO(b/324553908): Update to query by facet.
+  // Returns the complete list of plus profiles that are affiliated with `facet`
+  // based on their facet value. Only valid web facets must be passed-in.
   void GetAffiliatedPlusProfiles(
-      const PlusProfile& plus_profile,
+      const affiliations::FacetURI& facet,
       AffiliatedPlusProfilesCallback result_callback);
 
   // Requests and caches the list of PSL extensions.
diff --git a/components/plus_addresses/affiliations/plus_address_affiliation_match_helper_unittest.cc b/components/plus_addresses/affiliations/plus_address_affiliation_match_helper_unittest.cc
index 584c8e63..7d7272d 100644
--- a/components/plus_addresses/affiliations/plus_address_affiliation_match_helper_unittest.cc
+++ b/components/plus_addresses/affiliations/plus_address_affiliation_match_helper_unittest.cc
@@ -46,7 +46,7 @@
   }
 
   testing::AssertionResult ExpectMatchHelperToReturnProfiles(
-      const PlusProfile& requested_profile,
+      const FacetURI& requested_facet,
       const std::vector<PlusProfile>& expected_profiles) {
     base::MockCallback<
         PlusAddressAffiliationMatchHelper::AffiliatedPlusProfilesCallback>
@@ -54,8 +54,7 @@
     int calls = 0;
     ON_CALL(callback, Run(UnorderedElementsAreArray(expected_profiles)))
         .WillByDefault([&] { ++calls; });
-    match_helper()->GetAffiliatedPlusProfiles(requested_profile,
-                                              callback.Get());
+    match_helper()->GetAffiliatedPlusProfiles(requested_facet, callback.Get());
     return calls == 1
                ? testing::AssertionSuccess()
                : (testing::AssertionFailure() << "Error fetching profiles.");
@@ -138,6 +137,21 @@
   std::move(first_callback).Run(pls_extensions);
 }
 
+// Verifies that the list of returned values is empty if no profiles are stored.
+TEST_F(PlusAddressAffiliationMatchHelperTest, EmptyResult) {
+  FacetURI facet = FacetURI::FromCanonicalSpec("https://example.com");
+
+  EXPECT_CALL(*mock_affiliation_service(), GetPSLExtensions)
+      .WillOnce(RunOnceCallback<0>(std::vector<std::string>()));
+  affiliations::GroupedFacets group;
+  group.facets.emplace_back(facet);
+  EXPECT_CALL(*mock_affiliation_service(), GetGroupingInfo)
+      .WillOnce(
+          RunOnceCallback<1>(std::vector<affiliations::GroupedFacets>{group}));
+
+  EXPECT_TRUE(ExpectMatchHelperToReturnProfiles(facet, {}));
+}
+
 // Verifies that exact and PSL matches (respecting the PSL extensions list) are
 // returned.
 TEST_F(PlusAddressAffiliationMatchHelperTest, ExactAndPslMatchesTest) {
@@ -166,8 +180,8 @@
 
   // `profile3` is not a PSL match because it is an Android facet.
   // `profile4` is not a match due to the PSL extension list exception.
-  EXPECT_TRUE(
-      ExpectMatchHelperToReturnProfiles(profile1, {profile1, profile2}));
+  EXPECT_TRUE(ExpectMatchHelperToReturnProfiles(
+      absl::get<FacetURI>(profile1.facet), {profile1, profile2}));
 }
 
 // Verifies that group affiliation matches are returned.
@@ -198,7 +212,8 @@
 
   // `profile2` was not a PSL match nor group affiliated.
   EXPECT_TRUE(ExpectMatchHelperToReturnProfiles(
-      profile1, {profile1, android_profile, group_profile}));
+      absl::get<FacetURI>(profile1.facet),
+      {profile1, android_profile, group_profile}));
 }
 
 // Verifies that elements in both group and PSL matches matches are returned
@@ -229,8 +244,9 @@
 
   // `psl_match` is part of both group and PSL matches but must be returned only
   // once.
-  EXPECT_TRUE(ExpectMatchHelperToReturnProfiles(
-      profile1, {profile1, psl_match, group_profile}));
+  EXPECT_TRUE(
+      ExpectMatchHelperToReturnProfiles(absl::get<FacetURI>(profile1.facet),
+                                        {profile1, psl_match, group_profile}));
 }
 
 }  // namespace plus_addresses
diff --git a/components/resources/default_100_percent/autofill/OWNERS b/components/resources/default_100_percent/autofill/OWNERS
new file mode 100644
index 0000000..50a21bb
--- /dev/null
+++ b/components/resources/default_100_percent/autofill/OWNERS
@@ -0,0 +1 @@
+file://components/autofill/OWNERS
diff --git a/components/resources/default_200_percent/autofill/OWNERS b/components/resources/default_200_percent/autofill/OWNERS
new file mode 100644
index 0000000..50a21bb
--- /dev/null
+++ b/components/resources/default_200_percent/autofill/OWNERS
@@ -0,0 +1 @@
+file://components/autofill/OWNERS
diff --git a/components/resources/default_300_percent/autofill/OWNERS b/components/resources/default_300_percent/autofill/OWNERS
new file mode 100644
index 0000000..50a21bb
--- /dev/null
+++ b/components/resources/default_300_percent/autofill/OWNERS
@@ -0,0 +1 @@
+file://components/autofill/OWNERS
diff --git a/components/search_engine_descriptions_strings.grd b/components/search_engine_descriptions_strings.grd
index 2db4711..9f76d7c 100644
--- a/components/search_engine_descriptions_strings.grd
+++ b/components/search_engine_descriptions_strings.grd
@@ -213,7 +213,7 @@
         Search the web and answer life’s daily questions with Yahoo.
       </message>
       <message name="IDS_YOU_SEARCH_DESCRIPTION" use_name_for_id="true">
-        AI-enhanced searching and browsing. Search results, summaries, and citations.
+        AI enhanced search
       </message>
     </messages>
   </release>
diff --git a/components/signin/public/base/signin_prefs.cc b/components/signin/public/base/signin_prefs.cc
index 2dcb4cb06..19eff1e 100644
--- a/components/signin/public/base/signin_prefs.cc
+++ b/components/signin/public/base/signin_prefs.cc
@@ -32,6 +32,15 @@
 constexpr char kChromeSigninInterceptionDismissCount[] =
     "ChromeSigninInterceptionDismissCount";
 
+// Pref to store the number of times the password bubble signin promo
+// has been shown per account.
+constexpr char kPasswordSignInPromoShownCount[] =
+    "PasswordSignInPromoShownCount";
+// Pref to store the number of times any autofill bubble signin promo
+// has been dismissed per account.
+constexpr char kAutofillSignInPromoDismissCount[] =
+    "AutofillSignInPromoDismissCount";
+
 }  // namespace
 
 SigninPrefs::SigninPrefs(PrefService& pref_service)
@@ -104,24 +113,49 @@
 }
 
 int SigninPrefs::IncrementChromeSigninInterceptionDismissCount(GaiaId gaia_id) {
+  return IncrementIntPrefForAccount(gaia_id,
+                                    kChromeSigninInterceptionDismissCount);
+}
+
+int SigninPrefs::GetChromeSigninInterceptionDismissCount(GaiaId gaia_id) const {
+  return GetIntPrefForAccount(gaia_id, kChromeSigninInterceptionDismissCount);
+}
+
+void SigninPrefs::IncrementPasswordSigninPromoImpressionCount(GaiaId gaia_id) {
+  IncrementIntPrefForAccount(gaia_id, kPasswordSignInPromoShownCount);
+}
+
+int SigninPrefs::GetPasswordSigninPromoImpressionCount(GaiaId gaia_id) const {
+  return GetIntPrefForAccount(gaia_id, kPasswordSignInPromoShownCount);
+}
+
+void SigninPrefs::IncrementAutofillSigninPromoDismissCount(GaiaId gaia_id) {
+  IncrementIntPrefForAccount(gaia_id, kAutofillSignInPromoDismissCount);
+}
+
+int SigninPrefs::GetAutofillSigninPromoDismissCount(GaiaId gaia_id) const {
+  return GetIntPrefForAccount(gaia_id, kAutofillSignInPromoDismissCount);
+}
+
+int SigninPrefs::IncrementIntPrefForAccount(GaiaId gaia_id,
+                                            std::string_view pref) {
+  CHECK(!gaia_id.empty());
   ScopedDictPrefUpdate scoped_update(&pref_service_.get(), kSigninAccountPrefs);
 
   // `EnsureDict` gets or create the dictionary.
   base::Value::Dict* account_dict = scoped_update->EnsureDict(gaia_id);
   // Get the current value of the pref.
-  std::optional<int> value =
-      account_dict->FindInt(kChromeSigninInterceptionDismissCount);
-  // Increment the `value`. If `value` was not set before, default is 0.
-  int new_value = value.value_or(0) + 1;
+  int new_value = account_dict->FindInt(pref).value_or(0) + 1;
+  // `Set` will add an entry if it doesn't already exists, or if it does, it
+  // will overwrite it.
+  account_dict->Set(pref, new_value);
 
-  // `Set` will add an entry if it doesn't already exists.
-  account_dict->Set(kChromeSigninInterceptionDismissCount, new_value);
-
-  // Return the incremented value.
   return new_value;
 }
 
-int SigninPrefs::GetChromeSigninInterceptionDismissCount(GaiaId gaia_id) const {
+int SigninPrefs::GetIntPrefForAccount(GaiaId gaia_id,
+                                      std::string_view pref) const {
+  CHECK(!gaia_id.empty());
   const base::Value::Dict* account_dict =
       pref_service_->GetDict(kSigninAccountPrefs).FindDict(gaia_id);
   // If the account dict does not exist yet; return the default value.
@@ -130,6 +164,5 @@
   }
 
   // Return the pref value if it exists, otherwise return the default value.
-  return account_dict->FindInt(kChromeSigninInterceptionDismissCount)
-      .value_or(0);
+  return account_dict->FindInt(pref).value_or(0);
 }
diff --git a/components/signin/public/base/signin_prefs.h b/components/signin/public/base/signin_prefs.h
index 121814e..9f7c7b6 100644
--- a/components/signin/public/base/signin_prefs.h
+++ b/components/signin/public/base/signin_prefs.h
@@ -72,12 +72,18 @@
   int IncrementChromeSigninInterceptionDismissCount(GaiaId gaia_id);
   int GetChromeSigninInterceptionDismissCount(GaiaId gaia_id) const;
 
+  void IncrementPasswordSigninPromoImpressionCount(GaiaId gaia_id);
+  int GetPasswordSigninPromoImpressionCount(GaiaId gaia_id) const;
+
+  void IncrementAutofillSigninPromoDismissCount(GaiaId gaia_id);
+  int GetAutofillSigninPromoDismissCount(GaiaId gaia_id) const;
+
   // Note: `callback` will be notified on every change in the main dictionary
   // and sub-dictionries (account dictionaries).
   static void ObserveSigninPrefsChanges(PrefChangeRegistrar& registrar,
                                         base::RepeatingClosure callback);
 
-  // Checks if the account pref with the given `gaia_id` exists.
+  // Checks if the an account pref with the given `gaia_id` exists.
   bool HasAccountPrefs(GaiaId gaia_id) const;
 
   // Keeps all prefs with the gaia ids given in `gaia_ids_to_keep`.
@@ -90,6 +96,12 @@
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
  private:
+  // Increments any specified `pref` of type int for the given `gaia_id`.
+  int IncrementIntPrefForAccount(GaiaId gaia_id, std::string_view pref);
+  // Gets any specified `pref` of type int for the given `gaia_id`.
+  // Returns 0 if the corresponding `pref` doesn't exist for `gaia_id`.
+  int GetIntPrefForAccount(GaiaId gaia_id, std::string_view pref) const;
+
   const raw_ref<PrefService, DanglingUntriaged> pref_service_;
 };
 
diff --git a/components/site_engagement/content/site_engagement_service.h b/components/site_engagement/content/site_engagement_service.h
index 474f7df..b2fd3c8 100644
--- a/components/site_engagement/content/site_engagement_service.h
+++ b/components/site_engagement/content/site_engagement_service.h
@@ -42,6 +42,7 @@
 class HostContentSettingsMap;
 class PrefRegistrySimple;
 class NotificationPermissionReviewServiceTest;
+class SafetyHubCardDataHelperTest;
 
 namespace site_engagement {
 
@@ -208,6 +209,7 @@
   friend class SiteEngagementServiceTest;
   friend class web_app::WebAppEngagementBrowserTest;
   friend class ::NotificationPermissionReviewServiceTest;
+  friend class ::SafetyHubCardDataHelperTest;
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CheckHistograms);
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest, CleanupEngagementScores);
   FRIEND_TEST_ALL_PREFIXES(SiteEngagementServiceTest,
diff --git a/components/sync/service/resources/index.html b/components/sync/service/resources/index.html
index 630a6a78..5f3e2d1 100644
--- a/components/sync/service/resources/index.html
+++ b/components/sync/service/resources/index.html
@@ -4,6 +4,7 @@
 <!-- If you change the title, make sure you also update
 chrome/test/functional/special_tabs.py. -->
 <meta charset="utf-8">
+<meta content="width=device-width,initial-scale=1.0" name="viewport">
 <title>Sync Internals</title>
 <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
 <link rel="stylesheet" href="about.css">
@@ -19,6 +20,8 @@
 <body>
 
 <style>
+html { text-size-adjust: none; }
+
 #sync-page {
   /* TODO(akalin): Figure out a better way to make the tab box the
      same height no matter which tab is selected. */
diff --git a/content/browser/renderer_host/input/touch_input_browsertest.cc b/content/browser/renderer_host/input/touch_input_browsertest.cc
index 100c466..bb288f8 100644
--- a/content/browser/renderer_host/input/touch_input_browsertest.cc
+++ b/content/browser/renderer_host/input/touch_input_browsertest.cc
@@ -22,7 +22,6 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/hit_test_region_observer.h"
 #include "content/shell/browser/shell.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "ui/latency/latency_info.h"
@@ -71,13 +70,6 @@
     "  }"
     "</script>";
 
-blink::mojom::InputEventResultState ExpectedNotConsumedOrNotConsumedBlocking() {
-  return base::FeatureList::IsEnabled(
-             blink::features::kFixGestureScrollQueuingBug)
-             ? blink::mojom::InputEventResultState::kNotConsumedBlocking
-             : blink::mojom::InputEventResultState::kNotConsumed;
-}
-
 }  // namespace
 
 namespace content {
@@ -153,7 +145,8 @@
   filter = AddFilter(WebInputEvent::Type::kTouchEnd);
   touch.ReleasePoint(0);
   SendTouchEvent(&touch);
-  EXPECT_EQ(ExpectedNotConsumedOrNotConsumedBlocking(), filter->WaitForAck());
+  EXPECT_EQ(blink::mojom::InputEventResultState::kNotConsumed,
+            filter->WaitForAck());
 }
 
 IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchStartNoConsume) {
@@ -165,7 +158,8 @@
   touch.PressPoint(125, 25);
   auto filter = AddFilter(WebInputEvent::Type::kTouchStart);
   SendTouchEvent(&touch);
-  EXPECT_EQ(ExpectedNotConsumedOrNotConsumedBlocking(), filter->WaitForAck());
+  EXPECT_EQ(blink::mojom::InputEventResultState::kNotConsumed,
+            filter->WaitForAck());
 
   // Even though there is no touch-end handler there, the touch-end is still
   // dispatched for state consistency in downstream event-path.  That event
@@ -173,7 +167,8 @@
   filter = AddFilter(WebInputEvent::Type::kTouchEnd);
   touch.ReleasePoint(0);
   SendTouchEvent(&touch);
-  EXPECT_EQ(ExpectedNotConsumedOrNotConsumedBlocking(), filter->WaitForAck());
+  EXPECT_EQ(blink::mojom::InputEventResultState::kNotConsumed,
+            filter->WaitForAck());
 }
 
 IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, TouchStartConsume) {
@@ -194,7 +189,8 @@
   touch.ReleasePoint(0);
   filter = AddFilter(WebInputEvent::Type::kTouchEnd);
   SendTouchEvent(&touch);
-  EXPECT_EQ(ExpectedNotConsumedOrNotConsumedBlocking(), filter->WaitForAck());
+  EXPECT_EQ(blink::mojom::InputEventResultState::kNotConsumed,
+            filter->WaitForAck());
 }
 
 IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MultiPointTouchPress) {
diff --git a/content/browser/renderer_host/input/wheel_scroll_latching_browsertest.cc b/content/browser/renderer_host/input/wheel_scroll_latching_browsertest.cc
index ef360f4..d2eeca5b 100644
--- a/content/browser/renderer_host/input/wheel_scroll_latching_browsertest.cc
+++ b/content/browser/renderer_host/input/wheel_scroll_latching_browsertest.cc
@@ -15,7 +15,6 @@
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/public/test/hit_test_region_observer.h"
 #include "content/shell/browser/shell.h"
-#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
 
@@ -376,11 +375,8 @@
                                     ui::LatencyInfo());
 
   // Run until we get the callback, then check the target.
-  EXPECT_EQ(
-      base::FeatureList::IsEnabled(blink::features::kFixGestureScrollQueuingBug)
-          ? blink::mojom::InputEventResultState::kNotConsumedBlocking
-          : blink::mojom::InputEventResultState::kNotConsumed,
-      wheel_msg_watcher->WaitForAck());
+  EXPECT_EQ(blink::mojom::InputEventResultState::kNotConsumed,
+            wheel_msg_watcher->WaitForAck());
   EXPECT_EQ("redDiv", EvalJs(shell(), "domTarget"));
 }
 
diff --git a/content/browser/renderer_host/ui_events_helper.cc b/content/browser/renderer_host/ui_events_helper.cc
index 551433d2..ad16bd5 100644
--- a/content/browser/renderer_host/ui_events_helper.cc
+++ b/content/browser/renderer_host/ui_events_helper.cc
@@ -94,29 +94,13 @@
 
 bool InputEventResultStateIsSetBlocking(
     blink::mojom::InputEventResultState ack_state) {
-  static bool fix_input_queueing_bug = base::FeatureList::IsEnabled(
-      blink::features::kFixGestureScrollQueuingBug);
-  // The bug here is this function returning an inverted result
-  // The finch experiment |kFixGestureScrollQueuingBug| releases a fix
-  // for this bug that flips this inversion.
-  if (!fix_input_queueing_bug) {
-    switch (ack_state) {
-      case blink::mojom::InputEventResultState::kSetNonBlocking:
-      case blink::mojom::InputEventResultState::kSetNonBlockingDueToFling:
-        return true;
-      default:
-        return false;
-    }
-  } else {
-    // Default input events are marked as kNotConsumed and should not
-    // be marked as blocking.
-    switch (ack_state) {
-      case blink::mojom::InputEventResultState::kNotConsumedBlocking:
-      case blink::mojom::InputEventResultState::kConsumed:
-        return true;
-      default:
-        return false;
-    }
+  // Default input events are marked as kNotConsumed and should not
+  // be marked as blocking.
+  switch (ack_state) {
+    case blink::mojom::InputEventResultState::kConsumed:
+      return true;
+    default:
+      return false;
   }
 }
 
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 85053ee..314c1a33 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1000,8 +1000,8 @@
 void ServiceWorkerVersion::MoveControlleeToBackForwardCacheMap(
     const std::string& client_uuid) {
   DCHECK(IsBackForwardCacheEnabled());
-  DCHECK(base::Contains(controllee_map_, client_uuid));
-  DCHECK(!base::Contains(bfcached_controllee_map_, client_uuid));
+  CHECK(base::Contains(controllee_map_, client_uuid));
+  CHECK(!base::Contains(bfcached_controllee_map_, client_uuid));
   bfcached_controllee_map_[client_uuid] = controllee_map_[client_uuid];
   RemoveControllee(client_uuid);
 }
@@ -1039,7 +1039,7 @@
 void ServiceWorkerVersion::RemoveControlleeFromBackForwardCacheMap(
     const std::string& client_uuid) {
   DCHECK(IsBackForwardCacheEnabled());
-  DCHECK(base::Contains(bfcached_controllee_map_, client_uuid));
+  CHECK(base::Contains(bfcached_controllee_map_, client_uuid));
   bfcached_controllee_map_.erase(client_uuid);
 }
 
diff --git a/content/common/input/input_event_ack_state.cc b/content/common/input/input_event_ack_state.cc
index ccef819a..bc949f4e 100644
--- a/content/common/input/input_event_ack_state.cc
+++ b/content/common/input/input_event_ack_state.cc
@@ -17,8 +17,6 @@
       return "CONSUMED";
     case blink::mojom::InputEventResultState::kNotConsumed:
       return "NOT_CONSUMED";
-    case blink::mojom::InputEventResultState::kNotConsumedBlocking:
-      return "NOT_CONSUMED_BLOCKING";
     case blink::mojom::InputEventResultState::kNoConsumerExists:
       return "NO_CONSUMER_EXISTS";
     case blink::mojom::InputEventResultState::kIgnored:
diff --git a/google_apis/gcm/base/gcm_features.cc b/google_apis/gcm/base/gcm_features.cc
index 3aaae44c..ccbe556 100644
--- a/google_apis/gcm/base/gcm_features.cc
+++ b/google_apis/gcm/base/gcm_features.cc
@@ -13,11 +13,11 @@
 
 BASE_FEATURE(kGCMAvoidConnectionWhenNetworkUnavailable,
              "GCMAvoidConnectionWhenNetworkUnavailable",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 BASE_FEATURE(kGCMDoNotIncreaseBackoffDelayOnNetworkChange,
              "GCMDoNotIncreaseBackoffDelayOnNetworkChange",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace features
 }  // namespace gcm
diff --git a/google_apis/gcm/engine/connection_factory_impl_unittest.cc b/google_apis/gcm/engine/connection_factory_impl_unittest.cc
index 87957c7..278c2b7 100644
--- a/google_apis/gcm/engine/connection_factory_impl_unittest.cc
+++ b/google_apis/gcm/engine/connection_factory_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/task_environment.h"
 #include "google_apis/gcm/base/gcm_features.h"
 #include "google_apis/gcm/base/mcs_util.h"
+#include "google_apis/gcm/engine/connection_factory.h"
 #include "google_apis/gcm/engine/fake_connection_handler.h"
 #include "google_apis/gcm/monitoring/fake_gcm_stats_recorder.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -76,6 +77,13 @@
   return endpoints;
 }
 
+// Used as a builder for test login requests.
+void FillLoginRequest(mcs_proto::LoginRequest* login_request) {
+  std::unique_ptr<mcs_proto::LoginRequest> request =
+      BuildLoginRequest(0, 0, "");
+  login_request->CopyFrom(*request);
+}
+
 // Helper for calculating total expected exponential backoff delay given an
 // arbitrary number of failed attempts. See BackoffEntry::CalculateReleaseTime.
 double CalculateBackoff(int num_attempts) {
@@ -106,8 +114,6 @@
 
   // Overridden stubs.
   void StartConnection(bool ignore_connection_failure) override;
-  void InitHandler(mojo::ScopedDataPipeConsumerHandle receive_stream,
-                   mojo::ScopedDataPipeProducerHandle send_stream) override;
   std::unique_ptr<net::BackoffEntry> CreateBackoffEntry(
       const net::BackoffEntry::Policy* const policy) override;
   std::unique_ptr<ConnectionHandler> CreateConnectionHandler(
@@ -138,11 +144,6 @@
   int connect_result_ = net::ERR_UNEXPECTED;
   // The number of expected connection attempts;
   int num_expected_attempts_ = 0;
-  // Whether all expected connection attempts have been fulfilled since an
-  // expectation was last set.
-  bool connections_fulfilled_ = true;
-  // Whether to delay a login handshake completion or not.
-  bool delay_login_ = false;
   // Callback to invoke when all connection attempts have been made.
   base::RepeatingClosure finished_callback_;
   // A temporary scoped pointer to make sure we don't leak the handler in the
@@ -172,7 +173,10 @@
       finished_callback_(std::move(finished_callback)),
       scoped_handler_(std::make_unique<FakeConnectionHandler>(
           base::BindRepeating(&ReadContinuation),
-          base::BindRepeating(&WriteContinuation))),
+          base::BindRepeating(&WriteContinuation),
+          base::BindRepeating(
+              &TestConnectionFactoryImpl::ConnectionHandlerCallback,
+              base::Unretained(this)))),
       fake_handler_(scoped_handler_.get()) {
   // Set a non-null time.
   tick_clock_.Advance(base::Milliseconds(1));
@@ -191,36 +195,28 @@
 
 void TestConnectionFactoryImpl::StartConnection(
     bool ignore_connection_failure) {
-  ASSERT_GT(num_expected_attempts_, 0);
+  ASSERT_GT(num_expected_attempts_, 0) << "Unexpected connection attempt";
   ASSERT_FALSE(GetConnectionHandler()->CanSendMessage());
-  std::unique_ptr<mcs_proto::LoginRequest> request(BuildLoginRequest(0, 0, ""));
-  GetConnectionHandler()->Init(*request, std::move(receive_pipe_consumer_),
-                               std::move(send_pipe_producer_));
+
+  // Update the number of apptempts before calling OnConnectDone() because it
+  // can internally call StartConnection() again, e.g. during a network change.
+  --num_expected_attempts_;
   OnConnectDone(ignore_connection_failure, connect_result_, net::IPEndPoint(),
                 net::IPEndPoint(), mojo::ScopedDataPipeConsumerHandle(),
                 mojo::ScopedDataPipeProducerHandle());
+
   if (!NextRetryAttempt().is_null()) {
     // Advance the time to the next retry time.
     base::TimeDelta time_till_retry =
         NextRetryAttempt() - tick_clock_.NowTicks();
     tick_clock_.Advance(time_till_retry);
   }
-  --num_expected_attempts_;
   if (num_expected_attempts_ == 0) {
     connect_result_ = net::ERR_UNEXPECTED;
-    connections_fulfilled_ = true;
     finished_callback_.Run();
   }
 }
 
-void TestConnectionFactoryImpl::InitHandler(
-    mojo::ScopedDataPipeConsumerHandle receive_stream,
-    mojo::ScopedDataPipeProducerHandle send_stream) {
-  EXPECT_NE(connect_result_, net::ERR_UNEXPECTED);
-  if (!delay_login_)
-    ConnectionHandlerCallback(net::OK);
-}
-
 std::unique_ptr<net::BackoffEntry>
 TestConnectionFactoryImpl::CreateBackoffEntry(
     const net::BackoffEntry::Policy* const policy) {
@@ -243,7 +239,6 @@
 void TestConnectionFactoryImpl::SetConnectResult(int connect_result) {
   DCHECK_NE(connect_result, net::ERR_UNEXPECTED);
   ASSERT_EQ(0, num_expected_attempts_);
-  connections_fulfilled_ = false;
   connect_result_ = connect_result;
   num_expected_attempts_ = 1;
   fake_handler_->ExpectOutgoingMessage(
@@ -256,7 +251,6 @@
   DCHECK_NE(connect_result, net::ERR_UNEXPECTED);
   DCHECK_GT(num_expected_attempts, 0);
   ASSERT_EQ(0, num_expected_attempts_);
-  connections_fulfilled_ = false;
   connect_result_ = connect_result;
   num_expected_attempts_ = num_expected_attempts;
   for (int i = 0 ; i < num_expected_attempts; ++i) {
@@ -266,8 +260,7 @@
 }
 
 void TestConnectionFactoryImpl::SetDelayLogin(bool delay_login) {
-  delay_login_ = delay_login;
-  fake_handler_->set_fail_login(delay_login_);
+  fake_handler_->set_fail_login(delay_login);
 }
 
 void TestConnectionFactoryImpl::SetSocketError() {
@@ -351,7 +344,7 @@
       network_service_.get(),
       network_context_remote_.BindNewPipeAndPassReceiver(), std::move(params));
   factory()->SetConnectionListener(this);
-  factory()->Initialize(ConnectionFactory::BuildLoginRequestCallback(),
+  factory()->Initialize(base::BindRepeating(&FillLoginRequest),
                         ConnectionHandler::ProtoReceivedCallback(),
                         ConnectionHandler::ProtoSentCallback());
 }
@@ -476,9 +469,9 @@
       network::mojom::ConnectionType::CONNECTION_WIFI);
   WaitForConnections();
 
-  // Backoff should increase.
+  // Backoff should not change because of network change.
   base::TimeTicks next_backoff = factory()->NextRetryAttempt();
-  EXPECT_GT(next_backoff, initial_backoff);
+  EXPECT_EQ(next_backoff, initial_backoff);
   EXPECT_FALSE(factory()->IsEndpointReachable());
 }
 
@@ -780,7 +773,8 @@
 
   // Mimic a network change during handshake, and fail connection request.
   factory()->SetDelayLogin(false);
-  factory()->SetConnectResult(net::ERR_CONNECTION_FAILED);
+  factory()->SetMultipleConnectResults(net::ERR_CONNECTION_FAILED,
+                                       /*num_expected_attempts=*/2);
   factory()->OnConnectionChanged(
       network::mojom::ConnectionType::CONNECTION_WIFI);
   WaitForConnections();
diff --git a/google_apis/gcm/engine/fake_connection_factory.cc b/google_apis/gcm/engine/fake_connection_factory.cc
index 57afa4d..e4c1e7e 100644
--- a/google_apis/gcm/engine/fake_connection_factory.cc
+++ b/google_apis/gcm/engine/fake_connection_factory.cc
@@ -28,8 +28,9 @@
     const ConnectionHandler::ProtoReceivedCallback& read_callback,
     const ConnectionHandler::ProtoSentCallback& write_callback) {
   request_builder_ = request_builder;
-  connection_handler_ =
-      std::make_unique<FakeConnectionHandler>(read_callback, write_callback);
+  connection_handler_ = std::make_unique<FakeConnectionHandler>(
+      read_callback, write_callback,
+      ConnectionHandler::ConnectionChangedCallback());
 }
 
 ConnectionHandler* FakeConnectionFactory::GetConnectionHandler() const {
diff --git a/google_apis/gcm/engine/fake_connection_handler.cc b/google_apis/gcm/engine/fake_connection_handler.cc
index 8b1482ec..b8f718c 100644
--- a/google_apis/gcm/engine/fake_connection_handler.cc
+++ b/google_apis/gcm/engine/fake_connection_handler.cc
@@ -30,14 +30,15 @@
 
 FakeConnectionHandler::FakeConnectionHandler(
     const ConnectionHandler::ProtoReceivedCallback& read_callback,
-    const ConnectionHandler::ProtoSentCallback& write_callback)
+    const ConnectionHandler::ProtoSentCallback& write_callback,
+    const ConnectionHandler::ConnectionChangedCallback& connection_callback)
     : read_callback_(read_callback),
       write_callback_(write_callback),
+      connection_callback_(connection_callback),
       fail_login_(false),
       fail_send_(false),
       initialized_(false),
-      had_error_(false) {
-}
+      had_error_(false) {}
 
 FakeConnectionHandler::~FakeConnectionHandler() {
 }
@@ -46,13 +47,21 @@
     const mcs_proto::LoginRequest& login_request,
     mojo::ScopedDataPipeConsumerHandle receive_stream,
     mojo::ScopedDataPipeProducerHandle send_stream) {
+  DVLOG(1) << "Received init call.";
+
+  // Clear client events from the comparison.
+  mcs_proto::LoginRequest normalized_login_request = login_request;
+  normalized_login_request.clear_client_event();
+
   ASSERT_GE(expected_outgoing_messages_.size(), 1U);
   EXPECT_EQ(expected_outgoing_messages_.front().SerializeAsString(),
-            login_request.SerializeAsString());
+            normalized_login_request.SerializeAsString());
   expected_outgoing_messages_.pop_front();
-  DVLOG(1) << "Received init call.";
   read_callback_.Run(BuildLoginResponse(fail_login_));
   initialized_ = !fail_login_;
+  if (connection_callback_ && !fail_login_) {
+    connection_callback_.Run(net::OK);
+  }
 }
 
 void FakeConnectionHandler::Reset() {
diff --git a/google_apis/gcm/engine/fake_connection_handler.h b/google_apis/gcm/engine/fake_connection_handler.h
index 81e2128..ebe34666 100644
--- a/google_apis/gcm/engine/fake_connection_handler.h
+++ b/google_apis/gcm/engine/fake_connection_handler.h
@@ -19,7 +19,8 @@
  public:
   FakeConnectionHandler(
       const ConnectionHandler::ProtoReceivedCallback& read_callback,
-      const ConnectionHandler::ProtoSentCallback& write_callback);
+      const ConnectionHandler::ProtoSentCallback& write_callback,
+      const ConnectionHandler::ConnectionChangedCallback& connection_callback);
 
   FakeConnectionHandler(const FakeConnectionHandler&) = delete;
   FakeConnectionHandler& operator=(const FakeConnectionHandler&) = delete;
@@ -67,6 +68,7 @@
  private:
   ConnectionHandler::ProtoReceivedCallback read_callback_;
   ConnectionHandler::ProtoSentCallback write_callback_;
+  ConnectionHandler::ConnectionChangedCallback connection_callback_;
 
   std::list<MCSMessage> expected_outgoing_messages_;
 
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index bc24210..58c7b6a 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 126.0.6475.0',
+    'description': 'Run with ash-chrome version 126.0.6476.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v126.0.6475.0',
-          'revision': 'version:126.0.6475.0',
+          'location': 'lacros_version_skew_tests_v126.0.6476.0',
+          'revision': 'version:126.0.6476.0',
         },
       ],
     },
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index 4acc33c..03a25da 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 126.0.6475.0",
+    "description": "Run with ash-chrome version 126.0.6476.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v126.0.6475.0",
-          "revision": "version:126.0.6475.0"
+          "location": "lacros_version_skew_tests_v126.0.6476.0",
+          "revision": "version:126.0.6476.0"
         }
       ]
     }
diff --git a/ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.mm b/ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.mm
index 8a80a41..03ef0ed 100644
--- a/ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.mm
+++ b/ios/chrome/browser/passwords/model/ios_chrome_profile_password_store_factory.mm
@@ -87,8 +87,7 @@
     web::BrowserState* context) const {
   std::unique_ptr<password_manager::LoginDatabase> login_db(
       password_manager::CreateLoginDatabaseForProfileStorage(
-          context->GetStatePath(),
-          /*is_empty_cb=*/base::NullCallback()));
+          context->GetStatePath()));
 
   scoped_refptr<password_manager::PasswordStore> store =
       base::MakeRefCounted<password_manager::PasswordStore>(
diff --git a/ios/web_view/internal/passwords/web_view_profile_password_store_factory.mm b/ios/web_view/internal/passwords/web_view_profile_password_store_factory.mm
index 39933ceaa..469a0101 100644
--- a/ios/web_view/internal/passwords/web_view_profile_password_store_factory.mm
+++ b/ios/web_view/internal/passwords/web_view_profile_password_store_factory.mm
@@ -60,8 +60,7 @@
     web::BrowserState* context) const {
   std::unique_ptr<password_manager::LoginDatabase> login_db(
       password_manager::CreateLoginDatabaseForProfileStorage(
-          context->GetStatePath(),
-          /*is_empty_cb=*/base::NullCallback()));
+          context->GetStatePath()));
 
   scoped_refptr<base::SequencedTaskRunner> main_task_runner(
       base::SequencedTaskRunner::GetCurrentDefault());
diff --git a/ios_internal b/ios_internal
index a4bca5f..d1a37ce 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit a4bca5f99ed1d6460e444e82d9359ef57826fa0b
+Subproject commit d1a37ce7e9462472f99553f1d25f3f5a45eca986
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 9e50495..c481d8bd 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -282,6 +282,40 @@
   return features::kAlternativePortForGloballyReachableCheck.Get();
 }
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+//
+// LINT.IfChange(DnsClientCapability)
+enum class DnsClientCapability {
+  kSecureDisabledInsecureDisabled = 0,
+  kSecureDisabledInsecureEnabled = 1,
+  kSecureEnabledInsecureDisabled = 2,
+  kSecureEnabledInsecureEnabled = 3,
+  kMaxValue = kSecureEnabledInsecureEnabled,
+};
+// LINT.ThenChange(/tools/metrics/histograms/metadata/net/enums.xml:DnsClientCapability)
+
+void RecordDnsClientCapabilityMetrics(const DnsClient* dns_client) {
+  if (!dns_client) {
+    return;
+  }
+  DnsClientCapability capability;
+  if (dns_client->CanUseSecureDnsTransactions()) {
+    if (dns_client->CanUseInsecureDnsTransactions()) {
+      capability = DnsClientCapability::kSecureEnabledInsecureEnabled;
+    } else {
+      capability = DnsClientCapability::kSecureEnabledInsecureDisabled;
+    }
+  } else {
+    if (dns_client->CanUseInsecureDnsTransactions()) {
+      capability = DnsClientCapability::kSecureDisabledInsecureEnabled;
+    } else {
+      capability = DnsClientCapability::kSecureDisabledInsecureDisabled;
+    }
+  }
+  base::UmaHistogramEnumeration("Net.DNS.DnsConfig.DnsClientCapability",
+                                capability);
+}
 }  // namespace
 
 //-----------------------------------------------------------------------------
@@ -1287,6 +1321,10 @@
 
   switch (job_key.source) {
     case HostResolverSource::ANY:
+      // Records DnsClient capability metrics, only when `source` is ANY. This
+      // is to avoid the metrics being skewed by mechanical requests of other
+      // source types.
+      RecordDnsClientCapabilityMetrics(dns_client_.get());
       // Force address queries with canonname to use HostResolverSystemTask to
       // counter poor CNAME support in DnsTask. See https://crbug.com/872665
       //
diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src
index 690aed3..ee237e9 160000
--- a/net/third_party/quiche/src
+++ b/net/third_party/quiche/src
@@ -1 +1 @@
-Subproject commit 690aed3796de57c7f846b8f02dcb4569f0b95f07
+Subproject commit ee237e96f18ef123af9992f74645a8a0ce9ef6ef
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index d0829e4..194661f 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5487,9 +5487,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5499,8 +5499,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -5643,9 +5643,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5655,8 +5655,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 560f12e..99eb8be 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -19664,9 +19664,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19676,8 +19676,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -19820,9 +19820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -19832,8 +19832,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index eaf6514..776c164 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -41840,9 +41840,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -41851,8 +41851,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -41990,9 +41990,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -42001,8 +42001,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -43339,9 +43339,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43351,8 +43351,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -43495,9 +43495,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -43507,8 +43507,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -44820,9 +44820,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44831,8 +44831,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -44970,9 +44970,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44981,8 +44981,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index f0664153..c3b95a7 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -15763,12 +15763,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15778,8 +15778,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
@@ -15939,12 +15939,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 126.0.6475.0",
+        "description": "Run with ash-chrome version 126.0.6476.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -15954,8 +15954,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v126.0.6475.0",
-              "revision": "version:126.0.6475.0"
+              "location": "lacros_version_skew_tests_v126.0.6476.0",
+              "revision": "version:126.0.6476.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index bc24210..58c7b6a 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -267,16 +267,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 126.0.6475.0',
+    'description': 'Run with ash-chrome version 126.0.6476.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6475.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v126.0.6476.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v126.0.6475.0',
-          'revision': 'version:126.0.6475.0',
+          'location': 'lacros_version_skew_tests_v126.0.6476.0',
+          'revision': 'version:126.0.6476.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index c433701f7..555f4ed 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8151,35 +8151,6 @@
             ]
         }
     ],
-    "FixInputQueueingBug": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "chromeos_lacros",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "queue_all_gesture_scrolls",
-                    "enable_features": [
-                        "FixGestureScrollQueuingBug",
-                        "QueueBlockingGestureScrolls"
-                    ]
-                },
-                {
-                    "name": "fix_input_queueing_bug",
-                    "enable_features": [
-                        "FixGestureScrollQueuingBug"
-                    ]
-                }
-            ]
-        }
-    ],
     "Floss": [
         {
             "platforms": [
@@ -8330,29 +8301,6 @@
             ]
         }
     ],
-    "GCMAvoidConnectionWhenNetworkUnavailable": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "chromeos_lacros",
-                "fuchsia",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "GCMAvoidConnectionWhenNetworkUnavailable",
-                        "GCMDoNotIncreaseBackoffDelayOnNetworkChange"
-                    ]
-                }
-            ]
-        }
-    ],
     "GMSCoreEmoji": [
         {
             "platforms": [
@@ -14673,6 +14621,29 @@
             ]
         }
     ],
+    "PreInitializePageAndFrameForSVGImage": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "max_pre_initialize_count": "3"
+                    },
+                    "enable_features": [
+                        "PreInitializePageAndFrameForSVGImage"
+                    ]
+                }
+            ]
+        }
+    ],
     "PreReadDllBrowserProcess": [
         {
             "platforms": [
@@ -18820,6 +18791,26 @@
             ]
         }
     ],
+    "ShowWebauthnSuggestionsOnAutofocus": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ShowWebauthnSuggestionsOnAutofocus"
+                    ]
+                }
+            ]
+        }
+    ],
     "SidePanelCompanionDesktopM116Plus": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index d1bb6ed..4e88749 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit d1bb6ed8399dd12e79484f30f9e9ded95c25625a
+Subproject commit 4e887491e14a924c8d22a6419af24b613ceb1fa9
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index e9ef551..447ed1d 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -952,10 +952,6 @@
 const base::FeatureParam<std::string> kFilteringScrollPredictionFilterParam{
     &kFilteringScrollPrediction, "filter", "one_euro_filter"};
 
-BASE_FEATURE(kFixGestureScrollQueuingBug,
-             "FixGestureScrollQueuingBug",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // See https://github.com/WICG/turtledove/blob/main/FLEDGE.md
 // Enables FLEDGE implementation. See https://crbug.com/1186444.
 BASE_FEATURE(kFledge, "Fledge", base::FEATURE_DISABLED_BY_DEFAULT);
@@ -1383,6 +1379,9 @@
 const base::FeatureParam<double> kLCPPFontURLPredictorThresholdInMbps{
     &kLCPPFontURLPredictor, "lcpp_font_prefetch_threshold", -1};
 
+const base::FeatureParam<std::string> kLCPPFontURLPredictorExcludedHosts{
+    &kLCPPFontURLPredictor, "lcpp_font_prefetch_excluded_hosts", ""};
+
 BASE_FEATURE(kLCPPLazyLoadImagePreload,
              "LCPPLazyLoadImagePreload",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -1775,6 +1774,14 @@
     "IsPartitioned",
     base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kPreInitializePageAndFrameForSVGImage,
+             "PreInitializePageAndFrameForSVGImage",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+constexpr base::FeatureParam<int>
+    kMaxCountOfPreInitializePageAndFrameForSVGImage{
+        &kPreInitializePageAndFrameForSVGImage, "max_pre_initialize_count", 5};
+
 BASE_FEATURE(kPrecompileInlineScripts,
              "PrecompileInlineScripts",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -1949,10 +1956,6 @@
              "LocalCompileHints",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kQueueBlockingGestureScrolls,
-             "QueueBlockingGestureScrolls",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kQuoteEmptySecChUaStringHeadersConsistently,
              "QuoteEmptySecChUaStringHeadersConsistently",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 72ae30d..167982e 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -488,11 +488,6 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string>
     kFilteringScrollPredictionFilterParam;
 
-// (b/283408783): When enabled, first gesture scrolls on web pages that have
-// touch handlers registered will go through the normal queueing process if
-// while gesture scrolls that hit a touch handlers will be queued instantly.
-BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kFixGestureScrollQueuingBug);
-
 // FLEDGE ad serving runtime flag/JS API.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kFledge);
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kFledgeBiddingAndAuctionServer);
@@ -860,6 +855,10 @@
 BLINK_COMMON_EXPORT extern const base::FeatureParam<double>
     kLCPPFontURLPredictorThresholdInMbps;
 
+// A list of hosts to be excluded from the LCPPFontURLPredictor feature.
+BLINK_COMMON_EXPORT extern const base::FeatureParam<std::string>
+    kLCPPFontURLPredictorExcludedHosts;
+
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kLCPPLazyLoadImagePreload);
 
 // The type of preloading for LCP images which are loaded lazily.
@@ -1125,6 +1124,13 @@
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kPostMessageThirdPartyToThirdPartyDifferentBucketSameOriginBlockedIfStorageIsPartitioned);
 
+// If enabled, instantiate Pages and Frames beforehand to speed up SVGImage.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPreInitializePageAndFrameForSVGImage);
+
+// The max count of Pages and Frames that will be prepared.
+BLINK_COMMON_EXPORT extern const base::FeatureParam<int>
+    kMaxCountOfPreInitializePageAndFrameForSVGImage;
+
 // If enabled, inline scripts will be stream compiled using a background HTML
 // scanner.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPrecompileInlineScripts);
@@ -1263,11 +1269,6 @@
 // compiling those functions when the same script is loaded again.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kLocalCompileHints);
 
-// When enabled, gesture scroll updates that hit a JS touch handlers
-// will be queued normally on CC, enabling coalescing and consistent
-// input handling.
-BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kQueueBlockingGestureScrolls);
-
 // Whether Sec-CH-UA headers on subresource fetches that contain an empty
 // string should be quoted (`""`) as they are for navigation fetches. See
 // https://crbug.com/1416925.
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 44b1d5e..8d454b5 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -2626,6 +2626,7 @@
       marker
       backdrop
       selection
+      search-text
       target-text
       spelling-error
       grammar-error
diff --git a/third_party/blink/public/mojom/input/input_event_result.mojom b/third_party/blink/public/mojom/input/input_event_result.mojom
index 8251081..533fb479 100644
--- a/third_party/blink/public/mojom/input/input_event_result.mojom
+++ b/third_party/blink/public/mojom/input/input_event_result.mojom
@@ -20,8 +20,6 @@
   kConsumed,
   // Was not consumed.
   kNotConsumed,
-  // Event hit a blocking touch handler but was not consumed.
-  kNotConsumedBlocking,
   // Could not be targeted.
   kNoConsumerExists,
   // Discarded.
diff --git a/third_party/blink/public/mojom/input/touch_event.mojom b/third_party/blink/public/mojom/input/touch_event.mojom
index c959005c..bd5b610 100644
--- a/third_party/blink/public/mojom/input/touch_event.mojom
+++ b/third_party/blink/public/mojom/input/touch_event.mojom
@@ -12,7 +12,6 @@
   kStateMoved,
   kStateStationary,
   kStateCancelled,
-  kStateMax = kStateCancelled
 };
 
 // Describes which touch event consumers are present in the renderer, for use in
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index f36e190..6a3f2a28 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -4356,16 +4356,13 @@
   kPrefersReducedDataMediaFeature = 4963,
   kScriptingMediaFeature = 4964,
   kInvertedColorsMediaFeature = 4965,
-
   kCSSLightDark = 4966,
-
   kPrivateAggregationApiFilteringIds = 4967,
   kHTMLInputInSelect = 4968,
-
   kCanvas2DMesh = 4969,
   kCaretPositionFromPoint = 4970,
-
   kDocumentPolicyIncludeJSCallStacksInCrashReports = 4971,
+  kMaximumHTMLParserDOMTreeDepthHit = 4972,
 
 
   // Add new features immediately above this line. Don't change assigned
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index 4a2144d..7ae72bb 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -5204,6 +5204,7 @@
     {
       name: "text-box-edge",
       property_methods: ["ParseSingleValueFromRange", "CSSValueFromComputedStyleInternal"],
+      inherited: true,
       field_group: "box",
       field_template: "external",
       include_paths: ["third_party/blink/renderer/core/style/text_box_edge.h"],
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index 109dd09..23e1459e 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -299,6 +299,8 @@
       return kPseudoIdScrollbarTrackPiece;
     case kPseudoResizer:
       return kPseudoIdResizer;
+    case kPseudoSearchText:
+      return kPseudoIdSearchText;
     case kPseudoTargetText:
       return kPseudoIdTargetText;
     case kPseudoHighlight:
@@ -330,6 +332,7 @@
     case kPseudoClosed:
     case kPseudoCornerPresent:
     case kPseudoCue:
+    case kPseudoCurrent:
     case kPseudoDecrement:
     case kPseudoDefault:
     case kPseudoDefined:
@@ -502,6 +505,7 @@
     {"closed", CSSSelector::kPseudoClosed},
     {"corner-present", CSSSelector::kPseudoCornerPresent},
     {"cue", CSSSelector::kPseudoWebKitCustomElement},
+    {"current", CSSSelector::kPseudoCurrent},
     {"decrement", CSSSelector::kPseudoDecrement},
     {"default", CSSSelector::kPseudoDefault},
     {"defined", CSSSelector::kPseudoDefined},
@@ -558,6 +562,7 @@
     {"scope", CSSSelector::kPseudoScope},
     {"scroll-marker", CSSSelector::kPseudoScrollMarker},
     {"scroll-markers", CSSSelector::kPseudoScrollMarkers},
+    {"search-text", CSSSelector::kPseudoSearchText},
     {"select-fallback-button", CSSSelector::kPseudoSelectFallbackButton},
     {"select-fallback-button-icon",
      CSSSelector::kPseudoSelectFallbackButtonIcon},
@@ -687,6 +692,12 @@
     return CSSSelector::kPseudoUnknown;
   }
 
+  if ((match->type == CSSSelector::kPseudoSearchText ||
+       match->type == CSSSelector::kPseudoCurrent) &&
+      !RuntimeEnabledFeatures::SearchTextHighlightPseudoEnabled()) {
+    return CSSSelector::kPseudoUnknown;
+  }
+
   return static_cast<CSSSelector::PseudoType>(match->type);
 }
 
@@ -783,6 +794,7 @@
     case kPseudoSelection:
     case kPseudoWebKitCustomElement:
     case kPseudoSlotted:
+    case kPseudoSearchText:
     case kPseudoTargetText:
     case kPseudoHighlight:
     case kPseudoSpellingError:
@@ -827,6 +839,7 @@
     case kPseudoChecked:
     case kPseudoClosed:
     case kPseudoCornerPresent:
+    case kPseudoCurrent:
     case kPseudoDecrement:
     case kPseudoDefault:
     case kPseudoDefined:
@@ -1475,6 +1488,7 @@
     case kPseudoSelectFallbackButtonText:
     case kPseudoSelectFallbackDatalist:
     case kPseudoSelection:
+    case kPseudoSearchText:
     case kPseudoTargetText:
     case kPseudoHighlight:
     case kPseudoSpellingError:
diff --git a/third_party/blink/renderer/core/css/css_selector.h b/third_party/blink/renderer/core/css/css_selector.h
index b284ccf..72f7a5d 100644
--- a/third_party/blink/renderer/core/css/css_selector.h
+++ b/third_party/blink/renderer/core/css/css_selector.h
@@ -231,6 +231,7 @@
     kPseudoBefore,
     kPseudoChecked,
     kPseudoCornerPresent,
+    kPseudoCurrent,
     kPseudoDecrement,
     kPseudoDefault,
     kPseudoDetailsContent,
@@ -291,6 +292,7 @@
     kPseudoScrollbarThumb,
     kPseudoScrollbarTrack,
     kPseudoScrollbarTrackPiece,
+    kPseudoSearchText,
     kPseudoSelectFallbackButton,
     kPseudoSelectFallbackButtonIcon,
     kPseudoSelectFallbackButtonText,
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index 3ff5b35..a14ef80 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -1111,6 +1111,8 @@
     case CSSSelector::kPseudoViewTransitionOld:
     case CSSSelector::kPseudoViewTransitionNew:
       return pseudo_class == CSSSelector::kPseudoOnlyChild;
+    case CSSSelector::kPseudoSearchText:
+      return pseudo_class == CSSSelector::kPseudoCurrent;
     default:
       return false;
   }
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
index c667512..e95d8fc 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser_test.cc
@@ -176,6 +176,8 @@
   test::TaskEnvironment task_environment;
   const char* test_cases[] = {"::-webkit-volume-slider:hover",
                               "::selection:window-inactive",
+                              "::search-text:current",
+                              "::search-text:not(:current)",
                               "::-webkit-scrollbar:disabled",
                               "::-webkit-volume-slider:not(:hover)",
                               "::-webkit-scrollbar:not(:horizontal)",
@@ -206,6 +208,9 @@
       ".class::content::before",
       "::shadow.class",
       "::selection:window-inactive::before",
+      "::search-text.class",
+      "::search-text::before",
+      "::search-text:hover",
       "::-webkit-volume-slider.class",
       "::before:not(.a)",
       "::shadow:not(::after)",
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 9cb62231..63c07c3c 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1737,9 +1737,10 @@
   return CompositorKeyframeValueFactory::Create(property, *style, offset);
 }
 
-const ComputedStyle* StyleResolver::StyleForPage(
-    uint32_t page_index,
-    const AtomicString& page_name) {
+const ComputedStyle* StyleResolver::StyleForPage(uint32_t page_index,
+                                                 const AtomicString& page_name,
+                                                 float page_fitting_scale,
+                                                 bool ignore_author_style) {
   // The page context inherits from the root element.
   Element* root_element = GetDocument().documentElement();
   if (!root_element) {
@@ -1775,6 +1776,10 @@
   // Calling this function without being in print mode is unusual and special,
   // but it happens from unit tests, if nothing else.
   if (GetDocument().Printing()) {
+    auto* value = CSSNumericLiteralValue::Create(
+        page_fitting_scale, CSSPrimitiveValue::UnitType::kNumber);
+    StyleBuilder::ApplyProperty(GetCSSPropertyZoom(), state, *value);
+
     const WebPrintParams& params = GetDocument().GetFrame()->GetPrintParams();
     const WebPrintPageDescription& description =
         params.default_page_description;
@@ -1782,7 +1787,7 @@
     // unless params.ignore_css_margins is set.
     auto* set =
         MakeGarbageCollected<MutableCSSPropertyValueSet>(kHTMLStandardMode);
-    auto* value = CSSNumericLiteralValue::Create(
+    value = CSSNumericLiteralValue::Create(
         description.margin_top, CSSPrimitiveValue::UnitType::kPixels);
     set->SetProperty(CSSPropertyID::kMarginTop, *value,
                      /*important=*/params.ignore_css_margins);
@@ -1802,9 +1807,11 @@
         set, CascadeOrigin::kUserAgent);
   }
 
-  if (ScopedStyleResolver* scoped_resolver =
-          GetDocument().GetScopedStyleResolver()) {
-    scoped_resolver->MatchPageRules(collector);
+  if (!ignore_author_style) {
+    if (ScopedStyleResolver* scoped_resolver =
+            GetDocument().GetScopedStyleResolver()) {
+      scoped_resolver->MatchPageRules(collector);
+    }
   }
 
   cascade.Apply();
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index a2e23052..faa77ba 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -98,8 +98,21 @@
       const CSSValue*,
       double offset);
 
+  // Calculate computed style for a given page index and name.
+  //
+  // An optional scale factor may be supplied, which means that the 'zoom'
+  // property will be set to this value. This is used to scale borders etc. when
+  // the page border box needs to be scaled to match the scale factor used by
+  // layout. This should only be used when computing style for the page border
+  // box. The resulting margins should be ignored in that case.
+  //
+  // If ignore_author_style is false, only the input print job settings will be
+  // honored (to get default size and margins, and nothing else).
   const ComputedStyle* StyleForPage(uint32_t page_index,
-                                    const AtomicString& page_name);
+                                    const AtomicString& page_name,
+                                    float page_fitting_scale = 1.0,
+                                    bool ignore_author_style = false);
+
   const ComputedStyle* StyleForText(Text*);
   const ComputedStyle* StyleForViewport();
   const ComputedStyle* StyleForFormattedText(
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index c9e8ca76..430bee3 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -271,6 +271,7 @@
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/pagination_utils.h"
 #include "third_party/blink/renderer/core/layout/text_autosizer.h"
 #include "third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_critical_path_predictor.h"
 #include "third_party/blink/renderer/core/loader/anchor_element_interaction_tracker.h"
@@ -2893,19 +2894,6 @@
     focused_element_->blur();
 }
 
-const ComputedStyle* Document::StyleForPage(uint32_t page_index) {
-  AtomicString page_name;
-  if (const LayoutView* layout_view = GetLayoutView())
-    page_name = layout_view->NamedPageAtIndex(page_index);
-  return StyleForPage(page_index, page_name);
-}
-
-const ComputedStyle* Document::StyleForPage(uint32_t page_index,
-                                            const AtomicString& page_name) {
-  return GetStyleEngine().GetStyleResolver().StyleForPage(page_index,
-                                                          page_name);
-}
-
 void Document::EnsurePaintLocationDataValidForNode(
     const Node* node,
     DocumentUpdateReason reason) {
@@ -2914,82 +2902,7 @@
 
 WebPrintPageDescription Document::GetPageDescription(uint32_t page_index) {
   View()->UpdateLifecycleToLayoutClean(DocumentUpdateReason::kUnknown);
-  return GetPageDescriptionNoLifecycleUpdate(*StyleForPage(page_index));
-}
-
-WebPrintPageDescription Document::GetPageDescriptionNoLifecycleUpdate(
-    const ComputedStyle& style) {
-  DocumentLifecycle::DisallowTransitionScope scope(Lifecycle());
-  const WebPrintParams& print_params = GetFrame()->GetPrintParams();
-  WebPrintPageDescription description = print_params.default_page_description;
-
-  // TODO(mstensho): We may want to adjust page_size_type accordingly if we
-  // decide to disregard the specified @page size below.
-  description.page_size_type = style.GetPageSizeType();
-
-  switch (style.GetPageSizeType()) {
-    case PageSizeType::kAuto:
-      break;
-    case PageSizeType::kLandscape:
-      if (description.size.width() < description.size.height()) {
-        description.size.Transpose();
-      }
-      break;
-    case PageSizeType::kPortrait:
-      if (description.size.width() > description.size.height()) {
-        description.size.Transpose();
-      }
-      break;
-    case PageSizeType::kFixed: {
-      gfx::SizeF css_size = style.PageSize();
-      if (!print_params.ignore_page_size) {
-        description.size = css_size;
-        break;
-      }
-      if ((css_size.width() > css_size.height()) !=
-          (description.size.width() > description.size.height())) {
-        // Keep the page size, but match orientation.
-        description.size.Transpose();
-      }
-      break;
-    }
-    default:
-      NOTREACHED();
-  }
-
-  if (!style.MarginTop().IsAuto()) {
-    description.margin_top =
-        FloatValueForLength(style.MarginTop(), description.size.height());
-  }
-  if (!style.MarginRight().IsAuto()) {
-    description.margin_right =
-        FloatValueForLength(style.MarginRight(), description.size.width());
-  }
-  if (!style.MarginBottom().IsAuto()) {
-    description.margin_bottom =
-        FloatValueForLength(style.MarginBottom(), description.size.height());
-  }
-  if (!style.MarginLeft().IsAuto()) {
-    description.margin_left =
-        FloatValueForLength(style.MarginLeft(), description.size.width());
-  }
-
-  float page_area_width = description.size.width() -
-                          (description.margin_left + description.margin_right);
-  float page_area_height = description.size.height() -
-                           (description.margin_top + description.margin_bottom);
-
-  if (page_area_width < 1 || page_area_height < 1) {
-    // The resulting page area size would become zero (or very close to
-    // it). Ignore CSS, and use the default values provided as input. There are
-    // tests that currently expect this behavior. But see
-    // https://github.com/w3c/csswg-drafts/issues/8335
-    description = print_params.default_page_description;
-  }
-
-  description.orientation = style.GetPageOrientation();
-
-  return description;
+  return GetPageDescriptionFromLayout(*this, page_index);
 }
 
 void Document::SetIsXrOverlay(bool val, Element* overlay_element) {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index e48ef9e..9631069 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -150,7 +150,6 @@
 class ChromeClient;
 class Comment;
 class ComputedAccessibleNode;
-class ComputedStyle;
 class ConsoleMessage;
 class CookieJar;
 class DOMFeaturePolicy;
@@ -768,13 +767,6 @@
   void UpdateStyleAndLayoutForNode(const Node*, DocumentUpdateReason);
   void UpdateStyleAndLayoutForRange(const Range*, DocumentUpdateReason);
 
-  // Get the computed style for a given page and name. Note that when using the
-  // function that doesn't provide a page name, layout needs to be complete,
-  // since page names are determined during layout.
-  const ComputedStyle* StyleForPage(uint32_t page_index);
-  const ComputedStyle* StyleForPage(uint32_t page_index,
-                                    const AtomicString& page_name);
-
   // Ensures that location-based data will be valid for a given node.
   //
   // This will run style and layout if they are currently dirty, and it may also
@@ -787,12 +779,9 @@
                                            DocumentUpdateReason reason);
 
   // Gets the description for the specified page. This includes preferred page
-  // size and margins in pixels, assuming 96 pixels per inch. The size and
-  // margins must be initialized to the default values that are used if auto is
-  // specified. Updates layout as needed to get the description.
+  // size and margins in pixels, assuming 96 pixels per inch. Updates layout as
+  // needed to get the description.
   WebPrintPageDescription GetPageDescription(uint32_t page_index);
-  WebPrintPageDescription GetPageDescriptionNoLifecycleUpdate(
-      const ComputedStyle&);
 
   ResourceFetcher* Fetcher() const { return fetcher_.Get(); }
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index a9146c5..3eb55bc3 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3204,8 +3204,8 @@
     return;
   }
 
-  auto LayoutForPrinting = [&layout_view]() {
-    Document& document = layout_view->GetDocument();
+  Document& document = *frame_->GetDocument();
+  auto LayoutForPrinting = [&layout_view, &document]() {
     document.GetStyleEngine().UpdateViewportSize();
     document.MarkViewportUnitsDirty();
     layout_view->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
@@ -3216,7 +3216,7 @@
   // Need to update computed style before we can set the initial containing
   // block size. A zoom factor may have been set, and it shouldn't be applied
   // when printing, e.g. when resolving @page margins.
-  frame_->GetDocument()->UpdateStyleAndLayoutTree();
+  document.UpdateStyleAndLayoutTree();
 
   // Set up the initial containing block size for pagination. This is defined as
   // the page area size of the *first* page. [1] The size of the first page may
@@ -3229,29 +3229,23 @@
   // [1] https://www.w3.org/TR/css-page-3/#page-model
   // [2] https://www.w3.org/TR/css-page-3/#using-named-pages
   PhysicalSize initial_containing_block_size =
-      layout_view->PageAreaSize(/* page_index */ 0u,
-                                /* page_name */ AtomicString());
+      CalculateInitialContainingBlockSizeForPagination(document);
   layout_view->SetInitialContainingBlockSizeForPagination(
       initial_containing_block_size);
 
   LayoutForPrinting();
 
-  const auto& first_page = To<PhysicalBoxFragment>(
-      *layout_view->GetPhysicalFragment(0)->Children()[0]);
-  const AtomicString& first_page_name = first_page.PageName();
-  if (first_page_name) {
-    PhysicalSize new_size =
-        layout_view->PageAreaSize(/* page_index */ 0u, first_page_name);
-    if (new_size != initial_containing_block_size) {
-      // If the first page was named (this isn't something we can detect without
-      // laying out first), and the size of the first page is different from
-      // what we got above, the initial containing block used was wrong (which
-      // affects e.g. elements with viewport units). Set a new size and lay out
-      // again.
-      layout_view->SetInitialContainingBlockSizeForPagination(new_size);
+  PhysicalSize new_initial_containing_block_size =
+      CalculateInitialContainingBlockSizeForPagination(document);
+  if (new_initial_containing_block_size != initial_containing_block_size) {
+    // If the first page was named (this isn't something we can detect without
+    // laying out first), and the size of the first page is different from what
+    // we got above, the initial containing block used was wrong (which affects
+    // e.g. elements with viewport units). Set a new size and lay out again.
+    layout_view->SetInitialContainingBlockSizeForPagination(
+        new_initial_containing_block_size);
 
-      LayoutForPrinting();
-    }
+    LayoutForPrinting();
   }
 
   // If we don't fit in the given page width, we'll lay out again. If we don't
@@ -3269,13 +3263,14 @@
     layout_view->SetPaginationScaleFactor(layout_view->PaginationScaleFactor() *
                                           overall_scale_factor);
     PhysicalSize new_size =
-        layout_view->PageAreaSize(/* page_index */ 0u, first_page_name);
+        CalculateInitialContainingBlockSizeForPagination(document);
     layout_view->SetInitialContainingBlockSizeForPagination(new_size);
     LayoutForPrinting();
   }
 
-  if (TextAutosizer* text_autosizer = frame_->GetDocument()->GetTextAutosizer())
+  if (TextAutosizer* text_autosizer = document.GetTextAutosizer()) {
     text_autosizer->UpdatePageInfo();
+  }
   AdjustViewSize();
   UpdateStyleAndLayout();
 }
diff --git a/third_party/blink/renderer/core/frame/pagination_state.cc b/third_party/blink/renderer/core/frame/pagination_state.cc
index 70f071a..6ed08aee 100644
--- a/third_party/blink/renderer/core/frame/pagination_state.cc
+++ b/third_party/blink/renderer/core/frame/pagination_state.cc
@@ -79,12 +79,20 @@
       *GetPageContainer(layout_view, current_page_number_);
   float scale = TargetScaleForPage(page_container);
   const PhysicalBoxFragment& page_border_box = GetPageBorderBox(page_container);
-  // The page rectangle is in the coordinate system of layout, i.e. with layout
-  // scaling applied. Scale to target, to reverse layout scaling.
-  PhysicalRect target_content_rect = page_border_box.LocalRect();
+  // The content rectangle is in the coordinate system of layout, i.e. with
+  // layout scaling applied. Scale to target, to reverse layout scaling and to
+  // apply any shrinking needed to fit the target (if there's a given paper size
+  // to take into consideration).
+  PhysicalRect target_content_rect = page_border_box.ContentRect();
   target_content_rect.Scale(scale);
 
   gfx::Transform matrix;
+
+  // Translate by the distance from the top/left page border box to the top/left
+  // corner of the page content area, in the target coordinate system.
+  matrix.Translate(float(target_content_rect.offset.left),
+                   float(target_content_rect.offset.top));
+
   // Transform into the coordinate system used by layout.
   matrix.Scale(scale);
 
diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
index d121d86..95000299 100644
--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc
+++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
@@ -444,8 +444,11 @@
   // Add as a sibling of the parent if we have reached the maximum depth
   // allowed.
   if (open_elements_.StackDepth() > kMaximumHTMLParserDOMTreeDepth &&
-      task.parent->parentNode())
+      task.parent->parentNode()) {
+    UseCounter::Count(OwnerDocumentForCurrentNode(),
+                      WebFeature::kMaximumHTMLParserDOMTreeDepthHit);
     task.parent = task.parent->parentNode();
+  }
 
   DCHECK(task.parent);
   QueueTask(task, true);
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index dfa39c09..57c9b88 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -202,6 +202,8 @@
       return protocol::DOM::PseudoTypeEnum::Backdrop;
     case kPseudoIdSelection:
       return protocol::DOM::PseudoTypeEnum::Selection;
+    case kPseudoIdSearchText:
+      return protocol::DOM::PseudoTypeEnum::SearchText;
     case kPseudoIdTargetText:
       return protocol::DOM::PseudoTypeEnum::TargetText;
     case kPseudoIdSpellingError:
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index b64f138..d1aa553be 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -328,6 +328,7 @@
     DEFINE_STRING_MAPPING(PseudoFocusWithin)
     DEFINE_STRING_MAPPING(PseudoActive)
     DEFINE_STRING_MAPPING(PseudoChecked)
+    DEFINE_STRING_MAPPING(PseudoCurrent)
     DEFINE_STRING_MAPPING(PseudoEnabled)
     DEFINE_STRING_MAPPING(PseudoFullPageMedia)
     DEFINE_STRING_MAPPING(PseudoDefault)
@@ -415,6 +416,7 @@
     DEFINE_STRING_MAPPING(PseudoVideoPersistent)
     DEFINE_STRING_MAPPING(PseudoVideoPersistentAncestor)
     DEFINE_STRING_MAPPING(PseudoXrOverlay)
+    DEFINE_STRING_MAPPING(PseudoSearchText)
     DEFINE_STRING_MAPPING(PseudoTargetText)
     DEFINE_STRING_MAPPING(PseudoSelectorFragmentAnchor)
     DEFINE_STRING_MAPPING(PseudoModal)
diff --git a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h
index 89a222c..2b4211c 100644
--- a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h
+++ b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h
@@ -318,9 +318,8 @@
   std::optional<PhysicalOffset> ComputeAnchorCenterOffsets(
       const ComputedStyleBuilder&) override;
 
-  const LogicalAnchorQuery* AnchorQuery() const;
-
  private:
+  const LogicalAnchorQuery* AnchorQuery() const;
   const LogicalAnchorReference* ResolveAnchorReference(
       const AnchorSpecifierValue& anchor_specifier,
       const ScopedCSSName* position_anchor) const;
diff --git a/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc b/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc
index 91f3a5d..aea54fe 100644
--- a/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc
+++ b/third_party/blink/renderer/core/layout/anchor_position_scroll_data.cc
@@ -23,7 +23,7 @@
                                 : box.AcceptableImplicitAnchor();
 }
 
-const HeapVector<NonOverflowingScrollRange>* GetNonOverflowingScrollRanges(
+const Vector<NonOverflowingScrollRange>* GetNonOverflowingScrollRanges(
     const LayoutObject* layout_object) {
   if (!layout_object || !layout_object->IsOutOfFlowPositioned()) {
     return nullptr;
@@ -56,15 +56,6 @@
   return anchored_element_->GetAnchorPositionScrollData() == this;
 }
 
-gfx::Vector2dF AnchorPositionScrollData::TotalOffset(
-    const LayoutObject& anchor_object) const {
-  if (anchor_object == default_anchor_adjustment_data_.anchor_object) {
-    return default_anchor_adjustment_data_.TotalOffset();
-  }
-
-  return ComputeAdjustmentContainersData(anchor_object).TotalOffset();
-}
-
 AnchorPositionScrollData::AdjustmentData
 AnchorPositionScrollData::ComputeAdjustmentContainersData(
     const LayoutObject& anchor) const {
@@ -80,7 +71,6 @@
     return container;
   };
 
-  result.anchor_object = &anchor;
   const auto* bounding_container = container_ignore_layout_view_for_fixed_pos(
       *anchored_element_->GetLayoutObject());
 
@@ -184,24 +174,31 @@
   AdjustmentData new_adjustment_data = ComputeDefaultAnchorAdjustmentData();
 
   SnapshotDiff diff = SnapshotDiff::kNone;
-  if (default_anchor_adjustment_data_.anchor_object !=
-          new_adjustment_data.anchor_object ||
-      AdjustmentContainerIds() !=
-          new_adjustment_data.adjustment_container_ids ||
-      !IsFallbackPositionValid(new_adjustment_data)) {
+  if (AdjustmentContainerIds() !=
+      new_adjustment_data.adjustment_container_ids) {
     diff = SnapshotDiff::kScrollersOrFallbackPosition;
-  } else if (NeedsScrollAdjustmentInX() !=
-                 new_adjustment_data.needs_scroll_adjustment_in_x ||
-             NeedsScrollAdjustmentInY() !=
-                 new_adjustment_data.needs_scroll_adjustment_in_y ||
-             default_anchor_adjustment_data_.TotalOffset() !=
-                 new_adjustment_data.TotalOffset() ||
-             AccumulatedAdjustmentScrollOrigin() !=
-                 new_adjustment_data.accumulated_adjustment_scroll_origin) {
-    // When needs_scroll_adjustment_in_x/y changes, we still need to update
-    // paint properties so that compositor can calculate the translation
-    // offset correctly.
-    diff = SnapshotDiff::kOffsetOnly;
+  } else {
+    const bool anchor_scrolled =
+        TotalOffset() !=
+            new_adjustment_data.accumulated_adjustment +
+                new_adjustment_data.anchored_element_container_scroll_offset ||
+        AccumulatedAdjustmentScrollOrigin() !=
+            new_adjustment_data.accumulated_adjustment_scroll_origin;
+    if (anchor_scrolled &&
+        !IsFallbackPositionValid(
+            new_adjustment_data.accumulated_adjustment,
+            new_adjustment_data.anchored_element_container_scroll_offset)) {
+      diff = SnapshotDiff::kScrollersOrFallbackPosition;
+    } else if (anchor_scrolled ||
+               NeedsScrollAdjustmentInX() !=
+                   new_adjustment_data.needs_scroll_adjustment_in_x ||
+               NeedsScrollAdjustmentInY() !=
+                   new_adjustment_data.needs_scroll_adjustment_in_y) {
+      // When needs_scroll_adjustment_in_x/y changes, we still need to update
+      // paint properties so that compositor can calculate the translation
+      // offset correctly.
+      diff = SnapshotDiff::kOffsetOnly;
+    }
   }
 
   if (update && diff != SnapshotDiff::kNone) {
@@ -212,8 +209,9 @@
 }
 
 bool AnchorPositionScrollData::IsFallbackPositionValid(
-    const AdjustmentData& new_adjustment_data) const {
-  const HeapVector<NonOverflowingScrollRange>* non_overflowing_scroll_ranges =
+    const gfx::Vector2dF& new_accumulated_adjustment,
+    const gfx::Vector2dF& new_anchored_element_container_scroll_offset) const {
+  const Vector<NonOverflowingScrollRange>* non_overflowing_scroll_ranges =
       GetNonOverflowingScrollRanges(anchored_element_->GetLayoutObject());
   if (!non_overflowing_scroll_ranges ||
       non_overflowing_scroll_ranges->empty()) {
@@ -222,31 +220,39 @@
 
   for (const NonOverflowingScrollRange& range :
        *non_overflowing_scroll_ranges) {
-    if (range.anchor_object != new_adjustment_data.anchor_object) {
-      // The range was calculated with a different anchor object. Check if the
-      // anchored element (which previously overflowed with the try option that
-      // specified that anchor) will become non-overflowing with that option.
-      if (range.Contains(TotalOffset(*range.anchor_object))) {
-        return false;
-      }
-    } else {
-      // The range was calculated with the same anchor object as this data.
-      // Check if the overflow status of the anchored element will change with
-      // the new total offset.
-      if (range.Contains(default_anchor_adjustment_data_.TotalOffset()) !=
-          range.Contains(new_adjustment_data.TotalOffset())) {
-        return false;
-      }
+    if (range.Contains(TotalOffset()) !=
+        range.Contains(new_accumulated_adjustment +
+                       new_anchored_element_container_scroll_offset)) {
+      return false;
     }
   }
   return true;
 }
 
 void AnchorPositionScrollData::UpdateSnapshot() {
-  ValidateSnapshot();
+  if (!IsActive()) {
+    return;
+  }
+
+  SnapshotDiff diff = TakeAndCompareSnapshot(true /* update */);
+  switch (diff) {
+    case SnapshotDiff::kNone:
+      return;
+    case SnapshotDiff::kOffsetOnly:
+      InvalidatePaint();
+      return;
+    case SnapshotDiff::kScrollersOrFallbackPosition:
+      InvalidateLayoutAndPaint();
+      return;
+  }
 }
 
 bool AnchorPositionScrollData::ValidateSnapshot() {
+  if (is_snapshot_validated_) {
+    return true;
+  }
+  is_snapshot_validated_ = true;
+
   // If this AnchorPositionScrollData is detached in the previous style recalc,
   // we no longer need to validate it.
   if (!IsActive()) {
@@ -256,9 +262,10 @@
   SnapshotDiff diff = TakeAndCompareSnapshot(true /* update */);
   switch (diff) {
     case SnapshotDiff::kNone:
-      return true;
     case SnapshotDiff::kOffsetOnly:
-      InvalidatePaint();
+      // We don't need to rewind to layout recalc for offset-only diff, as this
+      // function is called at LayoutClean during lifecycle update, and
+      // offset-only diff only needs paint update.
       return true;
     case SnapshotDiff::kScrollersOrFallbackPosition:
       InvalidateLayoutAndPaint();
@@ -297,7 +304,6 @@
 
 void AnchorPositionScrollData::Trace(Visitor* visitor) const {
   visitor->Trace(anchored_element_);
-  visitor->Trace(default_anchor_adjustment_data_);
   visitor->Trace(position_visibility_observer_);
   ScrollSnapshotClient::Trace(visitor);
   ElementRareDataField::Trace(visitor);
diff --git a/third_party/blink/renderer/core/layout/anchor_position_scroll_data.h b/third_party/blink/renderer/core/layout/anchor_position_scroll_data.h
index 8d59a8a..04c76e4 100644
--- a/third_party/blink/renderer/core/layout/anchor_position_scroll_data.h
+++ b/third_party/blink/renderer/core/layout/anchor_position_scroll_data.h
@@ -66,20 +66,11 @@
     return default_anchor_adjustment_data_.needs_scroll_adjustment_in_y;
   }
 
-  // Returns the total offset of the anchored element from the layout location
-  // due to scroll and other adjustments from the containers between the given
-  // `anchor_object` and the anchored element and the scroll container of the
-  // anchored element itself. There are two cases:
-  // 1. If `anchor_object` is the anchor object used to create the snapshot,
-  //    The result will be from the last snapshotted result.
-  // 2. Otherwise the result will be calculated on the fly, which may use stale
-  //    layout data if this is called during layout.
-  // ValidateSnapshot() (called after the first layout during a lifecycle
-  // update) will reschedule layout, or ShouldScheduleNextService() (called at
-  // the end of a lifecycle update) will schedule another lifecycle update,
-  // if the final layout data may cause layout changes.
-  gfx::Vector2dF TotalOffset(const LayoutObject& anchor_object) const;
-
+  gfx::Vector2dF TotalOffset() const {
+    return default_anchor_adjustment_data_.accumulated_adjustment +
+           default_anchor_adjustment_data_
+               .anchored_element_container_scroll_offset;
+  }
   gfx::Vector2dF AccumulatedAdjustment() const {
     return default_anchor_adjustment_data_.accumulated_adjustment;
   }
@@ -136,9 +127,6 @@
   struct AdjustmentData {
     DISALLOW_NEW();
 
-    // The anchor object used when calculating this data.
-    Member<const LayoutObject> anchor_object;
-
     // Compositor element ids of the ancestor scroll adjustment containers
     // (see the class documentation) of some element (anchor), up to the
     // containing block of `anchored_element_` (exclusively), along the
@@ -171,12 +159,6 @@
     bool needs_scroll_adjustment_in_y = false;
 
     bool has_chained_anchor = false;
-
-    void Trace(Visitor* visitor) const { visitor->Trace(anchor_object); }
-
-    gfx::Vector2dF TotalOffset() const {
-      return accumulated_adjustment + anchored_element_container_scroll_offset;
-    }
   };
 
   AdjustmentData ComputeAdjustmentContainersData(
@@ -185,11 +167,17 @@
   // Takes an up-to-date snapshot, and compares it with the existing one.
   // If `update` is true, also rewrites the existing snapshot.
   SnapshotDiff TakeAndCompareSnapshot(bool update);
-  bool IsFallbackPositionValid(const AdjustmentData& new_adjustment_data) const;
+  bool IsFallbackPositionValid(
+      const gfx::Vector2dF& new_accumulated_adjustment,
+      const gfx::Vector2dF& new_anchored_element_container_scroll_offset) const;
 
   void InvalidateLayoutAndPaint();
   void InvalidatePaint();
 
+  // ValidateSnapshot is called every frame, but AnchorPositionScrollData only
+  // needs to perform the validation once (during the frame it was created).
+  bool is_snapshot_validated_ = false;
+
   // The anchor-positioned element.
   Member<Element> anchored_element_;
 
diff --git a/third_party/blink/renderer/core/layout/constraint_space.h b/third_party/blink/renderer/core/layout/constraint_space.h
index dab100b..874d091e 100644
--- a/third_party/blink/renderer/core/layout/constraint_space.h
+++ b/third_party/blink/renderer/core/layout/constraint_space.h
@@ -86,6 +86,19 @@
 // store a result in.
 enum class LayoutResultCacheSlot { kLayout, kMeasure };
 
+// How to resolve percentage-based margin and padding.
+enum class DecorationPercentageResolutionType {
+  // Resolve margins and padding on any side against the inline-size of the
+  // containing block. This is the default, and the behavior for regular CSS
+  // boxes.
+  kContainingBlockInlineSize,
+
+  // Resolve block margins and padding against the block-size of the containing
+  // block, and inline ones against the inline-size of the containing block.
+  // This is only used by @page boxes.
+  kContainingBlockSize
+};
+
 // The ConstraintSpace represents a set of constraints and available space
 // which a layout algorithm may produce a LogicalFragment within.
 class CORE_EXPORT ConstraintSpace final {
@@ -261,12 +274,17 @@
 
   // Return the size to use for percentage resolution for margin/padding.
   LogicalSize MarginPaddingPercentageResolutionSize() const {
+    if (GetDecorationPercentageResolutionType() ==
+        DecorationPercentageResolutionType::kContainingBlockSize) {
+      // @page margin and padding are different from those on regular CSS boxes.
+      // Inline percentages are resolved against the inline-size of the margin
+      // box, and block percentages are resolved against its block-size.
+      DCHECK(!IsOrthogonalWritingModeRoot());
+      return PercentageResolutionSize();
+    }
+
     // For regular CSS boxes, percentage-based margin and padding get computed
     // relatively to the inline-size of the containing block.
-    //
-    // TODO(mstensho): @page margin and padding resolution is different from the
-    // rest. Inline percentages are resolved against the inline-size of the
-    // margin box, and block percentages are resolved against its block-size.
     LayoutUnit cb_inline_size;
     if (!IsOrthogonalWritingModeRoot()) {
       cb_inline_size = PercentageResolutionInlineSize();
@@ -785,6 +803,16 @@
     return HasRareData() && rare_data_->should_text_box_trim_end;
   }
 
+  // Return how percentage-based margins and padding should be resolved.
+  DecorationPercentageResolutionType GetDecorationPercentageResolutionType()
+      const {
+    if (!HasRareData()) {
+      return DecorationPercentageResolutionType::kContainingBlockInlineSize;
+    }
+    return static_cast<DecorationPercentageResolutionType>(
+        rare_data_->decoration_percentage_resolution_type);
+  }
+
   const GridLayoutSubtree* GetGridLayoutSubtree() const {
     return HasRareData() ? rare_data_->GetGridLayoutSubtree() : nullptr;
   }
@@ -947,7 +975,9 @@
           should_repeat(other.should_repeat),
           is_inside_repeatable_content(other.is_inside_repeatable_content),
           should_text_box_trim_start(other.should_text_box_trim_start),
-          should_text_box_trim_end(other.should_text_box_trim_end) {
+          should_text_box_trim_end(other.should_text_box_trim_end),
+          decoration_percentage_resolution_type(
+              other.decoration_percentage_resolution_type) {
       switch (GetDataUnionType()) {
         case DataUnionType::kNone:
           break;
@@ -1030,7 +1060,9 @@
           should_repeat != other.should_repeat ||
           is_inside_repeatable_content != other.is_inside_repeatable_content ||
           should_text_box_trim_start != other.should_text_box_trim_start ||
-          should_text_box_trim_end != other.should_text_box_trim_end) {
+          should_text_box_trim_end != other.should_text_box_trim_end ||
+          decoration_percentage_resolution_type !=
+              other.decoration_percentage_resolution_type) {
         return false;
       }
 
@@ -1069,7 +1101,8 @@
           min_break_appeal != kBreakAppealLastResort ||
           propagate_child_break_values || is_at_fragmentainer_start ||
           should_repeat || is_inside_repeatable_content ||
-          should_text_box_trim_start || should_text_box_trim_end) {
+          should_text_box_trim_start || should_text_box_trim_end ||
+          decoration_percentage_resolution_type) {
         return false;
       }
 
@@ -1344,6 +1377,8 @@
     unsigned is_inside_repeatable_content : 1 = false;
     unsigned should_text_box_trim_start : 1 = false;
     unsigned should_text_box_trim_end : 1 = false;
+    unsigned decoration_percentage_resolution_type : 1 = static_cast<unsigned>(
+        DecorationPercentageResolutionType::kContainingBlockInlineSize);
 
    private:
     struct BlockData {
diff --git a/third_party/blink/renderer/core/layout/constraint_space_builder.h b/third_party/blink/renderer/core/layout/constraint_space_builder.h
index b56b1fe..48b5082 100644
--- a/third_party/blink/renderer/core/layout/constraint_space_builder.h
+++ b/third_party/blink/renderer/core/layout/constraint_space_builder.h
@@ -498,6 +498,12 @@
     space_.EnsureRareData()->should_text_box_trim_end = true;
   }
 
+  void SetDecorationPercentageResolutionType(
+      DecorationPercentageResolutionType type) {
+    space_.EnsureRareData()->decoration_percentage_resolution_type =
+        static_cast<unsigned>(type);
+  }
+
   void SetIsPushedByFloats() {
     space_.EnsureRareData()->is_pushed_by_floats = true;
   }
diff --git a/third_party/blink/renderer/core/layout/geometry/logical_size.h b/third_party/blink/renderer/core/layout/geometry/logical_size.h
index a2086ed1..9bba1dda 100644
--- a/third_party/blink/renderer/core/layout/geometry/logical_size.h
+++ b/third_party/blink/renderer/core/layout/geometry/logical_size.h
@@ -46,6 +46,11 @@
     return !(*this == other);
   }
 
+  LogicalSize operator*(float scale) const {
+    return LogicalSize(LayoutUnit(inline_size * scale),
+                       LayoutUnit(block_size * scale));
+  }
+
   constexpr bool IsEmpty() const {
     return inline_size == LayoutUnit() || block_size == LayoutUnit();
   }
@@ -83,6 +88,10 @@
   return a;
 }
 
+inline LogicalSize operator+(const LogicalSize& a, const BoxStrut& b) {
+  return {a.inline_size + b.InlineSum(), a.block_size + b.BlockSum()};
+}
+
 inline LogicalOffset operator+(const LogicalOffset& offset,
                                const LogicalSize& size) {
   return {offset.inline_offset + size.inline_size,
diff --git a/third_party/blink/renderer/core/layout/inline/inline_item_result.cc b/third_party/blink/renderer/core/layout/inline/inline_item_result.cc
index 27e8a68..8feadbc 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_item_result.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_item_result.cc
@@ -80,7 +80,7 @@
     builder.Append(
         ifc_text_content.Substring(TextOffset().start, TextOffset().Length())
             .EncodeForDebugging());
-  } else if (item->Type() == InlineItem::kOpenRubyColumn && ruby_column) {
+  } else if (IsRubyColumn()) {
     builder.Append(item->GetLayoutObject()->ToString());
     builder.Append(", base_line: [\n");
     String child_indent = indent + "\t";
diff --git a/third_party/blink/renderer/core/layout/inline/inline_item_result.h b/third_party/blink/renderer/core/layout/inline/inline_item_result.h
index 0b5683d..93105deb 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_item_result.h
+++ b/third_party/blink/renderer/core/layout/inline/inline_item_result.h
@@ -83,6 +83,10 @@
   InlineItemTextIndex Start() const { return {item_index, StartOffset()}; }
   InlineItemTextIndex End() const { return {item_index, EndOffset()}; }
 
+  // Return `true` if the InlineItem type is kOpenRubyColumn and this contains
+  // data for the base and annotation lines.
+  bool IsRubyColumn() const { return ruby_column; }
+
   // Compute/clear |hyphen_string| and |hyphen_shape_result|.
   void ShapeHyphen();
 
diff --git a/third_party/blink/renderer/core/layout/inline/inline_item_result_ruby_column.h b/third_party/blink/renderer/core/layout/inline/inline_item_result_ruby_column.h
index 602abeb..99dfe71 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_item_result_ruby_column.h
+++ b/third_party/blink/renderer/core/layout/inline/inline_item_result_ruby_column.h
@@ -26,6 +26,11 @@
   // A list of ruby-position values.  The size of this list must be same as
   // annotation_line_list.
   Vector<RubyPosition, 1> position_list;
+
+  // This is true if a ruby column is split into multiple segments by line
+  // breaking, and this InlineItemResult represents the second or later
+  // segment.
+  bool is_continuation = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc
index 87d63a65..7c5c071b 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.cc
@@ -296,7 +296,8 @@
 
   // If not, rebuild the box states for the break token.
   box_states_ = context_->ResetBoxStates();
-  LogicalLineBuilder(Node(), GetConstraintSpace(), box_states_, context_)
+  LogicalLineBuilder(Node(), GetConstraintSpace(), nullptr, box_states_,
+                     context_)
       .RebuildBoxStates(line_info, 0u, break_token->StartItemIndex());
 }
 
@@ -328,7 +329,7 @@
     return;
   }
   InlineLayoutStateStack rebuilt;
-  LogicalLineBuilder(Node(), GetConstraintSpace(), &rebuilt, context_)
+  LogicalLineBuilder(Node(), GetConstraintSpace(), nullptr, &rebuilt, context_)
       .RebuildBoxStates(line_info, 0u, GetBreakToken()->StartItemIndex());
   LogicalLineItems& line_box = context_->AcquireTempLogicalLineItems();
   rebuilt.OnBeginPlaceItems(Node(), line_info.LineStyle(), baseline_type_,
@@ -365,8 +366,8 @@
   // Clear the current line without releasing the buffer.
   line_container->Shrink();
 
-  LogicalLineBuilder line_builder(Node(), GetConstraintSpace(), box_states_,
-                                  context_);
+  LogicalLineBuilder line_builder(Node(), GetConstraintSpace(), GetBreakToken(),
+                                  box_states_, context_);
   line_builder.CreateLine(line_info, line_box, this);
 
   const LayoutUnit hang_width = line_info->HangWidth();
@@ -546,28 +547,7 @@
   const ConstraintSpace& space = GetConstraintSpace();
   if (UNLIKELY(space.ShouldTextBoxTrimStart() ||
                space.ShouldTextBoxTrimEnd())) {
-    // TODO(crbug.com/40254880): Just flags, trimming data isn't implemented.
-    if (space.ShouldTextBoxTrimStart() && line_info->IsFirstFormattedLine()) {
-      // Apply `text-box-trim: start` if this is the first formatted line.
-      // TODO(crbug.com/40254880): The edge should be determined by
-      // `text-box-edge` property.
-      FontHeight intrinsic_metrics =
-          Node().Style().GetFontHeight(baseline_type_);
-      LayoutUnit offset_for_trimming_box =
-          intrinsic_metrics.ascent - line_box_metrics.ascent;
-      container_builder_.SetIntrinsicMetrics(intrinsic_metrics);
-      container_builder_.SetLineBoxBfcBlockOffset(
-          container_builder_.LineBoxBfcBlockOffset()
-              ? offset_for_trimming_box +
-                    container_builder_.LineBoxBfcBlockOffset().value()
-              : offset_for_trimming_box);
-      container_builder_.SetIsTextBoxTrimApplied();
-    }
-    if (space.ShouldTextBoxTrimEnd() && !line_info->GetBreakToken()) {
-      // Apply `text-box-trim: end` if this is the last line.
-      container_builder_.SetIsTextBoxTrimApplied();
-    }
-    // TODO(crbug.com/40254880): Block-in-inline case probably needs a logic.
+    ApplyTextBoxTrim(*line_info);
   }
 
   // |container_builder_| is already set up by |PlaceBlockInInline|.
@@ -605,6 +585,32 @@
   container_builder_.SetInlineSize(inline_size);
 }
 
+void InlineLayoutAlgorithm::ApplyTextBoxTrim(const LineInfo& line_info) {
+  const ConstraintSpace& space = GetConstraintSpace();
+  DCHECK(space.ShouldTextBoxTrimStart() || space.ShouldTextBoxTrimEnd());
+  if (space.ShouldTextBoxTrimStart() && line_info.IsFirstFormattedLine()) {
+    // Apply `text-box-trim: start` if this is the first formatted line.
+    // TODO(crbug.com/40254880): The edge should be determined by
+    // `text-box-edge` property.
+    const FontHeight line_box_metrics = container_builder_.Metrics();
+    FontHeight intrinsic_metrics = Node().Style().GetFontHeight(baseline_type_);
+    LayoutUnit offset_for_trimming_box =
+        intrinsic_metrics.ascent - line_box_metrics.ascent;
+    container_builder_.SetIntrinsicMetrics(intrinsic_metrics);
+    container_builder_.SetLineBoxBfcBlockOffset(
+        container_builder_.LineBoxBfcBlockOffset()
+            ? offset_for_trimming_box +
+                  container_builder_.LineBoxBfcBlockOffset().value()
+            : offset_for_trimming_box);
+    container_builder_.SetIsTextBoxTrimApplied();
+  }
+  if (space.ShouldTextBoxTrimEnd() && !line_info.GetBreakToken()) {
+    // Apply `text-box-trim: end` if this is the last line.
+    container_builder_.SetIsTextBoxTrimApplied();
+  }
+  // TODO(crbug.com/40254880): Block-in-inline case probably needs a logic.
+}
+
 void InlineLayoutAlgorithm::PlaceBlockInInline(const InlineItem& item,
                                                InlineItemResult* item_result,
                                                LogicalLineItems* line_box) {
diff --git a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.h b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.h
index e41f5e2..dcf5d57 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm.h
@@ -89,6 +89,8 @@
 
   LayoutUnit ApplyTextAlign(LineInfo*);
 
+  void ApplyTextBoxTrim(const LineInfo&);
+
   // Add any trailing clearance requested by a BR 'clear' attribute on the line.
   // Return true if this was successful (this also includes cases where there is
   // no clearance needed). Return false if the floats that we need to clear past
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node.cc b/third_party/blink/renderer/core/layout/inline/inline_node.cc
index 404acbb..c2c3949bf 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node.cc
@@ -1913,7 +1913,7 @@
             continue;
           }
         }
-        if (item.Type() == InlineItem::kOpenRubyColumn && result.ruby_column) {
+        if (result.IsRubyColumn()) {
           ComputeFromMinSizeInternal(result.ruby_column->base_line);
           continue;
         }
diff --git a/third_party/blink/renderer/core/layout/inline/justification_utils.cc b/third_party/blink/renderer/core/layout/inline/justification_utils.cc
index dbff52df..09d25b5b 100644
--- a/third_party/blink/renderer/core/layout/inline/justification_utils.cc
+++ b/third_party/blink/renderer/core/layout/inline/justification_utils.cc
@@ -44,11 +44,14 @@
         line_text_builder.Append(kTextCombineItemMarker);
         continue;
       }
-      if (item_result.item->Type() == InlineItem::kOpenRubyColumn &&
-          item_result.ruby_column) {
-        line_text_builder.Append(StringView(text_content,
-                                            item_result.item->StartOffset(),
-                                            item_result.item->Length()));
+      if (item_result.IsRubyColumn()) {
+        // No need to add k*IsolateCharacter for kOpenRubyColumn if
+        // is_continuation is true. It is not followed by `base_line` results.
+        if (!item_result.ruby_column->is_continuation) {
+          line_text_builder.Append(StringView(text_content,
+                                              item_result.item->StartOffset(),
+                                              item_result.item->Length()));
+        }
         // Add the ruby-base results only if the ruby-base is wider than its
         // ruby-text. Shorter ruby-bases don't participate in the justification
         // for the whole line.
@@ -151,8 +154,7 @@
         // |spacing_before| is non-zero only before CJK characters.
         DCHECK_EQ(spacing_before, 0.0f);
       }
-    } else if (item_result.item->Type() == InlineItem::kOpenRubyColumn &&
-               item_result.ruby_column) {
+    } else if (item_result.IsRubyColumn()) {
       LineInfo& base_line = item_result.ruby_column->base_line;
       if (item_result.inline_size == base_line.Width()) {
         JustifyResults(text_content, line_text, line_text_start_offset, spacing,
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.cc b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
index 8b858cb6..4cfcad79 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.cc
@@ -451,12 +451,12 @@
                            node.IsStickyImagesQuirkForContentSize()),
       use_faster_min_content_(
           RuntimeEnabledFeatures::FasterMinContentEnabled()),
-      items_data_(node.ItemsData(use_first_line_style_)),
-      end_item_index_(items_data_.items.size()),
+      items_data_(&node.ItemsData(use_first_line_style_)),
+      end_item_index_(items_data_->items.size()),
       text_content_(
           !sticky_images_quirk_
-              ? items_data_.text_content
-              : InlineNode::TextContentForStickyImagesQuirk(items_data_)),
+              ? items_data_->text_content
+              : InlineNode::TextContentForStickyImagesQuirk(*items_data_)),
       constraint_space_(space),
       exclusion_space_(exclusion_space),
       break_token_(break_token),
@@ -517,7 +517,7 @@
   current_ = break_token->Start();
   break_iterator_.SetStartOffset(current_.text_offset);
   is_after_forced_break_ = break_token->IsForcedBreak();
-  items_data_.AssertOffset(current_);
+  items_data_->AssertOffset(current_);
   SetCurrentStyle(*line_initial_style);
 }
 
@@ -543,7 +543,7 @@
 inline InlineItemResult* LineBreaker::AddItem(const InlineItem& item,
                                               unsigned end_offset,
                                               LineInfo* line_info) {
-  DCHECK_EQ(&item, &items_data_.items[current_.item_index]);
+  DCHECK_EQ(&item, &items_data_->items[current_.item_index]);
   DCHECK_GE(current_.text_offset, item.StartOffset());
   DCHECK_GE(end_offset, current_.text_offset);
   DCHECK_LE(end_offset, item.EndOffset());
@@ -645,7 +645,7 @@
 
   // Compute which tags are not closed at |current_.item_index|.
   InlineItemsData::OpenTagItems open_items;
-  items_data_.GetOpenTagItems(0u, current_.item_index, &open_items);
+  items_data_->GetOpenTagItems(0u, current_.item_index, &open_items);
 
   for (const InlineItem* item : open_items) {
     if (item->Style()->BoxDecorationBreak() == EBoxDecorationBreak::kClone) {
@@ -758,7 +758,14 @@
   const InlineItemResults& item_results = line_info->Results();
   DCHECK(item_results.empty());
 
-  if (!current_.IsZero()) {
+  if (parent_breaker_) {
+    previous_line_had_forced_break_ =
+        parent_breaker_->previous_line_had_forced_break_;
+    is_after_forced_break_ = parent_breaker_->is_after_forced_break_;
+    is_first_formatted_line_ = parent_breaker_->is_first_formatted_line_;
+    use_first_line_style_ = parent_breaker_->use_first_line_style_;
+    items_data_ = parent_breaker_->items_data_;
+  } else if (!current_.IsZero()) {
     // We're past the first line
     previous_line_had_forced_break_ = is_after_forced_break_;
     is_after_forced_break_ = false;
@@ -768,7 +775,7 @@
 
   line_info->SetStart(current_);
   line_info->SetIsFirstFormattedLine(is_first_formatted_line_);
-  line_info->SetLineStyle(node_, items_data_, use_first_line_style_);
+  line_info->SetLineStyle(node_, *items_data_, use_first_line_style_);
 
   DCHECK(!line_info->TextIndent());
   if (is_first_formatted_line_) {
@@ -2031,15 +2038,15 @@
                                           unsigned end,
                                           ShapeOptions options) {
   ShapeResult* shape_result = nullptr;
-  if (!items_data_.segments) {
+  if (!items_data_->segments) {
     RunSegmenter::RunSegmenterRange segment_range =
         InlineItemSegment::UnpackSegmentData(start, end, item.SegmentData());
     shape_result = shaper_.Shape(&item.Style()->GetFont(), item.Direction(),
                                  start, end, segment_range, options);
   } else {
-    shape_result = items_data_.segments->ShapeText(
+    shape_result = items_data_->segments->ShapeText(
         &shaper_, &item.Style()->GetFont(), item.Direction(), start, end,
-        base::checked_cast<unsigned>(&item - items_data_.items.begin()),
+        base::checked_cast<unsigned>(&item - items_data_->items.begin()),
         options);
   }
   if (UNLIKELY(spacing_.HasSpacing()))
@@ -2522,7 +2529,7 @@
         ResetRewindLoopDetector();
         Rewind(end_index, line_info);
         current_ = end;
-        items_data_.AssertOffset(current_.item_index, current_.text_offset);
+        items_data_->AssertOffset(current_.item_index, current_.text_offset);
       }
       break;
     }
@@ -3272,7 +3279,7 @@
       /* column_spanner_path */ nullptr, &empty_exclusion_space);
   sub_line_breaker.disallow_auto_wrap_ = disallow_auto_wrap;
   sub_line_breaker.SetInputRange(start, end_item_index,
-                                 initial_whitespace_state);
+                                 initial_whitespace_state, this);
   // OverrideAvailableWidth() prevents HandleFloat() from updating
   // available_width_.
   sub_line_breaker.OverrideAvailableWidth(limit);
@@ -3828,7 +3835,7 @@
                 available_width + width_to_rewind + item_result->inline_size;
             DCHECK_EQ(position_, line_info->ComputeWidth());
             current_ = item_result->End();
-            items_data_.AssertOffset(current_);
+            items_data_->AssertOffset(current_);
             HandleTrailingSpaces(item, line_info);
             return;
           }
@@ -4306,11 +4313,13 @@
 
 void LineBreaker::SetInputRange(InlineItemTextIndex start,
                                 wtf_size_t end_item_index,
-                                WhitespaceState initial_whitespace_state) {
+                                WhitespaceState initial_whitespace_state,
+                                const LineBreaker* parent) {
   DCHECK(RuntimeEnabledFeatures::RubyLineBreakableEnabled());
   current_ = start;
   end_item_index_ = end_item_index;
   initial_whitespace_ = initial_whitespace_state;
+  parent_breaker_ = parent;
 }
 
 const InlineBreakToken* LineBreaker::CreateBreakToken(
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.h b/third_party/blink/renderer/core/layout/inline/line_breaker.h
index 9c33974..ce17823 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.h
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.h
@@ -53,7 +53,7 @@
               ExclusionSpace*);
   ~LineBreaker();
 
-  const InlineItemsData& ItemsData() const { return items_data_; }
+  const InlineItemsData& ItemsData() const { return *items_data_; }
 
   // True if the last line has `box-decoration-break: clone`, which affected the
   // size.
@@ -123,13 +123,14 @@
   // This LineBreaker handles only [start, end_item_index) of `Items()`.
   void SetInputRange(InlineItemTextIndex start,
                      wtf_size_t end_item_index,
-                     WhitespaceState initial_whitespace_state);
+                     WhitespaceState initial_whitespace_state,
+                     const LineBreaker* parent);
 
  private:
   Document& GetDocument() const { return node_.GetDocument(); }
 
   const String& Text() const { return text_content_; }
-  const HeapVector<InlineItem>& Items() const { return items_data_.items; }
+  const HeapVector<InlineItem>& Items() const { return items_data_->items; }
 
   String TextContentForLineBreak() const;
 
@@ -392,7 +393,7 @@
   bool has_considered_creating_break_token_ = false;
 #endif
 
-  const InlineItemsData& items_data_;
+  const InlineItemsData* items_data_;
 
   // `end_item_index_` is usually `Items().size()`.
   // SetInputRange() updates it.
@@ -465,6 +466,9 @@
 
   // This has a valid object if is_svg_text_.
   std::unique_ptr<ResolvedTextLayoutAttributesIterator> svg_resolved_iterator_;
+
+  // This member is available after calling SetInputRange().
+  const LineBreaker* parent_breaker_ = nullptr;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc b/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc
index 648588c..e6f9076 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker_test.cc
@@ -1140,7 +1140,8 @@
                            LineLayoutOpportunity(LayoutUnit::Max()),
                            leading_floats, nullptr, nullptr, &exclusion_space);
   // <span> to just after </span>.
-  line_breaker.SetInputRange({1, 6}, 4, LineBreaker::WhitespaceState::kLeading);
+  line_breaker.SetInputRange({1, 6}, 4, LineBreaker::WhitespaceState::kLeading,
+                             nullptr);
   LineInfo line_info;
   line_breaker.NextLine(&line_info);
   // The result should contain only <span>...</span>.
diff --git a/third_party/blink/renderer/core/layout/inline/line_info.cc b/third_party/blink/renderer/core/layout/inline/line_info.cc
index 61603ad..07a450e 100644
--- a/third_party/blink/renderer/core/layout/inline/line_info.cc
+++ b/third_party/blink/renderer/core/layout/inline/line_info.cc
@@ -176,18 +176,22 @@
   return ItemsData().text_content.length();
 }
 
-unsigned LineInfo::InflowEndOffset() const {
+unsigned LineInfo::InflowEndOffsetInternal(bool skip_forced_break) const {
   for (const auto& item_result : base::Reversed(Results())) {
     DCHECK(item_result.item);
     const InlineItem& item = *item_result.item;
+    if (skip_forced_break && item.Type() == InlineItem::kControl &&
+        ItemsData().text_content[item.StartOffset()] == kNewlineCharacter) {
+      continue;
+    }
     if (item.Type() == InlineItem::kText ||
         item.Type() == InlineItem::kControl ||
         item.Type() == InlineItem::kAtomicInline) {
       return item_result.EndOffset();
-    } else if (item.Type() == InlineItem::kOpenRubyColumn &&
-               item_result.ruby_column) {
+    } else if (item_result.IsRubyColumn()) {
       const LineInfo& base_line = item_result.ruby_column->base_line;
-      unsigned end_offset = base_line.InflowEndOffset();
+      unsigned end_offset =
+          base_line.InflowEndOffsetInternal(skip_forced_break);
       if (end_offset != base_line.StartOffset()) {
         return end_offset;
       }
diff --git a/third_party/blink/renderer/core/layout/inline/line_info.h b/third_party/blink/renderer/core/layout/inline/line_info.h
index eb31f41..c99d267 100644
--- a/third_party/blink/renderer/core/layout/inline/line_info.h
+++ b/third_party/blink/renderer/core/layout/inline/line_info.h
@@ -191,7 +191,12 @@
   unsigned EndTextOffset() const;
   // End text offset of this line, excluding out-of-flow objects such as
   // floating or positioned.
-  unsigned InflowEndOffset() const;
+  unsigned InflowEndOffset() const {
+    return InflowEndOffsetInternal(/* skip_forced_break */ false);
+  }
+  unsigned InflowEndOffsetWithoutForcedBreak() const {
+    return InflowEndOffsetInternal(/* skip_forced_break */ true);
+  }
   // End text offset for `text-align: justify`. This excludes preserved trailing
   // spaces. Available only when |TextAlign()| is |kJustify|.
   unsigned EndOffsetForJustify() const {
@@ -276,6 +281,7 @@
   // The width of preserved trailing spaces.
   LayoutUnit ComputeTrailingSpaceWidth(
       unsigned* end_offset_out = nullptr) const;
+  unsigned InflowEndOffsetInternal(bool skip_forced_break) const;
 
   Member<const InlineItemsData> items_data_;
   Member<const ComputedStyle> line_style_;
diff --git a/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc b/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
index 404edda..32e06faff 100644
--- a/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
+++ b/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
@@ -26,10 +26,12 @@
 
 LogicalLineBuilder::LogicalLineBuilder(InlineNode node,
                                        const ConstraintSpace& constraint_space,
+                                       const InlineBreakToken* break_token,
                                        InlineLayoutStateStack* state_stack,
                                        InlineChildLayoutContext* context)
     : node_(node),
       constraint_space_(constraint_space),
+      break_token_(break_token),
       box_states_(state_stack),
       context_(context),
       baseline_type_(node.Style().GetFontBaseline()),
@@ -424,8 +426,20 @@
     LogicalLineItems& line_box,
     InlineBoxState* box) {
   InlineItemResultRubyColumn& ruby_column = *item_result.ruby_column;
+  bool on_start_edge = false;
+  bool on_end_edge = false;
+  if (RuntimeEnabledFeatures::RubyLineEdgeAlignmentEnabled() &&
+      !node_.IsBidiEnabled()) {
+    on_start_edge =
+        ruby_column.base_line.StartOffset() == line_info.StartOffset() ||
+        item_result.StartOffset() == line_info.StartOffset();
+    wtf_size_t end_text_offset = ruby_column.base_line.EndTextOffset();
+    wtf_size_t inflow_end = line_info.InflowEndOffsetWithoutForcedBreak();
+    on_end_edge = end_text_offset == inflow_end;
+  }
   std::pair<LayoutUnit, LayoutUnit> base_insets =
-      ApplyRubyAlign(item_result.inline_size, ruby_column.base_line);
+      ApplyRubyAlign(item_result.inline_size, on_start_edge, on_end_edge,
+                     ruby_column.base_line);
 
   // Set up LogicalRubyColumns. This should be done before consuming the base
   // InlineItemResults because it might contain ruby columns, and annotation
@@ -477,11 +491,20 @@
     LineInfo& annotation_line,
     LogicalRubyColumn& logical_column) {
   std::pair<LayoutUnit, LayoutUnit> insets =
-      ApplyRubyAlign(item_result.inline_size, annotation_line);
+      ApplyRubyAlign(item_result.inline_size, /* on_start_edge */ false,
+                     /* on_end_edge */ false, annotation_line);
 
   auto* line_items = MakeGarbageCollected<LogicalLineItems>();
-  LogicalLineBuilder annotation_builder(node_, constraint_space_,
+  LogicalLineBuilder annotation_builder(node_, constraint_space_, nullptr,
                                         &logical_column.state_stack, context_);
+  if (item_result.ruby_column->is_continuation &&
+      !annotation_line.Results().empty()) {
+    CHECK(break_token_->RubyData());
+    annotation_builder.RebuildBoxStates(
+        annotation_line,
+        break_token_->RubyData()->annotation_data[index].start_item_index,
+        annotation_line.Results()[0].item_index);
+  }
   annotation_builder.CreateLine(&annotation_line, line_items,
                                 /* main_line_helper */ nullptr);
   ApplyLeftAndRightExpansion(insets.first, insets.second, line_items->begin(),
diff --git a/third_party/blink/renderer/core/layout/inline/logical_line_builder.h b/third_party/blink/renderer/core/layout/inline/logical_line_builder.h
index 52e9864..f57dd985 100644
--- a/third_party/blink/renderer/core/layout/inline/logical_line_builder.h
+++ b/third_party/blink/renderer/core/layout/inline/logical_line_builder.h
@@ -10,6 +10,7 @@
 
 namespace blink {
 
+class InlineBreakToken;
 class InlineChildLayoutContext;
 class InlineLayoutAlgorithm;
 class InlineLayoutStateStack;
@@ -26,6 +27,7 @@
  public:
   LogicalLineBuilder(InlineNode node,
                      const ConstraintSpace& constraint_space,
+                     const InlineBreakToken* break_token,
                      InlineLayoutStateStack* state_stack,
                      InlineChildLayoutContext* context);
 
@@ -99,6 +101,7 @@
 
   InlineNode node_;
   const ConstraintSpace& constraint_space_;
+  const InlineBreakToken* break_token_;
   InlineLayoutStateStack* box_states_;
   InlineChildLayoutContext* context_;
   const FontBaseline baseline_type_;
diff --git a/third_party/blink/renderer/core/layout/inline/ruby_utils.cc b/third_party/blink/renderer/core/layout/inline/ruby_utils.cc
index 94df68f..a9f96b9f 100644
--- a/third_party/blink/renderer/core/layout/inline/ruby_utils.cc
+++ b/third_party/blink/renderer/core/layout/inline/ruby_utils.cc
@@ -187,7 +187,7 @@
 
 AnnotationOverhang GetOverhang(const InlineItemResult& item) {
   AnnotationOverhang overhang;
-  if (item.item->Type() == InlineItem::kOpenRubyColumn && item.ruby_column) {
+  if (item.IsRubyColumn()) {
     DCHECK(RuntimeEnabledFeatures::RubyLineBreakableEnabled());
     const InlineItemResultRubyColumn& column = *item.ruby_column;
     LayoutUnit half_width_of_annotation_font;
@@ -314,8 +314,7 @@
   DCHECK_EQ(text_item.Type(), InlineItem::kText);
   wtf_size_t i = items->size() - 1;
   if (RuntimeEnabledFeatures::RubyLineBreakableEnabled()) {
-    while ((*items)[i].item->Type() != InlineItem::kOpenRubyColumn ||
-           !(*items)[i].ruby_column) {
+    while (!(*items)[i].IsRubyColumn()) {
       const auto type = (*items)[i].item->Type();
       if (type != InlineItem::kOpenTag && type != InlineItem::kCloseTag &&
           type != InlineItem::kCloseRubyColumn &&
@@ -383,6 +382,8 @@
 }
 
 std::pair<LayoutUnit, LayoutUnit> ApplyRubyAlign(LayoutUnit available_line_size,
+                                                 bool on_start_edge,
+                                                 bool on_end_edge,
                                                  LineInfo& line_info) {
   DCHECK(line_info.IsRubyBase() || line_info.IsRubyText());
   LayoutUnit space = available_line_size - line_info.WidthForAlignment();
@@ -395,15 +396,32 @@
     JustificationTarget target = JustificationTarget::kNormal;
     if (line_info.IsRubyBase()) {
       target = JustificationTarget::kRubyBase;
+      // Switch to `space-between` if this needs to align both edges.
+      if (on_start_edge && on_end_edge) {
+        target = JustificationTarget::kNormal;
+      }
     } else {
       DCHECK(line_info.IsRubyText());
       target = JustificationTarget::kRubyText;
     }
     std::optional<LayoutUnit> inset =
         ApplyJustification(space, target, &line_info);
+    // https://drafts.csswg.org/css-ruby/#line-edge
     if (inset) {
+      if (on_start_edge && !on_end_edge) {
+        return {LayoutUnit(), *inset * 2};
+      }
+      if (!on_start_edge && on_end_edge) {
+        return {*inset * 2, LayoutUnit()};
+      }
       return {*inset, *inset};
     }
+    if (on_start_edge && !on_end_edge) {
+      return {LayoutUnit(), space};
+    }
+    if (!on_start_edge && on_end_edge) {
+      return {space, LayoutUnit()};
+    }
     return {space / 2, space / 2};
   }
 
diff --git a/third_party/blink/renderer/core/layout/inline/ruby_utils.h b/third_party/blink/renderer/core/layout/inline/ruby_utils.h
index b9883ff..48a8f27 100644
--- a/third_party/blink/renderer/core/layout/inline/ruby_utils.h
+++ b/third_party/blink/renderer/core/layout/inline/ruby_utils.h
@@ -82,6 +82,8 @@
 // to LogicalLineItems generated from `line_info` after bidi reorder.
 [[nodiscard]] std::pair<LayoutUnit, LayoutUnit> ApplyRubyAlign(
     LayoutUnit available_line_size,
+    bool on_start_edge,
+    bool on_end_edge,
     LineInfo& line_info);
 
 // Stores ComputeAnnotationOverflow() results.
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index a70d30a..ab3ed1a 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -4357,8 +4357,8 @@
   return is_acceptable_anchor ? anchor_layout_object : nullptr;
 }
 
-const HeapVector<NonOverflowingScrollRange>*
-LayoutBox::NonOverflowingScrollRanges() const {
+const Vector<NonOverflowingScrollRange>* LayoutBox::NonOverflowingScrollRanges()
+    const {
   const auto& layout_results = GetLayoutResults();
   if (layout_results.empty()) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 56ff9114..8fbd555 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1271,8 +1271,7 @@
   // https://drafts.csswg.org/css-anchor-position-1/#ref-for-valdef-anchor-implicit
   const LayoutObject* AcceptableImplicitAnchor() const;
 
-  const HeapVector<NonOverflowingScrollRange>* NonOverflowingScrollRanges()
-      const;
+  const Vector<NonOverflowingScrollRange>* NonOverflowingScrollRanges() const;
 
   const BoxStrut& OutOfFlowInsetsForGetComputedStyle() const;
 
diff --git a/third_party/blink/renderer/core/layout/layout_result.cc b/third_party/blink/renderer/core/layout/layout_result.cc
index ca21be8..7399df527 100644
--- a/third_party/blink/renderer/core/layout/layout_result.cc
+++ b/third_party/blink/renderer/core/layout/layout_result.cc
@@ -376,7 +376,6 @@
 
 void LayoutResult::RareData::Trace(Visitor* visitor) const {
   visitor->Trace(early_break);
-  visitor->Trace(non_overflowing_scroll_ranges);
   // This will not cause TOCTOU issue because data_union_type is set in the
   // constructor and never changed.
   if (const BlockData* data = GetBlockData())
diff --git a/third_party/blink/renderer/core/layout/layout_result.h b/third_party/blink/renderer/core/layout/layout_result.h
index dc7d42dd..dfc9ef1 100644
--- a/third_party/blink/renderer/core/layout/layout_result.h
+++ b/third_party/blink/renderer/core/layout/layout_result.h
@@ -177,8 +177,7 @@
   // positioned nodes are set.
   void CopyMutableOutOfFlowData(const LayoutResult& previous_result) const;
 
-  const HeapVector<NonOverflowingScrollRange>* NonOverflowingScrollRanges()
-      const {
+  const Vector<NonOverflowingScrollRange>* NonOverflowingScrollRanges() const {
     return rare_data_ ? rare_data_->NonOverflowingScrollRanges() : nullptr;
   }
 
@@ -526,7 +525,7 @@
     }
 
     void SetNonOverflowingScrollRanges(
-        const HeapVector<NonOverflowingScrollRange>& non_overflowing_ranges) {
+        const Vector<NonOverflowingScrollRange>& non_overflowing_ranges) {
       if (layout_result_->rare_data_ || !non_overflowing_ranges.empty()) {
         layout_result_->EnsureRareData()->SetNonOverflowingScrollRanges(
             non_overflowing_ranges);
@@ -855,10 +854,10 @@
     }
 
     void SetNonOverflowingScrollRanges(
-        const HeapVector<NonOverflowingScrollRange>& non_overflowing_ranges) {
+        const Vector<NonOverflowingScrollRange>& non_overflowing_ranges) {
       non_overflowing_scroll_ranges = non_overflowing_ranges;
     }
-    const HeapVector<NonOverflowingScrollRange>* NonOverflowingScrollRanges()
+    const Vector<NonOverflowingScrollRange>* NonOverflowingScrollRanges()
         const {
       if (non_overflowing_scroll_ranges.empty()) {
         return nullptr;
@@ -904,7 +903,7 @@
     // Only valid if line_box_bfc_block_offset_is_set
     LayoutUnit line_box_bfc_block_offset;
 
-    HeapVector<NonOverflowingScrollRange> non_overflowing_scroll_ranges;
+    Vector<NonOverflowingScrollRange> non_overflowing_scroll_ranges;
 
     // Only valid if oof_positioned_offset_is_set
     LogicalOffset oof_positioned_offset;
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index d9dbdb4..d66b18c 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -71,7 +71,6 @@
 #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
 #include "ui/display/screen_info.h"
 #include "ui/gfx/geometry/quad_f.h"
-#include "ui/gfx/geometry/size_conversions.h"
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
@@ -714,34 +713,6 @@
 #undef RETURN_SCROLLBAR_MODE
 }
 
-PhysicalSize LayoutView::PageAreaSize(wtf_size_t page_index,
-                                      const AtomicString& page_name) const {
-  NOT_DESTROYED();
-  const ComputedStyle* page_style =
-      GetDocument().StyleForPage(page_index, page_name);
-  WebPrintPageDescription description =
-      GetDocument().GetPageDescriptionNoLifecycleUpdate(*page_style);
-
-  gfx::SizeF page_size(
-      std::max(.0f, description.size.width() -
-                        (description.margin_left + description.margin_right)),
-      std::max(.0f, description.size.height() -
-                        (description.margin_top + description.margin_bottom)));
-
-  page_size.Scale(pagination_scale_factor_);
-
-  // Round up to the nearest integer. Although layout itself could have handled
-  // subpixels just fine, the paint code cannot without bleeding across page
-  // boundaries. The printing code (outside Blink) also rounds up. It's
-  // important that all pieces of the machinery agree on which way to round, or
-  // we risk clipping away a pixel or so at the edges. The reason for rounding
-  // up (rather than down, or to the closest integer) is so that any box that
-  // starts exactly at the beginning of a page, and uses a block-size exactly
-  // equal to that of the page area (before rounding) will actually fit on one
-  // page.
-  return PhysicalSize(gfx::ToCeiledSize(page_size));
-}
-
 AtomicString LayoutView::NamedPageAtIndex(wtf_size_t page_index) const {
   // If layout is dirty, it's not possible to look up page names reliably.
   DCHECK_GE(GetDocument().Lifecycle().GetState(),
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index 9867c2d..0c113d2 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -210,11 +210,6 @@
     return pagination_scale_factor_;
   }
 
-  // Get the page area size (fragmentainer size) for a given page number and
-  // name.
-  PhysicalSize PageAreaSize(wtf_size_t page_index,
-                            const AtomicString& page_name) const;
-
   AtomicString NamedPageAtIndex(wtf_size_t page_index) const;
 
   PhysicalRect DocumentRect() const;
diff --git a/third_party/blink/renderer/core/layout/length_utils.cc b/third_party/blink/renderer/core/layout/length_utils.cc
index ee9e607..76e68ce 100644
--- a/third_party/blink/renderer/core/layout/length_utils.cc
+++ b/third_party/blink/renderer/core/layout/length_utils.cc
@@ -1523,8 +1523,11 @@
   LogicalSize child_available_size =
       ShrinkLogicalSize(border_box_size, border_scrollbar_padding);
 
-  if (space.IsAnonymous() || node.IsAnonymousBlock())
+  if (space.IsAnonymous() ||
+      (node.IsAnonymousBlock() &&
+       child_available_size.block_size == kIndefiniteSize)) {
     child_available_size.block_size = space.AvailableSize().block_size;
+  }
 
   return child_available_size;
 }
diff --git a/third_party/blink/renderer/core/layout/non_overflowing_scroll_range.h b/third_party/blink/renderer/core/layout/non_overflowing_scroll_range.h
index 4a74ac3..26bf73c 100644
--- a/third_party/blink/renderer/core/layout/non_overflowing_scroll_range.h
+++ b/third_party/blink/renderer/core/layout/non_overflowing_scroll_range.h
@@ -6,13 +6,9 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NON_OVERFLOWING_SCROLL_RANGE_H_
 
 #include "third_party/blink/renderer/core/layout/geometry/scroll_offset_range.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/wtf/vector_traits.h"
 
 namespace blink {
 
-class LayoutObject;
-
 // Helper structure for CSS anchor positioning's fallback positioning. Each
 // fallback position has a corresponding `NonOverflowingScrollRange`. See
 // https://drafts.csswg.org/css-anchor-position-1/#fallback-apply
@@ -24,9 +20,6 @@
   // containing block rect.
   PhysicalScrollRange containing_block_range;
 
-  // The default anchor used for the corresponding fallback position.
-  Member<const LayoutObject> anchor_object;
-
   // Checks if the given scroll offsets are within the scroll ranges, i.e., if
   // the fallback position's margin box overflows the bounds.
   bool Contains(const gfx::Vector2dF& anchor_scroll_offset) const {
@@ -36,13 +29,8 @@
   bool operator==(const NonOverflowingScrollRange& other) const {
     return containing_block_range == other.containing_block_range;
   }
-
-  void Trace(Visitor* visitor) const { visitor->Trace(anchor_object); }
 };
 
 }  // namespace blink
 
-WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(
-    blink::NonOverflowingScrollRange)
-
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NON_OVERFLOWING_SCROLL_RANGE_H_
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
index dd9196a..d8129f79 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -277,9 +277,8 @@
 
 const Element* GetPositionAnchorElement(
     const BlockNode& node,
-    const ComputedStyle& style,
     const LogicalAnchorQuery& anchor_query) {
-  if (const ScopedCSSName* specifier = style.PositionAnchor()) {
+  if (const ScopedCSSName* specifier = node.Style().PositionAnchor()) {
     if (const LogicalAnchorReference* reference =
             anchor_query.AnchorReference(*node.GetLayoutBox(), specifier);
         reference && reference->layout_object) {
@@ -293,30 +292,6 @@
   return nullptr;
 }
 
-const LayoutObject* GetPositionAnchorObject(
-    const BlockNode& node,
-    const ComputedStyle& style,
-    const LogicalAnchorQuery& anchor_query) {
-  if (const Element* element =
-          GetPositionAnchorElement(node, style, anchor_query)) {
-    return element->GetLayoutObject();
-  }
-  return nullptr;
-}
-
-gfx::Vector2dF GetAnchorOffset(const BlockNode& node,
-                               const ComputedStyle& style,
-                               const LogicalAnchorQuery& anchor_query) {
-  if (const LayoutObject* anchor_object =
-          GetPositionAnchorObject(node, style, anchor_query)) {
-    if (const AnchorPositionScrollData* data =
-            To<Element>(node.GetDOMNode())->GetAnchorPositionScrollData()) {
-      return data->TotalOffset(*anchor_object);
-    }
-  }
-  return gfx::Vector2dF();
-}
-
 // Updates `node`'s associated `PaintLayer` for `position-visibility`. See:
 // https://drafts.csswg.org/css-anchor-position-1/#position-visibility. The
 // values of `no-overflow` and `anchors-valid` are computed and directly update
@@ -357,8 +332,7 @@
   // The spec is still in-flux about whether we should use multiple anchors
   // (from `anchor()` and `anchor-size()`), or just the default anchor.
   const Element* anchor =
-      anchored ? GetPositionAnchorElement(node, node.Style(), *anchor_query)
-               : nullptr;
+      anchored ? GetPositionAnchorElement(node, *anchor_query) : nullptr;
   if (is_anchor_positioned && has_anchors_visible_visibility && anchor) {
     anchored->EnsureAnchorPositionScrollData()
         .EnsureAnchorPositionVisibilityObserver()
@@ -1852,8 +1826,16 @@
 OutOfFlowLayoutPart::OffsetInfo OutOfFlowLayoutPart::CalculateOffset(
     const NodeInfo& node_info,
     const LogicalAnchorQueryMap* anchor_queries) {
+  gfx::Vector2dF anchor_offset;
+  if (Element* element = DynamicTo<Element>(node_info.node.GetDOMNode())) {
+    if (const AnchorPositionScrollData* data =
+            element->GetAnchorPositionScrollData()) {
+      anchor_offset = data->TotalOffset();
+    }
+  }
+
   // See non_overflowing_scroll_range.h for documentation.
-  HeapVector<NonOverflowingScrollRange> non_overflowing_scroll_ranges;
+  Vector<NonOverflowingScrollRange> non_overflowing_scroll_ranges;
 
   // Note: This assumes @position-try rounds can't affect
   // writing-mode/position-anchor.
@@ -1887,7 +1869,7 @@
     // However, without @position-try, the style is the current style.
     CHECK(has_try_options || &style == &iter.GetStyle());
     std::optional<OffsetInfo> offset_info =
-        TryCalculateOffset(node_info, style, anchor_evaluator,
+        TryCalculateOffset(node_info, style, &anchor_evaluator,
                            try_fit_available_space, &non_overflowing_range);
 
     // Also check if it fits the containing block after applying scroll offset
@@ -1895,8 +1877,7 @@
     if (offset_info) {
       if (try_fit_available_space) {
         non_overflowing_scroll_ranges.push_back(non_overflowing_range);
-        if (!non_overflowing_range.Contains(GetAnchorOffset(
-                node_info.node, style, *anchor_evaluator.AnchorQuery()))) {
+        if (!non_overflowing_range.Contains(anchor_offset)) {
           continue;
         }
       }
@@ -1933,7 +1914,7 @@
     // offset again, using the non-base style.
     const ComputedStyle& style = iter.ActivateStyleForChosenOption();
     NonOverflowingScrollRange non_overflowing_range_unused;
-    offset_info = TryCalculateOffset(node_info, style, anchor_evaluator,
+    offset_info = TryCalculateOffset(node_info, style, &anchor_evaluator,
                                      /* try_fit_available_space */ false,
                                      &non_overflowing_range_unused);
     offset_info->overflows_containing_block = overflows_containing_block;
@@ -1954,7 +1935,7 @@
 OutOfFlowLayoutPart::TryCalculateOffset(
     const NodeInfo& node_info,
     const ComputedStyle& candidate_style,
-    AnchorEvaluatorImpl& anchor_evaluator,
+    AnchorEvaluatorImpl* anchor_evaluator,
     bool try_fit_available_space,
     NonOverflowingScrollRange* out_non_overflowing_range) {
   // TryCalculateOffset may be called multiple times if we have multiple @try
@@ -2221,8 +2202,6 @@
         LogicalScrollRange{inline_scroll_min, inline_scroll_max,
                            block_scroll_min, block_scroll_max}
             .ToPhysical(candidate_writing_direction);
-    out_non_overflowing_range->anchor_object = GetPositionAnchorObject(
-        node_info.node, candidate_style, *anchor_evaluator.AnchorQuery());
   }
 
   bool anchor_center_x = anchor_center_position.inline_offset.has_value();
@@ -2231,9 +2210,9 @@
     std::swap(anchor_center_x, anchor_center_y);
   }
   offset_info.needs_scroll_adjustment_in_x =
-      anchor_center_x || anchor_evaluator.NeedsScrollAdjustmentInX();
+      anchor_center_x || anchor_evaluator->NeedsScrollAdjustmentInX();
   offset_info.needs_scroll_adjustment_in_y =
-      anchor_center_y || anchor_evaluator.NeedsScrollAdjustmentInY();
+      anchor_center_y || anchor_evaluator->NeedsScrollAdjustmentInY();
 
   return offset_info;
 }
@@ -2830,7 +2809,6 @@
 
 void OutOfFlowLayoutPart::OffsetInfo::Trace(Visitor* visitor) const {
   visitor->Trace(initial_layout_result);
-  visitor->Trace(non_overflowing_scroll_ranges);
 }
 
 void OutOfFlowLayoutPart::NodeToLayout::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
index d8f50c7..b36653a 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.h
@@ -207,7 +207,7 @@
     // This field is set only if this |OffsetInfo| is calculated from a
     // position-try-options style, either from a @position-try rule or a tactic,
     // or the anchored element has position-visibility: no-overflow.
-    HeapVector<NonOverflowingScrollRange> non_overflowing_scroll_ranges;
+    Vector<NonOverflowingScrollRange> non_overflowing_scroll_ranges;
 
     // This field is set when we're calculating |OffsetInfo| with
     // try_fit_available_space=true, e.g. when we have a non-empty
@@ -322,7 +322,7 @@
   std::optional<OffsetInfo> TryCalculateOffset(
       const NodeInfo& node_info,
       const ComputedStyle& style,
-      AnchorEvaluatorImpl&,
+      AnchorEvaluatorImpl*,
       bool try_fit_available_space,
       NonOverflowingScrollRange* out_scroll_range);
 
diff --git a/third_party/blink/renderer/core/layout/page_border_box_layout_algorithm.cc b/third_party/blink/renderer/core/layout/page_border_box_layout_algorithm.cc
index e54e9c6..17321d8f 100644
--- a/third_party/blink/renderer/core/layout/page_border_box_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/page_border_box_layout_algorithm.cc
@@ -55,7 +55,19 @@
 
 ConstraintSpace PageBorderBoxLayoutAlgorithm::CreateConstraintSpaceForPageArea()
     const {
-  LogicalSize page_area_size = GetConstraintSpace().AvailableSize();
+  LogicalSize page_area_size = ChildAvailableSize();
+
+  // Round up to the nearest integer. Although layout itself could have handled
+  // subpixels just fine, the paint code cannot without bleeding across page
+  // boundaries. The printing code (outside Blink) also rounds up. It's
+  // important that all pieces of the machinery agree on which way to round, or
+  // we risk clipping away a pixel or so at the edges. The reason for rounding
+  // up (rather than down, or to the closest integer) is so that any box that
+  // starts exactly at the beginning of a page, and uses a block-size exactly
+  // equal to that of the page area (before rounding) will actually fit on one
+  // page.
+  page_area_size.inline_size = LayoutUnit(page_area_size.inline_size.Ceil());
+  page_area_size.block_size = LayoutUnit(page_area_size.block_size.Ceil());
 
   ConstraintSpaceBuilder space_builder(
       GetConstraintSpace(), Style().GetWritingDirection(), /*is_new_fc=*/true);
diff --git a/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc b/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc
index 065b25a1..1392acb3 100644
--- a/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/page_container_layout_algorithm.cc
@@ -4,39 +4,58 @@
 
 #include "third_party/blink/renderer/core/layout/page_container_layout_algorithm.h"
 
+#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/pagination_state.h"
 #include "third_party/blink/renderer/core/layout/layout_ng_block_flow.h"
+#include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/page_border_box_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/pagination_utils.h"
 
 namespace blink {
 
 PageContainerLayoutAlgorithm::PageContainerLayoutAlgorithm(
     const LayoutAlgorithmParams& params,
+    wtf_size_t page_index,
+    const AtomicString& page_name,
     const BlockNode& content_node,
-    const PageAreaLayoutParams& page_area_params)
+    const PageAreaLayoutParams& page_area_params,
+    bool ignore_author_page_style)
     : LayoutAlgorithm(params),
+      page_index_(page_index),
+      page_name_(page_name),
       content_node_(content_node),
-      page_area_params_(page_area_params) {}
+      page_area_params_(page_area_params),
+      ignore_author_page_style_(ignore_author_page_style) {}
 
 const LayoutResult* PageContainerLayoutAlgorithm::Layout() {
   DCHECK(!GetBreakToken());
   container_builder_.SetBoxType(PhysicalFragment::kPageContainer);
 
   Document& document = Node().GetDocument();
+  float layout_scale = document.GetLayoutView()->PaginationScaleFactor();
+  LogicalSize containing_block_size =
+      DesiredPageContainingBlockSize(document, Style()) * layout_scale;
 
-  // TODO(mstensho): The page container should include margins (which it
-  // currently doesn't), whereas they should not be part of the page border box.
-  FragmentGeometry fragment_geometry = {
-      .border_box_size = GetConstraintSpace().AvailableSize()};
+  const ComputedStyle* content_scaled_style = &Style();
+  if (layout_scale != 1 && !ignore_author_page_style_) {
+    // Scaling shouldn't apply to @page borders etc. Apply a zoom property to
+    // cancel out the effect of layout scaling.
+    content_scaled_style = document.GetStyleResolver().StyleForPage(
+        page_index_, page_name_, layout_scale);
+  }
 
   LayoutBlockFlow* page_border_box =
       document.View()->GetPaginationState()->CreateAnonymousPageLayoutObject(
-          document, Style());
+          document, *content_scaled_style);
   BlockNode page_border_box_node(page_border_box);
 
-  LayoutAlgorithmParams params(page_border_box_node, fragment_geometry,
+  FragmentGeometry geometry;
+  ResolvePageBoxGeometry(page_border_box_node, containing_block_size,
+                         &geometry);
+
+  LayoutAlgorithmParams params(page_border_box_node, geometry,
                                GetConstraintSpace(), /*break_token=*/nullptr);
   PageBorderBoxLayoutAlgorithm child_algorithm(params, content_node_,
                                                page_area_params_);
diff --git a/third_party/blink/renderer/core/layout/page_container_layout_algorithm.h b/third_party/blink/renderer/core/layout/page_container_layout_algorithm.h
index 439a66d..4850d20f 100644
--- a/third_party/blink/renderer/core/layout/page_container_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/page_container_layout_algorithm.h
@@ -36,8 +36,11 @@
     : public LayoutAlgorithm<BlockNode, BoxFragmentBuilder, BlockBreakToken> {
  public:
   PageContainerLayoutAlgorithm(const LayoutAlgorithmParams& params,
+                               wtf_size_t page_index,
+                               const AtomicString& page_name,
                                const BlockNode& content_node,
-                               const PageAreaLayoutParams&);
+                               const PageAreaLayoutParams&,
+                               bool ignore_author_page_style);
 
   const LayoutResult* Layout();
 
@@ -51,8 +54,11 @@
   }
 
  private:
+  wtf_size_t page_index_;
+  const AtomicString& page_name_;
   const BlockNode& content_node_;
   const PageAreaLayoutParams& page_area_params_;
+  bool ignore_author_page_style_;
 
   const BlockBreakToken* fragmentainer_break_token_ = nullptr;
 };
diff --git a/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.cc b/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.cc
index 5e54e3b5..e991b198 100644
--- a/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/layout/out_of_flow_layout_part.h"
 #include "third_party/blink/renderer/core/layout/page_border_box_layout_algorithm.h"
 #include "third_party/blink/renderer/core/layout/page_container_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/pagination_utils.h"
 #include "third_party/blink/renderer/core/layout/physical_box_fragment.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 
@@ -104,13 +105,6 @@
     const AtomicString& page_name,
     const PageAreaLayoutParams& page_area_params) {
   Document& document = root_node.GetDocument();
-  const LayoutView* view = document.GetLayoutView();
-  WritingMode writing_mode = parent_space.GetWritingMode();
-  LogicalSize page_size =
-      view->PageAreaSize(page_index, page_name).ConvertToLogical(writing_mode);
-
-  DCHECK(page_size.inline_size != kIndefiniteSize);
-  DCHECK(page_size.block_size != kIndefiniteSize);
   const ComputedStyle* page_container_style =
       document.GetStyleResolver().StyleForPage(page_index, page_name);
 
@@ -119,14 +113,87 @@
           document, *page_container_style);
   BlockNode page_container_node(page_container);
 
-  ConstraintSpace child_space =
-      CreateConstraintSpaceForPages(root_node, parent_space, page_size);
-  FragmentGeometry fragment_geometry = CalculateInitialFragmentGeometry(
-      child_space, root_node, /*break_token=*/nullptr);
-  LayoutAlgorithmParams params(page_container_node, fragment_geometry,
+  // Calculate the page border box size based on @page properties, such as
+  // 'size' and 'margin', but also padding, width, height, min-height, and so
+  // on. Auto margins will be resolved. One interesting detail here is how
+  // over-constrainedness is handled. Although, for regular CSS boxes, margins
+  // will be adjusted to resolve it, for page boxes, the containing block size
+  // (the one set by the 'size' descriptor / property) is adjusted instead.
+  //
+  // Example: @page { size:500px; margin:50px; width:100px; }
+  //
+  // The equation (omitting border and padding, since they are 0 in this
+  // example):
+  // 'margin-left' + 'width' + 'margin-right' = width of containing block
+  //
+  // The width of the containing block is 500px (from size). This is what needs
+  // to be adjusted to resolve the overconstraintedness - i.e. it needs to
+  // become 50+100+50=200. So we end up with a page box size of 200x500, and a
+  // page area size of 100x400.
+  //
+  // https://drafts.csswg.org/css-page-3/#page-model
+  FragmentGeometry geometry;
+  LogicalSize page_containing_block_size =
+      DesiredPageContainingBlockSize(document, *page_container_style);
+  ResolvePageBoxGeometry(page_container_node, page_containing_block_size,
+                         &geometry);
+
+  // Check if the resulting page area size is usable.
+  LogicalSize desired_page_area_size =
+      geometry.border_box_size - geometry.border - geometry.padding;
+  bool ignore_author_page_style = false;
+  if (desired_page_area_size.inline_size < LayoutUnit(1) ||
+      desired_page_area_size.block_size < LayoutUnit(1)) {
+    // The resulting page area size would become zero (or very close to
+    // it). Ignore CSS, and use the default values provided as input. There are
+    // tests that currently expect this behavior. But see
+    // https://github.com/w3c/csswg-drafts/issues/8335
+    ignore_author_page_style = true;
+    page_container_style = document.GetStyleResolver().StyleForPage(
+        page_index, page_name, 1.0, ignore_author_page_style);
+    page_container->SetStyle(page_container_style,
+                             LayoutObject::ApplyStyleChanges::kNo);
+    page_containing_block_size =
+        DesiredPageContainingBlockSize(document, *page_container_style);
+    ResolvePageBoxGeometry(page_container_node, page_containing_block_size,
+                           &geometry);
+  }
+
+  // TODO(mstensho): This should include page margins, once Blink gains control
+  // over that area. For now the size here coincides with the size of the page
+  // border box, since margins aren't part of layout yet.
+  LogicalSize page_container_size =
+      geometry.border_box_size *
+      document.GetLayoutView()->PaginationScaleFactor();
+  // Round up to the nearest integer. Although layout itself could have handled
+  // subpixels just fine, the paint code cannot without bleeding across page
+  // boundaries. The printing code (outside Blink) also rounds up. It's
+  // important that all pieces of the machinery agree on which way to round, or
+  // we risk clipping away a pixel or so at the edges. The reason for rounding
+  // up (rather than down, or to the closest integer) is so that any box that
+  // starts exactly at the beginning of a page, and uses a block-size exactly
+  // equal to that of the page area (before rounding) will actually fit on one
+  // page.
+  page_container_size.inline_size =
+      LayoutUnit(page_container_size.inline_size.Ceil());
+  page_container_size.block_size =
+      LayoutUnit(page_container_size.block_size.Ceil());
+
+  ConstraintSpaceBuilder space_builder(
+      parent_space, page_container_style->GetWritingDirection(),
+      /*is_new_fc=*/true);
+  SetUpSpaceBuilderForPageBox(page_container_size, &space_builder);
+  space_builder.SetShouldPropagateChildBreakValues();
+  ConstraintSpace child_space = space_builder.ToConstraintSpace();
+
+  FragmentGeometry margin_box_geometry = {.border_box_size =
+                                              page_container_size};
+
+  LayoutAlgorithmParams params(page_container_node, margin_box_geometry,
                                child_space, /*break_token=*/nullptr);
-  PageContainerLayoutAlgorithm child_algorithm(params, root_node,
-                                               page_area_params);
+  PageContainerLayoutAlgorithm child_algorithm(params, page_index, page_name,
+                                               root_node, page_area_params,
+                                               ignore_author_page_style);
   const LayoutResult* result = child_algorithm.Layout();
 
   // Since we didn't lay out via BlockNode::Layout(), but rather picked and
@@ -139,19 +206,4 @@
       child_algorithm.FragmentainerBreakToken());
 }
 
-ConstraintSpace PaginatedRootLayoutAlgorithm::CreateConstraintSpaceForPages(
-    const BlockNode& node,
-    const ConstraintSpace& space,
-    const LogicalSize& page_size) {
-  ConstraintSpaceBuilder space_builder(
-      space, node.Style().GetWritingDirection(), /*is_new_fc=*/true);
-  space_builder.SetAvailableSize(page_size);
-  space_builder.SetPercentageResolutionSize(page_size);
-  space_builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchImplicit);
-  space_builder.SetBlockAutoBehavior(AutoSizeBehavior::kStretchImplicit);
-  space_builder.SetShouldPropagateChildBreakValues();
-
-  return space_builder.ToConstraintSpace();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.h b/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.h
index 61a1811a..4031f9f 100644
--- a/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/paginated_root_layout_algorithm.h
@@ -15,7 +15,6 @@
 class BlockBreakToken;
 class BlockNode;
 class ConstraintSpace;
-struct LogicalSize;
 struct PageAreaLayoutParams;
 
 // This is the root layout algorithm when the document is paginated (for
@@ -114,10 +113,6 @@
       wtf_size_t page_index,
       const AtomicString& page_name,
       const PageAreaLayoutParams&);
-
-  static ConstraintSpace CreateConstraintSpaceForPages(const BlockNode&,
-                                                       const ConstraintSpace&,
-                                                       const LogicalSize& size);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/pagination_utils.cc b/third_party/blink/renderer/core/layout/pagination_utils.cc
index 8002e42..5a4fef0e 100644
--- a/third_party/blink/renderer/core/layout/pagination_utils.cc
+++ b/third_party/blink/renderer/core/layout/pagination_utils.cc
@@ -4,8 +4,19 @@
 
 #include "third_party/blink/renderer/core/layout/pagination_utils.h"
 
+#include "third_party/blink/public/web/web_print_page_description.h"
+#include "third_party/blink/public/web/web_print_params.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/layout/block_break_token.h"
+#include "third_party/blink/renderer/core/layout/block_node.h"
+#include "third_party/blink/renderer/core/layout/constraint_space.h"
+#include "third_party/blink/renderer/core/layout/constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/geometry/box_strut.h"
+#include "third_party/blink/renderer/core/layout/geometry/fragment_geometry.h"
+#include "third_party/blink/renderer/core/layout/layout_ng_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
+#include "third_party/blink/renderer/core/layout/length_utils.h"
 #include "third_party/blink/renderer/core/layout/physical_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/physical_fragment_link.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
@@ -14,6 +25,11 @@
 
 namespace {
 
+PhysicalSize PageBoxDefaultSize(const Document& document) {
+  const WebPrintParams& params = document.GetFrame()->GetPrintParams();
+  return PhysicalSize::FromSizeFRound(params.default_page_description.size);
+}
+
 wtf_size_t PageNumberFromPageArea(const PhysicalBoxFragment& page_area) {
   DCHECK_EQ(page_area.GetBoxType(), PhysicalFragment::kPageArea);
   if (const BlockBreakToken* break_token = page_area.GetBreakToken()) {
@@ -26,6 +42,149 @@
 
 }  // anonymous namespace
 
+void SetUpSpaceBuilderForPageBox(LogicalSize available_size,
+                                 ConstraintSpaceBuilder* builder) {
+  builder->SetAvailableSize(available_size);
+  builder->SetPercentageResolutionSize(available_size);
+  builder->SetInlineAutoBehavior(AutoSizeBehavior::kStretchImplicit);
+  builder->SetBlockAutoBehavior(AutoSizeBehavior::kStretchImplicit);
+  builder->SetDecorationPercentageResolutionType(
+      DecorationPercentageResolutionType::kContainingBlockSize);
+}
+
+LogicalSize DesiredPageContainingBlockSize(const Document& document,
+                                           const ComputedStyle& style) {
+  PhysicalSize layout_size = PageBoxDefaultSize(document);
+  switch (style.GetPageSizeType()) {
+    case PageSizeType::kAuto:
+      break;
+    case PageSizeType::kLandscape:
+      if (layout_size.width < layout_size.height) {
+        std::swap(layout_size.width, layout_size.height);
+      }
+      break;
+    case PageSizeType::kPortrait:
+      if (layout_size.width > layout_size.height) {
+        std::swap(layout_size.width, layout_size.height);
+      }
+      break;
+    case PageSizeType::kFixed: {
+      auto css_size = PhysicalSize::FromSizeFRound(style.PageSize());
+      if (document.GetFrame()->GetPrintParams().ignore_page_size) {
+        // Keep the page size, but match orientation.
+        if ((css_size.width > css_size.height) !=
+            (layout_size.width > layout_size.height)) {
+          std::swap(layout_size.width, layout_size.height);
+        }
+        break;
+      }
+      layout_size = css_size;
+      break;
+    }
+  }
+
+  return layout_size.ConvertToLogical(style.GetWritingMode());
+}
+
+void ResolvePageBoxGeometry(const BlockNode& page_box,
+                            LogicalSize page_containing_block_size,
+                            FragmentGeometry* geometry,
+                            BoxStrut* margins) {
+  const ComputedStyle& style = page_box.Style();
+  ConstraintSpaceBuilder space_builder(style.GetWritingMode(),
+                                       style.GetWritingDirection(),
+                                       /* is_new_fc */ true);
+  SetUpSpaceBuilderForPageBox(page_containing_block_size, &space_builder);
+  ConstraintSpace space = space_builder.ToConstraintSpace();
+  *geometry = CalculateInitialFragmentGeometry(space, page_box,
+                                               /*BlockBreakToken=*/nullptr);
+
+  if (!margins) {
+    return;
+  }
+
+  *margins = ComputeMarginsForSelf(space, style);
+
+  // Resolve any auto margins. Note that this may result in negative margins, if
+  // the specified width/height is larger than the specified containing block
+  // size (the 'size' property). See
+  // https://github.com/w3c/csswg-drafts/issues/8508 for discussion around
+  // negative page margins in general.
+  auto ResolveAutoMargin =
+      [](Length start_length, Length end_length, LayoutUnit additional_space,
+         LayoutUnit* start_result, LayoutUnit* end_result) {
+        bool start_is_auto = start_length.IsAuto();
+        bool end_is_auto = end_length.IsAuto();
+        if (start_is_auto) {
+          if (end_is_auto) {
+            *start_result = additional_space / 2;
+            additional_space -= *start_result;
+          } else {
+            *start_result = additional_space;
+          }
+        }
+        if (end_is_auto) {
+          *end_result = additional_space;
+        }
+      };
+  LayoutUnit additional_inline_space =
+      space.AvailableSize().inline_size - geometry->border_box_size.inline_size;
+  ResolveAutoMargin(style.MarginInlineStartUsing(style),
+                    style.MarginInlineEndUsing(style), additional_inline_space,
+                    &margins->inline_start, &margins->inline_end);
+  LayoutUnit additional_block_space =
+      space.AvailableSize().block_size - geometry->border_box_size.block_size;
+  ResolveAutoMargin(style.MarginBlockStartUsing(style),
+                    style.MarginBlockEndUsing(style), additional_block_space,
+                    &margins->block_start, &margins->block_end);
+}
+
+PhysicalSize CalculateInitialContainingBlockSizeForPagination(
+    Document& document) {
+  const LayoutView& layout_view = *document.GetLayoutView();
+  const ComputedStyle* page_style;
+  // The initial containing block is the size of the first page area.
+  if (const PhysicalBoxFragment* first_page =
+          GetPageContainer(layout_view, 0)) {
+    // We have already laid out. Grab the page style off the first page
+    // fragment. It may have been adjusted due to named pages or unusable sizes
+    // requested, which means that recomputing style here would not always give
+    // the correct results.
+    page_style = &first_page->Style();
+  } else {
+    page_style =
+        document.GetStyleResolver().StyleForPage(0, /*page_name=*/g_null_atom);
+  }
+
+  // Simply reading out the size of the page container fragment (if it exists at
+  // all) won't do, since we don't know if page scaling has been accounted for
+  // or not at this point. Note that we may not even have created the first page
+  // yet. This function is called before entering layout, so that viewport sizes
+  // (to resolve viewport units) are set up before entering layout (and, after
+  // layout, the sizes may need to be adjusted, if the initial estimate turned
+  // out to be wrong). Create a temporary node and resolve the size.
+  auto* page_box = LayoutNGBlockFlow::CreateAnonymous(&document, page_style);
+  BlockNode temporary_page_node(page_box);
+
+  FragmentGeometry geometry;
+  LogicalSize containing_block_size =
+      DesiredPageContainingBlockSize(document, *page_style);
+  ResolvePageBoxGeometry(temporary_page_node, containing_block_size, &geometry);
+  LogicalSize logical_size = ShrinkLogicalSize(
+      geometry.border_box_size, geometry.border + geometry.padding);
+
+  // Note: Don't get the writing mode directly from the LayoutView, since that
+  // one is untrustworthy unless we have entered layout (which we might not have
+  // at this point). See StyleResolver::StyleForViewport() and how it's called.
+  WritingMode writing_mode = page_style->GetWritingMode();
+
+  // So long, and thanks for all the size.
+  page_box->Destroy();
+
+  return ToPhysicalSize(logical_size, writing_mode) *
+         layout_view.PaginationScaleFactor();
+}
+
 float TargetScaleForPage(const PhysicalBoxFragment& page_container) {
   DCHECK_EQ(page_container.GetBoxType(), PhysicalFragment::kPageContainer);
   const Document& document = page_container.GetDocument();
@@ -68,18 +227,23 @@
   return &GetPageArea(GetPageBorderBox(*page_container));
 }
 
-const PhysicalBoxFragment& GetPageBorderBox(
+const PhysicalFragmentLink& GetPageBorderBoxLink(
     const PhysicalBoxFragment& page_container) {
   DCHECK_EQ(page_container.GetBoxType(), PhysicalFragment::kPageContainer);
   for (const auto& child : page_container.Children()) {
     if (child->GetBoxType() == PhysicalFragment::kPageBorderBox) {
-      return *To<PhysicalBoxFragment>(child.get());
+      return child;
     }
   }
   // A page container will never be laid out without a page border box child.
   NOTREACHED_NORETURN();
 }
 
+const PhysicalBoxFragment& GetPageBorderBox(
+    const PhysicalBoxFragment& page_container) {
+  return *To<PhysicalBoxFragment>(GetPageBorderBoxLink(page_container).get());
+}
+
 const PhysicalBoxFragment& GetPageArea(
     const PhysicalBoxFragment& page_border_box) {
   DCHECK_EQ(page_border_box.GetBoxType(), PhysicalFragment::kPageBorderBox);
@@ -166,4 +330,36 @@
   return overall_scale_factor;
 }
 
+WebPrintPageDescription GetPageDescriptionFromLayout(const Document& document,
+                                                     wtf_size_t page_number) {
+  const PhysicalBoxFragment& page_container =
+      *GetPageContainer(*document.GetLayoutView(), page_number);
+  const ComputedStyle& style = page_container.Style();
+
+  // TODO(mstensho): Once the margins are represented in the page fragments, we
+  // won't need to re-resolve geometry here. Instead we can read out the correct
+  // offsets and sizes from the page fragments.
+  LogicalSize page_containing_block_size =
+      DesiredPageContainingBlockSize(document, style);
+  FragmentGeometry geometry;
+  BoxStrut margins;
+  ResolvePageBoxGeometry(
+      BlockNode(To<LayoutBox>(page_container.GetMutableLayoutObject())),
+      page_containing_block_size, &geometry, &margins);
+  LogicalSize margin_box_size = geometry.border_box_size + margins;
+  WebPrintPageDescription description(
+      gfx::SizeF(ToPhysicalSize(margin_box_size, style.GetWritingMode())));
+  PhysicalBoxStrut insets =
+      margins.ConvertToPhysical(style.GetWritingDirection());
+  description.margin_top = insets.top.ToFloat();
+  description.margin_right = insets.right.ToFloat();
+  description.margin_bottom = insets.bottom.ToFloat();
+  description.margin_left = insets.left.ToFloat();
+
+  description.page_size_type = style.GetPageSizeType();
+  description.orientation = style.GetPageOrientation();
+
+  return description;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/pagination_utils.h b/third_party/blink/renderer/core/layout/pagination_utils.h
index 6764404..ed17516 100644
--- a/third_party/blink/renderer/core/layout/pagination_utils.h
+++ b/third_party/blink/renderer/core/layout/pagination_utils.h
@@ -5,14 +5,45 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_PAGINATION_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_PAGINATION_UTILS_H_
 
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
 namespace blink {
 
 class BlockBreakToken;
+class BlockNode;
+class ConstraintSpaceBuilder;
+class ComputedStyle;
+class Document;
 class LayoutView;
 class PhysicalBoxFragment;
+struct BoxStrut;
+struct FragmentGeometry;
+struct PhysicalFragmentLink;
 struct PhysicalRect;
+struct WebPrintPageDescription;
+
+void SetUpSpaceBuilderForPageBox(LogicalSize available_size,
+                                 ConstraintSpaceBuilder*);
+
+// Return the @page containing block size. If no size is specified in @page, the
+// size passed from the print settings will be used.
+LogicalSize DesiredPageContainingBlockSize(const Document&,
+                                           const ComputedStyle&);
+
+// Calculate the FragmentGeometry for the given page container or page border
+// box, and optionally its margins. page_containing_block_size is typically the
+// @page size returned from DesiredPageContainingBlockSize().
+void ResolvePageBoxGeometry(const BlockNode& page_box,
+                            LogicalSize page_containing_block_size,
+                            FragmentGeometry*,
+                            BoxStrut* margins = nullptr);
+
+// Calculate the initial containing block size to use when paginating (to be
+// used by viewport units, out-of-flow positioning, etc.). This is defined as
+// the page area of the first page:
+// https://drafts.csswg.org/css-page-3/#page-model
+PhysicalSize CalculateInitialContainingBlockSizeForPagination(Document&);
 
 // Return the scale factor to use when scaling paginated content (and the page
 // border box) from layout to target to the target output. Layout may use a
@@ -35,6 +66,8 @@
                                        wtf_size_t page_number);
 
 // Get the page border box (BoxType::kPageBorderBox) child of a page container.
+const PhysicalFragmentLink& GetPageBorderBoxLink(
+    const PhysicalBoxFragment& page_container);
 const PhysicalBoxFragment& GetPageBorderBox(
     const PhysicalBoxFragment& page_container);
 
@@ -56,6 +89,11 @@
 float CalculateOverflowShrinkForPrinting(const LayoutView&,
                                          float maximum_shrink_factor);
 
+// Populate and return a WebPrintPageDescription structure for a given page
+// based on layout and style.
+WebPrintPageDescription GetPageDescriptionFromLayout(const Document&,
+                                                     wtf_size_t page_number);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_PAGINATION_UTILS_H_
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.cc b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
index 3861d59..672a4d2 100644
--- a/third_party/blink/renderer/core/layout/physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/physical_box_fragment.cc
@@ -413,6 +413,14 @@
     ComputeItemsAddress()->~FragmentItems();
 }
 
+PhysicalRect PhysicalBoxFragment::ContentRect() const {
+  PhysicalRect rect(PhysicalOffset(), Size());
+  rect.Contract(Borders() + Padding());
+  DCHECK_GE(rect.size.width, LayoutUnit());
+  DCHECK_GE(rect.size.height, LayoutUnit());
+  return rect;
+}
+
 const LayoutBox* PhysicalBoxFragment::OwnerLayoutBox() const {
   // TODO(layout-dev): We should probably get rid of this method, now that it
   // does nothing, apart from some checking. The checks are useful, but could be
diff --git a/third_party/blink/renderer/core/layout/physical_box_fragment.h b/third_party/blink/renderer/core/layout/physical_box_fragment.h
index eca27dc..eb353777 100644
--- a/third_party/blink/renderer/core/layout/physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/physical_box_fragment.h
@@ -255,7 +255,7 @@
     return PhysicalBoxStrut();
   }
 
-  const PhysicalOffset ContentOffset() const {
+  PhysicalOffset ContentOffset() const {
     if (!HasBorders() && !HasPadding())
       return PhysicalOffset();
     PhysicalOffset offset;
@@ -266,6 +266,8 @@
     return offset;
   }
 
+  PhysicalRect ContentRect() const;
+
   // Returns the bounds of any inflow children for this fragment (specifically
   // no out-of-flow positioned objects). This will return |std::nullopt| if:
   //  - The fragment is *not* a scroll container.
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index dd7b9f9..adabf77 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -87,6 +87,7 @@
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme_overlay_mobile.h"
 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
+#include "third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h"
 #include "third_party/blink/renderer/core/svg/svg_resource_document_cache.h"
 #include "third_party/blink/renderer/platform/bindings/source_location.h"
@@ -253,6 +254,12 @@
                                !color_provider_colors->IsEmpty()
                            ? *color_provider_colors
                            : ColorProviderColorMaps::CreateDefault());
+  if (is_ordinary_) {
+    // TODO(crbug.com/336382906): We will revisit where we'll be doing this in
+    // production.
+    IsolatedSVGDocumentHostInitializer::Get()
+        ->MaybePrepareIsolatedSVGDocumentHost();
+  }
 }
 
 Page::~Page() {
@@ -1244,6 +1251,12 @@
     close_task_handler_->SetPage(nullptr);
     close_task_handler_ = nullptr;
   }
+
+  // Clear speculatively created resources for SVGImage when there are no
+  // ordinary pages. This is desirable to shutdown renderer gracefully.
+  if (is_ordinary_ && OrdinaryPages().empty()) {
+    IsolatedSVGDocumentHostInitializer::Get()->Clear();
+  }
 }
 
 void Page::RegisterPluginsChangedObserver(PluginsChangedObserver* observer) {
@@ -1448,6 +1461,9 @@
     // the page becomes interactive. Give it a chance to clean up.
     page->v8_compile_hints_producer_->ClearData();
   }
+
+  // Clear speculatively created resources for SVGImage.
+  IsolatedSVGDocumentHostInitializer::Get()->Clear();
 }
 
 // Ensure the 10 bits reserved for connected frame count in NodeRareData are
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index 405bd33..7a25f2c1 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2664,6 +2664,9 @@
   static_assert(kPseudoIdSelection >= kFirstPublicPseudoId &&
                     kPseudoIdSelection <= kLastTrackedPublicPseudoId,
                 "kPseudoIdSelection must be public");
+  static_assert(kPseudoIdSearchText >= kFirstPublicPseudoId &&
+                    kPseudoIdSearchText <= kLastTrackedPublicPseudoId,
+                "kPseudoIdSearchText must be public");
   static_assert(kPseudoIdTargetText >= kFirstPublicPseudoId &&
                     kPseudoIdTargetText <= kLastTrackedPublicPseudoId,
                 "kPseudoIdTargetText must be public");
@@ -2678,6 +2681,7 @@
                 "kPseudoIdHighlight must be public");
 
   const unsigned mask = (1 << (kPseudoIdSelection - kFirstPublicPseudoId)) |
+                        (1 << (kPseudoIdSearchText - kFirstPublicPseudoId)) |
                         (1 << (kPseudoIdTargetText - kFirstPublicPseudoId)) |
                         (1 << (kPseudoIdSpellingError - kFirstPublicPseudoId)) |
                         (1 << (kPseudoIdGrammarError - kFirstPublicPseudoId)) |
diff --git a/third_party/blink/renderer/core/style/computed_style_constants.h b/third_party/blink/renderer/core/style/computed_style_constants.h
index ebb034f2..c677e0ae 100644
--- a/third_party/blink/renderer/core/style/computed_style_constants.h
+++ b/third_party/blink/renderer/core/style/computed_style_constants.h
@@ -56,7 +56,8 @@
 enum PseudoId : uint8_t {
   // The order must be NOP ID, public IDs, and then internal IDs.
   // If you add or remove a public ID, you must update the field_size of
-  // "PseudoBits" in computed_style_extra_fields.json5.
+  // "PseudoElementStyles" in computed_style_extra_fields.json5 to
+  // (kLastTrackedPublicPseudoId - kFirstPublicPseudoId + 1).
   //
   // The above is necessary because presence of a public pseudo element style
   // for an element is tracked on the element's ComputedStyle. This is done for
@@ -72,6 +73,7 @@
   kPseudoIdScrollbar,
   kPseudoIdScrollMarker,
   kPseudoIdScrollMarkers,
+  kPseudoIdSearchText,
   kPseudoIdTargetText,
   kPseudoIdHighlight,
   kPseudoIdSpellingError,
@@ -102,6 +104,7 @@
 inline bool IsHighlightPseudoElement(PseudoId pseudo_id) {
   switch (pseudo_id) {
     case kPseudoIdSelection:
+    case kPseudoIdSearchText:
     case kPseudoIdTargetText:
     case kPseudoIdHighlight:
     case kPseudoIdSpellingError:
@@ -118,6 +121,7 @@
   // highlight inheritance feature is enabled.
   return ((IsHighlightPseudoElement(pseudo_id) &&
            RuntimeEnabledFeatures::HighlightInheritanceEnabled()) ||
+          pseudo_id == PseudoId::kPseudoIdSearchText ||
           pseudo_id == PseudoId::kPseudoIdHighlight ||
           pseudo_id == PseudoId::kPseudoIdSpellingError ||
           pseudo_id == PseudoId::kPseudoIdGrammarError);
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 9150139..3a8e44ad 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -236,7 +236,7 @@
     {
       name: "PseudoElementStyles",
       field_template: "primitive",
-      field_size: 14,
+      field_size: 15,
       default_value: "kPseudoIdNone",
       type_name: "unsigned",
       reset_on_new_style: true,
diff --git a/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.cc b/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.cc
index 479341b..5d1ad3f 100644
--- a/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.cc
+++ b/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.cc
@@ -27,8 +27,10 @@
 
 #include "third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.h"
 
+#include "base/system/sys_info.h"
 #include "base/trace_event/trace_event.h"
 #include "services/network/public/cpp/single_request_url_loader_factory.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -38,6 +40,9 @@
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h"
 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
+#include "third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/main_thread.h"
+#include "third_party/blink/renderer/platform/scheduler/public/main_thread_scheduler.h"
 
 namespace blink {
 
@@ -269,4 +274,116 @@
   visitor->Trace(frame_client_);
 }
 
+// static
+IsolatedSVGDocumentHostInitializer* IsolatedSVGDocumentHostInitializer::Get() {
+  DEFINE_STATIC_LOCAL(
+      Persistent<IsolatedSVGDocumentHostInitializer>, initializer,
+      (MakeGarbageCollected<IsolatedSVGDocumentHostInitializer>()));
+  return initializer;
+}
+
+IsolatedSVGDocumentHostInitializer::IsolatedSVGDocumentHostInitializer()
+    : max_pre_initialize_count_(
+          base::checked_cast<wtf_size_t>(
+              !base::SysInfo::IsLowEndDevice() &&
+              base::FeatureList::IsEnabled(
+                  features::kPreInitializePageAndFrameForSVGImage))
+              ? features::kMaxCountOfPreInitializePageAndFrameForSVGImage.Get()
+              : 0),
+      agent_group_scheduler_(Thread::MainThread()
+                                 ->Scheduler()
+                                 ->ToMainThreadScheduler()
+                                 ->CreateAgentGroupScheduler()),
+      task_runner_(agent_group_scheduler_->DefaultTaskRunner()) {}
+
+std::pair<SVGImageChromeClient*, IsolatedSVGDocumentHost*>
+IsolatedSVGDocumentHostInitializer::GetOrCreate() {
+  if (max_pre_initialize_count_ > 0 &&
+      !chrome_clients_and_document_hosts_.empty()) {
+    TRACE_EVENT("blink", "IsolatedSVGDocumentHostInitializer::GetOrCreate",
+                "return_from_cache", true);
+    auto [chrome_client, document_host] =
+        chrome_clients_and_document_hosts_.back();
+    chrome_clients_and_document_hosts_.pop_back();
+    return {chrome_client, document_host};
+  }
+
+  TRACE_EVENT("blink", "IsolatedSVGDocumentHostInitializer::GetOrCreate",
+              "return_from_cache", false);
+  return Create();
+}
+
+void IsolatedSVGDocumentHostInitializer::MaybePrepareIsolatedSVGDocumentHost() {
+  paused_ = false;
+
+  if (chrome_clients_and_document_hosts_.size() >= max_pre_initialize_count_) {
+    return;
+  }
+
+  task_runner_->PostTask(
+      FROM_HERE,
+      WTF::BindOnce(
+          [](IsolatedSVGDocumentHostInitializer* initializer) {
+            if (initializer->paused_) {
+              return;
+            }
+            if (initializer->chrome_clients_and_document_hosts_.size() >=
+                initializer->max_pre_initialize_count_) {
+              return;
+            }
+            TRACE_EVENT("blink",
+                        "IsolatedSVGDocumentHostInitializer::"
+                        "MaybePrepareIsolatedSVGDocumentHost");
+            initializer->chrome_clients_and_document_hosts_.push_back(
+                initializer->Create());
+            initializer->MaybePrepareIsolatedSVGDocumentHost();
+          },
+          WrapWeakPersistent(this)));
+}
+
+std::pair<SVGImageChromeClient*, IsolatedSVGDocumentHost*>
+IsolatedSVGDocumentHostInitializer::Create() {
+  // SVG will be shared via MemoryCache (which is renderer process
+  // global cache) across multiple AgentSchedulingGroups. That's
+  // why we can't use an existing AgentSchedulingGroup for now. If
+  // we incorrectly use the existing ASG/AGS and if we freeze task
+  // queues on a AGS, it will affect SVGs on other AGS. To
+  // mitigate this problem, we need to split the MemoryCache into
+  // smaller granularity. There is an active effort to mitigate
+  // this which is called "Memory Cache Per Context"
+  // (https://crbug.com/1127971).
+  AgentGroupScheduler* agent_group_scheduler =
+      Thread::MainThread()
+          ->Scheduler()
+          ->ToMainThreadScheduler()
+          ->CreateAgentGroupScheduler();
+
+  SVGImageChromeClient* chrome_client =
+      MakeGarbageCollected<SVGImageChromeClient>();
+
+  chrome_client->InitAnimationTimer(
+      agent_group_scheduler->CompositorTaskRunner());
+
+  IsolatedSVGDocumentHost* isolated_svg_document_host =
+      MakeGarbageCollected<IsolatedSVGDocumentHost>(*chrome_client,
+                                                    *agent_group_scheduler);
+
+  return {chrome_client, isolated_svg_document_host};
+}
+
+void IsolatedSVGDocumentHostInitializer::Clear() {
+  // Pause creation so that it doesn't continue creating.
+  paused_ = true;
+  for (auto [svg_image_chrome_client, isolated_svg_document_host] :
+       chrome_clients_and_document_hosts_) {
+    isolated_svg_document_host->Shutdown();
+  }
+  chrome_clients_and_document_hosts_.clear();
+}
+
+void IsolatedSVGDocumentHostInitializer::Trace(Visitor* visitor) const {
+  visitor->Trace(chrome_clients_and_document_hosts_);
+  visitor->Trace(agent_group_scheduler_);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.h b/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.h
index a5a395b6..9332cca 100644
--- a/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.h
+++ b/third_party/blink/renderer/core/svg/graphics/isolated_svg_document_host.h
@@ -29,6 +29,8 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SVG_GRAPHICS_ISOLATED_SVG_DOCUMENT_HOST_H_
 
 #include "base/functional/callback.h"
+#include "base/task/single_thread_task_runner.h"
+#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.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/wtf/shared_buffer.h"
@@ -37,6 +39,7 @@
 
 class AgentGroupScheduler;
 class IsolatedSVGChromeClient;
+class SVGImageChromeClient;
 class LocalFrame;
 class Page;
 class SVGSVGElement;
@@ -89,6 +92,36 @@
   LoadState load_state_ = kNotStarted;
 };
 
+// IsolatedSVGDocumentHostInitializer instantiates several
+// SVGImageChromeClients and IsolatedSVGDocumentHosts and uses the
+// precreated objects when it's really necessary because the
+// IsolatedSVGDocumentHost creation takes around 1 ms. We want to avoid
+// the latency.
+class IsolatedSVGDocumentHostInitializer final
+    : public GarbageCollected<IsolatedSVGDocumentHostInitializer> {
+ public:
+  static IsolatedSVGDocumentHostInitializer* Get();
+
+  IsolatedSVGDocumentHostInitializer();
+  ~IsolatedSVGDocumentHostInitializer() = default;
+
+  std::pair<SVGImageChromeClient*, IsolatedSVGDocumentHost*> GetOrCreate();
+  void Clear();
+  void MaybePrepareIsolatedSVGDocumentHost();
+  void Trace(Visitor* visitor) const;
+
+ private:
+  std::pair<SVGImageChromeClient*, IsolatedSVGDocumentHost*> Create();
+
+  const wtf_size_t max_pre_initialize_count_;
+  HeapVector<
+      std::pair<Member<SVGImageChromeClient>, Member<IsolatedSVGDocumentHost>>>
+      chrome_clients_and_document_hosts_;
+  Member<AgentGroupScheduler> agent_group_scheduler_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  bool paused_ = false;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SVG_GRAPHICS_ISOLATED_SVG_DOCUMENT_HOST_H_
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 4fccde71..36d25063 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -80,20 +80,6 @@
 
 SVGImage::SVGImage(ImageObserver* observer, bool is_multipart)
     : Image(observer, is_multipart),
-      // TODO(chikamune): use an existing AgentGroupScheduler
-      // SVG will be shared via MemoryCache (which is renderer process
-      // global cache) across multiple AgentSchedulingGroups. That's
-      // why we can't use an existing AgentSchedulingGroup for now. If
-      // we incorrectly use the existing ASG/AGS and if we freeze task
-      // queues on a AGS, it will affect SVGs on other AGS. To
-      // mitigate this problem, we need to split the MemoryCache into
-      // smaller granularity. There is an active effort to mitigate
-      // this which is called "Memory Cache Per Context"
-      // (https://crbug.com/1127971).
-      agent_group_scheduler_(Thread::MainThread()
-                                 ->Scheduler()
-                                 ->ToMainThreadScheduler()
-                                 ->CreateAgentGroupScheduler()),
       has_pending_timeline_rewind_(false) {}
 
 SVGImage::~SVGImage() {
@@ -640,12 +626,6 @@
 
   SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Blink.SVGImage.DataChanged");
 
-  CHECK(!document_host_);
-
-  chrome_client_ = MakeGarbageCollected<SVGImageChromeClient>(this);
-  chrome_client_->InitAnimationTimer(
-      agent_group_scheduler_->CompositorTaskRunner());
-
   // Because an SVGImage has no relation to a normal Page, it can't get default
   // font settings from the embedder. Copy settings for fonts and other things
   // so we have sensible defaults. These settings are fixed and will not update
@@ -660,8 +640,10 @@
   // This will become an issue when SVGImage will be able to load other
   // SVGImage objects, but we're safe now, because SVGImage can only be
   // loaded by a top-level document.
-  document_host_ = MakeGarbageCollected<IsolatedSVGDocumentHost>(
-      *chrome_client_, *agent_group_scheduler_);
+  CHECK(!document_host_);
+  std::tie(chrome_client_, document_host_) =
+      IsolatedSVGDocumentHostInitializer::Get()->GetOrCreate();
+  chrome_client_->SetImage(this);
   document_host_->InstallDocument(
       Data(),
       WTF::BindOnce(&SVGImage::NotifyAsyncLoadCompleted,
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.h b/third_party/blink/renderer/core/svg/graphics/svg_image.h
index 65b9b863..6b65c39 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.h
@@ -234,7 +234,6 @@
 
   Persistent<SVGImageChromeClient> chrome_client_;
   Persistent<IsolatedSVGDocumentHost> document_host_;
-  Persistent<AgentGroupScheduler> agent_group_scheduler_;
 
   // When an SVG image has no intrinsic size, the size depends on the default
   // object size, which in turn depends on the container. One SVGImage may
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc
index fa93f7d..cae88dd6 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.cc
@@ -44,9 +44,7 @@
   return true;
 }
 
-SVGImageChromeClient::SVGImageChromeClient(SVGImage* image)
-    : image_(image),
-      timeline_state_(kRunning) {}
+SVGImageChromeClient::SVGImageChromeClient() : timeline_state_(kRunning) {}
 
 void SVGImageChromeClient::InitAnimationTimer(
     scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner) {
@@ -99,6 +97,10 @@
 void SVGImageChromeClient::ScheduleAnimation(const LocalFrameView*,
                                              base::TimeDelta fire_time) {
   DCHECK(animation_timer_);
+  // If `image_` is null, nothing to do.
+  if (!image_) {
+    return;
+  }
   // Because a single SVGImage can be shared by multiple pages, we can't key
   // our svg image layout on the page's real animation frame. Therefore, we
   // run this fake animation timer to trigger layout in SVGImages. The name,
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
index e1a8f11..add5bdf 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
@@ -52,12 +52,16 @@
 
 class CORE_EXPORT SVGImageChromeClient final : public IsolatedSVGChromeClient {
  public:
-  explicit SVGImageChromeClient(SVGImage*);
+  explicit SVGImageChromeClient();
 
   void InitAnimationTimer(
       scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner);
 
   SVGImage* GetImage() const { return image_; }
+  void SetImage(SVGImage* image) {
+    CHECK(!image_);
+    image_ = image;
+  }
 
   void SuspendAnimation();
   void ResumeAnimation();
diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.cc b/third_party/blink/renderer/modules/manifest/manifest_parser.cc
index 5098b7d..bfd0770 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_parser.cc
+++ b/third_party/blink/renderer/modules/manifest/manifest_parser.cc
@@ -2322,7 +2322,8 @@
       continue;
     }
 
-    auto tokens = CSSTokenizer(media_query.value()).TokenizeToEOF();
+    CSSTokenizer tokenizer(media_query.value());
+    auto tokens = tokenizer.TokenizeToEOF();
     CSSParserTokenRange range(tokens);
     while (!range.AtEnd()) {
       if (range.Peek().GetType() == kIdentToken &&
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
index 0c8609b..5bc1f26 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver_impl.cc
@@ -22,7 +22,7 @@
 
 BASE_FEATURE(kRTCAlignReceivedEncodedVideoTransforms,
              "RTCAlignReceivedEncodedVideoTransforms",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 RtpReceiverState::RtpReceiverState(
     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index f1e2949..7e5932e 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -369,7 +369,9 @@
 
 const gpu::Mailbox& CanvasResourceSharedBitmap::GetOrCreateGpuMailbox(
     MailboxSyncMode sync_mode) {
-  return shared_bitmap_id_;
+  // By contract this method is valid to call only if
+  // SupportsAcceleratedCompositing() is true.
+  NOTREACHED_NORETURN();
 }
 
 void CanvasResourceSharedBitmap::TakeSkImage(sk_sp<SkImage> image) {
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index 5597ede1..4a20a57 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -141,6 +141,7 @@
   // The mailbox which can be used to reference this resource in GPU commands.
   // The sync mode indicates how the sync token for the resource should be
   // prepared.
+  // NOTE: Valid to call only if SupportsAcceleratedCompositing() is true.
   virtual const gpu::Mailbox& GetOrCreateGpuMailbox(MailboxSyncMode) = 0;
 
   // A CanvasResource is not thread-safe and does not allow concurrent usage
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 8b5b679..972e3418 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -3389,6 +3389,12 @@
       status: "experimental",
     },
     {
+      // crbug.com/340041662
+      name: "RubyLineEdgeAlignment",
+      status: "experimental",
+      depends_on: ["RubyLineBreakable"],
+    },
+    {
       // Runs a microtask checkpoint before creating custom elements in
       // XMLDocumentParser. Should be safe to remove after M125
       name: "RunMicrotaskBeforeXmlCustomElement",
@@ -3470,6 +3476,10 @@
       name: "ScrollTopLeftInterop",
       status: "stable",
     },
+    {
+      name: "SearchTextHighlightPseudo",
+      status: "test",
+    },
     // SecurePaymentConfirmation has shipped on some platforms, but its
     // availability is controlled by the browser process (via the
     // SecurePaymentConfirmationBrowser feature), as it requires browser
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 06dfecc..40e0f9fb 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -187,12 +187,6 @@
       base::Unretained(this)));
   on_audio_silent_closure_.Reset(base::BindRepeating(
       &PageSchedulerImpl::OnAudioSilent, base::Unretained(this)));
-  update_frozen_state_callback_.Reset(base::BindRepeating(
-      [](PageSchedulerImpl* page_scheduler) {
-        PolicyUpdater policy_updater;
-        page_scheduler->UpdateFrozenState(policy_updater);
-      },
-      base::Unretained(this)));
 }
 
 PageSchedulerImpl::~PageSchedulerImpl() {
@@ -258,7 +252,7 @@
   // Only pages owned by web views can be frozen.
   DCHECK(!frozen || IsOrdinary());
 
-  update_frozen_state_callback_.Cancel();
+  update_frozen_state_timer_.Stop();
   if (is_frozen_ == frozen)
     return;
   is_frozen_ = frozen;
@@ -831,7 +825,9 @@
   return !!unimportant_wake_up_budget_pool_;
 }
 
-void PageSchedulerImpl::UpdateFrozenState(PolicyUpdater& policy_updater) {
+void PageSchedulerImpl::UpdateFrozenState(
+    PolicyUpdater& policy_updater,
+    base::MemoryReductionTaskContext called_from) {
   // Only ordinary pages can be frozen.
   if (!IsOrdinary()) {
     CHECK(!IsFrozen());
@@ -854,24 +850,43 @@
       freeze_time = now;
     } else if (base::FeatureList::IsEnabled(
                    blink::features::kStopInBackground)) {
-      freeze_time =
-          std::max(page_visibility_changed_time_, audio_state_changed_time_) +
-          delay_for_background_tab_freezing_;
+      if (called_from == base::MemoryReductionTaskContext::kProactive) {
+        // Special case: Freeze now if the timer has been fast-forwarded to
+        // proactively reduce memory.
+        freeze_time = now;
+      } else {
+        freeze_time =
+            std::max(page_visibility_changed_time_, audio_state_changed_time_) +
+            delay_for_background_tab_freezing_;
+      }
     }
   }
 
   if (freeze_time > now) {
     SetPageFrozenImpl(/* frozen=*/false, policy_updater);
     if (!freeze_time.is_max()) {
-      base::PostDelayedMemoryReductionTask(
-          main_thread_scheduler_->ControlTaskRunner(), FROM_HERE,
-          update_frozen_state_callback_.GetCallback(), freeze_time - now);
+      update_frozen_state_timer_.SetTaskRunner(
+          main_thread_scheduler_->ControlTaskRunner());
+      update_frozen_state_timer_.Start(
+          FROM_HERE, freeze_time - now,
+          base::BindOnce(
+              [](PageSchedulerImpl* page_scheduler,
+                 base::MemoryReductionTaskContext called_from) {
+                PolicyUpdater policy_updater;
+                page_scheduler->UpdateFrozenState(policy_updater, called_from);
+              },
+              base::Unretained(this)));
     }
   } else {
     SetPageFrozenImpl(/* frozen=*/true, policy_updater);
   }
 }
 
+void PageSchedulerImpl::UpdateFrozenState(PolicyUpdater& policy_updater) {
+  PageSchedulerImpl::UpdateFrozenState(
+      policy_updater, base::MemoryReductionTaskContext::kDelayExpired);
+}
+
 std::array<WakeUpBudgetPool*, PageSchedulerImpl::kNumWakeUpBudgetPools>
 PageSchedulerImpl::AllWakeUpBudgetPools() {
   return {unimportant_wake_up_budget_pool_.get(),
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index 4525edb..9d0a78c 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <optional>
 
+#include "base/memory/post_delayed_memory_reduction_task.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/common/lazy_now.h"
@@ -219,6 +220,8 @@
   // now, perform the state transition. Otherwise, schedules another call to
   // this method at the time when it should change.
   void UpdateFrozenState(PolicyUpdater& policy_updater);
+  void UpdateFrozenState(PolicyUpdater& policy_updater,
+                         base::MemoryReductionTaskContext called_from);
 
   // Returns all WakeUpBudgetPools owned by this PageSchedulerImpl.
   static constexpr int kNumWakeUpBudgetPools = 4;
@@ -287,7 +290,7 @@
   CancelableClosureHolder do_intensively_throttle_wake_ups_callback_;
   CancelableClosureHolder reset_had_recent_title_or_favicon_update_;
   CancelableClosureHolder on_audio_silent_closure_;
-  CancelableClosureHolder update_frozen_state_callback_;
+  base::OneShotDelayedBackgroundTimer update_frozen_state_timer_;
   const base::TimeDelta delay_for_background_tab_freezing_;
 
   // Interval between throttled wake ups for unimportant frames (visible, small
diff --git a/third_party/blink/renderer/platform/widget/input/input_event_prediction_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_event_prediction_unittest.cc
index d3ef9bc..3f53475b 100644
--- a/third_party/blink/renderer/platform/widget/input/input_event_prediction_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_event_prediction_unittest.cc
@@ -437,7 +437,7 @@
 
   for (size_t state =
            static_cast<size_t>(blink::WebTouchPoint::State::kStateUndefined);
-       state <= static_cast<size_t>(blink::WebTouchPoint::State::kStateMax);
+       state <= static_cast<size_t>(blink::WebTouchPoint::State::kMaxValue);
        state++) {
     touch_event.touches[0].state =
         static_cast<blink::WebTouchPoint::State>(state);
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
index acd1fd9..8448e58 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
@@ -262,9 +262,6 @@
     EventDispositionCallback callback) {
   DCHECK(input_handler_);
 
-  static bool queue_blocking_gesture_scrolls =
-      base::FeatureList::IsEnabled(features::kQueueBlockingGestureScrolls);
-
   input_handler_->NotifyInputEvent();
 
   int64_t trace_id = event->latency_info().trace_id();
@@ -346,23 +343,6 @@
       DispatchQueuedInputEvents(false /* frame_aligned */);
       return;
     }
-    // Scroll updates should typically be queued and wait until a
-    // BeginImplFrame to dispatch. However, the first scroll update to be
-    // generated from a *blocking* touch sequence will have waited for the
-    // touch event to be ACK'ed by the renderer as unconsumed. Queueing here
-    // again until BeginImplFrame means we'll likely add a whole frame of
-    // latency to so we flush the queue immediately. This happens only for the
-    // first scroll update because once a scroll starts touch events are
-    // dispatched non-blocking so scroll updates don't wait for a touch ACK.
-    // The |is_source_touch_event_set_blocking| bit is set based on the
-    // renderer's reply that a blocking touch stream should be made
-    // non-blocking. Note: unlike wheel events below, the first GSU in a touch
-    // may have come from a non-blocking touch sequence, e.g. if the earlier
-    // touchstart determined we're in a |touch-action: pan-y| region. Because
-    // of this, we can't simply look at the first GSU like wheels do.
-    bool is_from_blocking_touch =
-        gesture_event.SourceDevice() == WebGestureDevice::kTouchscreen &&
-        gesture_event.is_source_touch_event_set_blocking;
 
     // TODO(bokan): This was added in https://crrev.com/c/557463 before async
     // wheel events. It's not clear to me why flushing on a scroll end would
@@ -385,8 +365,7 @@
 
     // |synchronous_input_handler_| is WebView only. WebView has different
     // mechanisms and we want to forward all events immediately.
-    if ((is_from_blocking_touch && !queue_blocking_gesture_scrolls) ||
-        is_scroll_end_from_wheel || is_first_wheel_scroll_update ||
+    if (is_scroll_end_from_wheel || is_first_wheel_scroll_update ||
         synchronous_input_handler_) {
       DispatchQueuedInputEvents(false /* frame_aligned */);
     }
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
index 58b4e0b7..8b86eb00 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.cc
@@ -336,9 +336,7 @@
   DCHECK(ack_result == mojom::blink::InputEventResultState::kSetNonBlocking ||
          ack_result ==
              mojom::blink::InputEventResultState::kSetNonBlockingDueToFling ||
-         ack_result == mojom::blink::InputEventResultState::kNotConsumed ||
-         ack_result ==
-             mojom::blink::InputEventResultState::kNotConsumedBlocking);
+         ack_result == mojom::blink::InputEventResultState::kNotConsumed);
   DCHECK(Allowed(event->Event(), allow_main_gesture_scroll));
 
   bool is_blocking =
diff --git a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
index 291d13c..6dfcd1b6 100644
--- a/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
+++ b/third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h
@@ -139,8 +139,6 @@
       mojom::blink::InputEventResultState ack_state) {
     return ack_state == mojom::blink::InputEventResultState::kNotConsumed ||
            ack_state ==
-               mojom::blink::InputEventResultState::kNotConsumedBlocking ||
-           ack_state ==
                mojom::blink::InputEventResultState::kSetNonBlockingDueToFling;
   }
 
diff --git a/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc b/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc
index dcc4bb5..4972be5 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc
+++ b/third_party/blink/renderer/platform/widget/input/widget_base_input_handler.cc
@@ -144,13 +144,11 @@
                                 std::move(predicted_pointer_events), latency);
 }
 
-mojom::InputEventResultState GetAckResult(WebInputEventResult processed) {
-  if (processed == WebInputEventResult::kNotHandled) {
-    return base::FeatureList::IsEnabled(features::kFixGestureScrollQueuingBug)
-               ? mojom::InputEventResultState::kNotConsumedBlocking
-               : mojom::InputEventResultState::kNotConsumed;
-  }
-  return mojom::InputEventResultState::kConsumed;
+mojom::blink::InputEventResultState GetAckResult(
+    WebInputEventResult processed) {
+  return processed == WebInputEventResult::kNotHandled
+             ? mojom::blink::InputEventResultState::kNotConsumed
+             : mojom::blink::InputEventResultState::kConsumed;
 }
 
 bool IsGestureScroll(WebInputEvent::Type type) {
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
index 38ad17e..5bb757d8 100644
--- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
+++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
@@ -94,9 +94,6 @@
     case InputHandlerProxy::DID_HANDLE:
       return mojom::blink::InputEventResultState::kConsumed;
     case InputHandlerProxy::DID_NOT_HANDLE:
-      if (base::FeatureList::IsEnabled(features::kFixGestureScrollQueuingBug)) {
-        return mojom::blink::InputEventResultState::kNotConsumedBlocking;
-      }
       return mojom::blink::InputEventResultState::kNotConsumed;
     case InputHandlerProxy::DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING:
       return mojom::blink::InputEventResultState::kSetNonBlockingDueToFling;
@@ -1034,8 +1031,7 @@
   if (ack_state == mojom::blink::InputEventResultState::kSetNonBlocking ||
       ack_state ==
           mojom::blink::InputEventResultState::kSetNonBlockingDueToFling ||
-      ack_state == mojom::blink::InputEventResultState::kNotConsumed ||
-      ack_state == mojom::blink::InputEventResultState::kNotConsumedBlocking) {
+      ack_state == mojom::blink::InputEventResultState::kNotConsumed) {
     DCHECK(!overscroll_params);
     DCHECK(!event->latency_info().coalesced());
     MainThreadEventQueue::DispatchType dispatch_type =
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 2c12b120..666eaf6a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -627,6 +627,9 @@
 
 crbug.com/333831247 http/tests/devtools/elements/styles-3/styles-disable-then-delete.js [ Crash Failure Pass Timeout ]
 
+# Temporarily disabling for landing a UI update in Chrome DevTools
+crbug.com/325442270 http/tests/devtools/network/preview-searchable.js [ Failure ]
+
 # Subpixel rounding differences that are incorrect.
 crbug.com/997202 compositing/overflow/scaled-overflow.html [ Failure ]
 # Only one pixel difference (187,187,187) vs (188,188,188) occasionally.
@@ -3400,11 +3403,21 @@
 crbug.com/40341678 external/wpt/css/css-page/page-box-001-print.html [ Failure ]
 crbug.com/40341678 external/wpt/css/css-page/page-box-002-print.html [ Failure ]
 crbug.com/40341678 external/wpt/css/css-page/page-box-003-print.html [ Failure ]
+crbug.com/40341678 external/wpt/css/css-page/page-box-004-print.html [ Failure ]
+crbug.com/40341678 external/wpt/css/css-page/page-margin-auto-negative-print.tentative.html [ Failure ]
+crbug.com/40341678 external/wpt/css/css-page/page-margin-auto-print.html [ Failure ]
+crbug.com/40341678 external/wpt/css/css-page/page-size-013-print.html [ Failure ]
+crbug.com/40341678 external/wpt/css/css-page/page-size-014-print.html [ Failure ]
 crbug.com/40341678 wpt_internal/printing/icb-scaling-005-print.html [ Failure ]
 crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-box-000-print.html [ Pass ]
 crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-box-001-print.html [ Pass ]
 crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-box-002-print.html [ Pass ]
 crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-box-003-print.html [ Pass ]
+crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-box-004-print.html [ Pass ]
+crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-margin-auto-negative-print.tentative.html [ Pass ]
+crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-margin-auto-print.html [ Pass ]
+crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-size-013-print.html [ Pass ]
+crbug.com/40341678 virtual/page-margin-boxes/external/wpt/css/css-page/page-size-014-print.html [ Pass ]
 crbug.com/40341678 virtual/page-margin-boxes/wpt_internal/printing/icb-scaling-005-print.html [ Pass ]
 
 crbug.com/1031667 external/wpt/css/css-pseudo/marker-content-007.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-from-fixed-anchor-ref.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-from-fixed-anchor-ref.html
deleted file mode 100644
index 72026a2..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-from-fixed-anchor-ref.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!doctype html>
-<style>
-body {
-  width: 150vw;
-  height: 150vh;
-}
-.anchor {
-  width: 50px;
-  height: 50px;
-  background: orange;
-}
-#anchor1 {
-  position: absolute;
-  top: 100px;
-  left: 350px;
-}
-#anchor2 {
-  position:fixed;
-  right: 0;
-  bottom: 0;
-}
-#anchored {
-  position: absolute;
-  top: 50px;
-  left: 350px;
-  width: 50px;
-  height: 50px;
-  background: blue;
-}
-</style>
-<div class="anchor" id="anchor1"></div>
-<div class="anchor" id="anchor2"></div>
-<div id="anchored"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-from-fixed-anchor.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-from-fixed-anchor.html
deleted file mode 100644
index 904aa55..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-from-fixed-anchor.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!doctype html>
-<html class=reftest-wait>
-<meta charset="utf-8">
-<title>CSS Anchor Positioning Test: @position-try with different default anchors,
-       switching to fixed-position anchor on scroll and back on another scroll</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-rule">
-<link rel="match" href="position-try-switch-from-fixed-anchor-ref.html">
-<script src="/common/reftest-wait.js"></script>
-<script src="/common/rendering-utils.js"></script>
-<style>
-body {
-  width: 150vw;
-  height: 150vh;
-}
-.anchor {
-  width: 50px;
-  height: 50px;
-  background: orange;
-}
-#anchor1 {
-  anchor-name: --anchor1;
-  position: absolute;
-  top: 100px;
-  left: 350px;
-}
-#anchor2 {
-  anchor-name: --anchor2;
-  position:fixed;
-  right: 0;
-  bottom: 0;
-}
-#anchored {
-  position-anchor: --anchor1;
-  inset-area: top;
-  position-try-options: --fixed;
-  position: fixed;
-  width: 50px;
-  height: 50px;
-  background: blue;
-}
-@position-try --fixed {
-  inset-area: top left;
-  position-anchor: --anchor2;
-}
-</style>
-<div class="anchor" id="anchor1"></div>
-<div class="anchor" id="anchor2"></div>
-<div id="anchored"></div>
-<script>
-waitForAtLeastOneFrame().then(() => {
-  window.scrollTo(250, 100);
-  waitForAtLeastOneFrame().then(() => {
-    window.scrollTo(0, 0);
-    takeScreenshot();
-  });
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-to-fixed-anchor-ref.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-to-fixed-anchor-ref.html
deleted file mode 100644
index 254cbf3..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-to-fixed-anchor-ref.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<style>
-body {
-  width: 150vw;
-  height: 150vh;
-}
-.anchor {
-  width: 50px;
-  height: 50px;
-  background: orange;
-}
-#anchor1 {
-  position: absolute;
-  top: 100px;
-  left: 350px;
-}
-#anchor2 {
-  position:fixed;
-  right: 0;
-  bottom: 0;
-}
-#anchored {
-  position: fixed;
-  right: 50px;
-  bottom: 50px;
-  width: 50px;
-  height: 50px;
-  background: blue;
-}
-</style>
-<div class="anchor" id="anchor1"></div>
-<div class="anchor" id="anchor2"></div>
-<div id="anchored"></div>
-<script>
-window.scrollTo(250, 100);
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-to-fixed-anchor.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-to-fixed-anchor.html
deleted file mode 100644
index 54fcb2e..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/position-try-switch-to-fixed-anchor.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!doctype html>
-<html class=reftest-wait>
-<meta charset="utf-8">
-<title>CSS Anchor Positioning Test: @position-try with different default anchors, switching to fixed-position anchor on scroll</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-rule">
-<link rel="match" href="position-try-switch-to-fixed-anchor-ref.html">
-<script src="/common/reftest-wait.js"></script>
-<script src="/common/rendering-utils.js"></script>
-<style>
-body {
-  width: 150vw;
-  height: 150vh;
-}
-.anchor {
-  width: 50px;
-  height: 50px;
-  background: orange;
-}
-#anchor1 {
-  anchor-name: --anchor1;
-  position: absolute;
-  top: 100px;
-  left: 350px;
-}
-#anchor2 {
-  anchor-name: --anchor2;
-  position:fixed;
-  right: 0;
-  bottom: 0;
-}
-#anchored {
-  position-anchor: --anchor1;
-  inset-area: top;
-  position-try-options: --fixed;
-  position: fixed;
-  width: 50px;
-  height: 50px;
-  background: blue;
-}
-@position-try --fixed {
-  inset-area: top left;
-  position-anchor: --anchor2;
-}
-</style>
-<div class="anchor" id="anchor1"></div>
-<div class="anchor" id="anchor2"></div>
-<div id="anchored"></div>
-<script>
-waitForAtLeastOneFrame().then(() => {
-  window.scrollTo(250, 100);
-  takeScreenshot();
-});
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/inheritance.html
new file mode 100644
index 0000000..a37a40e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-inline/text-box-trim/inheritance.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<title>Inheritance of CSS Inline Layout properties</title>
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-edge">
+<link rel="help" href="https://drafts.csswg.org/css-inline-3/#propdef-text-box-trim">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+<div id="container">
+  <div id="target"></div>
+</div>
+<script>
+assert_inherited('text-box-edge', 'leading', 'text');
+assert_not_inherited('text-box-trim', 'none', 'start');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-box-004-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-box-004-print-ref.html
new file mode 100644
index 0000000..ab2fda9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-box-004-print-ref.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  @page {
+    size: 500px;
+    margin: 0;
+  }
+  @page larger {
+    size: 500px 800px;
+  }
+  @page smaller {
+    size: 400px 300px;
+  }
+  body {
+    margin: 0;
+  }
+  .pagebox {
+    display: flow-root;
+  }
+  .pagebox > div {
+    border: 10px solid lightblue;
+  }
+  .pagebox > div > div {
+    background: yellow;
+  }
+  .normalpage {
+    page: normal;
+    height: 500px;
+  }
+  .normalpage > div {
+    margin: 25px 50px 75px 100px;
+    padding: 100px 75px 50px 25px;
+  }
+  .normalpage > div > div {
+    height: 230px;
+  }
+  .largerpage {
+    page: larger;
+    height: 800px;
+  }
+  .largerpage > div {
+    margin: 40px 50px 120px 100px;
+    padding: 160px 75px 80px 25px;
+  }
+  .largerpage > div > div {
+    height: 380px;
+  }
+  .smallerpage {
+    page: smaller;
+    height: 300px;
+  }
+  .smallerpage > div {
+    margin: 15px 40px 45px 80px;
+    padding: 60px 60px 30px 20px;
+  }
+  .smallerpage > div > div {
+    height: 130px;
+  }
+</style>
+<div class="pagebox normalpage">
+  <div>
+    <div>
+      On every page, the yellow rectangle should be centered within the page box
+      (the white area). (The lightblue rectangle should not be centered.)
+    </div>
+  </div>
+</div>
+<div class="pagebox normalpage">
+  <div>
+    <div></div>
+  </div>
+</div>
+<div class="pagebox largerpage">
+  <div>
+    <div></div>
+  </div>
+</div>
+<div class="pagebox largerpage">
+  <div>
+    <div></div>
+  </div>
+</div>
+<div class="pagebox smallerpage">
+  <div>
+    <div></div>
+  </div>
+</div>
+<div class="pagebox smallerpage">
+  <div>
+    <div></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-box-004-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-box-004-print.html
new file mode 100644
index 0000000..91ea6609
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-box-004-print.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-page-3/#page-properties">
+<link rel="match" href="page-box-004-print-ref.html">
+<!-- In a page context, vertical percentage margins and padding should be
+     resolved against the height of the containing block (unlike regular CSS
+     boxes, which always resolves such percentages against the inline-size of
+     the containing block. -->
+<style>
+  @page {
+    size: 500px;
+    margin: 5% 10% 15% 20%;
+    padding: 20% 15% 10% 5%;
+    border: 10px solid lightblue;
+  }
+  @page larger {
+    size: 500px 800px;
+  }
+  @page smaller {
+    size: 400px 300px;
+  }
+  body {
+    margin: 0;
+  }
+</style>
+<!-- Create two pages for each of the three page types. -->
+<div style="height:200vh; background:yellow;">
+  On every page, the yellow rectangle should be centered within the page box
+  (the white area). (The lightblue rectangle should not be centered.)
+</div>
+<div style="page:larger; height:760px; background:yellow;"></div>
+<div style="page:smaller; height:260px; background:yellow;"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-negative-print-ref.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-negative-print-ref.tentative.html
new file mode 100644
index 0000000..9e816c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-negative-print-ref.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  @page {
+    size: 300px;
+    margin: 0;
+  }
+  body {
+    margin: 0;
+    background: green;
+  }
+</style>
+<div>
+  Green background, no red / yellow.
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-negative-print.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-negative-print.tentative.html
new file mode 100644
index 0000000..452056a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-negative-print.tentative.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-page-3/#page-properties">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8508">
+<link rel="match" href="page-margin-auto-negative-print-ref.tentative.html">
+<style>
+  @page {
+    size: 300px;
+    width: 340px;
+    height: 340px;
+    margin: auto;
+  }
+  body {
+    margin: 0;
+    background: yellow;
+  }
+  .fullpager {
+    width: 300px;
+    height: 300px;
+    border: 20px solid red;
+    background: green;
+  }
+</style>
+<div class="fullpager">
+  Green background, no red / yellow.
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-print-ref.html
new file mode 100644
index 0000000..d7fda4a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-print-ref.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  @page {
+    size: 20em 7em;
+    margin: 0;
+  }
+  .pagebox {
+    break-before: page;
+    display: flex;
+    width: 20em;
+    height: 7em;
+  }
+  .pagebox > div {
+    width: 12em;
+    height: 3em;
+    margin: auto;
+    background: yellow;
+  }
+  body {
+    margin: 0;
+  }
+</style>
+<div class="pagebox">
+  <div>center / middle</div>
+</div>
+<div class="pagebox">
+  <div style="margin-top:0;">center / top</div>
+</div>
+<div class="pagebox">
+  <div style="margin-bottom:0;">center / bottom</div>
+</div>
+<div class="pagebox">
+  <div style="margin-top:0; margin-left:0;">top / left</div>
+</div>
+<div class="pagebox">
+  <div style="margin-top:0; margin-right:0;">top / right</div>
+</div>
+<div class="pagebox">
+  <div style="margin-bottom:0; margin-right:0;">bottom / right</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-print.html
new file mode 100644
index 0000000..1c94ec4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-margin-auto-print.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-page-3/#page-properties">
+<link rel="match" href="page-margin-auto-print-ref.html">
+<style>
+  @page {
+    size: 20em 7em;
+    width: 12em;
+    height: 3em;
+    margin: auto;
+  }
+  @page aaa { }
+  @page bbb {
+    margin-top: 0;
+  }
+  @page ccc {
+    margin-bottom: 0;
+  }
+  @page ddd {
+    margin-top: 0;
+    margin-left: 0;
+  }
+  @page eee {
+    margin-top: 0;
+    margin-right: 0;
+  }
+  @page fff {
+    margin-bottom: 0;
+    margin-right: 0;
+  }
+  body {
+    margin: 0;
+    background: yellow;
+  }
+</style>
+<div style="page:aaa;">
+  center / middle
+</div>
+<div style="page:bbb;">
+  center / top
+</div>
+<div style="page:ccc;">
+  center / bottom
+</div>
+<div style="page:ddd;">
+  top / left
+</div>
+<div style="page:eee;">
+  top / right
+</div>
+<div style="page:fff;">
+  bottom / right
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-size-013-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-013-print-ref.html
new file mode 100644
index 0000000..23c93e5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-013-print-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  @page {
+    size: 300px 400px;
+    margin: 50px;
+  }
+  body {
+    margin: 0;
+  }
+</style>
+<div style="width:200px; height:300px; background:yellow;">
+  This page should have a yellow background. Page margins should be 50px on each
+  side.
+</div>
+Second page. Nothing else here.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-size-013-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-013-print.html
new file mode 100644
index 0000000..5768e2d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-013-print.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>The containing block of the page should be resized in overconstrained sizing situations</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-page-3/#page-model">
+<link rel="match" href="page-size-013-print-ref.html">
+<style>
+  @page {
+    size: 500px;
+    margin: 50px;
+    width: 200px;
+    height: 300px;
+  }
+  body {
+    margin: 0;
+  }
+</style>
+<div style="width:200px; height:300px; background:yellow;">
+  This page should have a yellow background. Page margins should be 50px on each
+  side.
+</div>
+Second page. Nothing else here.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-size-014-print-ref.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-014-print-ref.html
new file mode 100644
index 0000000..23c93e5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-014-print-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  @page {
+    size: 300px 400px;
+    margin: 50px;
+  }
+  body {
+    margin: 0;
+  }
+</style>
+<div style="width:200px; height:300px; background:yellow;">
+  This page should have a yellow background. Page margins should be 50px on each
+  side.
+</div>
+Second page. Nothing else here.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-page/page-size-014-print.html b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-014-print.html
new file mode 100644
index 0000000..f224bafa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-page/page-size-014-print.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>The containing block of the page should be resized in overconstrained sizing situations</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-page-3/#page-model">
+<link rel="match" href="page-size-014-print-ref.html">
+<style>
+  @page {
+    size: 500px;
+    margin: 10%;
+    width: 40%;
+    height: 60%;
+  }
+  body {
+    margin: 0;
+  }
+</style>
+<div style="width:200px; height:300px; background:yellow;">
+  This page should have a yellow background. Page margins should be 50px on each
+  side.
+</div>
+Second page. Nothing else here.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/highlight-pseudos-001.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/highlight-pseudos-001.tentative.html
new file mode 100644
index 0000000..312135c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/highlight-pseudos-001.tentative.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Pseudo-Elements Test: highlight selectors parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo/#highlight-selectors">
+<link rel="author" name="Delan Azabani" href="mailto:dazabani@igalia.com">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+  for (const pseudo of [
+    "::search-text",
+    "::search-text:current",
+    "::search-text:not(:current)",
+  ]) {
+    test_valid_selector(`${pseudo}`);
+    test_valid_selector(`.a${pseudo}`);
+    test_valid_selector(`div ${pseudo}`);
+    test_valid_selector(`::part(my-part)${pseudo}`);
+
+    test_invalid_selector(`::before${pseudo}`);
+    test_invalid_selector(`${pseudo}.a`);
+    test_invalid_selector(`${pseudo} div`);
+    test_invalid_selector(`${pseudo}::after`);
+    test_invalid_selector(`${pseudo}:hover`);
+    test_invalid_selector(`:not(${pseudo})`);
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line-ref.html b/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line-ref.html
index 55c96fb..db20b50 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line-ref.html
@@ -1,3 +1,9 @@
 <!DOCTYPE html>
-<div>foo <ruby style="ruby-position:under">base<rt>annotation</rt></ruby><br>
+<style>
+.fl {
+  color: orange;
+  ruby-position: under;
+}
+</style>
+<div><span class="fl">foo </span><ruby class="fl">base<rt>annotation</rt></ruby><br>
 bar <ruby>base<rt>annotation</rt></ruby></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line.html b/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line.html
index 180420d..6be0a8b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-ruby/pseudo-first-line.html
@@ -5,6 +5,10 @@
 <style>
 div::first-line {
   ruby-position: under;
+  color: orange;
+}
+ruby::first-line, rt::first-line {
+  color: red;
 }
 </style>
 <div>foo <ruby>base<rt>annotation</rt></ruby><br>
diff --git a/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap1-expected.png b/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap1-expected.png
index 519ef3c5..377dbe7d 100644
--- a/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap1-expected.png
+++ b/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap2-expected.png b/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap2-expected.png
index 5409242..c84bccfb 100644
--- a/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap2-expected.png
+++ b/third_party/blink/web_tests/fast/ruby/overhang-horizontal-no-overlap2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/ruby/overhang-vertical-expected.png b/third_party/blink/web_tests/fast/ruby/overhang-vertical-expected.png
index 45643a0a..f68293fe 100644
--- a/third_party/blink/web_tests/fast/ruby/overhang-vertical-expected.png
+++ b/third_party/blink/web_tests/fast/ruby/overhang-vertical-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap1-expected.png b/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap1-expected.png
index 0e7d5df..d4f6bef3 100644
--- a/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap1-expected.png
+++ b/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap1-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap2-expected.png b/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap2-expected.png
index 49303a4..f406c093 100644
--- a/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap2-expected.png
+++ b/third_party/blink/web_tests/fast/ruby/overhang-vertical-no-overlap2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/ruby/base-shorter-than-text-expected.png b/third_party/blink/web_tests/platform/linux/fast/ruby/base-shorter-than-text-expected.png
index 64b633d4..af81f72 100644
--- a/third_party/blink/web_tests/platform/linux/fast/ruby/base-shorter-than-text-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/ruby/base-shorter-than-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-column-break-expected.png b/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-column-break-expected.png
index e23458c..8fcf8f95 100644
--- a/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-column-break-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-column-break-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-position-modern-japanese-fonts-expected.png b/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
index 9a66d3a..497c29b 100644
--- a/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-text-before-after-content-expected.png b/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-text-before-after-content-expected.png
index 1a5cd81..27ee9bc 100644
--- a/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-text-before-after-content-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/ruby/ruby-text-before-after-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/fast/writing-mode/Kusa-Makura-background-canvas-expected.png b/third_party/blink/web_tests/platform/linux/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
index 6f9fba8..998c38d8 100644
--- a/third_party/blink/web_tests/platform/linux/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/fast/ruby/ruby-position-modern-japanese-fonts-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
index f7e342e4..23de2c6 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.15/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/ruby/base-shorter-than-text-expected.png b/third_party/blink/web_tests/platform/mac/fast/ruby/base-shorter-than-text-expected.png
index 5490544f..a8186722 100644
--- a/third_party/blink/web_tests/platform/mac/fast/ruby/base-shorter-than-text-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/ruby/base-shorter-than-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/ruby/overhang-horizontal-expected.png b/third_party/blink/web_tests/platform/mac/fast/ruby/overhang-horizontal-expected.png
index bac16b0..58cbdc2 100644
--- a/third_party/blink/web_tests/platform/mac/fast/ruby/overhang-horizontal-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/ruby/overhang-horizontal-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-column-break-expected.png b/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-column-break-expected.png
index e67855d8..20ae39f 100644
--- a/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-column-break-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-column-break-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-position-modern-japanese-fonts-expected.png b/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
index af54254..37ee4ff 100644
--- a/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-text-before-after-content-expected.png b/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-text-before-after-content-expected.png
index d106926..f52ccb0 100644
--- a/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-text-before-after-content-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/ruby/ruby-text-before-after-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/fast/writing-mode/Kusa-Makura-background-canvas-expected.png b/third_party/blink/web_tests/platform/mac/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
index 1d299fa..cf111e7 100644
--- a/third_party/blink/web_tests/platform/mac/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/ruby/base-shorter-than-text-expected.png b/third_party/blink/web_tests/platform/win/fast/ruby/base-shorter-than-text-expected.png
index 0a51218..bad1e8b 100644
--- a/third_party/blink/web_tests/platform/win/fast/ruby/base-shorter-than-text-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/ruby/base-shorter-than-text-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/ruby/overhang-horizontal-expected.png b/third_party/blink/web_tests/platform/win/fast/ruby/overhang-horizontal-expected.png
index 4a06c2b..9c7d9dc9 100644
--- a/third_party/blink/web_tests/platform/win/fast/ruby/overhang-horizontal-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/ruby/overhang-horizontal-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/ruby/ruby-column-break-expected.png b/third_party/blink/web_tests/platform/win/fast/ruby/ruby-column-break-expected.png
index 96cf1cd..ffb51ec 100644
--- a/third_party/blink/web_tests/platform/win/fast/ruby/ruby-column-break-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/ruby/ruby-column-break-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/ruby/ruby-position-modern-japanese-fonts-expected.png b/third_party/blink/web_tests/platform/win/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
index 226ef59..069de55 100644
--- a/third_party/blink/web_tests/platform/win/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/ruby/ruby-position-modern-japanese-fonts-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/ruby/ruby-text-before-after-content-expected.png b/third_party/blink/web_tests/platform/win/fast/ruby/ruby-text-before-after-content-expected.png
index e48d987..b748e268d 100644
--- a/third_party/blink/web_tests/platform/win/fast/ruby/ruby-text-before-after-content-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/ruby/ruby-text-before-after-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/fast/writing-mode/Kusa-Makura-background-canvas-expected.png b/third_party/blink/web_tests/platform/win/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
index 5196991..2b0e7a68 100644
--- a/third_party/blink/web_tests/platform/win/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win10/fast/writing-mode/Kusa-Makura-background-canvas-expected.png b/third_party/blink/web_tests/platform/win10/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
index b6afae5..3d821028 100644
--- a/third_party/blink/web_tests/platform/win10/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
+++ b/third_party/blink/web_tests/platform/win10/fast/writing-mode/Kusa-Makura-background-canvas-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/dom/depth_limit.html b/third_party/blink/web_tests/wpt_internal/dom/depth_limit.html
new file mode 100644
index 0000000..4fcf7de
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/dom/depth_limit.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script type="module">
+import {WebFeature} from '/gen/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.m.js';
+
+// This test suite verifies that the following use counter work correctly:
+// - kMaximumHTMLParserDOMTreeDepthHit
+
+// from third_party/blink/renderer/core/html/parser/html_construction_site.h
+const kMaximumHTMLParserDOMTreeDepth = 512;
+const HTML_OVER_LIMIT = '<div>'.repeat(kMaximumHTMLParserDOMTreeDepth + 10) + 'hello';
+const HTML_BELOW_LIMIT = '<div>'.repeat(kMaximumHTMLParserDOMTreeDepth - 1) + 'hello';
+
+test(() => {
+    internals.clearUseCounter(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit);
+    assert_false(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+    document.createElement("div").innerHTML = HTML_OVER_LIMIT;
+
+    assert_true(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Super deeply nested DOM nodes are use counted (via innerHTML)');
+
+test(() => {
+    internals.clearUseCounter(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit);
+    assert_false(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+    const doc = new DOMParser().parseFromString(HTML_OVER_LIMIT, 'text/html');
+
+    assert_true(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Super deeply nested DOM nodes are use counted (via DOMParser)');
+
+promise_test(async () => {
+    const iframe = document.createElement("iframe");
+    iframe.srcdoc = HTML_OVER_LIMIT;
+    document.body.append(iframe);
+
+    await new Promise(r => iframe.addEventListener('load', r, {once:true}));
+
+    assert_true(internals.isUseCounted(iframe.contentDocument, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Super deeply nested DOM nodes are use counted (via iframe)');
+
+
+test(() => {
+    internals.clearUseCounter(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit);
+    assert_false(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+    document.createElement("div").innerHTML = HTML_BELOW_LIMIT;
+
+    assert_false(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Deeply nested DOM nodes under the limit are NOT use counted (via innerHTML)');
+
+test(() => {
+    internals.clearUseCounter(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit);
+    assert_false(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+    const doc = new DOMParser().parseFromString(HTML_BELOW_LIMIT, 'text/html');
+
+    assert_false(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Deeply nested DOM nodes under the limit are NOT use counted (via DOMParser)');
+
+promise_test(async () => {
+    const iframe = document.createElement("iframe");
+    iframe.srcdoc = HTML_BELOW_LIMIT;
+    document.body.append(iframe);
+
+    await new Promise(r => iframe.addEventListener('load', r, {once:true}));
+
+    assert_false(internals.isUseCounted(iframe.contentDocument, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Deeply nested DOM nodes under the limit are NOT use counted (via iframe)');
+
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/dom/depth_limit_doc.html b/third_party/blink/web_tests/wpt_internal/dom/depth_limit_doc.html
new file mode 100644
index 0000000..e1684509
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/dom/depth_limit_doc.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+
+<!-- below there are 520 nested divs -->
+<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div>
+
+<script type="module">
+import {WebFeature} from '/gen/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.m.js';
+
+// This test suite verifies that the following use counter work correctly:
+// - kMaximumHTMLParserDOMTreeDepthHit
+
+
+test(() => {
+    // Because deeply nested divs are already in the document, it should have already been use counted.
+    assert_true(internals.isUseCounted(document, WebFeature.kMaximumHTMLParserDOMTreeDepthHit));
+
+}, 'Super deeply nested DOM nodes are use counted (via document)');
+
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/printing/page-height-fit-content-print-ref.html b/third_party/blink/web_tests/wpt_internal/printing/page-height-fit-content-print-ref.html
new file mode 100644
index 0000000..5514e6f
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/printing/page-height-fit-content-print-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<div style="position:absolute; right:0; bottom:0; width:50px; height:50px; background:blue;"></div>
diff --git a/third_party/blink/web_tests/wpt_internal/printing/page-height-fit-content-print.html b/third_party/blink/web_tests/wpt_internal/printing/page-height-fit-content-print.html
new file mode 100644
index 0000000..41a5c0f6
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/printing/page-height-fit-content-print.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-page-3/#at-page-rule">
+<link rel="match" href="page-height-fit-content-print-ref.html">
+<!-- Intrinsic size is currently resolved to 0, which means that the page size
+     will be unusable, and that we should therefore fall back to default page
+     size. -->
+<style>
+  @page {
+    size: 10in;
+    margin: 66cm;
+    height: fit-content;
+  }
+</style>
+<div style="position:absolute; right:0; bottom:0; width:50px; height:50px; background:blue;"></div>
diff --git a/third_party/blink/web_tests/wpt_internal/printing/page-width-fit-content-print-ref.html b/third_party/blink/web_tests/wpt_internal/printing/page-width-fit-content-print-ref.html
new file mode 100644
index 0000000..5514e6f
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/printing/page-width-fit-content-print-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<div style="position:absolute; right:0; bottom:0; width:50px; height:50px; background:blue;"></div>
diff --git a/third_party/blink/web_tests/wpt_internal/printing/page-width-fit-content-print.html b/third_party/blink/web_tests/wpt_internal/printing/page-width-fit-content-print.html
new file mode 100644
index 0000000..f5e0f7bf
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/printing/page-width-fit-content-print.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-page-3/#at-page-rule">
+<link rel="match" href="page-width-fit-content-print-ref.html">
+<!-- Intrinsic size is currently resolved to 0, which means that the page size
+     will be unusable, and that we should therefore fall back to default page
+     size. -->
+<style>
+  @page {
+    size: 10in;
+    margin: 66cm;
+    width: fit-content;
+  }
+</style>
+<div style="position:absolute; right:0; bottom:0; width:50px; height:50px; background:blue;"></div>
diff --git a/third_party/blink/web_tests/wpt_internal/printing/unusable-page-size-print-ref.html b/third_party/blink/web_tests/wpt_internal/printing/unusable-page-size-print-ref.html
new file mode 100644
index 0000000..35bfc1ab
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/printing/unusable-page-size-print-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  @page second {
+    size: 6in;
+    margin: 1in;
+    border: 20px solid;
+    padding: 30px;
+  }
+  body {
+    margin: 0
+  }
+</style>
+This page should have default page settings (size / margins), and no
+border/padding.
+<div style="page:second;">
+  This page should be 6 by 6 inches large, with a 1 inch margin on every side,
+  and also, if @page properties are fully supported, a black border, and some
+  padding.
+</div>
diff --git a/third_party/blink/web_tests/wpt_internal/printing/unusable-page-size-print.html b/third_party/blink/web_tests/wpt_internal/printing/unusable-page-size-print.html
new file mode 100644
index 0000000..cea8e53
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/printing/unusable-page-size-print.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-page-3/#at-page-rule">
+<link rel="match" href="unusable-page-size-print-ref.html">
+<!-- The margins of the first page, combined with the specified page size, would
+     result in a 0x0 page area size. We don't allow this.
+     See https://github.com/w3c/csswg-drafts/issues/8335 -->
+<style>
+  @page {
+    size: 6in;
+    margin: 1in;
+    border: 20px solid;
+    padding: 30px;
+  }
+  @page :first {
+    border-color: red;
+    margin: 3in;
+  }
+  body {
+    margin: 0
+  }
+</style>
+This page should have default page settings (size / margins), and no
+border/padding.
+<div style="break-before:page;">
+  This page should be 6 by 6 inches large, with a 1 inch margin on every side,
+  and also, if @page properties are fully supported, a black border, and some
+  padding.
+</div>
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index 3097b90..1545704 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit 3097b9057e203656efcd2f6f04eb24198d0c5ec7
+Subproject commit 1545704ff52cfb5119f3693c9a9e971594e9cb43
diff --git a/third_party/cros-components/src b/third_party/cros-components/src
index 961957e..1985ff9 160000
--- a/third_party/cros-components/src
+++ b/third_party/cros-components/src
@@ -1 +1 @@
-Subproject commit 961957ea3bcc7ee4121c604859bc9b495c352e79
+Subproject commit 1985ff9dfd894b5cd958163bf9f4fde8716acbb4
diff --git a/third_party/depot_tools b/third_party/depot_tools
index a9b9284..e138529 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit a9b9284faf89cd1a426371237df8307c328cc818
+Subproject commit e1385296c4ab4c7ee0a809676635b52d1df23b87
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index f1404df..66623d9 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit f1404dfce216fea06d8ec5b0380b6cca25704051
+Subproject commit 66623d94d619797383f9872fa1fd0f82e4508e74
diff --git a/third_party/harfbuzz-ng/BUILD.gn b/third_party/harfbuzz-ng/BUILD.gn
index ca6f22d..1d326b3 100644
--- a/third_party/harfbuzz-ng/BUILD.gn
+++ b/third_party/harfbuzz-ng/BUILD.gn
@@ -274,6 +274,8 @@
       "src/src/hb-subset-cff2.cc",
       "src/src/hb-subset-input.cc",
       "src/src/hb-subset-input.hh",
+      "src/src/hb-subset-instancer-iup.hh",
+      "src/src/hb-subset-instancer-iup.cc",
       "src/src/hb-subset-instancer-solver.cc",
       "src/src/hb-subset-instancer-solver.hh",
       "src/src/hb-subset-plan-member-list.hh",
@@ -296,11 +298,16 @@
     # The following sources are explictly not used.
     # They are referenced to aid in detecting previously uncategorized files.
     unused_sources = [
+      "src/src/hb-cairo-utils.cc",
+      "src/src/hb-cairo-utils.hh",
+      "src/src/hb-cairo.cc",
+      "src/src/hb-cairo.h",
       "src/src/hb-coretext.cc",
       "src/src/hb-coretext.h",
       "src/src/hb-directwrite.cc",
       "src/src/hb-directwrite.h",
       "src/src/hb-fallback-shape.cc",
+      "src/src/hb-ft-colr.hh",
       "src/src/hb-gdi.cc",
       "src/src/hb-gdi.h",
       "src/src/hb-gobject-structs.cc",
@@ -308,10 +315,26 @@
       "src/src/hb-gobject.h",
       "src/src/hb-graphite2.cc",
       "src/src/hb-graphite2.h",
+      "src/src/hb-outline.cc",
+      "src/src/hb-outline.hh",
+      "src/src/hb-paint.cc",
+      "src/src/hb-paint.h",
+      "src/src/hb-paint.hh",
       "src/src/hb-style.cc",
       "src/src/hb-style.h",
       "src/src/hb-uniscribe.cc",
       "src/src/hb-uniscribe.h",
+      "src/src/hb-wasm-api-blob.hh",
+      "src/src/hb-wasm-api-buffer.hh",
+      "src/src/hb-wasm-api-common.hh",
+      "src/src/hb-wasm-api-face.hh",
+      "src/src/hb-wasm-api-font.hh",
+      "src/src/hb-wasm-api-list.hh",
+      "src/src/hb-wasm-api-shape.hh",
+      "src/src/hb-wasm-api.cc",
+      "src/src/hb-wasm-api.h",
+      "src/src/hb-wasm-api.hh",
+      "src/src/hb-wasm-shape.cc",
     ]
     assert(unused_sources != [])
 
diff --git a/third_party/harfbuzz-ng/README.chromium b/third_party/harfbuzz-ng/README.chromium
index 0c19021..f20f804 100644
--- a/third_party/harfbuzz-ng/README.chromium
+++ b/third_party/harfbuzz-ng/README.chromium
@@ -1,10 +1,10 @@
 Name: harfbuzz-ng
 Short Name: harfbuzz-ng
 URL: http://harfbuzz.org
-Version: 8.3.0-53
-CPEPrefix: cpe:/a:harfbuzz_project:harfbuzz:8.3.0
-Date: 2024-01-08
-Revision: 155015f4bec434ecc2f94621665844218f05ce51
+Version: 8.4.0-61
+CPEPrefix: cpe:/a:harfbuzz_project:harfbuzz:8.4.0
+Date: 2024-05-13
+Revision: bba0c0e27cf22244d6ffcd9dffc8e9ad1f4c1bc6
 Security Critical: yes
 Shipped: yes
 License: MIT
diff --git a/third_party/harfbuzz-ng/roll-harfbuzz.sh b/third_party/harfbuzz-ng/roll-harfbuzz.sh
index cda94bf1..9bf6b2b 100755
--- a/third_party/harfbuzz-ng/roll-harfbuzz.sh
+++ b/third_party/harfbuzz-ng/roll-harfbuzz.sh
@@ -10,7 +10,7 @@
   STEP="update README.chromium" &&
   HB_VERSION=$(git -C third_party/harfbuzz-ng/src/ describe --long) &&
   HB_COMMIT=$(git -C third_party/harfbuzz-ng/src/ rev-parse HEAD) &&
-  HB_DATE=$(date "+%Y%m%d")
+  HB_DATE=$(date "+%Y-%m-%d")
   HB_CPE_VERSION=$(echo ${HB_VERSION} | sed -r -e's/^([0-9]+)\.([0-9]+)\.([0-9]+)-[0-9]+-g[0-9a-f]+$/\1.\2.\3/') &&
   [ ${HB_VERSION} != ${HB_CPE_VERSION} ] &&
   sed -i'' -e "s/^Version: .*\$/Version: ${HB_VERSION%-*}/" third_party/harfbuzz-ng/README.chromium &&
diff --git a/third_party/harfbuzz-ng/src b/third_party/harfbuzz-ng/src
index 155015f..bba0c0e 160000
--- a/third_party/harfbuzz-ng/src
+++ b/third_party/harfbuzz-ng/src
@@ -1 +1 @@
-Subproject commit 155015f4bec434ecc2f94621665844218f05ce51
+Subproject commit bba0c0e27cf22244d6ffcd9dffc8e9ad1f4c1bc6
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 862e523..a06b82c 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 862e523d9e3d530220f34aa2a660d46dc556eda4
+Subproject commit a06b82c306ee85326c5d02518b88fbee434a34a0
diff --git a/third_party/webrtc b/third_party/webrtc
index 76c02af..36ecffa 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 76c02aff4c76cc2d47c0a975333789978986e2b9
+Subproject commit 36ecffa1291884ca8fd5f2bc579028e9664f42e6
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 0f963ba5..4adfe562 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -1548,6 +1548,27 @@
   <int value="5" label="Form submission"/>
 </enum>
 
+<enum name="AutofillSuggestionTriggerSource">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Form control elemnt clicked"/>
+  <int value="2" label="Textarea focused without click"/>
+  <int value="3" label="Content editable clicked"/>
+  <int value="4" label="Text field did change"/>
+  <int value="5" label="Text field did receive keydown,"/>
+  <int value="6" label="Open text data list chooser"/>
+  <int value="7" label="Show cards fromaccount"/>
+  <int value="8" label="Password manager"/>
+  <int value="9" label="iOS"/>
+  <int value="10" label="Manual fallback address"/>
+  <int value="11" label="Manual fallback payments"/>
+  <int value="12" label="Manual fallback passwords"/>
+  <int value="13" label="Manual fallback plus addresses"/>
+  <int value="14" label="Show prompt after dialog closed non manual fallback"/>
+  <int value="15" label="Compose dialog lost focus"/>
+  <int value="16" label="Compose elayed proactive nudge"/>
+  <int value="17" label="Password manager processed focused field"/>
+</enum>
+
 <enum name="AutofillWebDataBackendImplOperationResult">
   <int value="0" label="AddFormElements: Success"/>
   <int value="1" label="AddFormElements: Failure"/>
@@ -5559,6 +5580,16 @@
   <int value="5" label="Error"/>
 </enum>
 
+<enum name="ElementAnchoredBubbleAction">
+  <int value="0" label="GRANTED"/>
+  <int value="1" label="GRANTED_ONCE"/>
+  <int value="2" label="DENIED"/>
+  <int value="3" label="OK"/>
+  <int value="4" label="DISMISSED_X_BUTTON"/>
+  <int value="5" label="DISMISSED_SCRIM"/>
+  <int value="6" label="SYSTEM_SETTINGS"/>
+</enum>
+
 <enum name="ElementAnchoredBubbleVariant">
   <int value="0" label="UNINITIALIZED"/>
   <int value="1" label="ADMINISTRATOR_GRANTED"/>
@@ -11544,6 +11575,7 @@
   <int value="4969" label="Canvas2DMesh"/>
   <int value="4970" label="CaretPositionFromPoint"/>
   <int value="4971" label="DocumentPolicyIncludeJSCallStacksInCrashReports"/>
+  <int value="4972" label="MaximumHTMLParserDOMTreeDepthHit"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -17996,6 +18028,7 @@
       label="UserDisplayModeSyncStandaloneMitigation:enabled"/>
   <int value="-1412230070" label="query-tiles-instant-background-task"/>
   <int value="-1411980923" label="LinkCapturingUiUpdate:enabled"/>
+  <int value="-1411777757" label="WebRtcSendPacketBatch:enabled"/>
   <int value="-1411733990" label="OmniboxDedupeGoogleDriveURLs:disabled"/>
   <int value="-1411459387" label="EnterprisePolicyOnSignin:enabled"/>
   <int value="-1411003295" label="disable-encrypted-media"/>
@@ -29324,6 +29357,45 @@
   <int value="4" label="On-Device prediction model"/>
 </enum>
 
+<enum name="PermissionRequestType">
+  <int value="0" label="PERMISSION_BUBBLE_UNKNOWN"/>
+  <int value="1" label="PERMISSION_BUBBLE_MULTIPLE_AUDIO_AND_VIDEO_CAPTURE"/>
+  <int value="2" label="PERMISSION_BUBBLE_UNUSED_PERMISSION"/>
+  <int value="3" label="PERMISSION_BUBBLE_QUOTA"/>
+  <int value="4" label="PERMISSION_BUBBLE_DOWNLOAD"/>
+  <int value="5" label="PERMISSION_BUBBLE_MEDIA_STREAM"/>
+  <int value="6" label="PERMISSION_BUBBLE_REGISTER_PROTOCOL_HANDLER"/>
+  <int value="7" label="PERMISSION_BUBBLE_PERMISSION_GEOLOCATION"/>
+  <int value="8" label="PERMISSION_BUBBLE_PERMISSION_MIDI_SYSEX"/>
+  <int value="9" label="PERMISSION_BUBBLE_PERMISSION_NOTIFICATIONS"/>
+  <int value="10"
+      label="PERMISSION_BUBBLE_PERMISSION_PROTECTED_MEDIA_IDENTIFIER"/>
+  <int value="11" label="PERMISSION_BUBBLE_PERMISSION_PUSH_MESSAGING"/>
+  <int value="12" label="PERMISSION_BUBBLE_PERMISSION_FLASH"/>
+  <int value="13" label="PERMISSION_MEDIASTREAM_MIC"/>
+  <int value="14" label="PERMISSION_MEDIASTREAM_CAMERA"/>
+  <int value="15" label="PERMISSION_ACCESSIBILITY_EVENTS"/>
+  <int value="16" label="PERMISSION_CLIPBOARD_READ__REMOVED_IN_M81"/>
+  <int value="17" label="PERMISSION_SECURITY_KEY_ATTESTATION__REMOVED_IN_M117"/>
+  <int value="18" label="PERMISSION_PAYMENT_HANDLER"/>
+  <int value="19" label="PERMISSION_NFC"/>
+  <int value="20" label="PERMISSION_CLIPBOARD_READ_WRITE"/>
+  <int value="21" label="PERMISSION_VR"/>
+  <int value="22" label="PERMISSION_AR"/>
+  <int value="23" label="PERMISSION_STORAGE_ACCESS"/>
+  <int value="29" label="PERMISSION_U2F_API_REQUEST__REMOVED_IN_M117"/>
+  <int value="30" label="PERMISSION_TOP_LEVEL_STORAGE_ACCESS"/>
+  <int value="31" label="PERMISSION_MIDI"/>
+  <int value="32" label="PERMISSION_FILE_SYSTEM_ACCESS"/>
+  <int value="33" label="CAPTURED_SURFACE_CONTROL"/>
+  <int value="34" label="PERMISSION_SMART_CARD"/>
+  <int value="35" label="PERMISSION_WEB_PRINTING"/>
+  <int value="36" label="PERMISSION_IDENTITY_PROVIDER"/>
+  <int value="37" label="PERMISSION_KEYBOARD_LOCK"/>
+  <int value="38" label="PERMISSION_POINTER_LOCK"/>
+  <int value="39" label="MULTIPLE_KEYBOARD_AND_POINTER_LOCK"/>
+</enum>
+
 <enum name="PermissionType">
   <int value="0" label="PERMISSION_UNKNOWN"/>
   <int value="1" label="PERMISSION_MIDI_SYSEX"/>
@@ -32985,8 +33057,6 @@
   <int value="61" label="UMA_AUTO_TRANSLATE"/>
   <int value="63" label="UMA_CLEAR_BROWSING_DATA"/>
   <int value="64" label="UMA_SIGN_OUT"/>
-  <int value="65" label="UMA_TAB_GROUP_DELETE_UNDO"/>
-  <int value="66" label="UMA_SINGLE_TAB_GROUP_DELETE_UNDO"/>
 </enum>
 
 <enum name="SnapshotItemId">
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 36ee9b7..54a2c8c 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -2539,13 +2539,6 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.ScreenAI.Component.BinaryAvailable"
-    enum="BooleanSuccess" expires_after="2025-02-28">
-  <owner>rhalavati@chromium.org</owner>
-  <owner>chrome-a11y-core@google.com</owner>
-  <summary>Records if binary was found after component is installed.</summary>
-</histogram>
-
 <histogram name="Accessibility.ScreenAI.Component.InstallRetries" units="count"
     expires_after="2025-02-28">
   <owner>rhalavati@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 4b2b603..54d6991 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -826,6 +826,8 @@
     <variant name="storage-access" summary="Storage Access exceptions"/>
     <variant name="subresource-filter" summary="Subresource filter exceptions"/>
     <variant name="subresource-filter-data" summary="Subresource filter data"/>
+    <variant name="tracking-protection"
+        summary="Tracking Protection exceptions"/>
     <variant name="usb-chooser-data" summary="USB chooser data exceptions"/>
     <variant name="webid-active-session" summary="FedCM Active Session"/>
     <variant name="webid-request" summary="FedCM Request"/>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 60e95fd..28f9d8d 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -821,6 +821,8 @@
       name="ContentSettings.RegularProfile.Exceptions.storage-access"/>
   <affected-histogram
       name="ContentSettings.RegularProfile.Exceptions.subresource-filter"/>
+  <affected-histogram
+      name="ContentSettings.RegularProfile.Exceptions.tracking-protection"/>
 </histogram_suffixes>
 
 <histogram_suffixes name="ContentSuggestionCategory" separator=".">
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index 8174423..c0778cf 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -1987,6 +1987,24 @@
   <token key="PreloadingTriggerType" variants="PreloadingTriggerType"/>
 </histogram>
 
+<histogram
+    name="Prerender.Experimental.NewTabPage.TouchDuration.{ConversionResultType}"
+    units="ms" expires_after="2024-09-24">
+  <owner>robertlin@chromium.org</owner>
+  <owner>chrome-prerendering@google.com</owner>
+  <summary>
+    The metrics for preload NewTabPage to understand the distribution of touch
+    duration. It records touch duration time of a NewTabPage tile. And this
+    records both cases, the first being NotTaken if a navigation never happens,
+    the other being Taken when a NewTabPage is actually navigated per touch
+    event completion.
+  </summary>
+  <token key="ConversionResultType">
+    <variant name="NotTaken"/>
+    <variant name="Taken"/>
+  </token>
+</histogram>
+
 <histogram name="Prerender.Experimental.PredictionStatus.DefaultSearchEngine"
     enum="PrerenderPredictionStatus" expires_after="2024-11-03">
   <owner>nhiroki@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/net/enums.xml b/tools/metrics/histograms/metadata/net/enums.xml
index 0a83b92e7..c0b324f0 100644
--- a/tools/metrics/histograms/metadata/net/enums.xml
+++ b/tools/metrics/histograms/metadata/net/enums.xml
@@ -240,6 +240,17 @@
   <int value="3" label="DnsHTTPAttempt"/>
 </enum>
 
+<!-- LINT.IfChange(DnsClientCapability) -->
+
+<enum name="DNS.DnsClientCapability">
+  <int value="0" label="Secure DNS disabled, insecure DNS disabled"/>
+  <int value="1" label="Secure DNS disabled, insecure DNS enabled"/>
+  <int value="2" label="Secure DNS enabled, insecure DNS disabled"/>
+  <int value="3" label="Secure DNS enabled, insecure DNS enabled"/>
+</enum>
+
+<!-- LINT.ThenChange(/net/dns/host_resolver_manager.cc:DnsClientCapability) -->
+
 <enum name="DNS.SvcbHttpsTransactionError">
   <int value="0" label="No transaction error"/>
   <int value="1" label="Insecure DNS transaction error (never task-fatal)"/>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 8e6aece..c9752697 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -874,6 +874,17 @@
   </summary>
 </histogram>
 
+<histogram name="Net.DNS.DnsConfig.DnsClientCapability"
+    enum="DNS.DnsClientCapability" expires_after="2024-08-13">
+  <owner>horo@chromium.org</owner>
+  <owner>src/net/OWNERS</owner>
+  <summary>
+    Whether DNS clients can and are allowed to perform secure and insecure DNS
+    transactions. Logged when HostResolverManager receives a host resolution
+    request using HostResolverSource::ANY.
+  </summary>
+</histogram>
+
 <histogram name="Net.DNS.DnsConfig.Nsswitch.Compatible" enum="BooleanValid"
     expires_after="2024-09-22">
   <owner>horo@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index a3f3004..70d6f08 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -3538,6 +3538,16 @@
   </summary>
 </histogram>
 
+<histogram name="PasswordManager.SuggestionPopupTriggerSource"
+    enum="AutofillSuggestionTriggerSource" expires_after="2024-11-15">
+  <owner>kazinova@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
+  <summary>
+    Records the reason that triggered password suggestions popup, excluding
+    manual fallback suggestions. Recorded at the time the popup is shown.
+  </summary>
+</histogram>
+
 <histogram
     name="PasswordManager.SyncControllerDelegateNotifiesCredentialManager.APIErrorCode"
     enum="CredentialManagerAPIError" expires_after="2024-09-29">
diff --git a/tools/metrics/histograms/metadata/permissions/enums.xml b/tools/metrics/histograms/metadata/permissions/enums.xml
index 56081ac..2ea0d61 100644
--- a/tools/metrics/histograms/metadata/permissions/enums.xml
+++ b/tools/metrics/histograms/metadata/permissions/enums.xml
@@ -179,45 +179,6 @@
   <int value="3" label="Other"/>
 </enum>
 
-<enum name="PermissionRequestType">
-  <int value="0" label="PERMISSION_BUBBLE_UNKNOWN"/>
-  <int value="1" label="PERMISSION_BUBBLE_MULTIPLE_AUDIO_AND_VIDEO_CAPTURE"/>
-  <int value="2" label="PERMISSION_BUBBLE_UNUSED_PERMISSION"/>
-  <int value="3" label="PERMISSION_BUBBLE_QUOTA"/>
-  <int value="4" label="PERMISSION_BUBBLE_DOWNLOAD"/>
-  <int value="5" label="PERMISSION_BUBBLE_MEDIA_STREAM"/>
-  <int value="6" label="PERMISSION_BUBBLE_REGISTER_PROTOCOL_HANDLER"/>
-  <int value="7" label="PERMISSION_BUBBLE_PERMISSION_GEOLOCATION"/>
-  <int value="8" label="PERMISSION_BUBBLE_PERMISSION_MIDI_SYSEX"/>
-  <int value="9" label="PERMISSION_BUBBLE_PERMISSION_NOTIFICATIONS"/>
-  <int value="10"
-      label="PERMISSION_BUBBLE_PERMISSION_PROTECTED_MEDIA_IDENTIFIER"/>
-  <int value="11" label="PERMISSION_BUBBLE_PERMISSION_PUSH_MESSAGING"/>
-  <int value="12" label="PERMISSION_BUBBLE_PERMISSION_FLASH"/>
-  <int value="13" label="PERMISSION_MEDIASTREAM_MIC"/>
-  <int value="14" label="PERMISSION_MEDIASTREAM_CAMERA"/>
-  <int value="15" label="PERMISSION_ACCESSIBILITY_EVENTS"/>
-  <int value="16" label="PERMISSION_CLIPBOARD_READ__REMOVED_IN_M81"/>
-  <int value="17" label="PERMISSION_SECURITY_KEY_ATTESTATION__REMOVED_IN_M117"/>
-  <int value="18" label="PERMISSION_PAYMENT_HANDLER"/>
-  <int value="19" label="PERMISSION_NFC"/>
-  <int value="20" label="PERMISSION_CLIPBOARD_READ_WRITE"/>
-  <int value="21" label="PERMISSION_VR"/>
-  <int value="22" label="PERMISSION_AR"/>
-  <int value="23" label="PERMISSION_STORAGE_ACCESS"/>
-  <int value="29" label="PERMISSION_U2F_API_REQUEST__REMOVED_IN_M117"/>
-  <int value="30" label="PERMISSION_TOP_LEVEL_STORAGE_ACCESS"/>
-  <int value="31" label="PERMISSION_MIDI"/>
-  <int value="32" label="PERMISSION_FILE_SYSTEM_ACCESS"/>
-  <int value="33" label="CAPTURED_SURFACE_CONTROL"/>
-  <int value="34" label="PERMISSION_SMART_CARD"/>
-  <int value="35" label="PERMISSION_WEB_PRINTING"/>
-  <int value="36" label="PERMISSION_IDENTITY_PROVIDER"/>
-  <int value="37" label="PERMISSION_KEYBOARD_LOCK"/>
-  <int value="38" label="PERMISSION_POINTER_LOCK"/>
-  <int value="39" label="MULTIPLE_KEYBOARD_AND_POINTER_LOCK"/>
-</enum>
-
 <enum name="PermissionSourceUI">
   <int value="0" label="Prompt"/>
   <int value="1" label="Origin info bubble"/>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index 4a749f8..6d7ab15 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -1405,7 +1405,7 @@
 </histogram>
 
 <histogram name="Sync.Startup.AccountInfoFullyLoaded2" enum="BooleanLoaded"
-    expires_after="2024-10-20">
+    expires_after="2025-05-15">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <owner>treib@chromium.org</owner>
@@ -1416,7 +1416,7 @@
 </histogram>
 
 <histogram name="Sync.Startup.SignedInWithoutAccountInfo2"
-    enum="BooleanIsSignedIn" expires_after="2024-06-15">
+    enum="BooleanIsSignedIn" expires_after="2025-05-15">
   <owner>mastiz@chromium.org</owner>
   <owner>rushans@google.com</owner>
   <owner>treib@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 10911ae..eddc206 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -21703,6 +21703,51 @@
   </metric>
 </event>
 
+<event name="Permissions.EmbeddedPromptAction">
+  <owner>hkamila@chromium.org</owner>
+  <owner>andypaicu@chromium.org</owner>
+  <owner>permissions-dev@chromium.org</owner>
+  <summary>
+    Metrics for a user's permission actions for permission prompts triggered by
+    the user clicking on the Embedded Permission Element, logged when a
+    permission prompt is resolved.
+  </summary>
+  <metric name="Action" enum="ElementAnchoredBubbleAction">
+    <summary>
+      An enum of type ElementAnchoredBubbleAction. One of GRANTED, GRANTED_ONCE,
+      OK, DENIED, DISMISSED, DISMISSED_SCRIM, SYSTEM_SETTINGS or IGNORED.
+    </summary>
+  </metric>
+  <metric name="PermissionType" enum="PermissionRequestType">
+    <summary>
+      Represents the permission type that the prompt is for. An enum of type
+      ContentSettingsType.
+    </summary>
+  </metric>
+  <metric name="PreviousScreens">
+    <summary>
+      Represents how many screens have already been shown to the user prior to
+      the current one as part of the permission prompt. Also serves as an order
+      number. Should never be bigger than the number of screen variants. It is
+      recorded as int64_t (upper bound 10).
+    </summary>
+  </metric>
+  <metric name="ScreenPermissionType" enum="PermissionRequestType">
+    <summary>
+      Represents the permission type that current screen is for. This can
+      sometimes differ from PermissionType when the PermissionType is Multiple,
+      as some screens are designed to only be shown for one permission at a
+      time. An enum of type ContentSettingsType.
+    </summary>
+  </metric>
+  <metric name="Variant" enum="ElementAnchoredBubbleVariant">
+    <summary>
+      An enum of type ElementAnchoredBubbleVariant. The screen that the user has
+      currently taken action on.
+    </summary>
+  </metric>
+</event>
+
 <event name="PermissionUsage">
   <owner>engedy@chromium.org</owner>
   <owner>ravjit@chromium.org</owner>
diff --git a/ui/chromeos/file_manager_strings.grdp b/ui/chromeos/file_manager_strings.grdp
index ffa57294..d20f409a 100644
--- a/ui/chromeos/file_manager_strings.grdp
+++ b/ui/chromeos/file_manager_strings.grdp
@@ -1383,10 +1383,10 @@
   <message name="IDS_FILE_BROWSER_NO_TASK_FOR_CRX" desc="Message shown when a user tries to open a *.crx file, which we don't handle in the Files app.">
     We're constantly looking for ways to make your browsing safer. Previously, any website could prompt you to add an extension into your browser. In the latest versions of Google Chrome, you must explicitly tell Chrome that you want to install these extensions by adding them through the Extensions page. <ph name="BEGIN_LINK">&lt;a target='_blank' href='https://support.google.com/chrome_webstore/answer/2664769?p=crx_warning&amp;rd=1'&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
   </message>
-  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_CROSTINI_TITLE" desc="Message title shown when a user shares the root of a volume such as 'My files' with the crostini container.">
+  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_CROSTINI_TITLE" desc="Message title shown when a user shares the root of a volume such as 'This Chromebook' with the Crostini container.">
     Share folder with Linux
   </message>
-  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_CROSTINI" desc="Confirmation message shown when a user shares the root of a volume such as 'My files' with the crostini container.">
+  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_CROSTINI" desc="Confirmation message shown when a user shares the root of a volume such as 'This Chromebook' with the Crostini container.">
     Give Linux apps permission to access files in the <ph name="FOLDER_NAME">$1<ex>Downloads</ex></ph> folder
   </message>
   <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_CROSTINI_DRIVE" desc="Confirmation message shown when a user shares the root of a Drive volume (My Drive, Team Drives, Computers) with the crostini container.">
@@ -1398,10 +1398,10 @@
   <message name="IDS_FILE_BROWSER_FOLDER_SHARED_WITH_CROSTINI_PLURAL" desc="Confirmation message shown when a user shares more than 1 folder with the crostini container.">
     <ph name="NUMBER_OF_ITEMS">$1<ex>3</ex></ph> folders shared with Linux
   </message>
-  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_PLUGIN_VM_TITLE" desc="Message title shown when a user shares the root of a volume such as 'My files' with the Parallels.">
+  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_PLUGIN_VM_TITLE" desc="Message title shown when a user shares the root of a volume such as 'This Chromebook' with Parallels.">
     Share folder with Parallels Desktop
   </message>
-  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_PLUGIN_VM" desc="Confirmation message shown when a user shares the root of a volume such as 'My files' with Parallels." >
+  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_PLUGIN_VM" desc="Confirmation message shown when a user shares the root of a volume such as 'This Chromebook' with Parallels." >
     Give Parallels Desktop permission to access files in the <ph name="FOLDER_NAME">$1<ex>Downloads</ex></ph> folder
   </message>
   <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_PLUGIN_VM_DRIVE" desc="Confirmation message shown when a user shares the root of a Drive volume (My Drive, Team Drives, Computers) with Parallels.">
@@ -1413,10 +1413,10 @@
   <message name="IDS_FILE_BROWSER_FOLDER_SHARED_WITH_PLUGIN_VM_PLURAL" desc="Confirmation message shown when a user shares more than 1 folder with Parallels.">
     <ph name="NUMBER_OF_ITEMS">$1<ex>3</ex></ph> folders shared with Parallels Desktop
   </message>
-  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_BRUSCHETTA_TITLE" desc="Message title shown when a user shares the root of a volume such as 'My files' with the VM type codenamed Bruschetta.">
+  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_BRUSCHETTA_TITLE" desc="Message title shown when a user shares the root of a volume such as 'This Chromebook' with the VM type codenamed Bruschetta.">
     Share folder with <ph name="VM_NAME">$1<ex>MyVm</ex></ph>
   </message>
-  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_BRUSCHETTA" desc="Confirmation message shown when a user shares the root of a volume such as 'My files' with the VM type codenamed Bruschetta" >
+  <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_BRUSCHETTA" desc="Confirmation message shown when a user shares the root of a volume such as 'This Chromebook' with the VM type codenamed Bruschetta." >
     Give <ph name="VM_NAME">$1<ex>MyVm</ex></ph> permission to access files in the <ph name="FOLDER_NAME">$2<ex>Downloads</ex></ph> folder
   </message>
   <message name="IDS_FILE_BROWSER_SHARE_ROOT_FOLDER_WITH_BRUSCHETTA_DRIVE" desc="Confirmation message shown when a user shares the root of a Drive volume (My Drive, Team Drives, Computers) with the VM type codenamed Bruschetta.">
diff --git a/ui/events/ash/BUILD.gn b/ui/events/ash/BUILD.gn
index 7baf11a..108fec0 100644
--- a/ui/events/ash/BUILD.gn
+++ b/ui/events/ash/BUILD.gn
@@ -41,6 +41,7 @@
     "//base",
     "//components/user_manager",
     "//device/udev_linux",
+    "//google_apis",
     "//ui/base",
     "//ui/base:features",
     "//ui/base/ime/ash",
diff --git a/ui/events/ash/DEPS b/ui/events/ash/DEPS
index 251d07f9..ab34707 100644
--- a/ui/events/ash/DEPS
+++ b/ui/events/ash/DEPS
@@ -3,5 +3,6 @@
   "+components/account_id",
   "+components/user_manager",
   "+device/udev_linux",
+  "+google_apis/gaia/gaia_auth_util.h",
   "+ui/base",
 ]
diff --git a/ui/events/ash/modifier_split_dogfood_controller.cc b/ui/events/ash/modifier_split_dogfood_controller.cc
index 6e34a28..630ae50 100644
--- a/ui/events/ash/modifier_split_dogfood_controller.cc
+++ b/ui/events/ash/modifier_split_dogfood_controller.cc
@@ -7,13 +7,10 @@
 #include "ash/constants/ash_features.h"
 #include "base/strings/string_util.h"
 #include "components/user_manager/user_manager.h"
+#include "google_apis/gaia/gaia_auth_util.h"
 
 namespace ui {
 
-namespace {
-constexpr char kGoogleDomain[] = "@google.com";
-}
-
 ModifierSplitDogfoodController::ModifierSplitDogfoodController() {
   modifier_split_enabled_ = ash::features::IsModifierSplitEnabled() &&
                             !ash::features::IsModifierSplitDogfoodEnabled();
@@ -46,8 +43,8 @@
     return;
   }
 
-  modifier_split_enabled_ = base::EndsWith(
-      primary_user->GetAccountId().GetUserEmail(), kGoogleDomain);
+  modifier_split_enabled_ = gaia::IsGoogleInternalAccountEmail(
+      primary_user->GetAccountId().GetUserEmail());
 }
 
 }  // namespace ui
diff --git a/ui/ozone/OWNERS b/ui/ozone/OWNERS
index d954b88..13c6a62a 100644
--- a/ui/ozone/OWNERS
+++ b/ui/ozone/OWNERS
@@ -1,2 +1,3 @@
 spang@chromium.org
 petermcneeley@chromium.org
+fangzhoug@chromium.org
\ No newline at end of file
diff --git a/url/url_constants.h b/url/url_constants.h
index 3cb71099..fe4390e 100644
--- a/url/url_constants.h
+++ b/url/url_constants.h
@@ -21,6 +21,8 @@
 
 inline constexpr char kAboutScheme[] = "about";
 inline constexpr char16_t kAboutScheme16[] = u"about";
+inline constexpr char kAndroidScheme[] = "android";
+inline constexpr char16_t kAndroidScheme16[] = u"android";
 inline constexpr char kBlobScheme[] = "blob";
 inline constexpr char16_t kBlobScheme16[] = u"blob";
 inline constexpr char kContentScheme[] = "content";
diff --git a/url/url_util.cc b/url/url_util.cc
index fd6de92..3191f87 100644
--- a/url/url_util.cc
+++ b/url/url_util.cc
@@ -116,6 +116,13 @@
       kAboutScheme,
   };
 
+  // Non-special schemes that should be treated as opaque path URLs for
+  // compatibility reasons.
+  std::vector<std::string> opaque_non_special_schemes = {
+      // See https://crrev.com/c/5465607 for the reason.
+      kAndroidScheme,
+  };
+
   // Schemes with a predefined default custom handler.
   std::vector<SchemeWithHandler> predefined_handler_schemes;
 
@@ -189,6 +196,19 @@
                        GetSchemeRegistry().standard_schemes);
 }
 
+template <typename CHAR>
+bool DoIsOpaqueNonSpecial(const CHAR* spec, const Component& scheme) {
+  if (scheme.is_empty()) {
+    return false;
+  }
+  for (const std::string& s : GetSchemeRegistry().opaque_non_special_schemes) {
+    if (base::EqualsCaseInsensitiveASCII(
+            std::basic_string_view(&spec[scheme.begin], scheme.len), s)) {
+      return true;
+    }
+  }
+  return false;
+}
 
 template<typename CHAR>
 bool DoFindAndCompareScheme(const CHAR* str,
@@ -296,7 +316,8 @@
 
   } else {
     // Non-special scheme URLs like data: and javascript:.
-    if (url::IsUsingStandardCompliantNonSpecialSchemeURLParsing()) {
+    if (url::IsUsingStandardCompliantNonSpecialSchemeURLParsing() &&
+        !DoIsOpaqueNonSpecial(spec, scheme)) {
       success = CanonicalizeNonSpecialURL(
           spec, spec_len,
           ParseNonSpecialURLInternal(std::basic_string_view(spec, spec_len),
diff --git a/url/url_util_unittest.cc b/url/url_util_unittest.cc
index 324b086..016475b 100644
--- a/url/url_util_unittest.cc
+++ b/url/url_util_unittest.cc
@@ -884,6 +884,27 @@
   }
 }
 
+TEST_P(URLUtilTypedTest, OpaqueNonSpecialScheme) {
+  // Ensure that the behavior of "android:" scheme URL is preserved, which is
+  // not URL Standard compliant.
+  //
+  // URL Standard-wise, "android://a b" is an invalid URL because the host part
+  // includes a space character, which is not allowed.
+  std::optional<std::string> res = CanonicalizeSpec("android://a b", false);
+  ASSERT_TRUE(res);
+  EXPECT_EQ(*res, "android://a b");
+
+  // Test a "git:" scheme URL for comparison.
+  res = CanonicalizeSpec("git://a b", false);
+  if (use_standard_compliant_non_special_scheme_url_parsing_) {
+    // This is correct behavior because "git://a b" is an invalid URL.
+    EXPECT_FALSE(res);
+  } else {
+    ASSERT_TRUE(res);
+    EXPECT_EQ(*res, "git://a b");
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(All, URLUtilTypedTest, ::testing::Bool());
 
 }  // namespace url
diff --git a/v8 b/v8
index 9b54530..3c9fa12 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 9b5453026ea3517aea3a40ae86f229af12a25435
+Subproject commit 3c9fa12db3183a6f4ea53d2675adb66ea1194529