diff --git a/AUTHORS b/AUTHORS
index 4910dbc1..1b7a37ec 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1116,6 +1116,7 @@
 Sylvestre Ledru <sylvestre.ledru@gmail.com>
 Synthia Islam <synthia.is@samsung.com>
 Szabolcs David <davidsz@inf.u-szeged.hu>
+Szilard Szaloki <szilardszaloki@gmail.com>
 Szymon Piechowicz <szymonpiechowicz@o2.pl>
 Taeheon Kim <skyrabbits1@gmail.com>
 Taeho Nam <thn7440@gmail.com>
diff --git a/DEPS b/DEPS
index a9c7ca5..b63a668 100644
--- a/DEPS
+++ b/DEPS
@@ -201,7 +201,7 @@
   # By default, do not check out versions of toolschains and sdks that are
   # specifically only needed by Lacros.
   'checkout_lacros_sdk': False,
-  'lacros_sdk_version': '14395.0.0',
+  'lacros_sdk_version': '14408.0.0',
 
   # Generate location tag metadata to include in tests result data uploaded
   # to ResultDB. This isn't needed on some configs and the tool that generates
@@ -245,11 +245,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'd7f7cc87910800232c5900ebb64c0c00781cd0c2',
+  'skia_revision': '84632c9616cc23d8b18d80d83731c60f1f32dc0f',
   # 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': '63def35d81d668ee5eedd970616fa0f421813dc1',
+  'v8_revision': '091f110e3879526c44627ae12ecda87cdd4286a4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '47cb034b7a90184550e656850e98a760f55dcea2',
+  'devtools_frontend_revision': '1595cda8b6477193d9352ef368c5f4e669ff56fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -360,7 +360,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '377ead9d448eb42342c0a957e6e3e4b485c4f7c7',
+  'dawn_revision': '1991ad6b2ec8233ab12638f2a8b6c6288dbd4f8a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -663,7 +663,7 @@
     Var('chromium_git') + '/external/github.com/toji/webvr.info.git' + '@' + 'c58ae99b9ff9e2aa4c524633519570bf33536248',
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '80a87e883449376fa2b7ba82ebc81b177949d993',
+    'url': Var('chromium_git') + '/website.git' + '@' + '8f53a2e1eef4e0bad76e2877982ad3e19b57a211',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1090,7 +1090,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '66c9784478053eebdfefb6c2d17b093de4079262',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a6ea731ab20f48053514f6ced28b14d72164e43d',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1473,7 +1473,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '57b59d6e2effdc5d3b9dc8da68fa519d9d37f2e0',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '605bd3a3ad96c56f98780e7b4b6b247e1d9e0ae9',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1694,7 +1694,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '729be85c2f01876a1ea075c523d542ebaaa76301',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6cd64b6bd52703159c9290a22e599ca9bbeb0d1d',
+    Var('webrtc_git') + '/src.git' + '@' + 'e79f85c10e98f3a43570d8017321842aaf4165bf',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1764,7 +1764,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b6f15c40f717eb764baa7b0d93f4ad449c89158a',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3e0df66b4fc3223aeb9daf2fd59afbd93118edec',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/app_list_bubble_presenter_unittest.cc b/ash/app_list/app_list_bubble_presenter_unittest.cc
index 04753c1..e464170 100644
--- a/ash/app_list/app_list_bubble_presenter_unittest.cc
+++ b/ash/app_list/app_list_bubble_presenter_unittest.cc
@@ -570,7 +570,16 @@
                      GetPrimaryDisplay().work_area().top_right()));
 }
 
-TEST_F(AppListBubblePresenterTest, BubbleOpensInBottomRightForBottomShelfRTL) {
+#if BUILDFLAG(IS_LINUX)
+// Flaky on multiple Linux builders: crbug.com/1293695.
+#define MAYBE_BubbleOpensInBottomRightForBottomShelfRTL \
+  DISABLED_BubbleOpensInBottomRightForBottomShelfRTL
+#else
+#define MAYBE_BubbleOpensInBottomRightForBottomShelfRTL \
+  BubbleOpensInBottomRightForBottomShelfRTL
+#endif
+TEST_F(AppListBubblePresenterTest,
+       MAYBE_BubbleOpensInBottomRightForBottomShelfRTL) {
   base::test::ScopedRestoreICUDefaultLocale locale("he");
   GetPrimaryShelf()->SetAlignment(ShelfAlignment::kBottom);
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index b237cad..076b902b 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -4712,6 +4712,27 @@
       <message name="IDS_ASH_USB_NOTIFICATION_CABLE_WARNING_SPEED_LIMITED_BODY" desc="Notification body text for when a user connects a device to their Chromebook with a cable that has a lower maximum data rate than the device." translateable="false">
         Your device supports a higher data rate than your cable. Device performance may be limited.
       </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_NOTIFIER" desc="A string explaining that the origin of the notification is the snooping protection feature.">
+        Viewing protection
+      </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_TITLE" desc="Notification title explaining that a notification has been hidden.">
+        New notification hidden
+      </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_1" desc="Notification message explaining that a popup from one application has been blocked.">
+        <ph name="APP_TITLE">$1<ex>Google Chat</ex></ph> notification is hidden because viewing protection is on
+      </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_2" desc="Notification message explaining that a popup from two applications have been blocked.">
+        <ph name="APP_1_TITLE">$1<ex>Google Chat</ex></ph> and <ph name="APP_2_TITLE">$2<ex>Chrome</ex></ph> notifications are hidden because viewing protection is on
+      </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_3_PLUS" desc="Notification message explaining that a popup from three or more applications have been blocked.">
+        <ph name="APP_1_TITLE">$1<ex>Google Chat</ex></ph>, <ph name="APP_2_TITLE">$2<ex>Chrome</ex></ph> and other notifications are hidden because viewing protection is on
+      </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SHOW_BUTTON_TEXT" desc="Text in notification button to show previously-blocked notifications.">
+        Show
+      </message>
+      <message name="IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SETTINGS_BUTTON_TEXT" desc="Text in notification button to launch smart privacy settings.">
+        Privacy settings
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_1.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_1.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_1.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_2.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_2.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_2.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_3_PLUS.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_3_PLUS.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_MESSAGE_3_PLUS.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_NOTIFIER.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_NOTIFIER.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_NOTIFIER.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SETTINGS_BUTTON_TEXT.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SETTINGS_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SETTINGS_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SHOW_BUTTON_TEXT.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SHOW_BUTTON_TEXT.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_SHOW_BUTTON_TEXT.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_TITLE.png.sha1
new file mode 100644
index 0000000..825e1ba4
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SMART_PRIVACY_SNOOPING_NOTIFICATION_TITLE.png.sha1
@@ -0,0 +1 @@
+5523da4b68d94ffd9cf0b95963e46407a57ac347
\ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index b6f3253..991e4bc 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -208,7 +208,7 @@
 
 // Enable experimental disk management changes for Borealis.
 const base::Feature kBorealisDiskManagement{"BorealisDiskManagement",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Force the client to be on its beta version. If not set, the client will be on
 // its stable version.
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index d8daa31c..9d90247 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -399,6 +399,10 @@
       ->ClearLoginShelfSwipeHandler();
 }
 
+views::Widget* LoginScreenController::GetLoginWindowWidget() {
+  return client_ ? client_->GetLoginWindowWidget() : nullptr;
+}
+
 void LoginScreenController::ShowLockScreen() {
   CHECK(!LockScreen::HasInstance());
   OnShow();
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 9d315c83..b6af0c7 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -132,6 +132,7 @@
                                    const base::RepeatingClosure& fling_callback,
                                    base::OnceClosure exit_callback) override;
   void ClearLoginShelfGestureHandler() override;
+  views::Widget* GetLoginWindowWidget() override;
 
   // KioskAppMenu:
   void SetKioskApps(
diff --git a/ash/login/mock_login_screen_client.h b/ash/login/mock_login_screen_client.h
index bc7e4a00..c84cb4a 100644
--- a/ash/login/mock_login_screen_client.h
+++ b/ash/login/mock_login_screen_client.h
@@ -117,6 +117,7 @@
   MOCK_METHOD(void, OnUserActivity, (), (override));
   MOCK_METHOD(void, OnLoginScreenShown, (), (override));
   MOCK_METHOD(void, OnSystemTrayBubbleShown, (), (override));
+  MOCK_METHOD(views::Widget*, GetLoginWindowWidget, (), (override));
 
  private:
   bool authenticate_user_callback_result_ = true;
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index db59f16..66fbba7e 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -1415,6 +1415,7 @@
 }
 
 void LockContentsView::OnOobeDialogStateChanged(OobeDialogState state) {
+  bool oobe_dialog_was_visible = oobe_dialog_visible_;
   oobe_dialog_visible_ = state != OobeDialogState::HIDDEN;
   extension_ui_visible_ = state == OobeDialogState::EXTENSION_LOGIN;
 
@@ -1431,6 +1432,14 @@
              !oobe_dialog_visible_ && login_camera_timeout_view_) {
     login_camera_timeout_view_->RequestFocus();
   }
+  // If OOBE dialog visibility changes we need to force an update of the a11y
+  // tree to fix linear navigation from `StatusAreaWidget` to `LockScreen`.
+  if (oobe_dialog_visible_ != oobe_dialog_was_visible) {
+    Shelf* shelf = Shelf::ForWindow(GetWidget()->GetNativeWindow());
+    shelf->GetStatusAreaWidget()
+        ->status_area_widget_delegate()
+        ->NotifyAccessibilityEvent(ax::mojom::Event::kStateChanged, true);
+  }
 }
 
 void LockContentsView::MaybeUpdateExpandedView(const AccountId& account_id,
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_view.cc b/ash/public/cpp/external_arc/message_center/arc_notification_view.cc
index 1f7aa5a6..ef4a415 100644
--- a/ash/public/cpp/external_arc/message_center/arc_notification_view.cc
+++ b/ash/public/cpp/external_arc/message_center/arc_notification_view.cc
@@ -11,6 +11,7 @@
 #include "ash/public/cpp/external_arc/message_center/arc_notification_item.h"
 #include "ash/public/cpp/message_center/arc_notification_constants.h"
 #include "ash/style/ash_color_provider.h"
+#include "base/time/time.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -30,6 +31,13 @@
 
 DEFINE_UI_CLASS_PROPERTY_TYPE(ash::ArcNotificationView*)
 
+namespace {
+// The animation duration must be 360ms because it's separately defined in
+// Android side.
+constexpr base::TimeDelta kArcNotificationAnimationDuration =
+    base::Milliseconds(360);
+}  // namespace
+
 namespace ash {
 
 DEFINE_UI_CLASS_PROPERTY_KEY(ArcNotificationView*,
@@ -172,15 +180,28 @@
 
 void ArcNotificationView::OnThemeChanged() {
   message_center::MessageView::OnThemeChanged();
-  focus_painter_ = views::Painter::CreateSolidFocusPainter(
-      GetColorProvider()->GetColor(ui::kColorFocusableBorderFocused),
-      gfx::Insets(0, 1, 3, 2));
+
+  // TODO(yhanada): Migrate to views::FocusRing to support rounded-corner ring.
+  if (ash::features::IsNotificationsRefreshEnabled()) {
+    focus_painter_ = views::Painter::CreateSolidFocusPainter(
+        GetColorProvider()->GetColor(ui::kColorFocusableBorderFocused), 2,
+        gfx::Insets(3, 3));
+  } else {
+    focus_painter_ = views::Painter::CreateSolidFocusPainter(
+        GetColorProvider()->GetColor(ui::kColorFocusableBorderFocused),
+        gfx::Insets(0, 1, 3, 2));
+  }
 }
 
 void ArcNotificationView::OnContainerAnimationEnded() {
   content_view_->OnContainerAnimationEnded();
 }
 
+base::TimeDelta ArcNotificationView::GetBoundsAnimationDuration(
+    const message_center::Notification&) const {
+  return kArcNotificationAnimationDuration;
+}
+
 void ArcNotificationView::OnSlideChanged(bool in_progress) {
   MessageView::OnSlideChanged(in_progress);
   content_view_->OnSlideChanged(in_progress);
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_view.h b/ash/public/cpp/external_arc/message_center/arc_notification_view.h
index 2f641a28..325711d 100644
--- a/ash/public/cpp/external_arc/message_center/arc_notification_view.h
+++ b/ash/public/cpp/external_arc/message_center/arc_notification_view.h
@@ -63,6 +63,8 @@
   void OnSnoozeButtonPressed(const ui::Event& event) override;
   void OnThemeChanged() override;
   void UpdateCornerRadius(int top_radius, int bottom_radius) override;
+  base::TimeDelta GetBoundsAnimationDuration(
+      const message_center::Notification&) const override;
 
   // views::SlideOutControllerDelegate:
   void OnSlideChanged(bool in_progress) override;
diff --git a/ash/public/cpp/login_screen.h b/ash/public/cpp/login_screen.h
index 98aaea7..165ddb2 100644
--- a/ash/public/cpp/login_screen.h
+++ b/ash/public/cpp/login_screen.h
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/cpp/login_types.h"
 #include "base/callback_forward.h"
+#include "ui/views/widget/widget.h"
 
 namespace ash {
 
@@ -100,6 +101,10 @@
   // Stops login shelf gesture detection.
   virtual void ClearLoginShelfGestureHandler() = 0;
 
+  // Get login screen widget. Currently used to set proper accessibility
+  // navigation.
+  virtual views::Widget* GetLoginWindowWidget() = 0;
+
  protected:
   LoginScreen();
   virtual ~LoginScreen();
diff --git a/ash/public/cpp/login_screen_client.h b/ash/public/cpp/login_screen_client.h
index 16f2470..099d92b9 100644
--- a/ash/public/cpp/login_screen_client.h
+++ b/ash/public/cpp/login_screen_client.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/time/time.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/views/widget/widget.h"
 
 class AccountId;
 
@@ -168,6 +169,10 @@
   // Used by Ash to signal that user activity occurred on the login screen.
   virtual void OnUserActivity() = 0;
 
+  // Get login screen widget. Currently used to set proper accessibility
+  // navigation.
+  virtual views::Widget* GetLoginWindowWidget() = 0;
+
  protected:
   virtual ~LoginScreenClient() = default;
 };
diff --git a/ash/services/ime/decoder/system_engine.cc b/ash/services/ime/decoder/system_engine.cc
index 8021e22..a126480 100644
--- a/ash/services/ime/decoder/system_engine.cc
+++ b/ash/services/ime/decoder/system_engine.cc
@@ -33,6 +33,10 @@
     const std::string& ime_spec,
     mojo::PendingReceiver<mojom::InputMethod> receiver,
     mojo::PendingRemote<mojom::InputMethodHost> host) {
+  if (!decoder_entry_points_) {
+    return false;
+  }
+
   auto receiver_pipe_handle = receiver.PassPipe().release().value();
   auto host_pipe_version = host.version();
   auto host_pipe_handle = host.PassPipe().release().value();
@@ -43,12 +47,16 @@
 
 bool SystemEngine::BindConnectionFactory(
     mojo::PendingReceiver<mojom::ConnectionFactory> receiver) {
-  // TODO(b/209697256): Pass and bind receiver in shared library.
-  return false;
+  if (!decoder_entry_points_)
+    return false;
+  auto receiver_pipe_handle = receiver.PassPipe().release().value();
+  return decoder_entry_points_->initialize_connection_factory(
+      receiver_pipe_handle);
 }
 
 bool SystemEngine::IsConnected() {
-  return decoder_entry_points_->is_input_method_connected();
+  return decoder_entry_points_ &&
+         decoder_entry_points_->is_input_method_connected();
 }
 
 }  // namespace ime
diff --git a/ash/services/ime/ime_decoder.cc b/ash/services/ime/ime_decoder.cc
index f4cc147..21b067f 100644
--- a/ash/services/ime/ime_decoder.cc
+++ b/ash/services/ime/ime_decoder.cc
@@ -83,6 +83,9 @@
   entry_points.connect_to_input_method =
       reinterpret_cast<ConnectToInputMethodFn>(
           library.GetFunctionPointer(kConnectToInputMethodFnName));
+  entry_points.initialize_connection_factory =
+      reinterpret_cast<InitializeConnectionFactoryFn>(
+          library.GetFunctionPointer(kInitializeConnectionFactoryFnName));
   entry_points.is_input_method_connected =
       reinterpret_cast<IsInputMethodConnectedFn>(
           library.GetFunctionPointer(kIsInputMethodConnectedFnName));
@@ -91,7 +94,8 @@
   if (!entry_points.init_once || !entry_points.supports ||
       !entry_points.activate_ime || !entry_points.process ||
       !entry_points.close || !entry_points.connect_to_input_method ||
-      !entry_points.is_input_method_connected) {
+      !entry_points.is_input_method_connected ||
+      !entry_points.initialize_connection_factory) {
     return;
   }
 
diff --git a/ash/services/ime/ime_decoder.h b/ash/services/ime/ime_decoder.h
index 6e39fab..150615b 100644
--- a/ash/services/ime/ime_decoder.h
+++ b/ash/services/ime/ime_decoder.h
@@ -43,6 +43,11 @@
     uint32_t remote_input_method_host_handle,
     uint32_t remote_input_method_host_version);
 
+inline constexpr char kInitializeConnectionFactoryFnName[] =
+    "InitializeConnectionFactory";
+typedef bool (*InitializeConnectionFactoryFn)(
+    uint32_t receiver_connection_factory_handle);
+
 inline constexpr char kIsInputMethodConnectedFnName[] =
     "IsInputMethodConnected";
 typedef bool (*IsInputMethodConnectedFn)();
@@ -63,6 +68,7 @@
     ImeDecoderActivateImeFn activate_ime;
     ImeDecoderProcessFn process;
     ConnectToInputMethodFn connect_to_input_method;
+    InitializeConnectionFactoryFn initialize_connection_factory;
     IsInputMethodConnectedFn is_input_method_connected;
   };
 
diff --git a/ash/system/message_center/ash_notification_view.h b/ash/system/message_center/ash_notification_view.h
index 5fc1dea..0ed9867 100644
--- a/ash/system/message_center/ash_notification_view.h
+++ b/ash/system/message_center/ash_notification_view.h
@@ -57,11 +57,6 @@
   // Called when a child notificaiton's preferred size changes.
   void GroupedNotificationsPreferredSizeChanged();
 
-  // Gets the animation duration for a recent bounds change. Called after
-  // `PreferredSizeChanged()`, so the current state is the target state.
-  base::TimeDelta GetBoundsAnimationDuration(
-      const message_center::Notification& notification) const;
-
   // message_center::MessageView:
   void AddGroupNotification(const message_center::Notification& notification,
                             bool newest_first) override;
@@ -70,6 +65,10 @@
       override;
   void RemoveGroupNotification(const std::string& notification_id) override;
   const char* GetClassName() const override;
+  // Called after `PreferredSizeChanged()`, so the current state is the target
+  // state.
+  base::TimeDelta GetBoundsAnimationDuration(
+      const message_center::Notification& notification) const override;
 
   // message_center::NotificationViewBase:
   void UpdateViewForExpandedState(bool expanded) override;
diff --git a/ash/system/message_center/unified_message_list_view.cc b/ash/system/message_center/unified_message_list_view.cc
index f84dec1..7a35b02 100644
--- a/ash/system/message_center/unified_message_list_view.cc
+++ b/ash/system/message_center/unified_message_list_view.cc
@@ -115,14 +115,7 @@
         message_view()->notification_id());
     if (!notification)
       return base::Milliseconds(0);
-    if (message_view()->GetClassName() == AshNotificationView::kViewClassName) {
-      return static_cast<const AshNotificationView*>(message_view())
-          ->GetBoundsAnimationDuration(*notification);
-    }
-    // TODO(crbug/1278483): ARC notifications will require different animation
-    // durations. Default to kLargeImageExpandAndCollapseAnimationDuration for
-    // now.
-    return base::Milliseconds(kLargeImageExpandAndCollapseAnimationDuration);
+    return message_view()->GetBoundsAnimationDuration(*notification);
   }
 
   // Update the border and background corners based on if the notification is
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc
index 459eaee..218626f 100644
--- a/ash/system/status_area_widget_delegate.cc
+++ b/ash/system/status_area_widget_delegate.cc
@@ -5,6 +5,9 @@
 #include "ash/system/status_area_widget_delegate.h"
 
 #include "ash/focus_cycler.h"
+#include "ash/login/ui/lock_screen.h"
+#include "ash/public/cpp/login_screen.h"
+#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -18,6 +21,7 @@
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/skia_paint_util.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/accessible_pane_view.h"
 #include "ui/views/background.h"
 #include "ui/views/border.h"
@@ -128,6 +132,22 @@
   SetLayoutManager(nullptr);
 }
 
+void StatusAreaWidgetDelegate::GetAccessibleNodeData(
+    ui::AXNodeData* node_data) {
+  AccessiblePaneView::GetAccessibleNodeData(node_data);
+  // If OOBE dialog is visible it should be the next accessible widget,
+  // otherwise it should be LockScreen.
+  if (!!LoginScreen::Get()->GetLoginWindowWidget() &&
+      LoginScreen::Get()->GetLoginWindowWidget()->IsVisible()) {
+    GetViewAccessibility().OverrideNextFocus(
+        LoginScreen::Get()->GetLoginWindowWidget());
+  } else if (LockScreen::HasInstance()) {
+    GetViewAccessibility().OverrideNextFocus(LockScreen::Get()->widget());
+  }
+  Shelf* shelf = Shelf::ForWindow(GetWidget()->GetNativeWindow());
+  GetViewAccessibility().OverridePreviousFocus(shelf->shelf_widget());
+}
+
 views::View* StatusAreaWidgetDelegate::GetDefaultFocusableChild() {
   return default_last_focusable_child_ ? GetLastFocusableChild()
                                        : GetFirstFocusableChild();
diff --git a/ash/system/status_area_widget_delegate.h b/ash/system/status_area_widget_delegate.h
index 1f970455a..dbda88d 100644
--- a/ash/system/status_area_widget_delegate.h
+++ b/ash/system/status_area_widget_delegate.h
@@ -74,6 +74,7 @@
   void Shutdown();
 
   // views::AccessiblePaneView:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   View* GetDefaultFocusableChild() override;
   const char* GetClassName() const override;
   views::Widget* GetWidget() override;
diff --git a/ash/webui/media_app_ui/media_app_guest_ui.cc b/ash/webui/media_app_ui/media_app_guest_ui.cc
index 383ce8a..1c6844d 100644
--- a/ash/webui/media_app_ui/media_app_guest_ui.cc
+++ b/ash/webui/media_app_ui/media_app_guest_ui.cc
@@ -7,12 +7,16 @@
 #include "ash/webui/grit/ash_media_app_resources.h"
 #include "ash/webui/media_app_ui/url_constants.h"
 #include "ash/webui/web_applications/webui_test_prod_util.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/task/post_task.h"
+#include "base/task/task_runner_util.h"
+#include "base/task/thread_pool.h"
 #include "chromeos/grit/chromeos_media_app_bundle_resources.h"
 #include "chromeos/grit/chromeos_media_app_bundle_resources_map.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
-#include "content/public/browser/web_ui_data_source.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/mojom/autoplay/autoplay.mojom.h"
@@ -22,6 +26,27 @@
 
 namespace {
 
+constexpr base::FilePath::CharType kFontsRoot[] =
+    FILE_PATH_LITERAL("/usr/share/fonts");
+constexpr char kFontRequestPrefix[] = "fonts/";
+
+bool IsFontRequest(const std::string& path) {
+  return base::StartsWith(path, kFontRequestPrefix);
+}
+
+void FontLoaded(content::WebUIDataSource::GotDataCallback got_data_callback,
+                std::unique_ptr<std::string> font_data,
+                bool did_load_file) {
+  if (font_data->size() && did_load_file) {
+    std::move(got_data_callback)
+        .Run(new base::RefCountedBytes(
+            reinterpret_cast<const unsigned char*>(font_data->data()),
+            font_data->size()));
+  } else {
+    std::move(got_data_callback).Run(nullptr);
+  }
+}
+
 content::WebUIDataSource* CreateMediaAppUntrustedDataSource(
     content::WebUI* web_ui,
     MediaAppGuestUIDelegate* delegate) {
@@ -45,8 +70,6 @@
   source->AddResourcePath("js/app_image_handler_module.js",
                           IDR_MEDIA_APP_APP_IMAGE_HANDLER_MODULE_JS);
 
-  MaybeConfigureTestableDataSource(source);
-
   // Add all resources from chromeos_media_app_bundle_resources.pak.
   source->AddResourcePaths(base::make_span(
       kChromeosMediaAppBundleResources, kChromeosMediaAppBundleResourcesSize));
@@ -104,9 +127,18 @@
                                  MediaAppGuestUIDelegate* delegate)
     : UntrustedWebUIController(web_ui),
       WebContentsObserver(web_ui->GetWebContents()) {
+  task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
+      {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
+       base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+
   content::WebUIDataSource* untrusted_source =
       CreateMediaAppUntrustedDataSource(web_ui, delegate);
 
+  MaybeConfigureTestableDataSource(
+      untrusted_source, base::BindRepeating(&IsFontRequest),
+      base::BindRepeating(&MediaAppGuestUI::StartFontDataRequest,
+                          weak_factory_.GetWeakPtr()));
+
   auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
   content::WebUIDataSource::Add(browser_context, untrusted_source);
 }
@@ -127,4 +159,36 @@
                            blink::mojom::kAutoplayFlagForceAllow);
 }
 
+void MediaAppGuestUI::StartFontDataRequest(
+    const std::string& request_path,
+    content::WebUIDataSource::GotDataCallback got_data_callback) {
+  CHECK(IsFontRequest(request_path));
+  const std::string path = request_path.substr(sizeof(kFontRequestPrefix) - 1);
+  const base::FilePath font_path = base::FilePath(kFontsRoot).AppendASCII(path);
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+      base::BindOnce(&base::PathExists, font_path),
+      base::BindOnce(&MediaAppGuestUI::StartFontDataRequestAfterPathExists,
+                     weak_factory_.GetWeakPtr(), font_path,
+                     std::move(got_data_callback)));
+}
+
+void MediaAppGuestUI::StartFontDataRequestAfterPathExists(
+    const base::FilePath& font_path,
+    content::WebUIDataSource::GotDataCallback got_data_callback,
+    bool path_exists) {
+  if (path_exists) {
+    auto font_data = std::make_unique<std::string>();
+    std::string* data = font_data.get();
+    base::PostTaskAndReplyWithResult(
+        task_runner_.get(), FROM_HERE,
+        base::BindOnce(&base::ReadFileToString, font_path, data),
+        base::BindOnce(&FontLoaded, std::move(got_data_callback),
+                       std::move(font_data)));
+
+  } else {
+    std::move(got_data_callback).Run(nullptr);
+  }
+}
+
 }  // namespace ash
diff --git a/ash/webui/media_app_ui/media_app_guest_ui.h b/ash/webui/media_app_ui/media_app_guest_ui.h
index 3061725..053b3d2 100644
--- a/ash/webui/media_app_ui/media_app_guest_ui.h
+++ b/ash/webui/media_app_ui/media_app_guest_ui.h
@@ -5,12 +5,13 @@
 #ifndef ASH_WEBUI_MEDIA_APP_UI_MEDIA_APP_GUEST_UI_H_
 #define ASH_WEBUI_MEDIA_APP_UI_MEDIA_APP_GUEST_UI_H_
 
-#include "content/public/browser/web_contents_observer.h"
-#include "ui/webui/untrusted_web_ui_controller.h"
+#include <string>
 
-namespace content {
-class WebUIDataSource;
-}  // namespace content
+#include "base/files/file_path.h"
+#include "base/task/sequenced_task_runner.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "ui/webui/untrusted_web_ui_controller.h"
 
 namespace ash {
 
@@ -34,6 +35,20 @@
 
   // content::WebContentsObserver:
   void ReadyToCommitNavigation(content::NavigationHandle* handle) override;
+
+ private:
+  void StartFontDataRequest(
+      const std::string& path,
+      content::WebUIDataSource::GotDataCallback got_data_callback);
+  void StartFontDataRequestAfterPathExists(
+      const base::FilePath& font_path,
+      content::WebUIDataSource::GotDataCallback got_data_callback,
+      bool path_exists);
+
+  // The background task runner on which file I/O is performed.
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  base::WeakPtrFactory<MediaAppGuestUI> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js b/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js
index 12a2e6d..d5878cb2 100644
--- a/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_guest_ui_browsertest.js
@@ -83,3 +83,17 @@
   chai.assert.isDefined(window.customLaunchData.files);
   chai.assert.isTrue(window.customLaunchData.files.length === 0);
 });
+
+GUEST_TEST('GuestFailsToFetchMissingFonts', async () => {
+  let error;
+  try {
+    await fetch('/fonts/NotAFont.ttf');
+  } catch (/** @type {TypeError} */ e) {
+    error = e;
+  }
+
+  // Note failed webui requests are completely missing response headers, so
+  // fetch() will throw rather than returning a response.status of 404.
+  assertEquals(error.name, 'TypeError');
+  assertEquals(error.message, 'Failed to fetch');
+});
diff --git a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
index 39528c9..44e6784 100644
--- a/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
+++ b/ash/webui/media_app_ui/test/media_app_ui_gtest_browsertest.js
@@ -360,3 +360,7 @@
 TEST_F('MediaAppUIGtestBrowserTest', 'GuestStartsWithDefaultFileList', () => {
   runTestInGuest('GuestStartsWithDefaultFileList');
 });
+
+TEST_F('MediaAppUIGtestBrowserTest', 'GuestFailsToFetchMissingFonts', () => {
+  runTestInGuest('GuestFailsToFetchMissingFonts');
+});
diff --git a/ash/webui/web_applications/webui_test_prod_util.cc b/ash/webui/web_applications/webui_test_prod_util.cc
index b630473..519c2b2e 100644
--- a/ash/webui/web_applications/webui_test_prod_util.cc
+++ b/ash/webui/web_applications/webui_test_prod_util.cc
@@ -8,48 +8,78 @@
 #include "base/no_destructor.h"
 #include "content/public/common/content_switches.h"
 
+using content::WebUIDataSource;
+
 namespace {
 
-content::WebUIDataSource::ShouldHandleRequestCallback&
-GetTestShouldHandleRequest() {
-  static base::NoDestructor<
-      content::WebUIDataSource::ShouldHandleRequestCallback>
+WebUIDataSource::ShouldHandleRequestCallback& GetTestShouldHandleRequest() {
+  static base::NoDestructor<WebUIDataSource::ShouldHandleRequestCallback>
       callback;
   return *callback;
 }
 
-content::WebUIDataSource::HandleRequestCallback& GetTestRequestFilterHandler() {
-  static base::NoDestructor<content::WebUIDataSource::HandleRequestCallback>
-      callback;
+WebUIDataSource::HandleRequestCallback& GetTestRequestFilterHandler() {
+  static base::NoDestructor<WebUIDataSource::HandleRequestCallback> callback;
   return *callback;
 }
 
-bool InvokeTestShouldHandleRequestCallback(const std::string& path) {
-  const auto& callback = GetTestShouldHandleRequest();
-  return callback ? callback.Run(path) : false;
+bool InvokeTestShouldHandleRequestCallback(
+    const WebUIDataSource::ShouldHandleRequestCallback&
+        real_should_handle_request_callback,
+    const std::string& path) {
+  const auto& test_callback = GetTestShouldHandleRequest();
+  if (test_callback && test_callback.Run(path)) {
+    return true;
+  }
+  return real_should_handle_request_callback &&
+         real_should_handle_request_callback.Run(path);
 }
 
 void InvokeTestFileRequestFilterCallback(
+    const WebUIDataSource::HandleRequestCallback& real_handle_request_callback,
     const std::string& path,
-    content::WebUIDataSource::GotDataCallback callback) {
-  return GetTestRequestFilterHandler().Run(path, std::move(callback));
+    WebUIDataSource::GotDataCallback callback) {
+  // First, check whether this was request for a test-only resource. This
+  // requires the test handler to be installed by
+  // SetTestableDataSourceRequestHandlerForTesting() and for it to have returned
+  // true. Otherwise assume, that since the request was directed to the filter,
+  // that it is a request for the "real" filter installed by
+  // MaybeConfigureTestableDataSource().
+  const auto& test_callback = GetTestShouldHandleRequest();
+  if (test_callback && test_callback.Run(path)) {
+    GetTestRequestFilterHandler().Run(path, std::move(callback));
+  } else {
+    DCHECK(!real_handle_request_callback.is_null());
+    real_handle_request_callback.Run(path, std::move(callback));
+  }
 }
 
 }  // namespace
 
-bool MaybeConfigureTestableDataSource(content::WebUIDataSource* host_source) {
-  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType))
+bool MaybeConfigureTestableDataSource(
+    WebUIDataSource* host_source,
+    const WebUIDataSource::ShouldHandleRequestCallback&
+        real_should_handle_request_callback,
+    const WebUIDataSource::HandleRequestCallback&
+        real_handle_request_callback) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ::switches::kTestType)) {
+    host_source->SetRequestFilter(real_should_handle_request_callback,
+                                  real_handle_request_callback);
     return false;
+  }
 
   host_source->SetRequestFilter(
-      base::BindRepeating(&InvokeTestShouldHandleRequestCallback),
-      base::BindRepeating(&InvokeTestFileRequestFilterCallback));
+      base::BindRepeating(&InvokeTestShouldHandleRequestCallback,
+                          real_should_handle_request_callback),
+      base::BindRepeating(&InvokeTestFileRequestFilterCallback,
+                          real_handle_request_callback));
   return true;
 }
 
 void SetTestableDataSourceRequestHandlerForTesting(  // IN-TEST
-    content::WebUIDataSource::ShouldHandleRequestCallback should_handle,
-    content::WebUIDataSource::HandleRequestCallback handler) {
+    WebUIDataSource::ShouldHandleRequestCallback should_handle,
+    WebUIDataSource::HandleRequestCallback handler) {
   GetTestShouldHandleRequest() = std::move(should_handle);
   GetTestRequestFilterHandler() = std::move(handler);
 }
diff --git a/ash/webui/web_applications/webui_test_prod_util.h b/ash/webui/web_applications/webui_test_prod_util.h
index e2b444f..176a3bc 100644
--- a/ash/webui/web_applications/webui_test_prod_util.h
+++ b/ash/webui/web_applications/webui_test_prod_util.h
@@ -11,8 +11,17 @@
 // testing by installing a request filter that can be satisfied by tests wanting
 // to provide custom resources. Note that if these resources are scripts,
 // further CSP changes (e.g. trusted-types) may be required in order for them to
-// load. Returns true if the request filter was installed.
-bool MaybeConfigureTestableDataSource(content::WebUIDataSource* host_source);
+// load. If `real_[should_]handle_request_callback` arguments are provided, the
+// test request filter will be configured to handle these as well.
+// If not running for tests, `real_[should_]handle_request_callback` arguments
+// are passed directly to host_source->SetRequestFilter().
+// Returns true if the testing request filter was installed.
+bool MaybeConfigureTestableDataSource(
+    content::WebUIDataSource* host_source,
+    const content::WebUIDataSource::ShouldHandleRequestCallback&
+        real_should_handle_request_callback = {},
+    const content::WebUIDataSource::HandleRequestCallback&
+        real_handle_request_callback = {});
 
 // Configures the data source request filter used for testing. Only testing code
 // should call this.
diff --git a/base/allocator/partition_allocator/memory_reclaimer_unittest.cc b/base/allocator/partition_allocator/memory_reclaimer_unittest.cc
index 8e6fee4..158d5dc 100644
--- a/base/allocator/partition_allocator/memory_reclaimer_unittest.cc
+++ b/base/allocator/partition_allocator/memory_reclaimer_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/allocator/allocator_shim_default_dispatch_to_partition_alloc.h"
 #include "base/allocator/buildflags.h"
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/allocator/partition_allocator/partition_alloc_config.h"
@@ -102,10 +103,8 @@
   }
 }
 
-// ThreadCache tests disabled when USE_BACKUP_REF_PTR is enabled, because the
-// "original" PartitionRoot has ThreadCache disabled.
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
-    defined(PA_THREAD_CACHE_SUPPORTED) && !BUILDFLAG(USE_BACKUP_REF_PTR)
+    defined(PA_THREAD_CACHE_SUPPORTED)
 
 namespace {
 // malloc() / free() pairs can be removed by the compiler, this is enough (for
@@ -115,9 +114,11 @@
 }
 }  // namespace
 
-// Flaky. https://crbug.com/1208390
-TEST_F(PartitionAllocMemoryReclaimerTest,
-       DISABLED_DoNotAlwaysPurgeThreadCache) {
+TEST_F(PartitionAllocMemoryReclaimerTest, DoNotAlwaysPurgeThreadCache) {
+  // Make sure the thread cache is enabled in the main partition.
+  base::internal::PartitionAllocMalloc::Allocator()
+      ->EnableThreadCacheIfSupported();
+
   for (size_t i = 0; i < internal::ThreadCache::kDefaultSizeThreshold; i++) {
     void* data = malloc(i);
     FreeForTest(data);
@@ -144,8 +145,7 @@
 }
 
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
-        // defined(PA_THREAD_CACHE_SUPPORTED) && \
-        // !BUILDFLAG(USE_BACKUP_REF_PTR)
+        // defined(PA_THREAD_CACHE_SUPPORTED)
 
 }  // namespace base
 #endif  // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/allocator/partition_allocator/partition_alloc_forward.h b/base/allocator/partition_allocator/partition_alloc_forward.h
index 2e93f63c3..e4129db 100644
--- a/base/allocator/partition_allocator/partition_alloc_forward.h
+++ b/base/allocator/partition_allocator/partition_alloc_forward.h
@@ -13,7 +13,9 @@
 #include "base/compiler_specific.h"
 #include "base/dcheck_is_on.h"
 
-namespace partition_alloc::internal {
+namespace partition_alloc {
+
+namespace internal {
 
 // Alignment has two constraints:
 // - Alignment requirement for scalar types: alignof(std::max_align_t)
@@ -42,12 +44,17 @@
 template <bool thread_safe>
 struct SlotSpanMetadata;
 
-}  // namespace partition_alloc::internal
+}  // namespace internal
+
+class PartitionStatsDumper;
+
+}  // namespace partition_alloc
 
 namespace base {
 
 // TODO(https://crbug.com/1288247): Remove these 'using' declarations once
 // the migration to the new namespaces gets done.
+using ::partition_alloc::PartitionStatsDumper;
 using ::partition_alloc::internal::kAlignment;
 
 namespace internal {
@@ -67,8 +74,6 @@
 
 using ThreadSafePartitionRoot = PartitionRoot<internal::ThreadSafe>;
 
-class PartitionStatsDumper;
-
 }  // namespace base
 
 namespace partition_alloc {
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index 7ca2b4b..cdd4aeb 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -78,8 +78,6 @@
 
 namespace base {
 
-class PartitionStatsDumper;
-
 namespace internal {
 // Avoid including partition_address_space.h from this .h file, by moving the
 // call to IsManagedByPartitionAllocBRPPool into the .cc file.
@@ -810,7 +808,9 @@
   return slot_start;
 }
 
-// Gets the address to the beginning of the allocated slot.
+// Gets the address to the beginning of the allocated slot. The input |address|
+// can point anywhere in the slot, including the slot start as well as
+// immediately past the slot.
 //
 // This isn't a general purpose function, it is used specifically for obtaining
 // BackupRefPtr's ref-count. The caller is responsible for ensuring that the
@@ -832,7 +832,7 @@
       PartitionAllocGetDirectMapSlotStartInBRPPool(address);
   if (UNLIKELY(directmap_slot_start))
     return directmap_slot_start;
-  auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromObjectInnerAddr(address);
+  auto* slot_span = SlotSpanMetadata<ThreadSafe>::FromAddr(address);
   auto* root = PartitionRoot<ThreadSafe>::FromSlotSpan(slot_span);
   // Double check that ref-count is indeed present.
   PA_DCHECK(root->brp_enabled());
diff --git a/base/allocator/partition_allocator/partition_stats.cc b/base/allocator/partition_allocator/partition_stats.cc
index 07e3f34..a5835a8 100644
--- a/base/allocator/partition_allocator/partition_stats.cc
+++ b/base/allocator/partition_allocator/partition_stats.cc
@@ -2,11 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <cstring>
-
 #include "base/allocator/partition_allocator/partition_stats.h"
 
-namespace base {
+namespace partition_alloc {
 
 SimplePartitionStatsDumper::SimplePartitionStatsDumper() {
   memset(&stats_, 0, sizeof(stats_));
@@ -18,4 +16,4 @@
   stats_ = *memory_stats;
 }
 
-}  // namespace base
+}  // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_stats.h b/base/allocator/partition_allocator/partition_stats.h
index c8bf1b8..4d369a9e 100644
--- a/base/allocator/partition_allocator/partition_stats.h
+++ b/base/allocator/partition_allocator/partition_stats.h
@@ -12,7 +12,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/base_export.h"
 
-namespace base {
+namespace partition_alloc {
 
 // Most of these are not populated if PA_ENABLE_THREAD_CACHE_STATISTICS is not
 // defined.
@@ -37,7 +37,7 @@
   uint32_t metadata_overhead;
 
 #if defined(PA_THREAD_CACHE_ALLOC_STATS)
-  uint64_t allocs_per_bucket_[kNumBuckets + 1];
+  uint64_t allocs_per_bucket_[internal::kNumBuckets + 1];
 #endif  // defined(PA_THREAD_CACHE_ALLOC_STATS)
 };
 
@@ -124,6 +124,18 @@
   PartitionMemoryStats stats_;
 };
 
+}  // namespace partition_alloc
+
+namespace base {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::PartitionBucketMemoryStats;
+using ::partition_alloc::PartitionMemoryStats;
+using ::partition_alloc::PartitionStatsDumper;
+using ::partition_alloc::SimplePartitionStatsDumper;
+using ::partition_alloc::ThreadCacheStats;
+
 }  // namespace base
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_STATS_H_
diff --git a/base/allocator/partition_allocator/tagging.h b/base/allocator/partition_allocator/tagging.h
index fce2762e..b317ea9 100644
--- a/base/allocator/partition_allocator/tagging.h
+++ b/base/allocator/partition_allocator/tagging.h
@@ -16,13 +16,6 @@
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
 
-constexpr int kMemTagGranuleSize = 16u;
-#if defined(PA_HAS_MEMORY_TAGGING)
-constexpr uint64_t kMemTagUnmask = 0x00ffffffffffffffuLL;
-#else
-constexpr uint64_t kMemTagUnmask = 0xffffffffffffffffuLL;
-#endif
-
 namespace partition_alloc {
 
 // Enum configures Arm's MTE extension to operate in different modes
@@ -44,6 +37,13 @@
 
 namespace internal {
 
+constexpr int kMemTagGranuleSize = 16u;
+#if defined(PA_HAS_MEMORY_TAGGING)
+constexpr uint64_t kMemTagUnmask = 0x00ffffffffffffffuLL;
+#else
+constexpr uint64_t kMemTagUnmask = 0xffffffffffffffffuLL;
+#endif  // defined(PA_HAS_MEMORY_TAGGING)
+
 #if BUILDFLAG(IS_ANDROID)
 // Changes the memory tagging mode for all threads in the current process.
 BASE_EXPORT void ChangeMemoryTaggingModeForAllThreadsPerProcess(
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 3b9cc9d..01cf2f7c 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20220206.0.1
+7.20220207.0.1
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 55d38c7..01cf2f7c 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-7.20220204.2.1
+7.20220207.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 7128965d..01cf2f7c 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20220205.3.1
+7.20220207.0.1
diff --git a/chrome/VERSION b/chrome/VERSION
index 95b6019..f004d446 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=100
 MINOR=0
-BUILD=4874
+BUILD=4875
 PATCH=0
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
index b87d3f9..20996e1 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
@@ -328,10 +328,11 @@
             mShouldFetchPaymentsClientToken = true;
             return;
         }
+        byte[] emptyToken = new byte[0];
         if (mAccount == null) {
             // If there is no account, send an empty token.
             AutofillAssistantClientJni.get().onPaymentsClientToken(
-                    mNativeClientAndroid, AutofillAssistantClient.this, "");
+                    mNativeClientAndroid, AutofillAssistantClient.this, emptyToken);
             return;
         }
 
@@ -342,14 +343,13 @@
         if (activity == null) {
             // We require an activity to retrieve the token.
             AutofillAssistantClientJni.get().onPaymentsClientToken(
-                    mNativeClientAndroid, AutofillAssistantClient.this, "");
+                    mNativeClientAndroid, AutofillAssistantClient.this, emptyToken);
             return;
         }
         GmsIntegrator gmsIntegrator = new GmsIntegrator(mAccount.name, activity);
         gmsIntegrator.getClientToken((Callback<byte[]>) result -> {
-            String clientToken = result == null ? "" : new String(result);
-            AutofillAssistantClientJni.get().onPaymentsClientToken(
-                    mNativeClientAndroid, AutofillAssistantClient.this, clientToken);
+            AutofillAssistantClientJni.get().onPaymentsClientToken(mNativeClientAndroid,
+                    AutofillAssistantClient.this, result == null ? emptyToken : result);
         });
     }
 
@@ -408,7 +408,7 @@
         void onAccessToken(long nativeClientAndroid, AutofillAssistantClient caller,
                 boolean success, String accessToken);
         void onPaymentsClientToken(
-                long nativeClientAndroid, AutofillAssistantClient caller, String clientToken);
+                long nativeClientAndroid, AutofillAssistantClient caller, byte[] clientToken);
         String getPrimaryAccountName(long nativeClientAndroid, AutofillAssistantClient caller);
         void onJavaDestroyUI(long nativeClientAndroid, AutofillAssistantClient caller);
         void transferUITo(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index 8f342d8..227cf982 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -609,8 +609,8 @@
         boolean didTriggerPromo = false;
 
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_3)) {
-            didTriggerPromo =
-                    PrivacySandboxDialogController.maybeLaunchPrivacySandboxDialog(mActivity);
+            didTriggerPromo = PrivacySandboxDialogController.maybeLaunchPrivacySandboxDialog(
+                    mActivity, mTabModelSelectorSupplier.get().isIncognitoSelected());
         }
 
         if (!didTriggerPromo) {
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 6c02250..554fccd 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-99.0.4844.20_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-99.0.4844.23_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index 18c1bd74..c9639bc 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -29,7 +29,7 @@
     Preset window sizes
   </message>
   <message name="IDS_APP_MANAGEMENT_WINDOW" desc="Label for changing window mode.">
-    Open app in a separate window
+    Open as window
   </message>
   <message name="IDS_APP_MANAGEMENT_PRESET_WINDOW_SIZES_TEXT" desc="Label for the description of the window preset toggle in the app settings page.">
     Use presets for phone, tablet, or resizable windows to prevent app from misbehaving
diff --git a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_WINDOW.png.sha1 b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_WINDOW.png.sha1
index 95e66c0..c07ee35 100644
--- a/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_WINDOW.png.sha1
+++ b/chrome/app/app_management_strings_grdp/IDS_APP_MANAGEMENT_WINDOW.png.sha1
@@ -1 +1 @@
-99108bb839413247ac26a68c5340ff82959cc88e
\ No newline at end of file
+2a70978adab031dcf347f6535dc76a6067571b44
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 0d1c3e8..783cf3f 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6485,6 +6485,15 @@
         <message name="IDS_INTENT_PICKER_BUBBLE_VIEW_INITIATING_ORIGIN" desc="The label for the initiating origin of the intent picker.">
           From <ph name="ORIGIN">$1<ex>https://google.com</ex></ph>
         </message>
+
+        <if expr="chromeos_ash">
+          <message name="IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_MESSAGE" desc="The text of an infobar asking if the user wants to open web links supported by a particular app (e.g. https://www.youtube.com) in that app.">
+            Always use the <ph name="APP">$1<ex>YouTube</ex></ph> app to open supported web links?
+          </message>
+          <message name="IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_OK_LABEL" desc="The label of a confirm button for an infobar asking if the user wants to open web links in an app. If clicked, the selected app will always be opened when clicking supported web links.">
+            Always use
+          </message>
+        </if>
       </if>
 
       <!--Accessible name/action strings-->
@@ -10148,11 +10157,6 @@
         Continue blocking this site from having full control of MIDI devices
       </message>
 
-      <!-- Protocol Handler -->
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_TOOLTIP" desc="Location bar icon tooltip text when a page wants to use registerProtocolHandler.">
-        This page wants to install a service handler.
-      </message>
-
       <!-- Media Stream -->
       <message name="IDS_MICROPHONE_CAMERA_ALLOWED" desc="Status text that is used as location bar icon tooltip text and as media settings bubble status text when a page is allowed to use the camera and microphone.">
         This page is accessing your camera and microphone.
@@ -10402,36 +10406,6 @@
         Press |<ph name="ACCELERATOR">$1<ex>Esc</ex></ph>| to show your cursor
       </message>
 
-      <!-- Register Protocol Handler Strings -->
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME" desc="A more user friendly way of describing mailto: links.">
-        email
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME" desc="A more user friendly way of describing webcal: links.">
-        web calendar
-      </message>
-
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM" desc="The message to display when asking a user to confirm the registration of a protocol handler.">
-        Allow <ph name="HANDLER_HOSTNAME">$1<ex>google.com</ex></ph> to open all <ph name="PROTOCOL">$2<ex>search</ex></ph> links?
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE" desc="The message to display when asking a user to confirm the registration of a protocol handler.">
-        Allow <ph name="HANDLER_HOSTNAME">$1<ex>google.com</ex></ph> to open all <ph name="PROTOCOL">$2<ex>search</ex></ph> links instead of <ph name="REPLACED_HANDLER_TITLE">$3<ex>Elgoog Search</ex></ph>?
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_FRAGMENT" desc="The permission fragment to display when asking a user to confirm the registration of a protocol handler in a permission bubble. Follows a prompt 'This site would like to:'.">
-        Open <ph name="PROTOCOL">$1<ex>search</ex></ph> links
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE_FRAGMENT" desc="The permission fragment to display when asking a user to confirm the registration of a protocol handler. Follows a prompt 'This site would like to:'.">
-        Open <ph name="PROTOCOL">$1<ex>search</ex></ph> links instead of <ph name="REPLACED_HANDLER_TITLE">$2<ex>Elgoog Search</ex></ph>
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_ACCEPT" desc="Text to show for the accept button for the register protocol handler request infobar.">
-        Allow
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_DENY" desc="Text to show for the deny button for the register protocol handler request infobar.">
-        Deny
-      </message>
-      <message name="IDS_REGISTER_PROTOCOL_HANDLER_IGNORE" desc="Text to show for an ignore prompt for a register protocol handler registration request.">
-        Ignore
-      </message>
-
       <!-- Media Capture messages -->
       <message name="IDS_MEDIA_CAPTURE_SCREEN_INFOBAR_TEXT" desc="Text requesting permission for a site to access the device's screen.">
         <ph name="HOST">$1<ex>html5rocks.com</ex></ph> wants to share your screen
@@ -11214,14 +11188,6 @@
       </if>
     </if>
 
-  <!-- Protocol Handling intent prompt -->
-    <if expr="not is_android">
-      <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_QUESTION" desc="Label on the protocol handler intent picker.">
-    Allow app to open <ph name="PROTOCOL_SCHEME">$1<ex>mailto</ex></ph> links?
-  </message>
-  </if>
-
-
     <!-- Font Access chooser -->
     <if expr="not is_android">
       <message name="IDS_FONT_ACCESS_CHOOSER_PROMPT_ORIGIN" desc="The label that is used to introduce Local Font Access chooser details to the user in a popup when it is from a website.">
diff --git a/chrome/app/generated_resources_grd/IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_MESSAGE.png.sha1
new file mode 100644
index 0000000..d639d562
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_MESSAGE.png.sha1
@@ -0,0 +1 @@
+01cbb28552efe1c5e32bd57f66534673587b8451
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_OK_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_OK_LABEL.png.sha1
new file mode 100644
index 0000000..d639d562
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_OK_LABEL.png.sha1
@@ -0,0 +1 @@
+01cbb28552efe1c5e32bd57f66534673587b8451
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 9c0d913..c7aa2d6 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -4137,28 +4137,25 @@
   <message name="IDS_OS_SETTINGS_DATA_ACCESS_PROTECTION_CONFIRM_DIALOG_DISABLE_BUTTON_LABEL" desc="The label of the confirmation button to disable data access protection">
     Disable
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_TITLE" desc="Text on the privacy page that opens up the smart privacy section." translateable="false">
-    Smart privacy protections
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_TITLE" desc="Text on the privacy page that opens up the smart privacy section.">
+    Screen privacy
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SUBTEXT" desc="The sub-label describing the purpose of the smart privacy section." translateable="false">
-    Smart display lock and detect if others peeking from behind
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_DESC" desc="The text explaining to the user the manner in which their presence is sensed and data usage policy.">
+    Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> uses a built-in sensor to detect people in front of your device. All data is processed on your device immediately and then deleted. Sensor data is never sent to Google. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_DESC" desc="The text explaining to the user the manner in which their presence is sensed and data usage policy." translateable="false">
-    This feature uses the front camera for detection. The data is securely processed and discarded afterwards. Google doesn't use your data for any other purposes.
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_TITLE" desc="The name of the quick dim feature shown as the label of a toggle in the smart privacy subpage.">
+    Lock on Leave
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_TITLE" desc="The name of the smart screen lock feature shown as the label of a toggle in the smart privacy subpage." translateable="false">
-    Smart screen lock
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_SUBTEXT" desc="Sub-label elaborating on the meaning of smart screen locking.">
+    If you move away from your device, your screen will lock automatically. When you're in front of your device, your screen will stay awake.
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_SUBTEXT" desc="Sub-label elaborating on the meaning of smart screen locking." translateable="false">
-    Screen locks automatically if you walk off, and won't turn off if you're in front of it
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_TITLE" desc="Text in the smart privacy section which is the subheader for snooping protection options.">
+    Viewing protection
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_TITLE" desc="Text in the smart privacy section which is the subheader for snooping protection options." translateable="false">
-    Snooping protection
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_SUBTEXT" desc="Sub-label elaborating on the meaning of snooping protection.">
+    When someone else looks at your screen, show the Privacy eye icon on the bottom right of your screen
   </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_SUBTEXT" desc="Sub-label elaborating on the meaning of snooping protection." translateable="false">
-    When someone else looks at your screen, show the Privacy 'eye' icon on the bottom right of your screen
-  </message>
-  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS" desc="Text for the name of the toggle in the smart privacy section requesting notification hiding when a snooper is detected." translateable="false">
+  <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS" desc="Text for the name of the toggle in the smart privacy section requesting notification hiding when a snooper is detected.">
     Hide notification content when someone else is detected
   </message>
   <message name="IDS_OS_SETTINGS_HW_DATA_USAGE_TOGGLE_TITLE" desc="The label of the checkbox to enable/disable device hardware data collection and usage in CloudReady 2.0.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_DESC.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_DESC.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_DESC.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_SUBTEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_SUBTEXT.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_SUBTEXT.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_TITLE.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_TITLE.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_SUBTEXT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_SUBTEXT.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_SUBTEXT.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_TITLE.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_TITLE.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_TITLE.png.sha1
new file mode 100644
index 0000000..cbbabe2
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_SMART_PRIVACY_TITLE.png.sha1
@@ -0,0 +1 @@
+24b763e83948c424b7bfdc46881da886778b47eb
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6f7ed95a..a9589437 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4630,6 +4630,8 @@
       "apps/app_service/webapk/webapk_prefs.h",
       "apps/icon_standardizer.cc",
       "apps/icon_standardizer.h",
+      "apps/intent_helper/supported_links_infobar_delegate.cc",
+      "apps/intent_helper/supported_links_infobar_delegate.h",
       "browser_process_platform_part_chromeos.cc",
       "browser_process_platform_part_chromeos.h",
       "component_updater/cros_component_installer_chromeos.cc",
diff --git a/chrome/browser/alternative_error_page_override_info_browsertest.cc b/chrome/browser/alternative_error_page_override_info_browsertest.cc
index 6692d69..cf225e6 100644
--- a/chrome/browser/alternative_error_page_override_info_browsertest.cc
+++ b/chrome/browser/alternative_error_page_override_info_browsertest.cc
@@ -16,27 +16,13 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
 #include "skia/ext/skia_utils_base.h"
-#include "third_party/blink/public/common/features.h"
-#include "ui/native_theme/native_theme.h"
 #include "url/gurl.h"
 
-namespace {
-const std::string kYellow = skia::SkColorToHexString(SK_ColorYELLOW);
-const std::string kGreen = skia::SkColorToHexString(SK_ColorGREEN);
-const std::string kRed = skia::SkColorToHexString(SK_ColorRED);
-const std::string kBlue = skia::SkColorToHexString(SK_ColorBLUE);
-const std::string kBlack = skia::SkColorToHexString(SK_ColorBLACK);
-const std::string kWhite = skia::SkColorToHexString(SK_ColorWHITE);
-}  // namespace
 // Class to test browser error page display info.
 class AlternativeErrorPageOverrideInfoBrowserTest
     : public InProcessBrowserTest {
  public:
-  AlternativeErrorPageOverrideInfoBrowserTest() {
-    feature_list_.InitWithFeatures({features::kDesktopPWAsDefaultOfflinePage,
-                                    blink::features::kWebAppEnableDarkMode},
-                                   {});
-  }
+  AlternativeErrorPageOverrideInfoBrowserTest() = default;
 
   // Helper function to prepare PWA and retrieve information from the
   // alternative error page function.
@@ -62,7 +48,9 @@
   void TearDownOnMainThread() override {
     InProcessBrowserTest::TearDownOnMainThread();
   }
-  base::test::ScopedFeatureList feature_list_;
+
+  base::test::ScopedFeatureList feature_list_{
+      features::kDesktopPWAsDefaultOfflinePage};
 };
 
 // Testing app manifest with no theme or background color.
@@ -75,9 +63,9 @@
   EXPECT_TRUE(info);
   EXPECT_EQ(*info->alternative_error_page_params.FindKey(
                 "customized_background_color"),
-            base::Value(kWhite));
+            base::Value(skia::SkColorToHexString(SK_ColorWHITE)));
   EXPECT_EQ(*info->alternative_error_page_params.FindKey("theme_color"),
-            base::Value(kBlack));
+            base::Value(skia::SkColorToHexString(SK_ColorBLACK)));
 }
 
 // Testing app manifest with theme color.
@@ -92,7 +80,7 @@
   EXPECT_TRUE(info);
   EXPECT_EQ(*info->alternative_error_page_params.FindKey(
                 "customized_background_color"),
-            base::Value(kWhite));
+            base::Value(skia::SkColorToHexString(SK_ColorWHITE)));
   EXPECT_EQ(
       *info->alternative_error_page_params.FindKey("theme_color"),
       base::Value(skia::SkColorToHexString(SkColorSetRGB(0xAA, 0xCC, 0xEE))));
@@ -110,9 +98,9 @@
   EXPECT_TRUE(info);
   EXPECT_EQ(*info->alternative_error_page_params.FindKey(
                 "customized_background_color"),
-            base::Value(kBlue));
+            base::Value(skia::SkColorToHexString(SK_ColorBLUE)));
   EXPECT_EQ(*info->alternative_error_page_params.FindKey("theme_color"),
-            base::Value(kBlack));
+            base::Value(skia::SkColorToHexString(SK_ColorBLACK)));
 }
 
 // Testing url outside the scope of an installed app.
@@ -209,44 +197,7 @@
   EXPECT_TRUE(info);
   EXPECT_EQ(*info->alternative_error_page_params.FindKey(
                 "customized_background_color"),
-            base::Value(kYellow));
+            base::Value(skia::SkColorToHexString(SK_ColorYELLOW)));
   EXPECT_EQ(*info->alternative_error_page_params.FindKey("theme_color"),
-            base::Value(kGreen));
-}
-
-// Testing app manifest with dark mode theme and background colors.
-IN_PROC_BROWSER_TEST_F(AlternativeErrorPageOverrideInfoBrowserTest,
-                       ManifestWithDarkModeThemeAndBackgroundColor) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(true);
-  content::mojom::AlternativeErrorPageOverrideInfoPtr info =
-      GetErrorPageInfo("/web_apps/get_manifest.html?color_scheme_dark.json");
-
-  // Expect mojom struct with dark mode theme color and dark mode background
-  // color.
-  EXPECT_TRUE(info);
-  EXPECT_EQ(*info->alternative_error_page_params.FindKey(
-                "dark_mode_background_color"),
-            base::Value(kRed));
-  EXPECT_EQ(
-      *info->alternative_error_page_params.FindKey("dark_mode_theme_color"),
-      base::Value(kRed));
-}
-
-// Testing app manifest with no dark mode theme or background color.
-IN_PROC_BROWSER_TEST_F(AlternativeErrorPageOverrideInfoBrowserTest,
-                       ManifestWithNoDarkModeThemeAndBackgroundColor) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-  ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(true);
-  content::mojom::AlternativeErrorPageOverrideInfoPtr info =
-      GetErrorPageInfo("/banners/no-sw-with-colors.html");
-
-  // Expect mojom struct light mode background and theme color stored.
-  EXPECT_TRUE(info);
-  EXPECT_EQ(*info->alternative_error_page_params.FindKey(
-                "dark_mode_background_color"),
-            base::Value(kYellow));
-  EXPECT_EQ(
-      *info->alternative_error_page_params.FindKey("dark_mode_theme_color"),
-      base::Value(kGreen));
+            base::Value(skia::SkColorToHexString(SK_ColorGREEN)));
 }
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 8f551bb..828e99c 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -517,13 +517,14 @@
 void ClientAndroid::OnPaymentsClientToken(
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
-    const JavaParamRef<jstring>& jclient_token) {
+    const JavaParamRef<jbyteArray>& jclient_token) {
   if (!fetch_payments_client_token_callback_) {
     return;
   }
-  std::move(fetch_payments_client_token_callback_)
-      .Run(ui_controller_android_utils::SafeConvertJavaStringToNative(
-          AttachCurrentThread(), jclient_token));
+  std::string client_token;
+  base::android::JavaByteArrayToString(AttachCurrentThread(), jclient_token,
+                                       &client_token);
+  std::move(fetch_payments_client_token_callback_).Run(client_token);
 }
 
 AccessTokenFetcher* ClientAndroid::GetAccessTokenFetcher() {
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index 7e4620b..146c816 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -82,7 +82,7 @@
   void OnPaymentsClientToken(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
-      const base::android::JavaParamRef<jstring>& jclient_token);
+      const base::android::JavaParamRef<jbyteArray>& jclient_token);
 
   void FetchWebsiteActions(
       JNIEnv* env,
diff --git a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
index cb071c8..2938774 100644
--- a/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
+++ b/chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_internal.h"
 #include "chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h"
+#include "chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
@@ -205,6 +206,14 @@
 
   if (app_type == PickerEntryType::kWeb) {
     web_app::ReparentWebContentsIntoAppBrowser(web_contents, launch_name);
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    // TODO(crbug.com/1293173): Lacros support for the infobar UI.
+    if (base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
+      SupportedLinksInfoBarDelegate::MaybeShowSupportedLinksInfoBar(
+          web_contents, launch_name);
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   } else {
     // TODO(crbug.com/853604): Distinguish the source from link and omnibox.
     mojom::LaunchSource launch_source = mojom::LaunchSource::kFromLink;
diff --git a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate.cc b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate.cc
new file mode 100644
index 0000000..855b167
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate.cc
@@ -0,0 +1,100 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h"
+
+#include <memory>
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/infobars/confirm_infobar_creator.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/infobars/content/content_infobar_manager.h"
+#include "components/infobars/core/infobar.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/vector_icons/vector_icons.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/vector_icon_types.h"
+
+namespace apps {
+
+// static
+void SupportedLinksInfoBarDelegate::MaybeShowSupportedLinksInfoBar(
+    content::WebContents* web_contents,
+    const std::string& app_id) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+
+  AppServiceProxy* proxy = AppServiceProxyFactory::GetForProfile(profile);
+  if (!proxy) {
+    return;
+  }
+
+  if (proxy->PreferredApps().IsPreferredAppForSupportedLinks(app_id)) {
+    return;
+  }
+
+  // TODO(crbug.com/1293173): Track the number of times the infobar has been
+  // shown per app, and do not show it once it has been ignored a particular
+  // number of times.
+
+  infobars::ContentInfoBarManager::FromWebContents(web_contents)
+      ->AddInfoBar(CreateConfirmInfoBar(
+          std::make_unique<SupportedLinksInfoBarDelegate>(profile, app_id)));
+}
+
+SupportedLinksInfoBarDelegate::SupportedLinksInfoBarDelegate(
+    Profile* profile,
+    const std::string& app_id)
+    : profile_(profile), app_id_(app_id) {}
+
+infobars::InfoBarDelegate::InfoBarIdentifier
+SupportedLinksInfoBarDelegate::GetIdentifier() const {
+  return infobars::InfoBarDelegate::InfoBarIdentifier::
+      SUPPORTED_LINKS_INFOBAR_DELEGATE_CHROMEOS;
+}
+
+std::u16string SupportedLinksInfoBarDelegate::GetMessageText() const {
+  std::string name;
+
+  AppServiceProxy* proxy = AppServiceProxyFactory::GetForProfile(profile_);
+  bool found = proxy->AppRegistryCache().ForOneApp(
+      app_id_, [&name](const AppUpdate& app) { name = app.Name(); });
+  DCHECK(found);
+
+  return l10n_util::GetStringFUTF16(
+      IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_MESSAGE,
+      base::UTF8ToUTF16(name));
+}
+
+std::u16string SupportedLinksInfoBarDelegate::GetButtonLabel(
+    InfoBarButton button) const {
+  if (button == BUTTON_OK) {
+    return l10n_util::GetStringUTF16(
+        IDR_INTENT_PICKER_SUPPORTED_LINKS_INFOBAR_OK_LABEL);
+  }
+  return l10n_util::GetStringUTF16(IDS_NO_THANKS);
+}
+
+const gfx::VectorIcon& SupportedLinksInfoBarDelegate::GetVectorIcon() const {
+  return vector_icons::kSettingsIcon;
+}
+
+bool SupportedLinksInfoBarDelegate::Accept() {
+  AppServiceProxy* proxy = AppServiceProxyFactory::GetForProfile(profile_);
+  proxy->SetSupportedLinksPreference(app_id_);
+  return true;
+}
+
+bool SupportedLinksInfoBarDelegate::Cancel() {
+  // TODO(crbug.com/1293173): Update preference so that the infobar is not shown
+  // again for this app.
+  return true;
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h
new file mode 100644
index 0000000..1d65af6b
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_INTENT_HELPER_SUPPORTED_LINKS_INFOBAR_DELEGATE_H_
+#define CHROME_BROWSER_APPS_INTENT_HELPER_SUPPORTED_LINKS_INFOBAR_DELEGATE_H_
+
+#include <string>
+
+#include "components/infobars/core/confirm_infobar_delegate.h"
+
+class Profile;
+
+namespace content {
+class WebContents;
+}
+
+namespace gfx {
+struct VectorIcon;
+}
+
+namespace apps {
+
+// An infobar delegate asking if the user wants to enable the Supported Links
+// setting for an app.
+class SupportedLinksInfoBarDelegate : public ConfirmInfoBarDelegate {
+ public:
+  explicit SupportedLinksInfoBarDelegate(Profile* profile,
+                                         const std::string& app_id);
+
+  SupportedLinksInfoBarDelegate(const SupportedLinksInfoBarDelegate&) = delete;
+  SupportedLinksInfoBarDelegate& operator=(
+      const SupportedLinksInfoBarDelegate&) = delete;
+
+  // Creates and shows a supported links infobar for the given |web_contents|.
+  // The infobar will only be created if it is suitable for the given |app_id|
+  // (e.g. the app does not already have the supported links setting enabled).
+  static void MaybeShowSupportedLinksInfoBar(content::WebContents* web_contents,
+                                             const std::string& app_id);
+
+ private:
+  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+  std::u16string GetMessageText() const override;
+  std::u16string GetButtonLabel(InfoBarButton button) const override;
+  const gfx::VectorIcon& GetVectorIcon() const override;
+
+  bool Accept() override;
+  bool Cancel() override;
+
+  Profile* profile_;
+  std::string app_id_;
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_INTENT_HELPER_SUPPORTED_LINKS_INFOBAR_DELEGATE_H_
diff --git a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
new file mode 100644
index 0000000..c46396b9
--- /dev/null
+++ b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
@@ -0,0 +1,70 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/intent_helper/supported_links_infobar_delegate.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
+#include "components/infobars/content/content_infobar_manager.h"
+#include "components/infobars/core/infobar.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+
+class SupportedLinksInfoBarDelegateBrowserTest
+    : public web_app::WebAppNavigationBrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    web_app::WebAppNavigationBrowserTest::SetUpOnMainThread();
+
+    InstallTestWebApp();
+  }
+
+  infobars::InfoBar* GetInfoBar(content::WebContents* contents) {
+    auto* manager = infobars::ContentInfoBarManager::FromWebContents(contents);
+
+    if (manager->infobar_count() != 1)
+      return nullptr;
+    return manager->infobar_at(0);
+  }
+
+  ConfirmInfoBarDelegate* GetDelegate(infobars::InfoBar* infobar) {
+    return infobar->delegate()->AsConfirmInfoBarDelegate();
+  }
+
+  apps::AppServiceProxy* app_service_proxy() {
+    return apps::AppServiceProxyFactory::GetForProfile(profile());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(SupportedLinksInfoBarDelegateBrowserTest,
+                       AcceptInfoBarChangesSupportedLinks) {
+  Browser* browser = OpenTestWebApp();
+  auto* contents = browser->tab_strip_model()->GetActiveWebContents();
+  apps::SupportedLinksInfoBarDelegate::MaybeShowSupportedLinksInfoBar(
+      contents, test_web_app_id());
+
+  auto* infobar = GetInfoBar(contents);
+  EXPECT_TRUE(infobar);
+  GetDelegate(infobar)->Accept();
+
+  app_service_proxy()->FlushMojoCallsForTesting();
+
+  ASSERT_TRUE(
+      app_service_proxy()->PreferredApps().IsPreferredAppForSupportedLinks(
+          test_web_app_id()));
+}
+
+IN_PROC_BROWSER_TEST_F(SupportedLinksInfoBarDelegateBrowserTest,
+                       InfoBarNotShownForPreferredApp) {
+  app_service_proxy()->SetSupportedLinksPreference(test_web_app_id());
+  app_service_proxy()->FlushMojoCallsForTesting();
+
+  Browser* browser = OpenTestWebApp();
+  auto* contents = browser->tab_strip_model()->GetActiveWebContents();
+  apps::SupportedLinksInfoBarDelegate::MaybeShowSupportedLinksInfoBar(
+      contents, test_web_app_id());
+
+  ASSERT_FALSE(GetInfoBar(contents));
+}
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
index 1614685..45ecb168 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
@@ -197,10 +197,14 @@
   }
 
   void OnAndroidVirtualKeyboardVisibilityChanged(bool visible) override {
+    is_virtual_keyboard_shown_ = visible;
     owner_->OnAndroidVirtualKeyboardVisibilityChanged(visible);
   }
 
+  bool is_virtual_keyboard_shown() const { return is_virtual_keyboard_shown_; }
+
  private:
+  bool is_virtual_keyboard_shown_ = false;
   base::ScopedObservation<ArcInputMethodManagerService,
                           ArcInputMethodManagerService::Observer>
       arc_imms_observation_{this};
@@ -370,18 +374,23 @@
     // notification_key before this receives an accessibility event for it.
     return GetFromKey(KeyForNotification(notification_key));
   } else if (event_data->is_input_method_window) {
+    if (!input_manager_service_observer_->is_virtual_keyboard_shown())
+      return nullptr;
+
     exo::InputMethodSurface* input_method_surface =
         exo::InputMethodSurface::GetInputMethodSurface();
     if (!input_method_surface)
       return nullptr;
 
     auto key = KeyForInputMethod();
-    if (GetFromKey(key) == nullptr) {
-      auto* tree = CreateFromKey(key, input_method_surface->host_window());
+    auto* tree = GetFromKey(key);
+    if (!tree) {
+      tree = CreateFromKey(key, input_method_surface->host_window());
       input_method_surface->SetChildAxTreeId(tree->ax_tree_id());
     }
+    DCHECK(tree->window() == input_method_surface->host_window());
 
-    return GetFromKey(key);
+    return tree;
   } else {
     int task_id;
     if (event_data->task_id != kNoTaskId) {
@@ -479,6 +488,10 @@
 
 void ArcAccessibilityTreeTracker::OnAndroidVirtualKeyboardVisibilityChanged(
     bool visible) {
+  // The lifetime of AXTreeSourceArc should be bounded by the corresponding exo
+  // window. Always using OnWindowDestroying is ideal.
+  // But it seems that OnWindowDestroying sometimes not called when visually VK
+  // is made invisible. We're using this callback here to destroy the tree.
   if (!visible)
     trees_.erase(KeyForInputMethod());
 }
@@ -571,8 +584,8 @@
     TreeKey key,
     aura::Window* window) {
   auto tree = std::make_unique<AXTreeSourceArc>(tree_source_delegate_, window);
-  AXTreeSourceArc* tree_ptr = tree.get();
-  trees_.insert(std::make_pair(std::move(key), std::move(tree)));
+  auto [itr, inserted] = trees_.try_emplace(std::move(key), std::move(tree));
+  DCHECK(inserted);
 
   if (ash::AccessibilityManager::Get() &&
       ash::AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
@@ -580,7 +593,7 @@
     // compare this with TalkBack usage.
     base::UmaHistogramBoolean("Arc.AccessibilityWithTalkBack", false);
   }
-  return tree_ptr;
+  return itr->second.get();
 }
 
 void ArcAccessibilityTreeTracker::InvalidateTrees() {
diff --git a/chrome/browser/ash/arc/window_predictor/window_predictor.cc b/chrome/browser/ash/arc/window_predictor/window_predictor.cc
index 7ae35b09..778b6db 100644
--- a/chrome/browser/ash/arc/window_predictor/window_predictor.cc
+++ b/chrome/browser/ash/arc/window_predictor/window_predictor.cc
@@ -26,18 +26,12 @@
 // TODO(sstan): Replace by calculating from real display scale factor.
 constexpr float kArcUniformScaleFactor = 1.2;
 
-gfx::Size GetPhoneSize(bool is_disp_landscape) {
-  auto result = kDefaultPortraitPhoneSize;
-  if (is_disp_landscape)
-    result.Transpose();
-  return ScaleToCeiledSize(result, kArcUniformScaleFactor);
+gfx::Size GetPhoneSize() {
+  return ScaleToCeiledSize(kDefaultPortraitPhoneSize, kArcUniformScaleFactor);
 }
 
-gfx::Size GetTabletSize(bool is_disp_landscape) {
-  auto result = kDefaultLandscapeTabletSize;
-  if (!is_disp_landscape)
-    result.Transpose();
-  return ScaleToCeiledSize(result, kArcUniformScaleFactor);
+gfx::Size GetTabletSize() {
+  return ScaleToCeiledSize(kDefaultLandscapeTabletSize, kArcUniformScaleFactor);
 }
 
 // Get window bounds in the middle of a display in global coordinate.
@@ -97,16 +91,14 @@
     case arc::mojom::WindowSizeType::kTabletSize:
       window_info->state =
           static_cast<int32_t>(chromeos::WindowStateType::kNormal);
-      window_info->bounds =
-          GetMiddleBounds(disp, GetTabletSize(disp.is_landscape()));
+      window_info->bounds = GetMiddleBounds(disp, GetTabletSize());
       break;
     case arc::mojom::WindowSizeType::kPhoneSize:
     case arc::mojom::WindowSizeType::kUnknown:
     default:
       window_info->state =
           static_cast<int32_t>(chromeos::WindowStateType::kNormal);
-      window_info->bounds =
-          GetMiddleBounds(disp, GetPhoneSize(disp.is_landscape()));
+      window_info->bounds = GetMiddleBounds(disp, GetPhoneSize());
   }
 
   return window_info;
diff --git a/chrome/browser/ash/crosapi/dlp_ash.cc b/chrome/browser/ash/crosapi/dlp_ash.cc
index c104bda..954da91 100644
--- a/chrome/browser/ash/crosapi/dlp_ash.cc
+++ b/chrome/browser/ash/crosapi/dlp_ash.cc
@@ -133,7 +133,7 @@
   policy::DlpContentManagerAsh* dlp_content_manager =
       policy::DlpContentManagerAsh::Get();
   DCHECK(dlp_content_manager);
-  dlp_content_manager->OnScreenCaptureStarted(
+  dlp_content_manager->OnScreenShareStarted(
       label, {media_id}, application_title, std::move(stop_callback),
       std::move(state_change_callback));
 }
@@ -148,7 +148,7 @@
   policy::DlpContentManagerAsh* dlp_content_manager =
       policy::DlpContentManagerAsh::Get();
   DCHECK(dlp_content_manager);
-  dlp_content_manager->OnScreenCaptureStopped(label, media_id);
+  dlp_content_manager->OnScreenShareStopped(label, media_id);
 }
 
 void DlpAsh::ChangeScreenShareState(
diff --git a/chrome/browser/ash/crosapi/dlp_ash_unittest.cc b/chrome/browser/ash/crosapi/dlp_ash_unittest.cc
index d45297e..8c07aec9 100644
--- a/chrome/browser/ash/crosapi/dlp_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/dlp_ash_unittest.cc
@@ -119,7 +119,7 @@
   content::MediaStreamUI::StateChangeCallback state_change_callback;
   content::DesktopMediaID media_id;
 
-  EXPECT_CALL(mock_dlp_content_manager, OnScreenCaptureStarted)
+  EXPECT_CALL(mock_dlp_content_manager, OnScreenShareStarted)
       .WillOnce(
           [&](const std::string& label,
               std::vector<content::DesktopMediaID> ids,
@@ -175,7 +175,7 @@
   policy::ScopedDlpContentManagerAshForTesting scoped_dlp_content_manager(
       &mock_dlp_content_manager);
 
-  EXPECT_CALL(mock_dlp_content_manager, OnScreenCaptureStopped)
+  EXPECT_CALL(mock_dlp_content_manager, OnScreenShareStopped)
       .WillOnce([&](const std::string& label,
                     const content::DesktopMediaID& media_id) {
         EXPECT_EQ(kScreenShareLabel, label);
diff --git a/chrome/browser/ash/input_method/ui/input_method_menu_item.cc b/chrome/browser/ash/input_method/ui/input_method_menu_item.cc
index 9a20680..5e391b4 100644
--- a/chrome/browser/ash/input_method/ui/input_method_menu_item.cc
+++ b/chrome/browser/ash/input_method/ui/input_method_menu_item.cc
@@ -27,16 +27,6 @@
 
 InputMethodMenuItem::~InputMethodMenuItem() {}
 
-bool InputMethodMenuItem::operator==(const InputMethodMenuItem& other) const {
-  return key == other.key && label == other.label &&
-         is_selection_item == other.is_selection_item &&
-         is_selection_item_checked == other.is_selection_item_checked;
-}
-
-bool InputMethodMenuItem::operator!=(const InputMethodMenuItem& other) const {
-  return !(*this == other);
-}
-
 std::string InputMethodMenuItem::ToString() const {
   std::stringstream stream;
   stream << "key=" << key << ", label=" << label
diff --git a/chrome/browser/ash/input_method/ui/input_method_menu_item.h b/chrome/browser/ash/input_method/ui/input_method_menu_item.h
index 2061702..6bbce825 100644
--- a/chrome/browser/ash/input_method/ui/input_method_menu_item.h
+++ b/chrome/browser/ash/input_method/ui/input_method_menu_item.h
@@ -22,9 +22,6 @@
   InputMethodMenuItem();
   ~InputMethodMenuItem();
 
-  bool operator==(const InputMethodMenuItem& other) const;
-  bool operator!=(const InputMethodMenuItem& other) const;
-
   // Debug print function.
   std::string ToString() const;
 
diff --git a/chrome/browser/ash/input_method/ui/input_method_menu_item_unittest.cc b/chrome/browser/ash/input_method/ui/input_method_menu_item_unittest.cc
index 9b861b4..5654205 100644
--- a/chrome/browser/ash/input_method/ui/input_method_menu_item_unittest.cc
+++ b/chrome/browser/ash/input_method/ui/input_method_menu_item_unittest.cc
@@ -9,23 +9,5 @@
 namespace ui {
 namespace ime {
 
-TEST(InputMethodMenuItemTest, TestOperatorEqual) {
-  InputMethodMenuItem empty;
-  InputMethodMenuItem reference("key", "label", true, true);
-
-  InputMethodMenuItem p1("X", "label", true, true);
-  InputMethodMenuItem p2("key", "X", true, true);
-  InputMethodMenuItem p3("key", "label", false, true);
-  InputMethodMenuItem p4("key", "label", true, false);
-
-  EXPECT_EQ(empty, empty);
-  EXPECT_EQ(reference, reference);
-  EXPECT_NE(reference, empty);
-  EXPECT_NE(reference, p1);
-  EXPECT_NE(reference, p2);
-  EXPECT_NE(reference, p3);
-  EXPECT_NE(reference, p4);
-}
-
 }  // namespace ime
 }  // namespace ui
diff --git a/chrome/browser/ash/login/ui/fake_login_display_host.cc b/chrome/browser/ash/login/ui/fake_login_display_host.cc
index b68f016..f55e830 100644
--- a/chrome/browser/ash/login/ui/fake_login_display_host.cc
+++ b/chrome/browser/ash/login/ui/fake_login_display_host.cc
@@ -48,6 +48,10 @@
   return nullptr;
 }
 
+views::Widget* FakeLoginDisplayHost::GetLoginWindowWidget() const {
+  return nullptr;
+}
+
 OobeUI* FakeLoginDisplayHost::GetOobeUI() const {
   return nullptr;
 }
diff --git a/chrome/browser/ash/login/ui/fake_login_display_host.h b/chrome/browser/ash/login/ui/fake_login_display_host.h
index 6c429e6..9544a0ff 100644
--- a/chrome/browser/ash/login/ui/fake_login_display_host.h
+++ b/chrome/browser/ash/login/ui/fake_login_display_host.h
@@ -31,6 +31,7 @@
   LoginDisplay* GetLoginDisplay() override;
   ExistingUserController* GetExistingUserController() override;
   gfx::NativeWindow GetNativeWindow() const override;
+  views::Widget* GetLoginWindowWidget() const override;
   OobeUI* GetOobeUI() const override;
   content::WebContents* GetOobeWebContents() const override;
   WebUILoginView* GetWebUILoginView() const override;
diff --git a/chrome/browser/ash/login/ui/login_display_host.h b/chrome/browser/ash/login/ui/login_display_host.h
index 1a56330..d8f5db0 100644
--- a/chrome/browser/ash/login/ui/login_display_host.h
+++ b/chrome/browser/ash/login/ui/login_display_host.h
@@ -84,6 +84,9 @@
   // Returns corresponding native window.
   virtual gfx::NativeWindow GetNativeWindow() const = 0;
 
+  // Returns the current login window widget.
+  virtual views::Widget* GetLoginWindowWidget() const = 0;
+
   // Returns instance of the OOBE WebUI.
   virtual OobeUI* GetOobeUI() const = 0;
 
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index 9d5f6a079..2e01898 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -247,6 +247,10 @@
   return dialog_->GetNativeWindow();
 }
 
+views::Widget* LoginDisplayHostMojo::GetLoginWindowWidget() const {
+  return dialog_ ? dialog_->GetWebDialogWidget() : nullptr;
+}
+
 OobeUI* LoginDisplayHostMojo::GetOobeUI() const {
   if (!dialog_)
     return nullptr;
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.h b/chrome/browser/ash/login/ui/login_display_host_mojo.h
index 182bc38..3df5f4d 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.h
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.h
@@ -73,6 +73,7 @@
   LoginDisplay* GetLoginDisplay() override;
   ExistingUserController* GetExistingUserController() override;
   gfx::NativeWindow GetNativeWindow() const override;
+  views::Widget* GetLoginWindowWidget() const override;
   OobeUI* GetOobeUI() const override;
   content::WebContents* GetOobeWebContents() const override;
   WebUILoginView* GetWebUILoginView() const override;
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.cc b/chrome/browser/ash/login/ui/login_display_host_webui.cc
index 50b5a5c4b..60e7b17 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_webui.cc
@@ -490,6 +490,10 @@
   return login_window_ ? login_window_->GetNativeWindow() : nullptr;
 }
 
+views::Widget* LoginDisplayHostWebUI::GetLoginWindowWidget() const {
+  return login_window_;
+}
+
 WebUILoginView* LoginDisplayHostWebUI::GetWebUILoginView() const {
   return login_view_;
 }
diff --git a/chrome/browser/ash/login/ui/login_display_host_webui.h b/chrome/browser/ash/login/ui/login_display_host_webui.h
index 4ee48ec1..3f9345e7 100644
--- a/chrome/browser/ash/login/ui/login_display_host_webui.h
+++ b/chrome/browser/ash/login/ui/login_display_host_webui.h
@@ -69,6 +69,7 @@
   LoginDisplay* GetLoginDisplay() override;
   ExistingUserController* GetExistingUserController() override;
   gfx::NativeWindow GetNativeWindow() const override;
+  views::Widget* GetLoginWindowWidget() const override;
   OobeUI* GetOobeUI() const override;
   content::WebContents* GetOobeWebContents() const override;
   WebUILoginView* GetWebUILoginView() const override;
diff --git a/chrome/browser/ash/login/ui/mock_login_display_host.h b/chrome/browser/ash/login/ui/mock_login_display_host.h
index 1c184f5..0d534176 100644
--- a/chrome/browser/ash/login/ui/mock_login_display_host.h
+++ b/chrome/browser/ash/login/ui/mock_login_display_host.h
@@ -34,6 +34,7 @@
               (),
               (override));
   MOCK_METHOD(gfx::NativeWindow, GetNativeWindow, (), (const, override));
+  MOCK_METHOD(views::Widget*, GetLoginWindowWidget, (), (const, override));
   MOCK_METHOD(OobeUI*, GetOobeUI, (), (const, override));
   MOCK_METHOD(content::WebContents*, GetOobeWebContents, (), (const, override));
   MOCK_METHOD(WebUILoginView*, GetWebUILoginView, (), (const, override));
diff --git a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
index c2604dc..dc87de6cd 100644
--- a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.cc
@@ -340,6 +340,10 @@
   return widget_ ? widget_->GetNativeWindow() : nullptr;
 }
 
+views::Widget* OobeUIDialogDelegate::GetWebDialogWidget() const {
+  return widget_;
+}
+
 views::View* OobeUIDialogDelegate::GetWebDialogView() {
   return dialog_view_;
 }
diff --git a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h
index d9f656f..c265392 100644
--- a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h
@@ -88,6 +88,8 @@
   OobeUI* GetOobeUI() const;
   gfx::NativeWindow GetNativeWindow() const;
 
+  views::Widget* GetWebDialogWidget() const;
+
   views::View* GetWebDialogView();
 
   CaptivePortalDialogDelegate* captive_portal_delegate_for_test() {
diff --git a/chrome/browser/ash/login/users/affiliation.cc b/chrome/browser/ash/login/users/affiliation.cc
index 8052fe8..8fbfe10 100644
--- a/chrome/browser/ash/login/users/affiliation.cc
+++ b/chrome/browser/ash/login/users/affiliation.cc
@@ -6,14 +6,12 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/command_line.h"
 #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
 #include "chrome/browser/ash/policy/core/device_local_account.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "components/account_id/account_id.h"
-#include "components/policy/core/common/policy_switches.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 
@@ -73,14 +71,6 @@
     return true;
   }
 
-  // Not all test servers correctly support affiliation ids so far, so
-  // this is a work-around.
-  // TODO(antrim): remove this once all test servers support affiliation ids.
-  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(policy::switches::kUserAlwaysAffiliated)) {
-    return true;
-  }
-
   if (!device_affiliation_ids.empty() && !user_affiliation_ids.empty()) {
     return HaveCommonElement(user_affiliation_ids, device_affiliation_ids);
   }
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
index 600928a..773023d 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.cc
@@ -88,13 +88,6 @@
   MaybeChangeOnScreenRestrictions();
 }
 
-DlpContentRestrictionSet DlpContentManagerAsh::GetConfidentialRestrictions(
-    content::WebContents* web_contents) const {
-  if (!base::Contains(confidential_web_contents_, web_contents))
-    return DlpContentRestrictionSet();
-  return confidential_web_contents_.at(web_contents);
-}
-
 DlpContentRestrictionSet DlpContentManagerAsh::GetOnScreenPresentRestrictions()
     const {
   return on_screen_restrictions_;
@@ -114,17 +107,6 @@
   CheckScreenCaptureRestriction(info, std::move(callback));
 }
 
-bool DlpContentManagerAsh::IsScreenCaptureRestricted(
-    const content::DesktopMediaID& media_id) {
-  const ConfidentialContentsInfo info =
-      GetScreenShareConfidentialContentsInfo(media_id);
-  MaybeReportEvent(info.restriction_info,
-                   DlpRulesManager::Restriction::kScreenShare);
-  DlpBooleanHistogram(dlp::kScreenShareBlockedUMA,
-                      IsBlocked(info.restriction_info));
-  return IsBlocked(info.restriction_info);
-}
-
 void DlpContentManagerAsh::CheckScreenShareRestriction(
     const content::DesktopMediaID& media_id,
     const std::u16string& application_title,
@@ -227,22 +209,22 @@
   CheckScreenCaptureRestriction(info, std::move(callback));
 }
 
-void DlpContentManagerAsh::OnScreenCaptureStarted(
+void DlpContentManagerAsh::OnScreenShareStarted(
     const std::string& label,
-    std::vector<content::DesktopMediaID> screen_capture_ids,
+    std::vector<content::DesktopMediaID> screen_share_ids,
     const std::u16string& application_title,
     base::RepeatingClosure stop_callback,
     content::MediaStreamUI::StateChangeCallback state_change_callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  for (const content::DesktopMediaID& id : screen_capture_ids) {
+  for (const content::DesktopMediaID& id : screen_share_ids) {
     AddScreenShare(label, id, application_title, stop_callback,
                    state_change_callback);
   }
   CheckRunningScreenShares();
 }
 
-void DlpContentManagerAsh::OnScreenCaptureStopped(
+void DlpContentManagerAsh::OnScreenShareStopped(
     const std::string& label,
     const content::DesktopMediaID& media_id) {
   RemoveScreenShare(label, media_id);
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
index 8dc65ea4..2537507 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h
@@ -53,11 +53,6 @@
   void OnWindowOcclusionChanged(aura::Window* window) override;
   void OnWindowDestroying(aura::Window* window) override;
 
-  // Returns which restrictions are applied to the |web_contents| according to
-  // the policy.
-  DlpContentRestrictionSet GetConfidentialRestrictions(
-      content::WebContents* web_contents) const;
-
   // Returns which restrictions are applied to the WebContents which are
   // currently visible.
   DlpContentRestrictionSet GetOnScreenPresentRestrictions() const;
@@ -69,11 +64,6 @@
       const ScreenshotArea& area,
       ash::OnCaptureModeDlpRestrictionChecked callback);
 
-  // Returns whether screen capture of the defined content should be restricted.
-  // TODO(crbug.com/1257493): Remove when it won't be used anymore.
-  virtual bool IsScreenCaptureRestricted(
-      const content::DesktopMediaID& media_id);
-
   // Called when video capturing for |area| is started.
   void OnVideoCaptureStarted(const ScreenshotArea& area);
 
@@ -97,15 +87,15 @@
       const content::DesktopMediaID& media_id,
       const std::u16string& application_title,
       OnDlpRestrictionCheckedCallback callback) override;
-  void OnScreenCaptureStarted(
+  void OnScreenShareStarted(
       const std::string& label,
-      std::vector<content::DesktopMediaID> screen_capture_ids,
+      std::vector<content::DesktopMediaID> screen_share_ids,
       const std::u16string& application_title,
       base::RepeatingClosure stop_callback,
       content::MediaStreamUI::StateChangeCallback state_change_callback)
       override;
-  void OnScreenCaptureStopped(const std::string& label,
-                              const content::DesktopMediaID& media_id) override;
+  void OnScreenShareStopped(const std::string& label,
+                            const content::DesktopMediaID& media_id) override;
 
   // Called when an updated restrictions are received for Lacros window.
   void OnWindowRestrictionChanged(aura::Window* window,
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
index 850b0b90..6b9e477 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
@@ -786,7 +786,7 @@
       browser()->window()->GetNativeWindow()->GetRootWindow();
   const auto media_id = content::DesktopMediaID::RegisterNativeWindow(
       content::DesktopMediaID::TYPE_SCREEN, root_window);
-  manager->OnScreenCaptureStarted(
+  manager->OnScreenShareStarted(
       kLabel, {media_id}, kApplicationTitle, base::BindRepeating([]() {
         FAIL() << "Stop callback should not be called.";
       }),
@@ -825,7 +825,7 @@
   histogram_tester_.ExpectBucketCount(
       GetDlpHistogramPrefix() + dlp::kScreenSharePausedOrResumedUMA, false, 1);
 
-  manager->OnScreenCaptureStopped(kLabel, media_id);
+  manager->OnScreenShareStopped(kLabel, media_id);
 
   EXPECT_FALSE(
       display_service_tester.GetNotification(kScreenSharePausedNotificationId));
@@ -902,8 +902,8 @@
               Run(testing::_, blink::mojom::MediaStreamStateChange::PLAY))
       .Times(1);
 
-  manager->OnScreenCaptureStarted(kLabel, {media_id}, kApplicationTitle,
-                                  stop_cb.Get(), state_change_cb.Get());
+  manager->OnScreenShareStarted(kLabel, {media_id}, kApplicationTitle,
+                                stop_cb.Get(), state_change_cb.Get());
 
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1);
@@ -958,8 +958,8 @@
       .Times(1);
   EXPECT_CALL(stop_cb, Run()).Times(1);
 
-  manager->OnScreenCaptureStarted(kLabel, {media_id}, kApplicationTitle,
-                                  stop_cb.Get(), state_change_cb.Get());
+  manager->OnScreenShareStarted(kLabel, {media_id}, kApplicationTitle,
+                                stop_cb.Get(), state_change_cb.Get());
 
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1);
@@ -1006,8 +1006,8 @@
               Run(testing::_, blink::mojom::MediaStreamStateChange::PLAY))
       .Times(1);
 
-  manager->OnScreenCaptureStarted(kLabel, {media_id}, kApplicationTitle,
-                                  stop_cb.Get(), state_change_cb.Get());
+  manager->OnScreenShareStarted(kLabel, {media_id}, kApplicationTitle,
+                                stop_cb.Get(), state_change_cb.Get());
 
   manager->OnWindowRestrictionChanged(browser()->window()->GetNativeWindow(),
                                       kScreenShareWarned);
diff --git a/chrome/browser/ash/policy/dlp/mock_dlp_content_manager_ash.h b/chrome/browser/ash/policy/dlp/mock_dlp_content_manager_ash.h
index 10bae3b..6fe557c 100644
--- a/chrome/browser/ash/policy/dlp/mock_dlp_content_manager_ash.h
+++ b/chrome/browser/ash/policy/dlp/mock_dlp_content_manager_ash.h
@@ -26,10 +26,6 @@
               (const GURL&),
               (const));
   MOCK_METHOD(void, OnVisibilityChanged, (content::WebContents*));
-  MOCK_METHOD(bool,
-              IsScreenCaptureRestricted,
-              (const content::DesktopMediaID& media_id),
-              (override));
   MOCK_METHOD(void,
               CheckScreenShareRestriction,
               (const content::DesktopMediaID& media_id,
@@ -37,7 +33,7 @@
                OnDlpRestrictionCheckedCallback callback),
               (override));
   MOCK_METHOD(void,
-              OnScreenCaptureStarted,
+              OnScreenShareStarted,
               (const std::string&,
                std::vector<content::DesktopMediaID>,
                const std::u16string&,
@@ -45,7 +41,7 @@
                content::MediaStreamUI::StateChangeCallback),
               (override));
   MOCK_METHOD(void,
-              OnScreenCaptureStopped,
+              OnScreenShareStopped,
               (const std::string&, const content::DesktopMediaID&),
               (override));
 
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
index 6aa6b3ea..c31d8e1 100644
--- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -9,6 +9,7 @@
 #include "ash/webui/media_app_ui/test/media_app_ui_browsertest.h"
 #include "ash/webui/media_app_ui/url_constants.h"
 #include "base/containers/cxx20_erase_vector.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/path_service.h"
@@ -192,6 +193,23 @@
   return path;
 }
 
+std::string FindAnyTTF() {
+  const base::FilePath root_path(FILE_PATH_LITERAL("/usr/share/fonts"));
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::FileEnumerator enumerator(
+      root_path, true, base::FileEnumerator::FILES, FILE_PATH_LITERAL("*.ttf"),
+      base::FileEnumerator::FolderSearchPolicy::ALL,
+      base::FileEnumerator::ErrorPolicy::IGNORE_ERRORS);
+  const base::FilePath candidate = enumerator.Next();
+  std::vector<std::string> components;
+  candidate.GetComponents(&components);
+  if (components.size() < 5) {
+    return {};
+  }
+  std::vector<std::string> slice(components.begin() + 4, components.end());
+  return base::JoinString(slice, "/");
+}
+
 void PrepareAppForTest(content::WebContents* web_ui) {
   EXPECT_TRUE(WaitForLoadStop(web_ui));
   EXPECT_EQ(nullptr, MediaAppUiBrowserTest::EvalJsInAppFrame(
@@ -1251,6 +1269,47 @@
   EXPECT_FALSE(app_browser->window()->IsFullscreen());
 }
 
+IN_PROC_BROWSER_TEST_P(MediaAppIntegrationTest, GuestCanReadLocalFonts) {
+  // For this test, we first need to find a valid font to request from
+  // /usr/share/fonts. build/linux/install-chromeos-fonts.py installs some known
+  // fonts into /usr/*local*/share/fonts, so that's no good. A set of known font
+  // files *should* be available on any machine, but the subdirectory varies.
+  // E.g. NotoSans-Regular.ttf exists in fonts/truetype/noto/ on some machines,
+  // but it has a different parent folder on others.
+  //
+  // For a robust test, poke around on disk and pick the first non-zero .ttf
+  // file to deliver.
+  //
+  // Note that although the path differs across bots, it will be consistent on
+  // the ChromeOS image.
+  const std::string font_to_try = FindAnyTTF();
+  DLOG(INFO) << "Found: " << font_to_try;
+
+  constexpr char kFetchTestFont[] = R"(
+      (async function fetchTestFont() {
+        try {
+          const response = await fetch('/fonts/$1');
+          const blob = await response.blob();
+
+          if (response.status === 200 && blob.size > 0) {
+            domAutomationController.send('success');
+          } else {
+            domAutomationController.send(
+                `Failed: status:$${response.status} size:$${blob.size}`);
+          }
+        } catch (e) {
+          domAutomationController.send(`Failed: $${e}`);
+        }
+      })();
+  )";
+  const std::string script =
+      base::ReplaceStringPlaceholders(kFetchTestFont, {font_to_try}, nullptr);
+
+  WaitForTestSystemAppInstall();
+  content::WebContents* web_ui = LaunchApp(web_app::SystemAppType::MEDIA);
+  EXPECT_EQ("success", ExtractStringInGlobalScope(web_ui, script));
+}
+
 INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
     MediaAppIntegrationAudioEnabledTest);
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 36c3bb7..bbb6802 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -3412,6 +3412,7 @@
     "policy/dlp/dlp_confidential_contents.h",
     "policy/dlp/dlp_content_manager.cc",
     "policy/dlp/dlp_content_manager.h",
+    "policy/dlp/dlp_content_manager_observer.h",
     "policy/dlp/dlp_content_observer.cc",
     "policy/dlp/dlp_content_observer.h",
     "policy/dlp/dlp_content_restriction_set.cc",
diff --git a/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc b/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc
index ead6b7fd..4dab135 100644
--- a/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc
+++ b/chrome/browser/chromeos/policy/dlp/clipboard_bubble.cc
@@ -117,8 +117,8 @@
 
 void OnLearnMoreLinkClicked() {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  ash::NewWindowDelegate::GetInstance()->OpenUrl(
-      GURL(kDlpLearnMoreUrl), /*from_user_interaction=*/true);
+  ash::NewWindowDelegate::GetPrimary()->OpenUrl(GURL(kDlpLearnMoreUrl),
+                                                /*from_user_interaction=*/true);
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
   // TODO(hidehiko): Instantiating BrowserServiceLacros here is an unexpected
   // use case. Get rid of this by replacing with Navigate() API invocation
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
index e730b6bc6..a2c83c50a 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notifier.cc
@@ -94,8 +94,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void OnToastClicked() {
-  ash::NewWindowDelegate::GetInstance()->OpenUrl(
-      GURL(kDlpLearnMoreUrl), /*from_user_interaction=*/true);
+  ash::NewWindowDelegate::GetPrimary()->OpenUrl(GURL(kDlpLearnMoreUrl),
+                                                /*from_user_interaction=*/true);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
index 77575b4c..cc0a7c9 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -11,9 +11,11 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/check.h"
+#include "base/check_op.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_confidential_contents.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager_observer.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_notification_helper.h"
@@ -78,6 +80,13 @@
   return confidential_web_contents_.at(web_contents);
 }
 
+bool DlpContentManager::IsScreenShareBlocked(
+    content::WebContents* web_contents) const {
+  return IsBlocked(GetConfidentialRestrictions(web_contents)
+                       .GetRestrictionLevelAndUrl(
+                           policy::DlpContentRestriction::kScreenShare));
+}
+
 void DlpContentManager::CheckPrintingRestriction(
     content::WebContents* web_contents,
     OnDlpRestrictionCheckedCallback callback) {
@@ -202,21 +211,21 @@
 }
 
 void DlpContentManager::ScreenShareInfo::Pause() {
-  DCHECK(state_ == State::kRunning);
+  DCHECK_EQ(state_, State::kRunning);
   state_change_callback_.Run(media_id_,
                              blink::mojom::MediaStreamStateChange::PAUSE);
   state_ = State::kPaused;
 }
 
 void DlpContentManager::ScreenShareInfo::Resume() {
-  DCHECK(state_ == State::kPaused);
+  DCHECK_EQ(state_, State::kPaused);
   state_change_callback_.Run(media_id_,
                              blink::mojom::MediaStreamStateChange::PLAY);
   state_ = State::kRunning;
 }
 
 void DlpContentManager::ScreenShareInfo::Stop() {
-  DCHECK(state_ != State::kStopped);
+  DCHECK_NE(state_, State::kStopped);
   if (stop_callback_) {
     std::move(stop_callback_).Run();
     state_ = State::kStopped;
@@ -245,7 +254,7 @@
       show)
     return;
   if (show) {
-    DCHECK(state_ == State::kPaused);
+    DCHECK_EQ(state_, State::kPaused);
     ShowDlpScreenSharePausedNotification(label_, application_title_);
     notification_state_ = NotificationState::kShowingPausedNotification;
   } else {
@@ -259,7 +268,7 @@
       show)
     return;
   if (show) {
-    DCHECK(state_ == State::kRunning);
+    DCHECK_EQ(state_, State::kRunning);
     ShowDlpScreenShareResumedNotification(label_, application_title_);
     notification_state_ = NotificationState::kShowingResumedNotification;
   } else {
@@ -268,6 +277,17 @@
   }
 }
 
+void DlpContentManager::AddObserver(DlpContentManagerObserver* observer,
+                                    DlpContentRestriction restriction) {
+  observer_lists_[restriction].AddObserver(observer);
+}
+
+void DlpContentManager::RemoveObserver(
+    const DlpContentManagerObserver* observer,
+    DlpContentRestriction restriction) {
+  observer_lists_[restriction].RemoveObserver(observer);
+}
+
 DlpContentManager::DlpContentManager() = default;
 DlpContentManager::~DlpContentManager() = default;
 
@@ -312,11 +332,17 @@
 void DlpContentManager::OnConfidentialityChanged(
     content::WebContents* web_contents,
     const DlpContentRestrictionSet& restriction_set) {
+  DlpContentRestrictionSet old_restriction_set;
+  if (confidential_web_contents_.contains(web_contents)) {
+    old_restriction_set = confidential_web_contents_[web_contents];
+  }
   if (restriction_set.IsEmpty()) {
     RemoveFromConfidential(web_contents);
   } else {
     confidential_web_contents_[web_contents] = restriction_set;
   }
+  NotifyOnConfidentialityChanged(old_restriction_set, restriction_set,
+                                 web_contents);
 }
 
 void DlpContentManager::OnWebContentsDestroyed(
@@ -558,4 +584,24 @@
       });
 }
 
+void DlpContentManager::NotifyOnConfidentialityChanged(
+    const DlpContentRestrictionSet& old_restriction_set,
+    const DlpContentRestrictionSet& new_restriction_set,
+    content::WebContents* web_contents) {
+  for (int i = 0; i <= DlpContentRestriction::kMaxValue; ++i) {
+    auto restriction = static_cast<DlpContentRestriction>(i);
+    auto old_level = old_restriction_set.GetRestrictionLevel(restriction);
+    auto new_level = new_restriction_set.GetRestrictionLevel(restriction);
+    if (old_level == new_level) {
+      // If there is no change in this restriction, do not notify its
+      // observers.
+      continue;
+    }
+    auto& observer_list = observer_lists_[restriction];
+    for (DlpContentManagerObserver& observer : observer_list) {
+      observer.OnConfidentialityChanged(old_level, new_level, web_contents);
+    }
+  }
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
index a7bbb10..b7fee162 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
@@ -11,7 +11,9 @@
 
 #include "base/callback.h"
 #include "base/containers/flat_map.h"
+#include "base/observer_list.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_confidential_contents.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager_observer.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_observer.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
@@ -49,6 +51,10 @@
   DlpContentRestrictionSet GetConfidentialRestrictions(
       content::WebContents* web_contents) const;
 
+  // Returns whether screenshare should be blocked for the specified
+  // WebContents.
+  bool IsScreenShareBlocked(content::WebContents* web_contents) const;
+
   // Checks whether printing of |web_contents| is restricted or not advised.
   // Depending on the result, calls |callback| and passes an indicator whether
   // to proceed or not.
@@ -67,21 +73,28 @@
       const std::u16string& application_title,
       OnDlpRestrictionCheckedCallback callback) = 0;
 
-  // Called when screen capture is started.
+  // Called when screen share is started.
   // |state_change_callback| will be called when restricted content will appear
-  // or disappear in the captured area.
-  virtual void OnScreenCaptureStarted(
+  // or disappear in the captured area to pause/resume the share.
+  // |stop_callback| will be called after a user dismisses a warning.
+  virtual void OnScreenShareStarted(
       const std::string& label,
-      std::vector<content::DesktopMediaID> screen_capture_ids,
+      std::vector<content::DesktopMediaID> screen_share_ids,
       const std::u16string& application_title,
       base::RepeatingClosure stop_callback,
       content::MediaStreamUI::StateChangeCallback state_change_callback) = 0;
 
-  // Called when screen capture is stopped.
-  virtual void OnScreenCaptureStopped(
+  // Called when screen share is stopped.
+  virtual void OnScreenShareStopped(
       const std::string& label,
       const content::DesktopMediaID& media_id) = 0;
 
+  void AddObserver(DlpContentManagerObserver* observer,
+                   DlpContentRestriction restriction);
+
+  void RemoveObserver(const DlpContentManagerObserver* observer,
+                      DlpContentRestriction restriction);
+
  protected:
   friend class DlpContentManagerTestHelper;
 
@@ -270,6 +283,12 @@
   void RemoveAllowedContents(DlpConfidentialContents& contents,
                              DlpRulesManager::Restriction restriction);
 
+  // Notifies observers if the restrictions they are listening to changed.
+  void NotifyOnConfidentialityChanged(
+      const DlpContentRestrictionSet& old_restriction_set,
+      const DlpContentRestrictionSet& new_restriction_set,
+      content::WebContents* web_contents);
+
   // Map from currently known confidential WebContents to the restrictions.
   base::flat_map<content::WebContents*, DlpContentRestrictionSet>
       confidential_web_contents_;
@@ -285,6 +304,11 @@
   raw_ptr<DlpReportingManager> reporting_manager_{nullptr};
 
   std::unique_ptr<DlpWarnNotifier> warn_notifier_;
+
+  // One ObserverList per restriction.
+  std::array<base::ObserverList<DlpContentManagerObserver>,
+             DlpContentRestriction::kMaxValue + 1>
+      observer_lists_;
 };
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.cc
index d843c85..a59b6fd 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.cc
@@ -123,13 +123,13 @@
       std::move(area), application_title, std::move(callback));
 }
 
-void DlpContentManagerLacros::OnScreenCaptureStarted(
+void DlpContentManagerLacros::OnScreenShareStarted(
     const std::string& label,
-    std::vector<content::DesktopMediaID> screen_capture_ids,
+    std::vector<content::DesktopMediaID> screen_share_ids,
     const std::u16string& application_title,
     base::RepeatingClosure stop_callback,
     content::MediaStreamUI::StateChangeCallback state_change_callback) {
-  for (const content::DesktopMediaID& media_id : screen_capture_ids) {
+  for (const content::DesktopMediaID& media_id : screen_share_ids) {
     if (media_id.type == content::DesktopMediaID::Type::TYPE_WEB_CONTENTS) {
       AddScreenShare(label, media_id, application_title, stop_callback,
                      state_change_callback);
@@ -155,7 +155,7 @@
   CheckRunningScreenShares();
 }
 
-void DlpContentManagerLacros::OnScreenCaptureStopped(
+void DlpContentManagerLacros::OnScreenShareStopped(
     const std::string& label,
     const content::DesktopMediaID& media_id) {
   if (media_id.type == content::DesktopMediaID::Type::TYPE_WEB_CONTENTS) {
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.h
index 6864375..1281ede 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros.h
@@ -38,15 +38,15 @@
       const content::DesktopMediaID& media_id,
       const std::u16string& application_title,
       OnDlpRestrictionCheckedCallback callback) override;
-  void OnScreenCaptureStarted(
+  void OnScreenShareStarted(
       const std::string& label,
-      std::vector<content::DesktopMediaID> screen_capture_ids,
+      std::vector<content::DesktopMediaID> screen_share_ids,
       const std::u16string& application_title,
       base::RepeatingClosure stop_callback,
       content::MediaStreamUI::StateChangeCallback state_change_callback)
       override;
-  void OnScreenCaptureStopped(const std::string& label,
-                              const content::DesktopMediaID& media_id) override;
+  void OnScreenShareStopped(const std::string& label,
+                            const content::DesktopMediaID& media_id) override;
 
  private:
   friend class DlpContentManagerTestHelper;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros_browsertest.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros_browsertest.cc
index f0d564b..3f7077ab 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros_browsertest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_lacros_browsertest.cc
@@ -208,7 +208,7 @@
 
   // Call DLP manager and expect stop callback.
   base::RunLoop stopped_run_loop;
-  manager()->OnScreenCaptureStarted(
+  manager()->OnScreenShareStarted(
       kScreenShareLabel,
       {content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
                                content::DesktopMediaID::kFakeId)},
@@ -258,7 +258,7 @@
   base::RunLoop resumed_run_loop;
   content::DesktopMediaID media_id(content::DesktopMediaID::TYPE_SCREEN,
                                    content::DesktopMediaID::kFakeId);
-  manager()->OnScreenCaptureStarted(
+  manager()->OnScreenShareStarted(
       kScreenShareLabel, {media_id}, kAppId, base::DoNothing(),
       base::BindLambdaForTesting(
           [&](const content::DesktopMediaID& in_media_id,
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_observer.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_observer.h
new file mode 100644
index 0000000..7051f3b
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_observer.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_OBSERVER_H_
+
+#include "base/observer_list_types.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace policy {
+// Observer that is notified by the DlpContentManager.
+// They are registered using DlpContentManager::AddObserver().
+// When registering the observer, it is registered for a specific restriction.
+class DlpContentManagerObserver : public base::CheckedObserver {
+ public:
+  // This method is called when the confidentiality of a tab changes.
+  // It will only be called when the level of restriction changed with which
+  // this observer was added to the DlpContentManager.
+  virtual void OnConfidentialityChanged(
+      DlpRulesManager::Level old_restriction_level,
+      DlpRulesManager::Level new_restriction_level,
+      content::WebContents* web_contents) = 0;
+};
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_OBSERVER_H_
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_notification_helper.cc b/chrome/browser/chromeos/policy/dlp/dlp_notification_helper.cc
index 7d6ced9..d576ae3 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_notification_helper.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_notification_helper.cc
@@ -43,8 +43,8 @@
 
 void OnNotificationClicked(const std::string id) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  ash::NewWindowDelegate::GetInstance()->OpenUrl(
-      GURL(kDlpLearnMoreUrl), /*from_user_interaction=*/true);
+  ash::NewWindowDelegate::GetPrimary()->OpenUrl(GURL(kDlpLearnMoreUrl),
+                                                /*from_user_interaction=*/true);
 #elif BUILDFLAG(IS_CHROMEOS_LACROS)
   // TODO(hidehiko): Instantiating BrowserServiceLacros here is an unexpected
   // use case. Get rid of this by replacing with Navigate() API invocation
diff --git a/chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h b/chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h
index 3f82bf33..800aff3 100644
--- a/chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h
@@ -35,7 +35,7 @@
                OnDlpRestrictionCheckedCallback),
               (override));
   MOCK_METHOD(void,
-              OnScreenCaptureStarted,
+              OnScreenShareStarted,
               (const std::string&,
                std::vector<content::DesktopMediaID>,
                const std::u16string&,
@@ -43,7 +43,7 @@
                content::MediaStreamUI::StateChangeCallback),
               (override));
   MOCK_METHOD(void,
-              OnScreenCaptureStopped,
+              OnScreenShareStopped,
               (const std::string&, const content::DesktopMediaID&),
               (override));
   MOCK_METHOD(ConfidentialContentsInfo,
diff --git a/chrome/browser/custom_handlers/register_protocol_handler_permission_request.cc b/chrome/browser/custom_handlers/register_protocol_handler_permission_request.cc
index fc3f85df..9be4d8e 100644
--- a/chrome/browser/custom_handlers/register_protocol_handler_permission_request.cc
+++ b/chrome/browser/custom_handlers/register_protocol_handler_permission_request.cc
@@ -7,9 +7,9 @@
 #include "base/bind.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/custom_handlers/protocol_handler_registry.h"
 #include "components/permissions/request_type.h"
+#include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
diff --git a/chrome/browser/infobars/infobars_browsertest.cc b/chrome/browser/infobars/infobars_browsertest.cc
index 4b831c9..064b7046 100644
--- a/chrome/browser/infobars/infobars_browsertest.cc
+++ b/chrome/browser/infobars/infobars_browsertest.cc
@@ -365,9 +365,13 @@
       break;
 
     case IBD::TAB_SHARING_INFOBAR_DELEGATE:
-      TabSharingInfoBarDelegate::Create(GetInfoBarManager(), u"example.com",
-                                        u"application.com", false, true,
-                                        absl::nullopt, nullptr);
+      TabSharingInfoBarDelegate::Create(
+          /*infobar_manager=*/GetInfoBarManager(),
+          /*shared_tab_name=*/u"example.com", /*app_name=*/u"application.com",
+          /*shared_tab=*/false,
+          /*share_this_tab_instead_button_state=*/
+          TabSharingInfoBarDelegate::ButtonState::ENABLED,
+          /*focus_target=*/absl::nullopt, /*ui=*/nullptr);
       break;
 
     default:
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index f5a5d87..966dffbb 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -241,7 +241,7 @@
     }
 
 #if BUILDFLAG(IS_CHROMEOS)
-    policy::DlpContentManager::Get()->OnScreenCaptureStarted(
+    policy::DlpContentManager::Get()->OnScreenShareStarted(
         label, screen_capture_ids, application_title_, stop_callback,
         state_change_callback);
 #endif
@@ -258,7 +258,7 @@
   void OnDeviceStopped(const std::string& label,
                        const content::DesktopMediaID& media_id) override {
 #if BUILDFLAG(IS_CHROMEOS)
-    policy::DlpContentManager::Get()->OnScreenCaptureStopped(label, media_id);
+    policy::DlpContentManager::Get()->OnScreenShareStopped(label, media_id);
 #endif
   }
 
diff --git a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc
index 3f3a4f0..8a02ebf8 100644
--- a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc
+++ b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h"
 
+#include "base/callback.h"
 #include "base/feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
@@ -18,6 +19,9 @@
     ChromePasswordManagerClient* client)
     : client_(client) {}
 
+ChromeWebAuthnCredentialsDelegate::~ChromeWebAuthnCredentialsDelegate() =
+    default;
+
 bool ChromeWebAuthnCredentialsDelegate::IsWebAuthnAutofillEnabled() const {
   return base::FeatureList::IsEnabled(features::kWebAuthConditionalUI);
 }
@@ -36,16 +40,20 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
 }
 
-std::vector<autofill::Suggestion>
+const std::vector<autofill::Suggestion>&
 ChromeWebAuthnCredentialsDelegate::GetWebAuthnSuggestions() const {
-#if BUILDFLAG(IS_ANDROID)
-  return {};
-#else
+  return suggestions_;
+}
+
+void ChromeWebAuthnCredentialsDelegate::RetrieveWebAuthnSuggestions(
+    base::OnceClosure callback) {
+#if !BUILDFLAG(IS_ANDROID)
   ChromeAuthenticatorRequestDelegate* authenticator_delegate =
       AuthenticatorRequestScheduler::GetRequestDelegate(
           client_->web_contents());
   if (!authenticator_delegate) {
-    return {};
+    std::move(callback).Run();
+    return;
   }
   std::vector<autofill::Suggestion> suggestions;
   for (const auto& credential :
@@ -68,6 +76,8 @@
         std::string(credential.id.begin(), credential.id.end());
     suggestions.push_back(std::move(suggestion));
   }
-  return suggestions;
+  suggestions_ = std::move(suggestions);
 #endif
+
+  std::move(callback).Run();
 }
diff --git a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h
index 2f6628d..217d0d3 100644
--- a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h
+++ b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h
@@ -16,7 +16,7 @@
  public:
   explicit ChromeWebAuthnCredentialsDelegate(
       ChromePasswordManagerClient* client);
-  ~ChromeWebAuthnCredentialsDelegate() override = default;
+  ~ChromeWebAuthnCredentialsDelegate() override;
   ChromeWebAuthnCredentialsDelegate(const ChromeWebAuthnCredentialsDelegate&) =
       delete;
   ChromeWebAuthnCredentialsDelegate operator=(
@@ -27,10 +27,18 @@
   // password_manager::WebAuthnCredentialsDelegate:
   bool IsWebAuthnAutofillEnabled() const override;
   void SelectWebAuthnCredential(std::string backend_id) override;
-  std::vector<autofill::Suggestion> GetWebAuthnSuggestions() const override;
+  const std::vector<autofill::Suggestion>& GetWebAuthnSuggestions()
+      const override;
+  void RetrieveWebAuthnSuggestions(base::OnceClosure callback) override;
 
  protected:
   const raw_ptr<ChromePasswordManagerClient> client_;
+
+ private:
+  // List of autofill suggestions populated from an authenticator from a call
+  // to RetrieveWebAuthnSuggestions, and returned to the client via
+  // GetWebAuthnSuggestions.
+  std::vector<autofill::Suggestion> suggestions_;
 };
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_CHROME_WEBAUTHN_CREDENTIALS_DELEGATE_H_
diff --git a/chrome/browser/prerender/prerender_browsertest.cc b/chrome/browser/prerender/prerender_browsertest.cc
index 71784e09..23692d1 100644
--- a/chrome/browser/prerender/prerender_browsertest.cc
+++ b/chrome/browser/prerender/prerender_browsertest.cc
@@ -158,19 +158,39 @@
   histogram_tester.ExpectBucketCount(
       "Blink.UseCounter.Features",
       blink::mojom::WebFeature::kSpeculationRulesPrerender, 0);
+  histogram_tester.ExpectBucketCount(
+      "Blink.UseCounter.Features",
+      blink::mojom::WebFeature::kV8Document_Prerendering_AttributeGetter, 0);
+  histogram_tester.ExpectBucketCount(
+      "Blink.UseCounter.Features",
+      blink::mojom::WebFeature::
+          kV8Document_Onprerenderingchange_AttributeSetter,
+      0);
 
   // Start a prerender. The API call should be recorded.
   GURL prerender_url = embedded_test_server()->GetURL("/simple.html");
   prerender_helper().AddPrerender(prerender_url);
-  histogram_tester.ExpectBucketCount(
-      "Blink.UseCounter.Features",
-      blink::mojom::WebFeature::kSpeculationRulesPrerender, 1);
 
-  // The API call should be recorded only one time per page.
-  prerender_helper().AddPrerender(prerender_url);
+  // Accessing related attributes should also be recorded.
+  ASSERT_TRUE(content::ExecJs(GetActiveWebContents()->GetMainFrame(),
+                              "const value = document.prerendering;"));
+  ASSERT_TRUE(content::ExecJs(GetActiveWebContents()->GetMainFrame(),
+                              "document.onprerenderingchange = e => {};"));
+
+  // Make sure the counts are stored by navigating away.
+  prerender_helper().NavigatePrimaryPage(prerender_url);
+
   histogram_tester.ExpectBucketCount(
       "Blink.UseCounter.Features",
       blink::mojom::WebFeature::kSpeculationRulesPrerender, 1);
+  histogram_tester.ExpectBucketCount(
+      "Blink.UseCounter.Features",
+      blink::mojom::WebFeature::kV8Document_Prerendering_AttributeGetter, 1);
+  histogram_tester.ExpectBucketCount(
+      "Blink.UseCounter.Features",
+      blink::mojom::WebFeature::
+          kV8Document_Onprerenderingchange_AttributeSetter,
+      1);
 }
 
 // Tests that Prerender2 cannot be triggered when preload setting is disabled.
diff --git a/chrome/browser/privacy_sandbox/android/BUILD.gn b/chrome/browser/privacy_sandbox/android/BUILD.gn
index 562e16b..9f03ec4 100644
--- a/chrome/browser/privacy_sandbox/android/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/android/BUILD.gn
@@ -16,8 +16,9 @@
     "java/src/org/chromium/chrome/browser/privacy_sandbox/FlocSettingsFragment.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/LearnMoreFragment.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxBridge.java",
-    "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialog.java",
+    "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsent.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogController.java",
+    "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNotice.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxHelpers.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxReferrer.java",
     "java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSettingsFragment.java",
@@ -83,8 +84,9 @@
     "java/res/drawable-night/privacy_sandbox_illustration.xml",
     "java/res/drawable/privacy_sandbox_dialog_illustration.xml",
     "java/res/drawable/privacy_sandbox_illustration.xml",
-    "java/res/layout/privacy_sandbox_dialog.xml",
+    "java/res/layout/privacy_sandbox_consent.xml",
     "java/res/layout/privacy_sandbox_header.xml",
+    "java/res/layout/privacy_sandbox_notice.xml",
     "java/res/values/dimens.xml",
     "java/res/xml/ad_measurement_preference.xml",
     "java/res/xml/ad_personalization_preference.xml",
diff --git a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_dialog.xml b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent.xml
similarity index 95%
rename from chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_dialog.xml
rename to chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent.xml
index c5873e0..6245350 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_dialog.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent.xml
@@ -90,7 +90,7 @@
             android:layout_width="0dp"
             android:layout_weight="1"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/privacy_sandbox_dialog_button_margin_between"
+            android:layout_marginEnd="@dimen/privacy_sandbox_consent_button_margin_between"
             android:text="@string/privacy_sandbox_dialog_no_button"
             style="@style/FilledButton.Flat" />
 
@@ -100,7 +100,7 @@
             android:layout_width="0dp"
             android:layout_weight="1"
             android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/privacy_sandbox_dialog_button_margin_between"
+            android:layout_marginStart="@dimen/privacy_sandbox_consent_button_margin_between"
             android:text="@string/privacy_sandbox_dialog_yes_button"
             style="@style/FilledButton.Flat" />
     </LinearLayout>
diff --git a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_dialog.xml b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice.xml
similarity index 75%
copy from chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_dialog.xml
copy to chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice.xml
index c5873e0..5b2ce4c 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_dialog.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice.xml
@@ -33,20 +33,14 @@
                 android:importantForAccessibility="no" />
 
             <TextView
-                android:layout_marginBottom="@dimen/promo_between_text_margin"
+                android:layout_marginBottom="@dimen/list_item_default_margin"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/privacy_sandbox_consent_title"
+                android:text="@string/privacy_sandbox_notice_title"
+                android:gravity="center"
                 style="@style/TextAppearance.Headline.Primary" />
 
             <TextView
-                android:layout_marginBottom="@dimen/list_item_default_margin"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/privacy_sandbox_consent_subtitle"
-                style="@style/TextAppearance.TextMedium.Primary" />
-
-            <TextView
                 android:layout_marginBottom="@dimen/promo_between_text_margin"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
@@ -78,31 +72,27 @@
     </ScrollView>
 
     <LinearLayout
-        android:orientation="horizontal"
+        android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_margin="@dimen/promo_dialog_padding"
-        android:weightSum="2">
+        android:layout_margin="@dimen/promo_dialog_padding">
 
         <org.chromium.ui.widget.ButtonCompat
-            android:id="@+id/no_button"
+            android:id="@+id/ack_button"
             android:focusable="true"
-            android:layout_width="0dp"
-            android:layout_weight="1"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="@dimen/privacy_sandbox_dialog_button_margin_between"
-            android:text="@string/privacy_sandbox_dialog_no_button"
+            android:layout_marginBottom="@dimen/privacy_sandbox_notice_button_margin_between"
+            android:text="@string/privacy_sandbox_dialog_acknowledge_button"
             style="@style/FilledButton.Flat" />
 
         <org.chromium.ui.widget.ButtonCompat
-            android:id="@+id/yes_button"
+            android:id="@+id/settings_button"
             android:focusable="true"
-            android:layout_width="0dp"
-            android:layout_weight="1"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/privacy_sandbox_dialog_button_margin_between"
-            android:text="@string/privacy_sandbox_dialog_yes_button"
-            style="@style/FilledButton.Flat" />
+            android:text="@string/privacy_sandbox_dialog_settings_button"
+            style="@style/TextButton" />
     </LinearLayout>
 
 </LinearLayout>
diff --git a/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml b/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml
index 4947b503..69e71335 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml
@@ -8,5 +8,6 @@
   <dimen name="privacy_sandbox_illustration_width">120dp</dimen>
   <dimen name="privacy_sandbox_dialog_illustration_margin_top">32dp</dimen>
   <dimen name="privacy_sandbox_dialog_illustration_margin_bottom">16dp</dimen>
-  <dimen name="privacy_sandbox_dialog_button_margin_between">8dp</dimen>
+  <dimen name="privacy_sandbox_consent_button_margin_between">8dp</dimen>
+  <dimen name="privacy_sandbox_notice_button_margin_between">4dp</dimen>
 </resources>
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialog.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsent.java
similarity index 64%
rename from chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialog.java
rename to chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsent.java
index 4930750..d4f44d7 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialog.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsent.java
@@ -12,18 +12,25 @@
 import org.chromium.ui.widget.ButtonCompat;
 
 /**
- * Dialog shown for the Privacy Sandbox.
+ * Dialog in the form of a consent shown for the Privacy Sandbox.
  */
-public class PrivacySandboxDialog extends Dialog {
-    public PrivacySandboxDialog(Context context) {
+public class PrivacySandboxDialogConsent extends Dialog implements View.OnClickListener {
+    public PrivacySandboxDialogConsent(Context context) {
         super(context, R.style.ThemeOverlay_BrowserUI_Fullscreen);
-        View view = LayoutInflater.from(context).inflate(R.layout.privacy_sandbox_dialog, null);
+        View view = LayoutInflater.from(context).inflate(R.layout.privacy_sandbox_consent, null);
         setContentView(view);
 
-        // TODO(crbug.com/1286276): Add the actual logic for the buttons.
         ButtonCompat yesButton = (ButtonCompat) view.findViewById(R.id.yes_button);
-        yesButton.setOnClickListener((View v) -> dismiss());
+        yesButton.setOnClickListener(this);
         ButtonCompat noButton = (ButtonCompat) view.findViewById(R.id.no_button);
-        noButton.setOnClickListener((View v) -> dismiss());
+        noButton.setOnClickListener(this);
+    }
+
+    // OnClickListener:
+
+    @Override
+    public void onClick(View view) {
+        // TODO(crbug.com/1286276): Add the actual logic for the buttons.
+        dismiss();
     }
 }
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogController.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogController.java
index 59851a3..d5e6576 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogController.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogController.java
@@ -10,10 +10,28 @@
  * Controller for the dialog shown for the Privacy Sandbox.
  */
 public class PrivacySandboxDialogController {
-    public static boolean maybeLaunchPrivacySandboxDialog(Context context) {
-        // TODO(crbug.com/1286276): Add logic for conditional display.
-        PrivacySandboxDialog dialog = new PrivacySandboxDialog(context);
-        dialog.show();
-        return true;
+    /**
+     * Launches an appropriate dialog if necessary and returns whether that happened.
+     */
+    public static boolean maybeLaunchPrivacySandboxDialog(Context context, boolean isIncognito) {
+        if (isIncognito) {
+            return false;
+        }
+        @DialogType
+        int dialogType = PrivacySandboxBridge.getRequiredDialogType();
+        switch (dialogType) {
+            case DialogType.NONE:
+                return false;
+            case DialogType.NOTICE:
+                new PrivacySandboxDialogNotice(context).show();
+                return true;
+            case DialogType.CONSENT:
+                new PrivacySandboxDialogConsent(context).show();
+                return true;
+            default:
+                assert false : "Unknown DialogType value.";
+                // Should not be reached.
+                return false;
+        }
     }
 }
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNotice.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNotice.java
new file mode 100644
index 0000000..8ed0213
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNotice.java
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.privacy_sandbox;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import org.chromium.ui.widget.ButtonCompat;
+
+/**
+ * Dialog in the form of a notice shown for the Privacy Sandbox.
+ */
+public class PrivacySandboxDialogNotice extends Dialog implements View.OnClickListener {
+    public PrivacySandboxDialogNotice(Context context) {
+        super(context, R.style.ThemeOverlay_BrowserUI_Fullscreen);
+        View view = LayoutInflater.from(context).inflate(R.layout.privacy_sandbox_notice, null);
+        setContentView(view);
+
+        ButtonCompat ackButton = (ButtonCompat) view.findViewById(R.id.ack_button);
+        ackButton.setOnClickListener(this);
+        ButtonCompat settingsButton = (ButtonCompat) view.findViewById(R.id.settings_button);
+        settingsButton.setOnClickListener(this);
+    }
+
+    // OnClickListener:
+
+    @Override
+    public void onClick(View view) {
+        // TODO(crbug.com/1286276): Add the actual logic for the buttons.
+        dismiss();
+    }
+}
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
index 392e867..4cb824c 100644
--- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
@@ -62,11 +62,18 @@
     @Test
     @SmallTest
     @Feature({"RenderTest"})
-    public void testRenderDialog() throws IOException {
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            PrivacySandboxDialog dialog = new PrivacySandboxDialog(sActivityTestRule.getActivity());
-            dialog.show();
-        });
+    public void testRenderConsent() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { new PrivacySandboxDialogConsent(sActivityTestRule.getActivity()).show(); });
         renderViewWithId(R.id.privacy_sandbox_dialog, "privacy_sandbox_consent_dialog");
     }
+
+    @Test
+    @SmallTest
+    @Feature({"RenderTest"})
+    public void testRenderNotice() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { new PrivacySandboxDialogNotice(sActivityTestRule.getActivity()).show(); });
+        renderViewWithId(R.id.privacy_sandbox_dialog, "privacy_sandbox_notice_dialog");
+    }
 }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
index 311f994..da7a7f06 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -161,14 +161,22 @@
     'ChromeVoxDesktopAutomationHandlerTest', 'DatalistSelection', function() {
       const mockFeedback = this.createMockFeedback();
       const site = `
-    <input list="list">
+    <input aria-label="Choose one" list="list">
     <datalist id="list">
     <option>foo</option>
     <option>bar</option>
     </datalist>
   `;
-      this.runWithLoadedTree(site, function(root) {
-        root.find({role: RoleType.TEXT_FIELD_WITH_COMBO_BOX}).focus();
+      this.runWithLoadedTree(site, async function(root) {
+        const combobox = root.find({
+          role: RoleType.TEXT_FIELD_WITH_COMBO_BOX,
+          attributes: {name: 'Choose one'}
+        });
+        assertTrue(!!combobox);
+        combobox.focus();
+        await new Promise(r => combobox.addEventListener(EventType.FOCUS, r));
+
+        // The combobox is now actually focused, safe to send arrows.
         mockFeedback.call(press(KeyCode.DOWN))
             .expectSpeech('foo', 'List item', ' 1 of 2 ')
             .expectBraille('foo lstitm 1/2 (x)')
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
index d244a3e4..e6199b5 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
@@ -242,6 +242,7 @@
     emoji-data="[[emojiData]]"
     search="{{search}}"
     on-scroll="onSearchScroll"
+    category-metadata="[[getCategoryMetadata(category)]]"
     v2-enabled="[[v2Enabled]]">
   </emoji-search>
 
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index 34fadc2..6b2fb9d 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -18,9 +18,9 @@
 import {Feature} from './emoji_picker.mojom-webui.js';
 import {EmojiPickerApiProxy, EmojiPickerApiProxyImpl} from './emoji_picker_api_proxy.js';
 import {CATEGORY_BUTTON_CLICK, createCustomEvent, EMOJI_BUTTON_CLICK, EMOJI_CLEAR_RECENTS_CLICK, EMOJI_DATA_LOADED, EMOJI_REMAINING_DATA_LOADED, EMOJI_VARIANTS_SHOWN, EmojiVariantsShownEvent, GROUP_BUTTON_CLICK, V2_CONTENT_LOADED} from './events.js';
-import {V2_SUBCATEGORY_TABS} from './metadata_extension.js';
+import {CATEGORY_METADATA, V2_SUBCATEGORY_TABS} from './metadata_extension.js';
 import {RecentlyUsedStore} from './store.js';
-import {CategoryEnum, Emoji, EmojiGroup, EmojiGroupData, EmojiVariants, StoredItem, SubcategoryData} from './types.js';
+import {CategoryData, CategoryEnum, Emoji, EmojiGroup, EmojiGroupData, EmojiVariants, StoredItem, SubcategoryData} from './types.js';
 
 const EMOJI_ORDERING_JSON_TEMPLATE = '/emoji_14_0_ordering';
 const EMOTICON_ORDERING_JSON = '/emoticon_test_ordering.json';
@@ -271,7 +271,7 @@
       if (this.v2Enabled) {
         this.addEventListener(
             CATEGORY_BUTTON_CLICK,
-            ev => this.set('category', ev.detail.categoryName));
+            ev => this.onCategoryButtonClick(ev.detail.categoryName));
         this.addEventListener(EMOJI_REMAINING_DATA_LOADED, () => {
           this.fetchOrderingData(EMOTICON_ORDERING_JSON).then((data) => {
             this.emoticonData = data;
@@ -414,8 +414,10 @@
     if (this.scrollTimeout) {
       clearTimeout(this.scrollTimeout);
     }
-    this.scrollTimeout = setTimeout(
-        () => this.updateActiveGroup(/*updateTabsScroll=*/ true), 100);
+    this.scrollTimeout = setTimeout(() => {
+      this.updateActiveCategory();
+      this.updateActiveGroup(/*updateTabsScroll=*/ true);
+    }, 100);
   }
 
   onRightChevronClick() {
@@ -520,14 +522,9 @@
   }
 
   /**
-   *
-   * @param {boolean} updateTabsScroll
+   * @returns {string} the id of the emoji or emoticon group currently in view.
    */
-  updateActiveGroup(updateTabsScroll) {
-    // no need to update scroll state if search is showing.
-    if (this.search)
-      return;
-
+  getActiveGroupIdFromScrollPosition() {
     // get bounding rect of scrollable emoji region.
     const thisRect = this.$.groups.getBoundingClientRect();
 
@@ -542,6 +539,18 @@
 
     const activeGroupId = activeGroup ? activeGroup.dataset.group : 'history';
 
+    return activeGroupId;
+  }
+
+  /**
+   * @param {boolean} updateTabsScroll
+   */
+  updateActiveGroup(updateTabsScroll) {
+    // no need to update scroll state if search is showing.
+    if (this.search)
+      return;
+
+    const activeGroupId = this.getActiveGroupIdFromScrollPosition();
     this.set('pagination', this.getPaginationFromGroupId(activeGroupId));
     this.updateChevrons();
 
@@ -555,8 +564,12 @@
       this.set(['emojiGroupTabs', i, 'active'], isActive);
     });
 
+    const shouldDeactivateEmojiHistoryTab = this.category === 'emoji' &&
+        index === 0 && this.emojiHistory.emoji.length === 0;
+    const shouldDeactivateEmoticonHistoryTab = this.category === 'emoticon' &&
+        index === 0 && this.emoticonHistory.emoji.length === 0;
     // Ensure that the history tab is not set as active if it is empty.
-    if (index === 0 && this.emojiHistory.emoji.length === 0) {
+    if (shouldDeactivateEmojiHistoryTab || shouldDeactivateEmoticonHistoryTab) {
       this.set(['emojiGroupTabs', 0, 'active'], false);
       this.set(['emojiGroupTabs', 1, 'active'], true);
       index = 1;
@@ -630,6 +643,20 @@
     }
   }
 
+  /**
+   * Update active category by using vertical scroll position.
+   */
+  updateActiveCategory() {
+    if (this.v2Enabled) {
+      const activeGroupId = this.getActiveGroupIdFromScrollPosition();
+
+      const currentCategory =
+          V2_SUBCATEGORY_TABS.find((tab) => tab.groupId === activeGroupId)
+              .category;
+      this.set('category', currentCategory);
+    }
+  }
+
   hideDialogs() {
     this.hideEmojiVariants();
     if (this.emojiHistory.emoji.length > 0) {
@@ -739,16 +766,23 @@
    * @param {string} newCategoryName
    */
   onCategoryChanged(newCategoryName) {
-    if (this.v2Enabled) {
-      const categoryTabs =
-          V2_SUBCATEGORY_TABS.filter(tab => tab.category === newCategoryName);
-      this.set('emojiGroupTabs', categoryTabs);
-      this.set('pagination', 1);
-      this.updateChevrons();
-      this.scrollToGroup(this.emojiGroupTabs[0].groupId);
+    const categoryTabs =
+        V2_SUBCATEGORY_TABS.filter(tab => tab.category === newCategoryName);
+    this.set('emojiGroupTabs', categoryTabs);
+    afterNextRender(this, () => {
       this.updateActiveGroup(true);
-      this.$.tabs.scrollLeft = 0;
-    }
+      this.$.tabs.scrollLeft =
+          this.calculateTabScrollLeftPosition(this.pagination);
+    });
+  }
+
+  /**
+   * @param {string} newCategoryName
+   */
+  onCategoryButtonClick(newCategoryName) {
+    this.set('category', newCategoryName);
+    this.set('pagination', 1);
+    this.scrollToGroup(this.emojiGroupTabs[0].groupId);
   }
 
   /**
@@ -841,6 +875,19 @@
     return (page === 1) ? 0 :
                           (page - 1) * EMOJI_PICKER_WIDTH - offsetByLeftChevron;
   }
+
+  /**
+   * @private
+   * @param {string} category
+   * @returns {!Array<!CategoryData>}
+   */
+  getCategoryMetadata(category) {
+    return CATEGORY_METADATA.map(data => ({
+                                   name: data.name,
+                                   icon: data.icon,
+                                   active: data.name === category
+                                 }));
+  }
 }
 
 customElements.define(EmojiPicker.is, EmojiPicker);
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
index b371e7d..1538df35 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
@@ -129,8 +129,8 @@
   </cr-search-field>
   <template is="dom-if" if="[[v2Enabled]]">
     <div id="category-button-group">
-      <template is="dom-repeat" items="[[categoryData]]">
-          <emoji-category-button 
+      <template is="dom-repeat" items="[[categoryMetadata]]">
+          <emoji-category-button
             active="[[item.active]]"
             name="[[item.name]]"
             icon="[[item.icon]]">
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.js
index ba8d0f7..ec384cd 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.js
@@ -8,10 +8,8 @@
 
 import {EmojiButton} from './emoji_button.js';
 import {EmojiCategoryButton} from './emoji_category_button.js';
-import {CATEGORY_BUTTON_CLICK} from './events.js';
 import Fuse from './fuse.js';
-import {CATEGORY_DATA} from './metadata_extension.js';
-import {CategoryData, EmojiGroupData, EmojiVariants} from './types.js';
+import {EmojiGroupData, EmojiVariants} from './types.js';
 
 /**
  * @typedef {!Array<{item: !EmojiVariants}>} FuseResults
@@ -29,8 +27,6 @@
 
   static get properties() {
     return {
-      /** @type {!Array<!CategoryData>} */
-      categoryData: {type: Array, value: CATEGORY_DATA, notify: true},
       /** @type {EmojiGroupData} */
       emojiData: {type: Array, readonly: true},
       /** @type {!string} */
@@ -49,8 +45,7 @@
         type: Boolean,
         value: false,
         reflectToAttribute: true,
-        readonly: true,
-        observer: 'onV2EnabledChanged'
+        readonly: true
       }
     };
   }
@@ -85,13 +80,6 @@
     this.search = newSearch;
   }
 
-  onV2EnabledChanged(newFlag) {
-    if (newFlag) {
-      this.addEventListener(
-          CATEGORY_BUTTON_CLICK,
-          ev => this.onSelectCategory(ev.detail.categoryName));
-    }
-  }
   /**
    * Event handler for keydown anywhere in the search component.
    * Used to move the focused result up/down on arrow presses.
@@ -218,16 +206,6 @@
         .shadowRoot.querySelector('button')
         .click();
   }
-
-  /**
-   * @param {string} categoryName
-   */
-  onSelectCategory(categoryName) {
-    this.categoryData.forEach((category, idx) => {
-      const isActive = (categoryName === category.name);
-      this.set(['categoryData', idx, 'active'], isActive);
-    });
-  }
 }
 
 customElements.define(EmojiSearch.is, EmojiSearch);
diff --git a/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js b/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js
index fbb85efa..4ec3cfc8 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/metadata_extension.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-export const CATEGORY_DATA = [
+export const CATEGORY_METADATA = [
   {
     name: 'emoji',
     icon: 'emoji_picker:insert_emoticon',
diff --git a/chrome/browser/resources/chromeos/login/display_manager.js b/chrome/browser/resources/chromeos/login/display_manager.js
index 460de4b..7e380b17 100644
--- a/chrome/browser/resources/chromeos/login/display_manager.js
+++ b/chrome/browser/resources/chromeos/login/display_manager.js
@@ -216,17 +216,6 @@
     }
 
     /**
-     * Toggles background of main body between transparency and solid.
-     * @param {boolean} solid Whether to show a solid background.
-     */
-    set solidBackground(solid) {
-      if (solid)
-        document.body.classList.add('solid');
-      else
-        document.body.classList.remove('solid');
-    }
-
-    /**
      * Forces keyboard based OOBE navigation.
      * @param {boolean} value True if keyboard navigation flow is forced.
      */
diff --git a/chrome/browser/resources/chromeos/login/oobe.css b/chrome/browser/resources/chromeos/login/oobe.css
index f6e5ede..2217f0d 100644
--- a/chrome/browser/resources/chromeos/login/oobe.css
+++ b/chrome/browser/resources/chromeos/login/oobe.css
@@ -98,10 +98,6 @@
   }
 }
 
-body.solid {
-  background-color: white;
-}
-
 button {
   font-family: inherit;
   outline: none;
diff --git a/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.js b/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.js
index dee0c61a..78195ea 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/app_launch_splash.js
@@ -97,15 +97,6 @@
     this.updateApp(data['appInfo']);
 
     this.$.shortcutInfo.hidden = !data['shortcutEnabled'];
-
-    Oobe.getInstance().solidBackground = true;
-  }
-
-  /**
-   * Event handler that is invoked just before the frame is hidden.
-   */
-  onBeforeHide() {
-    Oobe.getInstance().solidBackground = false;
   }
 
   /**
@@ -146,4 +137,4 @@
   }
 }
 
-customElements.define(AppLaunchSplash.is, AppLaunchSplash);
\ No newline at end of file
+customElements.define(AppLaunchSplash.is, AppLaunchSplash);
diff --git a/chrome/browser/resources/history/app.ts b/chrome/browser/resources/history/app.ts
index 1a26df3..9172bd0 100644
--- a/chrome/browser/resources/history/app.ts
+++ b/chrome/browser/resources/history/app.ts
@@ -567,6 +567,10 @@
   searchInputHasFocus(): boolean {
     return this.$.toolbar.searchField.isSearchFocused();
   }
+
+  setHasDrawerForTesting(enabled: boolean) {
+    this.hasDrawer_ = enabled;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/history/history.ts b/chrome/browser/resources/history/history.ts
index 832a79c..4b73bf7 100644
--- a/chrome/browser/resources/history/history.ts
+++ b/chrome/browser/resources/history/history.ts
@@ -4,6 +4,7 @@
 
 import './app.js';
 
+export {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 export {ensureLazyLoaded, HistoryAppElement, listenForPrivilegedLinkClicks} from './app.js';
 export {BrowserService, BrowserServiceImpl, QueryResult, RemoveVisitsRequest} from './browser_service.js';
 export {HistoryPageViewHistogram, SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram} from './constants.js';
@@ -12,5 +13,7 @@
 export {PageCallbackRouter, PageHandlerRemote} from './history_clusters/history_clusters.mojom-webui.js';
 export {ClusterAction, RelatedSearchAction, VisitAction, VisitType} from './history_clusters/metrics_proxy.js';
 export {MetricsProxy, MetricsProxyImpl} from './history_clusters/metrics_proxy.js';
+export {HistoryItemElement} from './history_item.js';
+export {ActionMenuModel, HistoryListElement} from './history_list.js';
 export {HistorySearchedLabelElement} from './searched_label.js';
 export {HistorySideBarElement} from './side_bar.js';
diff --git a/chrome/browser/resources/history/history_item.ts b/chrome/browser/resources/history/history_item.ts
index 38ea150d..266be4e9 100644
--- a/chrome/browser/resources/history/history_item.ts
+++ b/chrome/browser/resources/history/history_item.ts
@@ -115,6 +115,7 @@
   private eventTracker_: EventTracker = new EventTracker();
 
   item: HistoryEntry;
+  hasTimeGap: boolean;
   index: number;
   searchTerm: string;
   isCardStart: boolean;
diff --git a/chrome/browser/resources/history/history_list.ts b/chrome/browser/resources/history/history_list.ts
index e38ef02c..83d36ee 100644
--- a/chrome/browser/resources/history/history_list.ts
+++ b/chrome/browser/resources/history/history_list.ts
@@ -28,20 +28,14 @@
 import {HistoryItemElement, searchResultsTitle} from './history_item.js';
 import {getTemplate} from './history_list.html.js';
 
-type OpenMenuEvent = CustomEvent<{
+export type ActionMenuModel = {
   index: number,
   item: HistoryEntry,
-  path: string,
-  target: HTMLElement,
-}>;
-
-type ActionMenuModel = {
-  index: number,
-  item: HistoryEntry,
-  path: string,
   target: HTMLElement,
 };
 
+type OpenMenuEvent = CustomEvent<ActionMenuModel>;
+
 type HistoryCheckboxSelectEvent = CustomEvent<{
   index: number,
   shiftKey: boolean,
@@ -612,4 +606,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-list': HistoryListElement;
+  }
+}
+
 customElements.define(HistoryListElement.is, HistoryListElement);
diff --git a/chrome/browser/resources/history/side_bar.ts b/chrome/browser/resources/history/side_bar.ts
index 82ff352..27fd11a0 100644
--- a/chrome/browser/resources/history/side_bar.ts
+++ b/chrome/browser/resources/history/side_bar.ts
@@ -232,4 +232,10 @@
   }
 }
 
+declare global {
+  interface HTMLElementTagNameMap {
+    'history-side-bar': HistorySideBarElement;
+  }
+}
+
 customElements.define(HistorySideBarElement.is, HistorySideBarElement);
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.html b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.html
index 50a8973..47817d6 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.html
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.html
@@ -76,13 +76,19 @@
 
 <div class="buttons-container">
   <template is="dom-if" if="[[isConsent_]]">
-    <cr-button class="action-button">Cancel</cr-button>
-    <cr-button class="action-button" on-click="onClose_">Close</cr-button>
+    <cr-button class="action-button" on-click="onConsentDeclined_">
+      No
+    </cr-button>
+    <cr-button class="action-button" on-click="onConsentAccepted_">
+      Ok
+    </cr-button>
   </template>
 
   <template is="dom-if" if="[[!isConsent_]]">
-    <cr-button>Settings</cr-button>
-    <cr-button class="action-button" on-click="onClose_">Close</cr-button>
+    <cr-button on-click="onNoticeOpenSettings_">Settings</cr-button>
+    <cr-button class="action-button" on-click="onNoticeAcknowledge_">
+      Ok
+    </cr-button>
 </div>
 </template>
 </div>
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.ts b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.ts
index c203540..5e73ca2 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.ts
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_app.ts
@@ -11,7 +11,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {PrivacySandboxDialogBrowserProxy} from './privacy_sandbox_dialog_browser_proxy.js';
+import {PrivacySandboxDialogAction, PrivacySandboxDialogBrowserProxy} from './privacy_sandbox_dialog_browser_proxy.js';
 
 const PrivacySandboxDialogAppElementBase = PolymerElement;
 
@@ -27,7 +27,10 @@
 
   static get properties() {
     return {
-      expanded_: Boolean,
+      expanded_: {
+        type: Boolean,
+        observer: 'onLearnMoreExpandedChanged_',
+      },
       isConsent_: {
         type: Boolean,
         value: () => {
@@ -40,8 +43,35 @@
   private expanded_: boolean;
   private isConsent_: boolean;
 
-  private onClose_() {
-    PrivacySandboxDialogBrowserProxy.getInstance().closeDialog();
+  private onNoticeOpenSettings_() {
+    this.dialogActionOccurred(PrivacySandboxDialogAction.NOTICE_OPEN_SETTINGS);
+  }
+
+  private onNoticeAcknowledge_() {
+    this.dialogActionOccurred(PrivacySandboxDialogAction.NOTICE_ACKNOWLEDGE);
+  }
+
+  private onConsentAccepted_() {
+    this.dialogActionOccurred(PrivacySandboxDialogAction.CONSENT_ACCEPTED);
+  }
+
+  private onConsentDeclined_() {
+    this.dialogActionOccurred(PrivacySandboxDialogAction.CONSENT_DECLINED);
+  }
+
+  private onLearnMoreExpandedChanged_(newVal: boolean, oldVal: boolean) {
+    if (!oldVal && newVal) {
+      this.dialogActionOccurred(
+          PrivacySandboxDialogAction.CONSENT_MORE_INFO_OPENED);
+    }
+    if (oldVal && !newVal) {
+      this.dialogActionOccurred(
+          PrivacySandboxDialogAction.CONSENT_MORE_INFO_CLOSED);
+    }
+  }
+
+  private dialogActionOccurred(action: PrivacySandboxDialogAction) {
+    PrivacySandboxDialogBrowserProxy.getInstance().dialogActionOccurred(action);
   }
 }
 
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts
index 1aa43b8..f7861ee0 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts
@@ -2,9 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// All actions related to showing & interacting with the privacy sandbox
+// dialog. Includes actions which do not impact consent / notice state.
+// Must be kept in sync with the enum of the same name in
+// privacy_sandbox_service.h.
+export enum PrivacySandboxDialogAction {
+  NOTICE_SHOWN = 0,
+  NOTICE_OPEN_SETTINGS = 1,
+  NOTICE_ACKNOWLEDGE = 2,
+  NOTICE_DISMISS = 3,
+  NOTICE_CLOSED_NO_INTERACTION = 4,
+  CONSENT_SHOWN = 5,
+  CONSENT_ACCEPTED = 6,
+  CONSENT_DECLINED = 7,
+  CONSENT_MORE_INFO_OPENED = 8,
+  CONSENT_MORE_INFO_CLOSED = 9,
+  CONSENT_CLOSED_NO_INTERACTION = 10,
+}
+
 export class PrivacySandboxDialogBrowserProxy {
-  closeDialog() {
-    chrome.send('closeDialog');
+  dialogActionOccurred(action: PrivacySandboxDialogAction) {
+    chrome.send('dialogActionOccurred', [action]);
   }
 
   static getInstance(): PrivacySandboxDialogBrowserProxy {
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
index c2281ad8..2c7db84 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
@@ -110,10 +110,7 @@
     <template is="dom-if" route-path="/osSync">
       <settings-subpage page-title="$i18n{syncAdvancedPageTitle}"
           learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
-        <os-sync-controls sync-status="[[syncStatus]]"
-            profile-icon-url="[[profileIconUrl_]]"
-            profile-name="[[profileName_]]"
-            profile-email="[[profileEmail_]]">
+        <os-sync-controls>
         </os-sync-controls>
       </settings-subpage>
     </template>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html
index 1eab5fb..06bfeae 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html
@@ -1,61 +1,4 @@
 <style include="settings-shared">
-  :host {
-    --shown-avatar-size: 40px;
-    --sync-icon-border-size: 2px;
-    --sync-icon-size: 16px;
-  }
-
-  #avatarContainer {
-    height: var(--shown-avatar-size);
-    position: relative;
-  }
-
-  #avatarIcon {
-    border-radius: 20px;
-    flex-shrink: 0;
-    height: var(--shown-avatar-size);
-    width: var(--shown-avatar-size);
-  }
-
-  /* Similar to browser settings-sync-account-control styling. */
-  #syncIconContainer {     align-items: center;
-    background: var(--cros-icon-color-positive);
-    border: var(--sync-icon-border-size) solid
-        var(--cros-bg-color-elevation-1);
-    border-radius: 50%;
-    display: flex;
-    height: var(--sync-icon-size);
-    position: absolute;
-    right: -6px;
-    top: calc(var(--shown-avatar-size) - var(--sync-icon-size) -
-        var(--sync-icon-border-size));
-    width: var(--sync-icon-size);
-  }
-
-  :host-context([dir='rtl']) #syncIconContainer {
-    left: -6px;
-    right: initial;
-  }
-
-  #syncIconContainer.sync-problem {
-    background: var(--cros-icon-color-alert);
-  }
-
-  #syncIconContainer.sync-paused {
-    background: var(--cros-icon-color-prominent);
-  }
-
-  #syncIconContainer.sync-disabled {
-    background: var(--cros-icon-color-disabled);
-  }
-
-  #syncIconContainer iron-icon {
-    fill: var(--cros-bg-color-elevation-1);
-    height: 12px;
-    margin: auto;
-    width: 12px;
-  }
-
   .settings-box {
     border-top: none;
   }
@@ -78,29 +21,6 @@
     opacity: var(--cr-disabled-opacity);
   }
 </style>
-<div class="settings-box first two-line">
-  <div id="avatarContainer">
-    <img id="avatarIcon" alt="" src="[[profileIconUrl]]">
-    <div id="syncIconContainer"
-        class$="[[getSyncIconStyle_(
-            syncStatus.hasError, syncStatus.statusAction,
-            syncStatus.disabled)]]">
-      <iron-icon icon$="[[getSyncIcon_(
-          syncStatus.hasError, syncStatus.statusAction,
-          syncStatus.disabled)]]"></iron-icon>
-    </div>
-  </div>
-  <div class="middle two-line no-min-width">
-    <div class="flex text-elide settings-box-text">
-      <span id="accountTitle" aria-hidden="true">
-        [[getAccountTitle_(profileName, syncStatus.hasError)]]
-      </span>
-      <div id="accountSubtitle" class="secondary" aria-hidden="true">
-        [[getAccountSubtitle_(profileEmail, syncStatus.hasError)]]
-      </div>
-    </div>
-  </div>
-</div>
 <div id="featureLabel" class="settings-box">
   <localized-link class="secondary"
       localized-string="$i18n{osSyncFeatureLabel}"
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
index c9ddc39..0b0feef 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
@@ -63,31 +63,6 @@
     },
 
     /**
-     * Injected sync system status. Undefined until the parent component injects
-     * the value.
-     * @type {SyncStatus|undefined}
-     */
-    syncStatus: Object,
-
-    /**
-     * Injected profile icon URL, usually a data:image/png URL.
-     * @private
-     */
-    profileIconUrl: String,
-
-    /**
-     * Injected profile name, e.g. "John Cena".
-     * @private
-     */
-    profileName: String,
-
-    /**
-     * Injected profile email address, e.g. "john.cena@gmail.com".
-     * @private
-     */
-    profileEmail: String,
-
-    /**
      * The current OS sync preferences. Cached so we can restore individual
      * toggle state when turning "sync everything" on and off, without affecting
      * the underlying chrome prefs.
@@ -150,73 +125,6 @@
   },
 
   /**
-   * @return {string} The top label for the account row.
-   * @private
-   */
-  getAccountTitle_() {
-    if (!this.syncStatus) {
-      return '';
-    }
-    return this.syncStatus.hasError ? this.i18n('syncNotWorking') :
-                                      this.profileName;
-  },
-
-  /**
-   * @return {string} The bottom label for the account row.
-   * @private
-   */
-  getAccountSubtitle_() {
-    if (!this.syncStatus) {
-      return '';
-    }
-    return !this.syncStatus.hasError ?
-        this.i18n('syncingTo', this.profileEmail) :
-        this.profileEmail;
-  },
-
-  /**
-   * Returns the CSS class for the sync status icon.
-   * @return {string}
-   * @private
-   */
-  getSyncIconStyle_() {
-    if (!this.syncStatus) {
-      return 'sync';
-    }
-    if (this.syncStatus.disabled) {
-      return 'sync-disabled';
-    }
-    if (!this.syncStatus.hasError) {
-      return 'sync';
-    }
-    // Specific error cases below.
-    if (this.syncStatus.hasUnrecoverableError) {
-      return 'sync-problem';
-    }
-    if (this.syncStatus.statusAction === StatusAction.REAUTHENTICATE) {
-      return 'sync-paused';
-    }
-    return 'sync-problem';
-  },
-
-  /**
-   * Returns the image to use for the sync status icon. The value must match
-   * one of iron-icon's settings:(*) icon names.
-   * @return {string}
-   * @private
-   */
-  getSyncIcon_() {
-    switch (this.getSyncIconStyle_()) {
-      case 'sync-problem':
-        return 'settings:sync-problem';
-      case 'sync-paused':
-        return 'settings:sync-disabled';
-      default:
-        return 'cr:sync';
-    }
-  },
-
-  /**
    * Handler for when the sync preferences are updated.
    * @private
    */
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html
index b67d70b..bbf4b6a 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html
@@ -37,7 +37,6 @@
         <cr-link-row id="smartPrivacySubpageTrigger"
             on-click="onSmartPrivacy_"
             label="$i18n{smartPrivacyTitle}"
-            sub-label="$i18n{smartPrivacySubtext}"
             role-description="$i18n{subpageArrowRoleDescription}">
         </cr-link-row>
         <div class="hr"></div>
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html b/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html
index b988b1f5..21eef57 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/smart_privacy_page.html
@@ -46,7 +46,10 @@
     </iron-icon>
   </div>
   <div>
-    $i18n{smartPrivacyDesc}
+    <localized-link
+        localized-string="$i18n{smartPrivacyDesc}"
+        link-url="$i18n{smartPrivacyLearnMoreLink}">
+    </localized-link>
   </div>
 </div>
 <template is="dom-if" if="[[isQuickDimEnabled_]]" restamp>
diff --git a/chrome/browser/resources/tab_strip/alert_indicator.html b/chrome/browser/resources/tab_strip/alert_indicator.html
index 078f7f2..e0f027e 100644
--- a/chrome/browser/resources/tab_strip/alert_indicator.html
+++ b/chrome/browser/resources/tab_strip/alert_indicator.html
@@ -1,7 +1,7 @@
 <style>
   :host {
     -webkit-mask: center / contain no-repeat;
-    background-color: currentColor;
+    background-color: var(--indicator-color);
     display: block;
     height: 100%;
     overflow: hidden;
diff --git a/chrome/browser/resources/tab_strip/drag_manager.ts b/chrome/browser/resources/tab_strip/drag_manager.ts
index 27948f0..783f0b9 100644
--- a/chrome/browser/resources/tab_strip/drag_manager.ts
+++ b/chrome/browser/resources/tab_strip/drag_manager.ts
@@ -51,9 +51,11 @@
     title: '',
     url: {url: ''},
 
-    // Remove once Mojo can produce proper TypeScript or TypeScript definitions,
-    // so that these properties are recognized as optional.
+    // TODO(crbug.com/1293911): Remove once Mojo can produce proper TypeScript
+    // or TypeScript definitions, so that these properties are recognized as
+    // optional.
     faviconUrl: undefined,
+    activeFaviconUrl: undefined,
     groupId: undefined,
   };
 }
diff --git a/chrome/browser/resources/tab_strip/tab.html b/chrome/browser/resources/tab_strip/tab.html
index 19ded05..87f40f8 100644
--- a/chrome/browser/resources/tab_strip/tab.html
+++ b/chrome/browser/resources/tab_strip/tab.html
@@ -18,6 +18,10 @@
     display: none;
   }
 
+  tabstrip-alert-indicators {
+    --indicator-color: var(--tabstrip-tab-text-color);
+  }
+
   #tab {
     background: var(--tabstrip-tab-background-color);
     border-radius: var(--tabstrip-tab-border-radius);
@@ -38,13 +42,36 @@
     box-shadow: 0 0 0 2px var(--tabstrip-focus-outline-color);
   }
 
-  :host([active]) #tab {
-    box-shadow: 0 0 0 2px var(--tabstrip-tab-active-border-color);
+  :host([active]) #title {
+    background-color: var(--tabstrip-tab-active-title-background-color);
   }
 
-  :host-context(.focus-outline-visible):host([active]) #tab:focus {
-    box-shadow: 0 0 0 4px var(--tabstrip-focus-outline-color),
-                0 0 0 2px var(--tabstrip-tab-active-border-color);
+  :host([active]) #titleText {
+    color: var(--tabstrip-tab-active-title-content-color);
+  }
+
+  :host([active]) #titleText::after {
+    background: linear-gradient(
+        to right, transparent,
+        var(--tabstrip-tab-active-title-background-color));
+  }
+
+  :host-context([dir='rtl']):host([active]) #titleText::after {
+    background: linear-gradient(
+        to left, transparent,
+        var(--tabstrip-tab-active-title-background-color));
+  }
+
+  :host([active]) tabstrip-alert-indicators {
+    --indicator-color: var(--tabstrip-tab-active-title-content-color);
+    --tabstrip-indicator-capturing-color:
+        var(--tabstrip-tab-active-title-content-color);
+    --tabstrip-indicator-pip-color:
+        var(--tabstrip-tab-active-title-content-color);
+  }
+
+  :host([active]) #closeIcon {
+    background-color: var(--tabstrip-tab-active-title-content-color);
   }
 
   #title {
@@ -160,6 +187,10 @@
               translate(-50%, -50%) scaleX(-1);
   }
 
+  :host([active][waiting_]) #progressSpinner {
+    background-color: var(--tabstrip-tab-active-title-content-color);
+  }
+
   :host([waiting_]) #favicon {
     display: none;
   }
@@ -168,6 +199,10 @@
     background-color: var(--tabstrip-tab-loading-spinning-color);
   }
 
+  :host([active][loading_]) #progressSpinner {
+    background-color: var(--tabstrip-tab-active-title-content-color);
+  }
+
   :host([crashed_]) #favicon {
     opacity: 0;
     transform: translate(-50%, 100%);
diff --git a/chrome/browser/resources/tab_strip/tab.ts b/chrome/browser/resources/tab_strip/tab.ts
index 4aafe4ee..bbc2924 100644
--- a/chrome/browser/resources/tab_strip/tab.ts
+++ b/chrome/browser/resources/tab_strip/tab.ts
@@ -143,7 +143,9 @@
          tab.isDefaultFavicon)) {
       this.faviconEl_.style.backgroundImage = 'none';
     } else if (tab.faviconUrl) {
-      this.faviconEl_.style.backgroundImage = `url(${tab.faviconUrl.url})`;
+      this.faviconEl_.style.backgroundImage = `url(${
+          tab.active && tab.activeFaviconUrl ? tab.activeFaviconUrl.url :
+                                               tab.faviconUrl.url})`;
     } else {
       this.faviconEl_.style.backgroundImage = getFavicon('');
     }
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index ecc6ab0..56167512 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -96,7 +96,7 @@
 // changed default theme assets, if you need themes to recreate their generated
 // images (which are cached), if you changed how missing values are
 // generated, or if you changed any constants.
-const int kThemePackVersion = 80;
+const int kThemePackVersion = 81;
 
 // IDs that are in the DataPack won't clash with the positive integer
 // uint16_t. kHeaderID should always have the maximum value because we want the
diff --git a/chrome/browser/themes/theme_helper.cc b/chrome/browser/themes/theme_helper.cc
index 9cb0a6d..8a44501 100644
--- a/chrome/browser/themes/theme_helper.cc
+++ b/chrome/browser/themes/theme_helper.cc
@@ -426,6 +426,29 @@
                           : TP::COLOR_FRAME_INACTIVE,
                       incognito, theme_supplier);
     }
+    case TP::COLOR_THUMBNAIL_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE: {
+      // TODO(crbug.com/1292546): Make into a recipe once we have Color
+      // Pipeline support for Theme Properties.
+      SkColor background_color =
+          GetColor(TP::COLOR_FRAME_ACTIVE, incognito, theme_supplier, nullptr);
+      // TODO(crbug.com/1292546): : Use kColorAccent when porting to color
+      // pipeline.
+      SkColor active_tab_title_color = color_utils::IsDark(background_color)
+                                           ? gfx::kGoogleBlue300
+                                           : gfx::kGoogleBlue600;
+
+      // Check if the preferred light/dark color meets desired minimum contrast
+      // against the current background color and if not, adjust alpha.
+      color_utils::BlendResult blend_color_result =
+          color_utils::BlendForMinContrast(
+              active_tab_title_color, background_color, absl::nullopt,
+              color_utils::kMinimumVisibleContrastRatio);
+      return blend_color_result.color;
+    }
+    case TP::COLOR_THUMBNAIL_TAB_FOREGROUND_ACTIVE_FRAME_ACTIVE:
+      return color_utils::GetColorWithMaxContrast(GetDefaultColor(
+          TP::COLOR_THUMBNAIL_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE, incognito,
+          theme_supplier));
   }
 
   return TP::GetDefaultColor(id, incognito, UseDarkModeColors(theme_supplier));
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index 8666884..42f1b19b 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -283,6 +283,10 @@
     COLOR_OMNIBOX_SECURITY_CHIP_SECURE,
     COLOR_OMNIBOX_SECURITY_CHIP_DANGEROUS,
 
+    // The colors used for thumbnail tab visualizations,
+    COLOR_THUMBNAIL_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE,
+    COLOR_THUMBNAIL_TAB_FOREGROUND_ACTIVE_FRAME_ACTIVE,
+
     // /!\ If you make any changes to this enum, you must also increment
     // kThemePackVersion in browser_theme_pack.cc, or else themes will display
     // incorrectly.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 42bb2ae..a3e3e00 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1957,6 +1957,8 @@
       "app_list/search/files/file_search_provider.h",
       "app_list/search/files/item_suggest_cache.cc",
       "app_list/search/files/item_suggest_cache.h",
+      "app_list/search/files/justifications.cc",
+      "app_list/search/files/justifications.h",
       "app_list/search/files/zero_state_drive_provider.cc",
       "app_list/search/files/zero_state_drive_provider.h",
       "app_list/search/files/zero_state_file_provider.cc",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 38423ca..048b7c57 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -958,6 +958,12 @@
       <message name="IDS_PRIVACY_SANDBOX_DIALOG_YES_BUTTON" desc="Button to accept enabling Privacy Sandbox features." translateable="false">
         I’m in
       </message>
+      <message name="IDS_PRIVACY_SANDBOX_DIALOG_ACKNOWLEDGE_BUTTON" desc="Button to acknowledge the notice about Privacy Sandbox features." translateable="false">
+        Got it
+      </message>
+      <message name="IDS_PRIVACY_SANDBOX_DIALOG_SETTINGS_BUTTON" desc="Button to go to Privacy Sandbox settings." translateable="false">
+        Settings
+      </message>
       <message name="IDS_PRIVACY_SANDBOX_CONSENT_TITLE" desc="Title for the Privacy Sandbox consent dialog." translateable="false">
         Help us build a more private web
       </message>
@@ -976,6 +982,9 @@
       <message name="IDS_PRIVACY_SANDBOX_CONSENT_DESCRIPTION_TWO" desc="Second paragraph for the Privacy Sandbox consent dialog text." translateable="false">
         Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
       </message>
+      <message name="IDS_PRIVACY_SANDBOX_NOTICE_TITLE" desc="Title for the Privacy Sandbox notice dialog." translateable="false">
+        Chrome is exploring new features that allow sites to deliver the same browsing experience using less of your data
+      </message>
 
       <!-- Secure DNS Settings.  Used by //chrome/browser/privacy. -->
       <message name="IDS_SETTINGS_CUSTOM" desc="Label for a custom option in a dropdown menu.">
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 2630f68bd..ff7618f 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -873,7 +873,7 @@
     notifications_enabled = deferred;
 
   WindowLayout window_layout =
-      WindowLayoutFromDict(apps->FindDictKey(kWindowLayout));
+      WindowLayoutFromDict(app->FindDictKey(kWindowLayout));
 
   return std::make_unique<AppInfo>(
       name, package_name, activity, intent_uri, icon_resource_id,
diff --git a/chrome/browser/ui/app_list/search/files/drive_search_provider.cc b/chrome/browser/ui/app_list/search/files/drive_search_provider.cc
index 4c87a713..4d829958 100644
--- a/chrome/browser/ui/app_list/search/files/drive_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/drive_search_provider.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/files/file_result.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace app_list {
 namespace {
@@ -132,7 +133,7 @@
       drive_service_->GetMountPointPath().Append(relative_path.value());
 
   return std::make_unique<FileResult>(
-      kDriveSearchSchema, reparented_path,
+      kDriveSearchSchema, reparented_path, absl::nullopt,
       ash::AppListSearchResultType::kDriveSearch,
       ash::SearchResultDisplayType::kList, relevance, last_query_, type,
       profile_);
diff --git a/chrome/browser/ui/app_list/search/files/file_result.cc b/chrome/browser/ui/app_list/search/files/file_result.cc
index ebb485b..3cd4b97 100644
--- a/chrome/browser/ui/app_list/search/files/file_result.cc
+++ b/chrome/browser/ui/app_list/search/files/file_result.cc
@@ -16,7 +16,6 @@
 #include "base/bind.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
-#include "base/i18n/rtl.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
@@ -26,10 +25,8 @@
 #include "chrome/browser/ui/app_list/search/common/icon_constants.h"
 #include "chrome/browser/ui/app_list/search/search_tags_util.h"
 #include "chrome/browser/ui/ash/thumbnail_loader.h"
-#include "chrome/grit/generated_resources.h"
 #include "chromeos/components/string_matching/tokenized_string_match.h"
 #include "third_party/skia/include/core/SkBitmap.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -86,6 +83,7 @@
 
 FileResult::FileResult(const std::string& schema,
                        const base::FilePath& filepath,
+                       const absl::optional<std::u16string>& details,
                        ResultType result_type,
                        DisplayType display_type,
                        float relevance,
@@ -129,11 +127,8 @@
       StripHostedFileExtensions(filepath.BaseName().value())));
   SetTitleTags(CalculateTags(query, title()));
 
-  // Set the details to the display name of the Files app.
-  std::u16string sanitized_name = base::CollapseWhitespace(
-      l10n_util::GetStringUTF16(IDS_FILEMANAGER_APP_NAME), true);
-  base::i18n::SanitizeUserSuppliedString(&sanitized_name);
-  SetDetails(sanitized_name);
+  if (details)
+    SetDetails(details.value());
 
   // Launcher search results UI is light by default, so use icons for light
   // background if dark/light mode feature is not enabled.
diff --git a/chrome/browser/ui/app_list/search/files/file_result.h b/chrome/browser/ui/app_list/search/files/file_result.h
index ee66c05..e64d1c8f 100644
--- a/chrome/browser/ui/app_list/search/files/file_result.h
+++ b/chrome/browser/ui/app_list/search/files/file_result.h
@@ -28,6 +28,7 @@
 
   FileResult(const std::string& schema,
              const base::FilePath& filepath,
+             const absl::optional<std::u16string>& details,
              ResultType result_type,
              DisplayType display_type,
              float relevance,
diff --git a/chrome/browser/ui/app_list/search/files/file_result_unittest.cc b/chrome/browser/ui/app_list/search/files/file_result_unittest.cc
index 01586b9..bffd073 100644
--- a/chrome/browser/ui/app_list/search/files/file_result_unittest.cc
+++ b/chrome/browser/ui/app_list/search/files/file_result_unittest.cc
@@ -14,6 +14,7 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace app_list {
 
@@ -32,13 +33,14 @@
 };
 
 TEST_F(FileResultTest, CheckMetadata) {
-  FileResult result("zero_state_file://",
-                    base::FilePath("/my/test/MIXED_case_FILE.Pdf"),
-                    ash::AppListSearchResultType::kZeroStateFile,
-                    ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
-                    FileResult::Type::kFile, profile_.get());
+  FileResult result(
+      "zero_state_file://", base::FilePath("/my/test/MIXED_case_FILE.Pdf"),
+      u"some details", ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
+      FileResult::Type::kFile, profile_.get());
   EXPECT_EQ(base::UTF16ToUTF8(result.title()),
             std::string("MIXED_case_FILE.Pdf"));
+  EXPECT_EQ(result.details(), u"some details");
   EXPECT_EQ(result.id(), "zero_state_file:///my/test/MIXED_case_FILE.Pdf");
   EXPECT_EQ(result.result_type(), ash::AppListSearchResultType::kZeroStateFile);
   EXPECT_EQ(result.display_type(), ash::SearchResultDisplayType::kList);
@@ -46,16 +48,16 @@
 }
 
 TEST_F(FileResultTest, HostedExtensionsIgnored) {
-  FileResult result_1("zero_state_file://", base::FilePath("my/Document.gdoc"),
-                      ash::AppListSearchResultType::kZeroStateFile,
-                      ash::SearchResultDisplayType::kList, 0.2f,
-                      std::u16string(), FileResult::Type::kFile,
-                      profile_.get());
-  FileResult result_2("zero_state_file://", base::FilePath("my/Map.gmaps"),
-                      ash::AppListSearchResultType::kZeroStateFile,
-                      ash::SearchResultDisplayType::kList, 0.2f,
-                      std::u16string(), FileResult::Type::kFile,
-                      profile_.get());
+  FileResult result_1(
+      "zero_state_file://", base::FilePath("my/Document.gdoc"), absl::nullopt,
+      ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
+      FileResult::Type::kFile, profile_.get());
+  FileResult result_2(
+      "zero_state_file://", base::FilePath("my/Map.gmaps"), absl::nullopt,
+      ash::AppListSearchResultType::kZeroStateFile,
+      ash::SearchResultDisplayType::kList, 0.2f, std::u16string(),
+      FileResult::Type::kFile, profile_.get());
 
   EXPECT_EQ(base::UTF16ToUTF8(result_1.title()), std::string("Document"));
   EXPECT_EQ(base::UTF16ToUTF8(result_2.title()), std::string("Map"));
diff --git a/chrome/browser/ui/app_list/search/files/file_search_provider.cc b/chrome/browser/ui/app_list/search/files/file_search_provider.cc
index b631abe..51409f8 100644
--- a/chrome/browser/ui/app_list/search/files/file_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/file_search_provider.cc
@@ -8,6 +8,7 @@
 #include <cmath>
 
 #include "base/files/file_enumerator.h"
+#include "base/i18n/rtl.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
@@ -17,6 +18,8 @@
 #include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/files/file_result.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace app_list {
 namespace {
@@ -155,10 +158,16 @@
     const double relevance) {
   const auto type = path.is_directory ? FileResult::Type::kDirectory
                                       : FileResult::Type::kFile;
-  auto result = std::make_unique<FileResult>(
-      kFileSearchSchema, path.path, ash::AppListSearchResultType::kFileSearch,
-      ash::SearchResultDisplayType::kList, relevance, last_query_, type,
-      profile_);
+  // Use the display name of the Files app as the details text.
+  std::u16string details_text = base::CollapseWhitespace(
+      l10n_util::GetStringUTF16(IDS_FILEMANAGER_APP_NAME), true);
+  base::i18n::SanitizeUserSuppliedString(&details_text);
+
+  auto result =
+      std::make_unique<FileResult>(kFileSearchSchema, path.path, details_text,
+                                   ash::AppListSearchResultType::kFileSearch,
+                                   ash::SearchResultDisplayType::kList,
+                                   relevance, last_query_, type, profile_);
   result->RequestThumbnail(&thumbnail_loader_);
   return result;
 }
diff --git a/chrome/browser/ui/app_list/search/files/justifications.cc b/chrome/browser/ui/app_list/search/files/justifications.cc
new file mode 100644
index 0000000..ea3c02f
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/files/justifications.cc
@@ -0,0 +1,75 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/files/justifications.h"
+
+#include "ash/strings/grit/ash_strings.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/time/time.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace app_list {
+namespace {
+
+// Time limits for how last accessed or modified time maps to each justification
+// string.
+constexpr base::TimeDelta kJustNow = base::Minutes(15);
+constexpr base::TimeDelta kToday = base::Days(1);
+constexpr base::TimeDelta kYesterday = base::Days(2);
+constexpr base::TimeDelta kPastWeek = base::Days(7);
+constexpr base::TimeDelta kPastMonth = base::Days(31);
+
+absl::optional<std::u16string> GetEditStringFromTime(const base::Time& time) {
+  const auto& delta = base::Time::Now() - time;
+  if (delta <= kJustNow) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_EDITED_JUST_NOW);
+  } else if (delta <= kToday) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_EDITED_TODAY);
+  } else if (delta <= kYesterday) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_EDITED_YESTERDAY);
+  } else if (delta <= kPastWeek) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_EDITED_PAST_WEEK);
+  } else if (delta <= kPastMonth) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_EDITED_PAST_MONTH);
+  } else {
+    return absl::nullopt;
+  }
+}
+
+absl::optional<std::u16string> GetOpenStringFromTime(const base::Time& time) {
+  const auto& delta = base::Time::Now() - time;
+  if (delta <= kJustNow) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_OPENED_JUST_NOW);
+  } else if (delta <= kToday) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_OPENED_TODAY);
+  } else if (delta <= kYesterday) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_OPENED_YESTERDAY);
+  } else if (delta <= kPastWeek) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_OPENED_PAST_WEEK);
+  } else if (delta <= kPastMonth) {
+    return l10n_util::GetStringUTF16(IDS_APP_LIST_CONTINUE_OPENED_PAST_MONTH);
+  } else {
+    return absl::nullopt;
+  }
+}
+
+}  // namespace
+
+absl::optional<std::u16string> GetJustificationString(
+    const base::FilePath& path) {
+  base::File::Info info;
+  if (!base::GetFileInfo(path, &info)) {
+    return absl::nullopt;
+  }
+
+  // t1 > t2 means t1 is more recent. When there's a tie, choose modified.
+  if (info.last_modified >= info.last_accessed) {
+    return GetEditStringFromTime(info.last_modified);
+  } else {
+    return GetOpenStringFromTime(info.last_accessed);
+  }
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/files/justifications.h b/chrome/browser/ui/app_list/search/files/justifications.h
new file mode 100644
index 0000000..0d25814
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/files/justifications.h
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_FILES_JUSTIFICATIONS_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_FILES_JUSTIFICATIONS_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace app_list {
+
+// Returns an appropriate justification string for displaying |path| as a file
+// suggestion, for example "you opened yesterday".
+//
+// This uses the most recent of the edited and opened times, and returns a
+// different message for times within the last few minutes, day, two days, week,
+// and month. If there is an error stating |path| or the time is longer than a
+// month ago, nullopt is returned.
+absl::optional<std::u16string> GetJustificationString(
+    const base::FilePath& path);
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_FILES_JUSTIFICATIONS_H_
diff --git a/chrome/browser/ui/app_list/search/files/justifications_unittest.cc b/chrome/browser/ui/app_list/search/files/justifications_unittest.cc
new file mode 100644
index 0000000..c677f3b
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/files/justifications_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/files/justifications.h"
+
+#include "ash/strings/grit/ash_strings.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace app_list {
+
+class JustificationsTest : public testing::Test {
+ protected:
+  JustificationsTest() = default;
+  ~JustificationsTest() override = default;
+
+  void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
+  // Returns a file path with its last_accessed set to |now - accessed_ago|, and
+  // modified set to |now - modified_ago|.
+  base::FilePath TouchFile(base::TimeDelta accessed_ago,
+                           base::TimeDelta modified_ago) {
+    auto now = base::Time::Now();
+    base::FilePath path(temp_dir_.GetPath().Append("somefile"));
+    base::WriteFile(path, "content");
+    base::TouchFile(path, now - accessed_ago, now - modified_ago);
+    return path;
+  }
+
+  base::FilePath TouchFile(base::TimeDelta time) {
+    return TouchFile(time, time);
+  }
+
+  std::u16string Localized(int id) { return l10n_util::GetStringUTF16(id); }
+
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(JustificationsTest, OpenedOrEdited) {
+  // Opened more recently than edited.
+  EXPECT_EQ(
+      GetJustificationString(TouchFile(base::Seconds(10), base::Seconds(30))),
+      Localized(IDS_APP_LIST_CONTINUE_OPENED_JUST_NOW));
+
+  // Edited more recently than opened.
+  EXPECT_EQ(
+      GetJustificationString(TouchFile(base::Seconds(30), base::Seconds(10))),
+      Localized(IDS_APP_LIST_CONTINUE_EDITED_JUST_NOW));
+
+  // When tied, should be edited.
+  EXPECT_EQ(
+      GetJustificationString(TouchFile(base::Seconds(30), base::Seconds(30))),
+      Localized(IDS_APP_LIST_CONTINUE_EDITED_JUST_NOW));
+}
+
+TEST_F(JustificationsTest, EditTimes) {
+  // Just now.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Minutes(5))),
+            Localized(IDS_APP_LIST_CONTINUE_EDITED_JUST_NOW));
+
+  // Today.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Hours(23))),
+            Localized(IDS_APP_LIST_CONTINUE_EDITED_TODAY));
+
+  // Yesterday.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Hours(47))),
+            Localized(IDS_APP_LIST_CONTINUE_EDITED_YESTERDAY));
+
+  // Past week.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Days(6))),
+            Localized(IDS_APP_LIST_CONTINUE_EDITED_PAST_WEEK));
+
+  // Past month.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Days(30))),
+            Localized(IDS_APP_LIST_CONTINUE_EDITED_PAST_MONTH));
+
+  // No string, file too old.
+  EXPECT_FALSE(GetJustificationString(TouchFile(base::Days(32))));
+}
+
+TEST_F(JustificationsTest, OpenTimes) {
+  // Just now.
+  EXPECT_EQ(
+      GetJustificationString(TouchFile(base::Minutes(5), base::Days(100))),
+      Localized(IDS_APP_LIST_CONTINUE_OPENED_JUST_NOW));
+
+  // Today.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Hours(23), base::Days(100))),
+            Localized(IDS_APP_LIST_CONTINUE_OPENED_TODAY));
+
+  // Yesterday.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Hours(47), base::Days(100))),
+            Localized(IDS_APP_LIST_CONTINUE_OPENED_YESTERDAY));
+
+  // Past week.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Days(6), base::Days(100))),
+            Localized(IDS_APP_LIST_CONTINUE_OPENED_PAST_WEEK));
+
+  // Past month.
+  EXPECT_EQ(GetJustificationString(TouchFile(base::Days(30), base::Days(100))),
+            Localized(IDS_APP_LIST_CONTINUE_OPENED_PAST_MONTH));
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
index ed032ae..a5991fe 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
@@ -22,6 +22,7 @@
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/files/justifications.h"
 #include "chrome/browser/ui/app_list/search/ranking/util.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
@@ -31,6 +32,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace app_list {
 namespace {
@@ -298,8 +300,9 @@
     const base::FilePath& filepath,
     const absl::optional<std::string>& prediction_reason,
     const float relevance) {
+  const auto reparented_path = ReparentToDriveMount(filepath, drive_service_);
   auto result = std::make_unique<FileResult>(
-      kSchema, ReparentToDriveMount(filepath, drive_service_),
+      kSchema, reparented_path, GetJustificationString(reparented_path),
       ash::AppListSearchResultType::kZeroStateDrive, GetDisplayType(),
       relevance, std::u16string(), FileResult::Type::kFile, profile_);
   // If it exists, override the details text with the prediction reason in the
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
index 4e424d5..8335ff0 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
@@ -15,6 +15,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "base/task/task_runner_util.h"
 #include "base/task/task_traits.h"
@@ -23,6 +24,7 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/search/files/file_result.h"
+#include "chrome/browser/ui/app_list/search/files/justifications.h"
 #include "chrome/browser/ui/app_list/search/ranking/util.h"
 #include "chrome/browser/ui/app_list/search/util/persistent_proto.h"
 #include "components/prefs/pref_service.h"
@@ -140,10 +142,10 @@
   // Use valid results for search results.
   SearchProvider::Results new_results;
   for (size_t i = 0; i < std::min(valid_results.size(), kMaxLocalFiles); ++i) {
-    const auto& filepath_score = valid_results[i];
-    double score = filepath_score.second;
+    const auto& filepath = valid_results[i].first;
+    double score = valid_results[i].second;
     auto result = std::make_unique<FileResult>(
-        kSchema, filepath_score.first,
+        kSchema, filepath, GetJustificationString(filepath),
         ash::AppListSearchResultType::kZeroStateFile, GetDisplayType(), score,
         std::u16string(), FileResult::Type::kFile, profile_);
     // TODO(crbug.com/1258415): Only generate thumbnails if the old launcher is
@@ -195,7 +197,7 @@
         kSchema,
         base::FilePath(FILE_PATH_LITERAL(
             base::StrCat({"Fake-file-", base::NumberToString(i), ".png"}))),
-        ash::AppListSearchResultType::kZeroStateFile,
+        u"-", ash::AppListSearchResultType::kZeroStateFile,
         ash::SearchResultDisplayType::kContinue, 0.1f, std::u16string(),
         FileResult::Type::kFile, profile_));
   }
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc
index 52c1a60a..85c0a431 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc
@@ -9,8 +9,7 @@
 // TODO(crbug.com/1290682): Continue implementation.
 KeyboardShortcutData::KeyboardShortcutData(
     const ash::KeyboardShortcutItem& item)
-    : description_message(
-          l10n_util::GetStringUTF16(item.description_message_id)) {}
+    : description(l10n_util::GetStringUTF16(item.description_message_id)) {}
 
 KeyboardShortcutData::~KeyboardShortcutData() = default;
 
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h
index 5038bb0..aa6c5ca 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h
@@ -27,13 +27,15 @@
 // TODO(crbug.com/1290682): Complete implementation.
 struct KeyboardShortcutData {
   explicit KeyboardShortcutData(const ash::KeyboardShortcutItem& item);
-  KeyboardShortcutData(const KeyboardShortcutData&) = delete;
-  KeyboardShortcutData& operator=(const KeyboardShortcutData&) = delete;
+  // For testing purposes.
+  explicit KeyboardShortcutData(const std::u16string description);
+  KeyboardShortcutData(const KeyboardShortcutData&) = default;
+  KeyboardShortcutData& operator=(const KeyboardShortcutData&) = default;
 
   ~KeyboardShortcutData();
 
   // The description of the shortcut action e.g. "Dock a window on the right".
-  const std::u16string description_message;
+  std::u16string description;
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.cc b/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.cc
index 207557f..d36a6d1 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.cc
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.cc
@@ -4,25 +4,93 @@
 
 #include "chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h"
 
+#include <algorithm>
+
 #include "ash/public/cpp/app_list/app_list_types.h"
+#include "ash/public/cpp/keyboard_shortcut_item.h"
+#include "ash/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
+#include "base/bind.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/search/keyboard_shortcut_result.h"
 #include "chrome/browser/ui/app_list/search/search_controller.h"
 
 namespace app_list {
 
+namespace {
+
+using chromeos::string_matching::TokenizedString;
+
+constexpr size_t kMaxResults = 10;
+constexpr double kResultRelevanceThreshold = 0.5;
+
+}  // namespace
+
 KeyboardShortcutProvider::KeyboardShortcutProvider(Profile* profile)
     : profile_(profile) {
   DCHECK(profile_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  ProcessShortcutList();
 }
 
 KeyboardShortcutProvider::~KeyboardShortcutProvider() = default;
 
 void KeyboardShortcutProvider::Start(const std::u16string& query) {
-  // TODO(crbug.com/1290682): Implement.
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  last_query_ = query;
+  last_tokenized_query_.emplace(query, TokenizedString::Mode::kWords);
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
+      base::BindOnce(&KeyboardShortcutProvider::Search, base::Unretained(this)),
+      base::BindOnce(&KeyboardShortcutProvider::OnSearchComplete,
+                     weak_factory_.GetWeakPtr()));
 }
 
 ash::AppListSearchResultType KeyboardShortcutProvider::ResultType() const {
   return ash::AppListSearchResultType::kKeyboardShortcut;
 }
 
+void KeyboardShortcutProvider::ProcessShortcutList() {
+  DCHECK(shortcut_data_.empty());
+  for (const auto& item :
+       keyboard_shortcut_viewer::GetKeyboardShortcutItemList()) {
+    shortcut_data_.push_back(KeyboardShortcutData(item));
+  }
+}
+
+KeyboardShortcutProvider::ShortcutDataAndScores
+KeyboardShortcutProvider::Search() {
+  // Find all shortcuts which meet the relevance threshold.
+  KeyboardShortcutProvider::ShortcutDataAndScores candidates;
+  for (const auto& shortcut : shortcut_data_) {
+    double relevance = KeyboardShortcutResult::CalculateRelevance(
+        last_tokenized_query_.value(), shortcut.description);
+    if (relevance > kResultRelevanceThreshold) {
+      candidates.push_back(std::make_pair(shortcut, relevance));
+    }
+  }
+  return candidates;
+}
+
+void KeyboardShortcutProvider::OnSearchComplete(
+    KeyboardShortcutProvider::ShortcutDataAndScores candidates) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // Sort candidates by descending relevance score.
+  std::sort(candidates.begin(), candidates.end(),
+            [](auto& a, auto& b) { return a.second > b.second; });
+
+  // Convert final candidates into correct type, and publish.
+  SearchProvider::Results results;
+  for (size_t i = 0; i < std::min(candidates.size(), kMaxResults); ++i) {
+    results.push_back(std::make_unique<KeyboardShortcutResult>(
+        candidates[i].first, candidates[i].second));
+  }
+  SwapResults(&results);
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h b/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h
index 110d9f0..9bc0458 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h
@@ -5,7 +5,11 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_KEYBOARD_SHORTCUT_PROVIDER_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_KEYBOARD_SHORTCUT_PROVIDER_H_
 
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "chrome/browser/ui/app_list/search/keyboard_shortcut_data.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
+#include "chromeos/components/string_matching/tokenized_string.h"
 
 class Profile;
 
@@ -24,7 +28,28 @@
   ash::AppListSearchResultType ResultType() const override;
 
  private:
+  using ShortcutDataAndScores =
+      std::vector<std::pair<KeyboardShortcutData, double>>;
+
+  // Fetch the list of hardcoded shortcuts, process, and save into
+  // |shortcut_data_|.
+  void ProcessShortcutList();
+
+  ShortcutDataAndScores Search();
+  void OnSearchComplete(ShortcutDataAndScores);
+
   Profile* const profile_;
+
+  // A full collection of keyboard shortcuts, against which a query is compared
+  // during a search.
+  std::vector<KeyboardShortcutData> shortcut_data_;
+
+  std::u16string last_query_;
+  absl::optional<chromeos::string_matching::TokenizedString>
+      last_tokenized_query_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<KeyboardShortcutProvider> weak_factory_{this};
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_provider_unittest.cc b/chrome/browser/ui/app_list/search/keyboard_shortcut_provider_unittest.cc
new file mode 100644
index 0000000..3d6f21f6
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_provider_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h"
+
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace app_list {
+
+class KeyboardShortcutProviderTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    profile_ = std::make_unique<TestingProfile>();
+    provider_ = std::make_unique<KeyboardShortcutProvider>(profile_.get());
+    Wait();
+  }
+
+  void Wait() { task_environment_.RunUntilIdle(); }
+
+  content::BrowserTaskEnvironment task_environment_;
+
+  std::unique_ptr<Profile> profile_;
+  std::unique_ptr<KeyboardShortcutProvider> provider_;
+};
+
+TEST_F(KeyboardShortcutProviderTest, Search) {
+  provider_->Start(u"loc");
+  Wait();
+
+  ASSERT_GT(provider_->results().size(), 0);
+  EXPECT_EQ(provider_->results()[0]->title(), u"Lock screen");
+  EXPECT_GT(provider_->results()[0]->relevance(), 0.8);
+}
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc b/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc
index 6a9e72a..1dee314 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/ui/app_list/search/keyboard_shortcut_result.h"
 
+#include "base/i18n/rtl.h"
+#include "base/strings/string_util.h"
+#include "chrome/grit/generated_resources.h"
 #include "chromeos/components/string_matching/tokenized_string_match.h"
 
 namespace app_list {
@@ -18,8 +21,20 @@
 // TODO(crbug.com/1290682): Complete implementation.
 KeyboardShortcutResult::KeyboardShortcutResult(const KeyboardShortcutData& data,
                                                double relevance)
-    : description_(data.description_message) {
+    : description_(data.description) {
   set_relevance(relevance);
+  SetTitle(data.description);
+  SetResultType(ResultType::kKeyboardShortcut);
+  SetMetricsType(ash::KEYBOARD_SHORTCUT);
+  SetDisplayType(DisplayType::kList);
+  SetCategory(Category::kHelp);
+
+  // Set the details to the display name of the Keyboard Shortcut Viewer app.
+  std::u16string sanitized_name = base::CollapseWhitespace(
+      l10n_util::GetStringUTF16(IDS_INTERNAL_APP_KEYBOARD_SHORTCUT_VIEWER),
+      true);
+  base::i18n::SanitizeUserSuppliedString(&sanitized_name);
+  SetDetails(sanitized_name);
 }
 
 // TODO(crbug.com/1290682): Implement.
diff --git a/chrome/browser/ui/ash/login_screen_client_impl.cc b/chrome/browser/ui/ash/login_screen_client_impl.cc
index 56e2a24..e62c56a 100644
--- a/chrome/browser/ui/ash/login_screen_client_impl.cc
+++ b/chrome/browser/ui/ash/login_screen_client_impl.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ash/login/saml/in_session_password_sync_manager_factory.h"
 #include "chrome/browser/ash/login/startup_utils.h"
 #include "chrome/browser/ash/login/ui/login_display_host.h"
+#include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/login/ui/user_adding_screen.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_metrics.h"
@@ -359,6 +360,13 @@
   }
 }
 
+views::Widget* LoginScreenClientImpl::GetLoginWindowWidget() {
+  if (ash::LoginDisplayHost::default_host()) {
+    return ash::LoginDisplayHost::default_host()->GetLoginWindowWidget();
+  }
+  return nullptr;
+}
+
 void LoginScreenClientImpl::OnParentAccessValidation(
     const AccountId& prefilled_account,
     bool success) {
diff --git a/chrome/browser/ui/ash/login_screen_client_impl.h b/chrome/browser/ui/ash/login_screen_client_impl.h
index ee38675..9e87c63 100644
--- a/chrome/browser/ui/ash/login_screen_client_impl.h
+++ b/chrome/browser/ui/ash/login_screen_client_impl.h
@@ -133,6 +133,7 @@
   void OnSystemTrayBubbleShown() override;
   void OnLoginScreenShown() override;
   void OnUserActivity() override;
+  views::Widget* GetLoginWindowWidget() override;
 
  private:
   void SetPublicSessionKeyboardLayout(
diff --git a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc
index d79443c..9e7da4b 100644
--- a/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc
@@ -239,7 +239,8 @@
   // InstanceRegistry, call MaybeModifyInstance to check the saved app id and
   // the expected shelf_app_id, and if they are not consistent, modify the app
   // id to use `shelf_app_id`.
-  MaybeModifyInstance(primary_account_profile, window, shelf_app_id);
+  if (!shelf_app_id.empty())
+    MaybeModifyInstance(primary_account_profile, window, shelf_app_id);
   return shelf_app_id;
 }
 
diff --git a/chrome/browser/ui/ash/test_login_screen.cc b/chrome/browser/ui/ash/test_login_screen.cc
index b7367517..55653dac 100644
--- a/chrome/browser/ui/ash/test_login_screen.cc
+++ b/chrome/browser/ui/ash/test_login_screen.cc
@@ -69,3 +69,7 @@
 }
 
 void TestLoginScreen::ClearLoginShelfGestureHandler() {}
+
+views::Widget* TestLoginScreen::GetLoginWindowWidget() {
+  return nullptr;
+}
diff --git a/chrome/browser/ui/ash/test_login_screen.h b/chrome/browser/ui/ash/test_login_screen.h
index c9f661a..233479d5 100644
--- a/chrome/browser/ui/ash/test_login_screen.h
+++ b/chrome/browser/ui/ash/test_login_screen.h
@@ -48,6 +48,7 @@
                                    const base::RepeatingClosure& fling_callback,
                                    base::OnceClosure exit_callback) override;
   void ClearLoginShelfGestureHandler() override;
+  views::Widget* GetLoginWindowWidget() override;
 
  private:
   TestLoginScreenModel test_screen_model_;
diff --git a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc
index 80d80d4..db83ede 100644
--- a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc
+++ b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.h"
 
+#include <string>
 #include <utility>
 
 #include "base/memory/ptr_util.h"
@@ -16,7 +17,9 @@
 #include "chrome/browser/ui/tab_sharing/tab_sharing_ui.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/infobars/content/content_infobar_manager.h"
+#include "components/infobars/core/confirm_infobar_delegate.h"
 #include "components/infobars/core/infobar.h"
+#include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/render_frame_host.h"
@@ -24,21 +27,37 @@
 #include "net/base/url_util.h"
 #include "ui/base/l10n/l10n_util.h"
 
-class TabSharingInfoBarDelegateButton {
+class TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
  public:
+  TabSharingInfoBarDelegateButton() = default;
+
+  // Deleted copy and assignment operator.
+  TabSharingInfoBarDelegateButton(const TabSharingInfoBarDelegateButton&) =
+      delete;
+  TabSharingInfoBarDelegateButton& operator=(
+      const TabSharingInfoBarDelegateButton&) = delete;
+
   virtual ~TabSharingInfoBarDelegateButton() = default;
   virtual void Click(infobars::InfoBar* infobar) = 0;
   virtual std::u16string GetLabel() const = 0;
-  virtual ui::ImageModel GetImage() const { return ui::ImageModel(); }
+  virtual ui::ImageModel GetImage() const { return {}; }
+  virtual bool IsEnabled() const { return true; }
+  virtual std::u16string GetTooltip() const { return u""; }
 };
 
 namespace {
 
 // Represents a button which, when clicked, changes the shared tab to be
 // the current tab (the one associated with this infobar.)
-class ShareTabInsteadButton : public TabSharingInfoBarDelegateButton {
+class ShareTabInsteadButton
+    : public TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
  public:
-  explicit ShareTabInsteadButton(TabSharingUI* ui) : ui_(ui) {}
+  ShareTabInsteadButton(TabSharingUI* ui,
+                        TabSharingInfoBarDelegate::ButtonState
+                            share_this_tab_instead_button_state)
+      : ui_(ui),
+        share_this_tab_instead_button_state_(
+            share_this_tab_instead_button_state) {}
 
   ~ShareTabInsteadButton() override = default;
 
@@ -51,14 +70,30 @@
     return l10n_util::GetStringUTF16(IDS_TAB_SHARING_INFOBAR_SHARE_BUTTON);
   }
 
+  bool IsEnabled() const override {
+    return share_this_tab_instead_button_state_ ==
+           TabSharingInfoBarDelegate::ButtonState::ENABLED;
+  }
+
+  std::u16string GetTooltip() const override {
+    return share_this_tab_instead_button_state_ ==
+                   TabSharingInfoBarDelegate::ButtonState::DISABLED
+               ? l10n_util::GetStringUTF16(
+                     IDS_POLICY_DLP_SCREEN_SHARE_BLOCKED_TITLE)
+               : u"";
+  }
+
  private:
   const raw_ptr<TabSharingUI> ui_;
+  const TabSharingInfoBarDelegate::ButtonState
+      share_this_tab_instead_button_state_;
 };
 
 // Represents a button which, when clicked, changes the activated tab to be
 // the one which was hard-coded into this infobar. The intended use for this
 // class is for the captured tab to activate the capturing tab, and vice versa.
-class SwitchToTabButton : public TabSharingInfoBarDelegateButton {
+class SwitchToTabButton
+    : public TabSharingInfoBarDelegate::TabSharingInfoBarDelegateButton {
  public:
   SwitchToTabButton(const TabSharingInfoBarDelegate::FocusTarget& focus_target,
                     bool focus_target_is_capturer)
@@ -120,22 +155,23 @@
     const std::u16string& shared_tab_name,
     const std::u16string& app_name,
     bool shared_tab,
-    bool can_share_instead,
+    ButtonState share_this_tab_instead_button_state,
     absl::optional<FocusTarget> focus_target,
     TabSharingUI* ui,
     bool favicons_used_for_switch_to_tab_button) {
   DCHECK(infobar_manager);
   return infobar_manager->AddInfoBar(
       CreateConfirmInfoBar(base::WrapUnique(new TabSharingInfoBarDelegate(
-          shared_tab_name, app_name, shared_tab, can_share_instead,
-          focus_target, ui, favicons_used_for_switch_to_tab_button))));
+          shared_tab_name, app_name, shared_tab,
+          share_this_tab_instead_button_state, focus_target, ui,
+          favicons_used_for_switch_to_tab_button))));
 }
 
 TabSharingInfoBarDelegate::TabSharingInfoBarDelegate(
     std::u16string shared_tab_name,
     std::u16string app_name,
     bool shared_tab,
-    bool can_share_instead,
+    ButtonState share_this_tab_instead_button_state,
     absl::optional<FocusTarget> focus_target,
     TabSharingUI* ui,
     bool favicons_used_for_switch_to_tab_button)
@@ -148,8 +184,9 @@
   if (focus_target.has_value()) {
     secondary_button_ =
         std::make_unique<SwitchToTabButton>(*focus_target, shared_tab);
-  } else if (can_share_instead) {
-    secondary_button_ = std::make_unique<ShareTabInsteadButton>(ui_);
+  } else if (share_this_tab_instead_button_state != ButtonState::NOT_SHOWN) {
+    secondary_button_ = std::make_unique<ShareTabInsteadButton>(
+        ui_, share_this_tab_instead_button_state);
   }
 }
 
@@ -200,7 +237,24 @@
     DCHECK(secondary_button_);
     return secondary_button_->GetImage();
   }
-  return ui::ImageModel();
+  return ConfirmInfoBarDelegate::GetButtonImage(button);
+}
+
+bool TabSharingInfoBarDelegate::GetButtonEnabled(InfoBarButton button) const {
+  if (button == BUTTON_CANCEL) {
+    DCHECK(secondary_button_);
+    return secondary_button_->IsEnabled();
+  }
+  return ConfirmInfoBarDelegate::GetButtonEnabled(button);
+}
+
+std::u16string TabSharingInfoBarDelegate::GetButtonTooltip(
+    InfoBarButton button) const {
+  if (button == BUTTON_CANCEL) {
+    DCHECK(secondary_button_);
+    return secondary_button_->GetTooltip();
+  }
+  return ConfirmInfoBarDelegate::GetButtonTooltip(button);
 }
 
 int TabSharingInfoBarDelegate::GetButtons() const {
diff --git a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.h b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.h
index cc2243e..637bd81 100644
--- a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.h
+++ b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_TAB_SHARING_TAB_SHARING_INFOBAR_DELEGATE_H_
 #define CHROME_BROWSER_UI_TAB_SHARING_TAB_SHARING_INFOBAR_DELEGATE_H_
 
+#include <string>
+
 #include "base/memory/raw_ptr.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
 #include "content/public/browser/global_routing_id.h"
@@ -15,7 +17,6 @@
 class InfoBar;
 }
 
-class TabSharingInfoBarDelegateButton;
 class TabSharingUI;
 
 // Creates an infobar for sharing a tab using desktopCapture() API; one delegate
@@ -26,8 +27,8 @@
 //
 // 2. Layout for capturing/captured tab:
 // "Sharing |shared_tab_name_| to |app_name_| [Stop] [Switch-Label]"
-// Where [Switch-Label] is "Switch to tab <hostname>", with the hostname for
-// in the captured tab being the capturer's, and vice versa.
+// Where [Switch-Label] is "Switch to tab <hostname>", with the hostname in
+// the captured tab being the capturer's, and vice versa.
 //
 // 3a. Layout for all other tabs:
 // "Sharing |shared_tab_name_| to |app_name_| [Stop] [Share this tab instead]"
@@ -41,6 +42,14 @@
     ui::ImageModel icon;
   };
 
+  enum class ButtonState {
+    ENABLED,
+    DISABLED,
+    NOT_SHOWN,
+  };
+
+  class TabSharingInfoBarDelegateButton;
+
   // Creates a tab sharing infobar, which has 1-2 buttons.
   //
   // The primary button is for stopping the capture. It is always present.
@@ -55,7 +64,7 @@
       const std::u16string& shared_tab_name,
       const std::u16string& app_name,
       bool shared_tab,
-      bool can_share_instead,
+      ButtonState share_this_tab_instead_button_state,
       absl::optional<FocusTarget> focus_target,
       TabSharingUI* ui,
       bool favicons_used_for_switch_to_tab_button = false);
@@ -66,7 +75,7 @@
   TabSharingInfoBarDelegate(std::u16string shared_tab_name,
                             std::u16string app_name,
                             bool shared_tab,
-                            bool can_share_instead,
+                            ButtonState share_this_tab_instead_button_state,
                             absl::optional<FocusTarget> focus_target,
                             TabSharingUI* ui,
                             bool favicons_used_for_switch_to_tab_button);
@@ -78,6 +87,8 @@
   std::u16string GetMessageText() const override;
   std::u16string GetButtonLabel(InfoBarButton button) const override;
   ui::ImageModel GetButtonImage(InfoBarButton button) const override;
+  bool GetButtonEnabled(InfoBarButton button) const override;
+  std::u16string GetButtonTooltip(InfoBarButton button) const override;
   int GetButtons() const override;
   bool Accept() override;
   bool Cancel() override;
diff --git a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
index 28aa0ec..6df35805 100644
--- a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
+++ b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
@@ -60,8 +60,11 @@
     return TabSharingInfoBarDelegate::Create(
         infobars::ContentInfoBarManager::FromWebContents(
             browser()->tab_strip_model()->GetWebContentsAt(tab_index)),
-        shared_tab_name, app_name, shared_tab, can_share_instead, focus_target,
-        tab_sharing_mock_ui(), favicons_used_for_switch_to_tab_button_);
+        shared_tab_name, app_name, shared_tab,
+        can_share_instead ? TabSharingInfoBarDelegate::ButtonState::ENABLED
+                          : TabSharingInfoBarDelegate::ButtonState::NOT_SHOWN,
+        focus_target, tab_sharing_mock_ui(),
+        favicons_used_for_switch_to_tab_button_);
   }
 
   ConfirmInfoBarDelegate* CreateDelegate(
diff --git a/chrome/browser/ui/views/infobars/confirm_infobar.cc b/chrome/browser/ui/views/infobars/confirm_infobar.cc
index 072c34b1..0aaa79d 100644
--- a/chrome/browser/ui/views/infobars/confirm_infobar.cc
+++ b/chrome/browser/ui/views/infobars/confirm_infobar.cc
@@ -48,16 +48,28 @@
           ok_button_,
           base::BindOnce(&ConfirmInfoBar::Layout, base::Unretained(this)));
     }
+    ok_button_->SetImageModel(
+        views::Button::STATE_NORMAL,
+        delegate_ptr->GetButtonImage(ConfirmInfoBarDelegate::BUTTON_OK));
+    ok_button_->SetEnabled(
+        delegate_ptr->GetButtonEnabled(ConfirmInfoBarDelegate::BUTTON_OK));
+    ok_button_->SetTooltipText(
+        delegate_ptr->GetButtonTooltip(ConfirmInfoBarDelegate::BUTTON_OK));
   }
 
   if (buttons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
     cancel_button_ = create_button(ConfirmInfoBarDelegate::BUTTON_CANCEL,
                                    &ConfirmInfoBar::CancelButtonPressed);
-    if (buttons == ConfirmInfoBarDelegate::BUTTON_CANCEL)
+    if (buttons == ConfirmInfoBarDelegate::BUTTON_CANCEL) {
       cancel_button_->SetProminent(true);
+    }
     cancel_button_->SetImageModel(
         views::Button::STATE_NORMAL,
         delegate_ptr->GetButtonImage(ConfirmInfoBarDelegate::BUTTON_CANCEL));
+    cancel_button_->SetEnabled(
+        delegate_ptr->GetButtonEnabled(ConfirmInfoBarDelegate::BUTTON_CANCEL));
+    cancel_button_->SetTooltipText(
+        delegate_ptr->GetButtonTooltip(ConfirmInfoBarDelegate::BUTTON_CANCEL));
   }
 
   link_ = CreateLink(delegate_ptr->GetLinkText());
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
index 311376c..b697aee 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view.cc
@@ -57,7 +57,7 @@
   web_ui->Initialize(
       browser->profile(),
       base::BindOnce(&PrivacySandboxDialogView::Close, base::Unretained(this)),
-      dialog_type);
+      dialog_type, browser);
 
   SetButtons(ui::DIALOG_BUTTON_NONE);
   SetModalType(ui::MODAL_TYPE_WINDOW);
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
index aa30ec8..66357df 100644
--- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h"
 
 #include <limits>
+#include <string>
 #include <utility>
 
 #include "base/memory/ptr_util.h"
@@ -38,6 +39,12 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/views/border.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
+#endif
+
 #if BUILDFLAG(IS_WIN)
 #include "ui/views/widget/native_widget_aura.h"
 #endif
@@ -48,6 +55,10 @@
 using content::RenderFrameHost;
 using content::WebContents;
 
+#if BUILDFLAG(IS_CHROMEOS)
+bool g_apply_dlp_for_all_users_for_testing_ = false;
+#endif
+
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 const int kContentsBorderThickness = 5;
 const float kContentsBorderOpacity = 0.50;
@@ -183,9 +194,9 @@
     std::u16string app_name,
     bool region_capture_capable,
     bool favicons_used_for_switch_to_tab_button) {
-  return base::WrapUnique(new TabSharingUIViews(
+  return std::make_unique<TabSharingUIViews>(
       capturer, media_id, app_name, region_capture_capable,
-      favicons_used_for_switch_to_tab_button));
+      favicons_used_for_switch_to_tab_button);
 }
 
 TabSharingUIViews::TabSharingUIViews(
@@ -273,6 +284,10 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!stop_callback_.is_null())
     std::move(stop_callback_).Run();
+#if BUILDFLAG(IS_CHROMEOS)
+  policy::DlpContentManager::Get()->RemoveObserver(
+      this, policy::DlpContentRestriction::kScreenShare);
+#endif
   RemoveInfobarsForAllTabs();
   UpdateTabCaptureData(shared_tab_, TabCaptureUpdate::kCaptureRemoved);
   tab_capture_indicator_ui_.reset();
@@ -303,6 +318,12 @@
     }
   }
 
+  if (change.type() == TabStripModelChange::kRemoved) {
+    for (const auto& contents : change.GetRemove()->contents) {
+      same_origin_observers_.erase(contents.contents);
+    }
+  }
+
   if (selection.active_tab_changed()) {
     UpdateTabCaptureData(selection.old_contents,
                          TabCaptureUpdate::kCapturedVisibilityUpdated);
@@ -357,7 +378,28 @@
   StopSharing();
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+void TabSharingUIViews::OnConfidentialityChanged(
+    policy::DlpRulesManager::Level old_restriction_level,
+    policy::DlpRulesManager::Level new_restriction_level,
+    content::WebContents* web_contents) {
+  DCHECK(old_restriction_level != new_restriction_level);
+  if (old_restriction_level == policy::DlpRulesManager::Level::kBlock ||
+      new_restriction_level == policy::DlpRulesManager::Level::kBlock) {
+    // We only call this function if it was previously blocked or should be
+    // blocked now.
+    CreateInfobarForWebContents(web_contents);
+  }
+}
+
+// static
+void TabSharingUIViews::ApplyDlpForAllUsersForTesting() {
+  g_apply_dlp_for_all_users_for_testing_ = true;
+}
+#endif
+
 void TabSharingUIViews::CreateInfobarsForAllTabs() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   BrowserList* browser_list = BrowserList::GetInstance();
   for (auto* browser : *browser_list) {
     OnBrowserAdded(browser);
@@ -368,9 +410,18 @@
     }
   }
   browser_list->AddObserver(this);
+#if BUILDFLAG(IS_CHROMEOS)
+  // Observe only for managed users.
+  if (g_apply_dlp_for_all_users_for_testing_ ||
+      policy::DlpRulesManagerFactory::GetForPrimaryProfile()) {
+    policy::DlpContentManager::Get()->AddObserver(
+        this, policy::DlpContentRestriction::kScreenShare);
+  }
+#endif
 }
 
 void TabSharingUIViews::CreateInfobarForWebContents(WebContents* contents) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(contents);
 
   auto infobars_entry = infobars_.find(contents);
@@ -394,7 +445,7 @@
 
   // If sharing this tab instead of the currently captured tab is possible, it
   // may still be blocked by enterprise policy. If the enterprise policy is
-  // active, create an observer that will inform us when it's compliance state
+  // active, create an observer that will inform us when its compliance state
   // changes.
   if (capturer_restricted_to_same_origin_ && is_share_instead_button_possible &&
       !base::Contains(same_origin_observers_, contents)) {
@@ -432,10 +483,29 @@
   const bool can_show_share_instead_button =
       is_share_instead_button_possible && is_sharing_allowed_by_policy;
 
+  TabSharingInfoBarDelegate::ButtonState share_this_tab_instead_button_state =
+      can_show_share_instead_button
+          ? TabSharingInfoBarDelegate::ButtonState::ENABLED
+          : TabSharingInfoBarDelegate::ButtonState::NOT_SHOWN;
+
+#if BUILDFLAG(IS_CHROMEOS)
+  const bool dlp_enabled =
+      g_apply_dlp_for_all_users_for_testing_ ||
+      policy::DlpRulesManagerFactory::GetForPrimaryProfile();
+  const bool screenshare_allowed_by_dlp =
+      !dlp_enabled ||
+      !policy::DlpContentManager::Get()->IsScreenShareBlocked(contents);
+  if (!screenshare_allowed_by_dlp && can_show_share_instead_button) {
+    share_this_tab_instead_button_state =
+        TabSharingInfoBarDelegate::ButtonState::DISABLED;
+  }
+#endif
+
   infobars_[contents] = TabSharingInfoBarDelegate::Create(
       infobar_manager, shared_tab_name_, app_name_,
-      shared_tab_ == contents /*shared_tab*/, can_show_share_instead_button,
-      focus_target, this, favicons_used_for_switch_to_tab_button_);
+      shared_tab_ == contents /*shared_tab*/,
+      share_this_tab_instead_button_state, focus_target, this,
+      favicons_used_for_switch_to_tab_button_);
 }
 
 void TabSharingUIViews::RemoveInfobarsForAllTabs() {
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h
index c609e32..b941363 100644
--- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views.h
@@ -23,6 +23,10 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/base/models/image_model.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager_observer.h"
+#endif
+
 namespace content {
 class WebContents;
 }
@@ -36,6 +40,9 @@
                           public BrowserListObserver,
                           public TabStripModelObserver,
                           public infobars::InfoBarManager::Observer,
+#if BUILDFLAG(IS_CHROMEOS)
+                          public policy::DlpContentManagerObserver,
+#endif
                           public content::WebContentsObserver {
  public:
   TabSharingUIViews(content::GlobalRenderFrameHostId capturer,
@@ -86,9 +93,24 @@
   // toggle its favicon back and forth at an arbitrary rate, but we implicitly
   // rate-limit our response.
 
+ protected:
+#if BUILDFLAG(IS_CHROMEOS)
+  // DlpContentManagerObserver:
+  void OnConfidentialityChanged(
+      policy::DlpRulesManager::Level old_restriction_level,
+      policy::DlpRulesManager::Level new_restriction_level,
+      content::WebContents* web_contents) override;
+#endif
+
  private:
   friend class TabSharingUIViewsBrowserTest;
 
+#if BUILDFLAG(IS_CHROMEOS)
+  // Allows to test the DLP functionality of TabSharingUIViews even if the user
+  // is not managed and without the need to initialize DlpRulesManager in tests.
+  static void ApplyDlpForAllUsersForTesting();
+#endif
+
   enum class TabCaptureUpdate {
     kCaptureAdded,
     kCaptureRemoved,
diff --git a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
index 25e60c0..393e9e8 100644
--- a/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
+++ b/chrome/browser/ui/views/tab_sharing/tab_sharing_ui_views_browsertest.cc
@@ -27,12 +27,18 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/favicon_size.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/views/widget/widget.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h"
+#endif
+
 namespace {
 
 using ::testing::Not;
@@ -78,6 +84,12 @@
       ->GetButtonImage(ConfirmInfoBarDelegate::InfoBarButton::BUTTON_CANCEL);
 }
 
+bool SecondaryButtonIsEnabled(Browser* browser, int tab) {
+  DCHECK(HasSecondaryButton(browser, tab));  // Test error otherwise.
+  return GetDelegate(browser, tab)
+      ->GetButtonEnabled(ConfirmInfoBarDelegate::InfoBarButton::BUTTON_CANCEL);
+}
+
 std::u16string GetExpectedSwitchToMessage(Browser* browser, int tab) {
   content::RenderFrameHost* const rfh =
       GetWebContents(browser, tab)->GetMainFrame();
@@ -116,6 +128,13 @@
 constexpr int kNullTabIndex = -1;
 const std::u16string kShareThisTabInsteadMessage = u"Share this tab instead";
 
+#if BUILDFLAG(IS_CHROMEOS)
+const policy::DlpContentRestrictionSet kEmptyRestrictionSet;
+const policy::DlpContentRestrictionSet kScreenshareRestrictionSet(
+    policy::DlpContentRestriction::kScreenShare,
+    policy::DlpRulesManager::Level::kBlock);
+#endif
+
 }  // namespace
 
 class TabSharingUIViewsBrowserTest
@@ -129,6 +148,8 @@
     InProcessBrowserTest::SetUpOnMainThread();
     DCHECK_EQ(browser()->tab_strip_model()->count(), 1);
     CreateUniqueFaviconFor(browser()->tab_strip_model()->GetWebContentsAt(0));
+    embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data");
+    host_resolver()->AddRule("*", "127.0.0.1");
   }
 
   void CreateUiAndStartSharing(Browser* browser,
@@ -167,7 +188,8 @@
                 int capturing_tab,
                 int captured_tab,
                 size_t infobar_count = 1,
-                bool has_border = true) {
+                bool has_border = true,
+                int tab_with_disabled_button = kNullTabIndex) {
     DCHECK((capturing_tab != kNullTabIndex && captured_tab != kNullTabIndex) ||
            (capturing_tab == kNullTabIndex && captured_tab == kNullTabIndex));
 
@@ -221,6 +243,9 @@
         EXPECT_EQ(GetSecondaryButtonLabel(browser, i),
                   kShareThisTabInsteadMessage);
         EXPECT_EQ(GetSecondaryButtonImage(browser, i), ui::ImageModel());
+        EXPECT_EQ(SecondaryButtonIsEnabled(browser, i),
+                  i != tab_with_disabled_button)
+            << "Tab: " << i;
       }
     }
   }
@@ -283,6 +308,13 @@
     return static_cast<TabSharingUIViews*>(tab_sharing_ui_.get());
   }
 
+ protected:
+#if BUILDFLAG(IS_CHROMEOS)
+  void ApplyDlpForAllUsers() {
+    TabSharingUIViews::ApplyDlpForAllUsersForTesting();
+  }
+#endif
+
  private:
   void OnStartSharing(const content::DesktopMediaID& media_id) {
     tab_sharing_ui_->OnStarted(
@@ -528,6 +560,61 @@
       ::testing::HasSubstr("about:blank"));
 }
 
+#if BUILDFLAG(IS_CHROMEOS)
+
+IN_PROC_BROWSER_TEST_P(TabSharingUIViewsBrowserTest,
+                       SharingWithDlpAndNavigation) {
+  // DLP setup
+  ApplyDlpForAllUsers();
+  policy::DlpContentTabHelper::ScopedIgnoreDlpRulesManager
+      ignore_dlp_rules_manager =
+          policy::DlpContentTabHelper::IgnoreDlpRulesManagerForTesting();
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL kUrlRestricted =
+      embedded_test_server()->GetURL("restricted.com", "/title1.html");
+  GURL kUrlUnrestricted =
+      embedded_test_server()->GetURL("unrestricted.com", "/title1.html");
+
+  policy::DlpContentRestrictionSet::SetRestrictionsForURLForTesting(
+      kUrlRestricted, kScreenshareRestrictionSet);
+  policy::DlpContentRestrictionSet::SetRestrictionsForURLForTesting(
+      kUrlUnrestricted, kEmptyRestrictionSet);
+
+  // Start actual test
+  AddTabs(browser(), 2);
+  ASSERT_EQ(browser()->tab_strip_model()->count(), 3);
+
+  // Create UI and start sharing the tab at index 1.
+  CreateUiAndStartSharing(browser(), /*capturing_tab=*/0, /*captured_tab=*/1);
+
+  // Test that infobars were created, and contents border and tab capture
+  // indicator are displayed on the shared tab.
+  VerifyUi(browser(), /*capturing_tab=*/0, /*captured_tab=*/1,
+           /*infobar_count=*/1, /*has_border=*/true,
+           /*tab_with_disabled_button=*/kNullTabIndex);
+
+  constexpr int kRestrictedTab = 2;
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetWebContentsAt(kRestrictedTab);
+  // Navigate to restricted URL.
+  ASSERT_TRUE(content::NavigateToURL(web_contents, kUrlRestricted));
+
+  // Test that button on tab 2 is now disabled.
+  VerifyUi(browser(), /*capturing_tab=*/0, /*captured_tab=*/1,
+           /*infobar_count=*/1, /*has_border=*/true,
+           /*tab_with_disabled_button=*/kRestrictedTab);
+
+  // Navigate to unrestricted URL.
+  ASSERT_TRUE(content::NavigateToURL(web_contents, kUrlUnrestricted));
+
+  // Verify that button on tab 2 is re-enabled.
+  VerifyUi(browser(), /*capturing_tab=*/0, /*captured_tab=*/1,
+           /*infobar_count=*/1, /*has_border=*/true,
+           /*tab_with_disabled_button=*/kNullTabIndex);
+}
+#endif
+
 class MultipleTabSharingUIViewsBrowserTest : public InProcessBrowserTest {
  public:
   MultipleTabSharingUIViewsBrowserTest() {}
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
index 7a4e458..7435cb0 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
@@ -30,8 +30,9 @@
   helper_.CheckPlatformShortcutNotExists("SiteA");
 }
 
+// Disabled due to flakiness. crbug.com/1294464
 IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTestMacWinLinux,
-                       CheckRunOnOsLoginModeOnPolicyAppWorks) {
+                       DISABLED_CheckRunOnOsLoginModeOnPolicyAppWorks) {
   helper_.InstallPolicyAppTabbedNoShortcut("SiteA");
   helper_.CheckPlatformShortcutNotExists("SiteA");
   helper_.EnableRunOnOSLoginMode("SiteA");
diff --git a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc
index b93c206b..4b934e2 100644
--- a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc
@@ -10,6 +10,7 @@
 
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/common/custom_handlers/protocol_handler.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc
index f7ef00a..873a363 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc
@@ -4,23 +4,56 @@
 
 #include "chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.h"
 
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/chrome_pages.h"
+
 PrivacySandboxDialogHandler::PrivacySandboxDialogHandler(
-    base::OnceClosure close_callback)
-    : close_callback_(std::move(close_callback)) {}
+    base::OnceClosure close_callback,
+    Browser* browser)
+    : close_callback_(std::move(close_callback)), browser_(browser) {}
 
 PrivacySandboxDialogHandler::~PrivacySandboxDialogHandler() {
   DisallowJavascript();
 }
 
 void PrivacySandboxDialogHandler::RegisterMessages() {
-  web_ui()->RegisterDeprecatedMessageCallback(
-      "closeDialog",
-      base::BindRepeating(&PrivacySandboxDialogHandler::HandleCloseDialog,
-                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "dialogActionOccurred",
+      base::BindRepeating(
+          &PrivacySandboxDialogHandler::HandleDialogActionOccurred,
+          base::Unretained(this)));
 }
 
-void PrivacySandboxDialogHandler::HandleCloseDialog(
-    const base::ListValue* args) {
-  if (close_callback_)
-    std::move(close_callback_).Run();
+void PrivacySandboxDialogHandler::HandleDialogActionOccurred(
+    base::Value::ConstListView args) {
+  AllowJavascript();
+
+  CHECK_EQ(1U, args.size());
+  auto action =
+      static_cast<PrivacySandboxService::DialogAction>(args[0].GetInt());
+
+  // TODO(crbug.com/1286276): Handle all other actions.
+  if (action == PrivacySandboxService::DialogAction::kNoticeOpenSettings) {
+    DCHECK(browser_);
+    chrome::ShowPrivacySandboxSettings(browser_);
+  }
+
+  switch (action) {
+    case PrivacySandboxService::DialogAction::kNoticeAcknowledge:
+    case PrivacySandboxService::DialogAction::kNoticeOpenSettings:
+    case PrivacySandboxService::DialogAction::kConsentAccepted:
+    case PrivacySandboxService::DialogAction::kConsentDeclined:
+      if (close_callback_)
+        std::move(close_callback_).Run();
+      break;
+    default:
+      break;
+  }
+
+  LogDialogAction(action);
+}
+
+void PrivacySandboxDialogHandler::LogDialogAction(
+    PrivacySandboxService::DialogAction action) {
+  // TODO(crbug.com/1286276):Add metrics.
 }
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.h b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.h
index 9d7e60a4..511cfefc 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.h
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.h
@@ -5,22 +5,28 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_PRIVACY_SANDBOX_PRIVACY_SANDBOX_DIALOG_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_PRIVACY_SANDBOX_PRIVACY_SANDBOX_DIALOG_HANDLER_H_
 
+#include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
+class Browser;
+
 class PrivacySandboxDialogHandler : public content::WebUIMessageHandler {
  public:
-  explicit PrivacySandboxDialogHandler(base::OnceClosure close_callback);
+  explicit PrivacySandboxDialogHandler(base::OnceClosure close_callback,
+                                       Browser* browser);
   ~PrivacySandboxDialogHandler() override;
 
   // content::WebUIMessageHandler:
   void RegisterMessages() override;
 
  protected:
-  void HandleCloseDialog(const base::ListValue* args);
+  void HandleDialogActionOccurred(base::Value::ConstListView args);
+  void LogDialogAction(PrivacySandboxService::DialogAction action);
 
   base::OnceClosure close_callback_;
+  const raw_ptr<Browser> browser_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_PRIVACY_SANDBOX_PRIVACY_SANDBOX_DIALOG_HANDLER_H_
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc
index a568780..fef3bc74 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.cc
@@ -33,7 +33,8 @@
 void PrivacySandboxDialogUI::Initialize(
     Profile* profile,
     base::OnceClosure close_callback,
-    PrivacySandboxService::DialogType dialog_type) {
+    PrivacySandboxService::DialogType dialog_type,
+    Browser* browser) {
   std::unique_ptr<base::DictionaryValue> update =
       std::make_unique<base::DictionaryValue>();
   update->SetBoolean(
@@ -41,8 +42,8 @@
   content::WebUIDataSource::Update(
       profile, chrome::kChromeUIPrivacySandboxDialogHost, std::move(update));
 
-  auto handler =
-      std::make_unique<PrivacySandboxDialogHandler>(std::move(close_callback));
+  auto handler = std::make_unique<PrivacySandboxDialogHandler>(
+      std::move(close_callback), browser);
   web_ui()->AddMessageHandler(std::move(handler));
 }
 
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.h b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.h
index e2408e84..ca47334 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.h
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_ui.h
@@ -8,6 +8,7 @@
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_service.h"
 #include "content/public/browser/web_ui_controller.h"
 
+class Browser;
 class Profile;
 
 // WebUI which is shown to the user as part of the PrivacySandboxDialog.
@@ -18,7 +19,8 @@
 
   void Initialize(Profile* profile,
                   base::OnceClosure close_callback,
-                  PrivacySandboxService::DialogType dialog_type);
+                  PrivacySandboxService::DialogType dialog_type,
+                  Browser* browser);
 
  private:
   WEB_UI_CONTROLLER_TYPE_DECL();
diff --git a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
index d503fc04..97aabc7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
@@ -29,6 +29,7 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/chromeos/devicetype_utils.h"
 
 namespace chromeos {
 namespace settings {
@@ -309,8 +310,6 @@
        IDS_OS_SETTINGS_DATA_ACCESS_PROTECTION_CONFIRM_DIALOG_DISABLE_BUTTON_LABEL},
       {"privacyPageTitle", IDS_SETTINGS_PRIVACY_V2},
       {"smartPrivacyTitle", IDS_OS_SETTINGS_SMART_PRIVACY_TITLE},
-      {"smartPrivacySubtext", IDS_OS_SETTINGS_SMART_PRIVACY_SUBTEXT},
-      {"smartPrivacyDesc", IDS_OS_SETTINGS_SMART_PRIVACY_DESC},
       {"smartPrivacyQuickDimTitle",
        IDS_OS_SETTINGS_SMART_PRIVACY_QUICK_DIM_TITLE},
       {"smartPrivacyQuickDimSubtext",
@@ -329,6 +328,13 @@
   html_source->AddBoolean("isQuickDimEnabled",
                           ash::features::IsQuickDimEnabled());
 
+  html_source->AddString(
+      "smartPrivacyDesc",
+      ui::SubstituteChromeOSDeviceType(IDS_OS_SETTINGS_SMART_PRIVACY_DESC));
+
+  // TODO(1294649): update this to the real link.
+  html_source->AddString("smartPrivacyLearnMoreLink", "about:blank");
+
   html_source->AddString("suggestedContentLearnMoreURL",
                          chrome::kSuggestedContentLearnMoreURL);
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
index 0afaf79..f5a096de 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip.mojom
@@ -30,9 +30,13 @@
   // Whether the tab is crashed.
   bool crashed;
 
-  // The URL of the favicon in data scheme.
+  // The URL of the favicon in data scheme for a tab.
   url.mojom.Url? favicon_url;
 
+  // The URL of the favicon in data scheme for an active tab. Allows custom
+  // favicon theming for certain chrome:// URLs.
+  url.mojom.Url? active_favicon_url;
+
   // The group identifier of the tab.
   string? group_id;
 
@@ -196,4 +200,4 @@
 
   // Called when the browser theme changed.
   ThemeChanged();
-};
\ No newline at end of file
+};
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
index 1bf1ec8..fa61a3cf5 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.cc
@@ -514,11 +514,19 @@
   tab_data->url = tab_renderer_data.visible_url;
 
   if (!tab_renderer_data.favicon.isNull()) {
-    tab_data->favicon_url = GURL(webui::EncodePNGAndMakeDataURI(
-        tab_renderer_data.should_themify_favicon
-            ? ThemeFavicon(tab_renderer_data.favicon)
-            : tab_renderer_data.favicon,
-        web_ui_->GetDeviceScaleFactor()));
+    // Themified icons only apply to a few select chrome URLs.
+    if (tab_renderer_data.should_themify_favicon) {
+      tab_data->favicon_url = GURL(webui::EncodePNGAndMakeDataURI(
+          ThemeFavicon(tab_renderer_data.favicon, false),
+          web_ui_->GetDeviceScaleFactor()));
+      tab_data->active_favicon_url = GURL(webui::EncodePNGAndMakeDataURI(
+          ThemeFavicon(tab_renderer_data.favicon, true),
+          web_ui_->GetDeviceScaleFactor()));
+    } else {
+      tab_data->favicon_url = GURL(webui::EncodePNGAndMakeDataURI(
+          tab_renderer_data.favicon, web_ui_->GetDeviceScaleFactor()));
+    }
+
     tab_data->is_default_favicon =
         tab_renderer_data.favicon.BackedBySameObjectAs(
             favicon::GetDefaultFavicon().AsImageSkia());
@@ -537,6 +545,7 @@
        chrome::GetTabAlertStatesForContents(contents)) {
     tab_data->alert_states.push_back(alert_state);
   }
+
   return tab_data;
 }
 
@@ -615,6 +624,12 @@
       embedder_->GetColorProviderColor(ui::kColorButtonBackgroundProminent));
   colors["--tabstrip-focus-outline-color"] = color_utils::SkColorToRgbaString(
       embedder_->GetColorProviderColor(ui::kColorFocusableBorderFocused));
+  colors["--tabstrip-tab-active-title-background-color"] =
+      color_utils::SkColorToRgbaString(embedder_->GetColor(
+          ThemeProperties::COLOR_THUMBNAIL_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE));
+  colors["--tabstrip-tab-active-title-content-color"] =
+      color_utils::SkColorToRgbaString(embedder_->GetColor(
+          ThemeProperties::COLOR_THUMBNAIL_TAB_FOREGROUND_ACTIVE_FRAME_ACTIVE));
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
   colors["--tabstrip-scrollbar-thumb-color-rgb"] =
@@ -908,7 +923,22 @@
   base::UmaHistogramTimes(histogram_name, duration);
 }
 
-gfx::ImageSkia TabStripPageHandler::ThemeFavicon(const gfx::ImageSkia& source) {
+gfx::ImageSkia TabStripPageHandler::ThemeFavicon(const gfx::ImageSkia& source,
+                                                 bool active_tab_icon) {
+  if (active_tab_icon) {
+    return favicon::ThemeFavicon(
+        source,
+        embedder_->GetColor(
+            ThemeProperties::
+                COLOR_THUMBNAIL_TAB_FOREGROUND_ACTIVE_FRAME_ACTIVE),
+        embedder_->GetColor(
+            ThemeProperties::
+                COLOR_THUMBNAIL_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE),
+        embedder_->GetColor(
+            ThemeProperties::
+                COLOR_THUMBNAIL_TAB_BACKGROUND_ACTIVE_FRAME_ACTIVE));
+  }
+
   return favicon::ThemeFavicon(
       source, embedder_->GetColor(ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON),
       embedder_->GetColor(
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
index 9f664e08..65cf8524 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_page_handler.h
@@ -97,7 +97,8 @@
   void ReportTabDurationHistogram(const char* histogram_fragment,
                                   int tab_count,
                                   base::TimeDelta duration);
-  gfx::ImageSkia ThemeFavicon(const gfx::ImageSkia& source);
+  gfx::ImageSkia ThemeFavicon(const gfx::ImageSkia& source,
+                              bool active_tab_icon);
 
   // ThemeServiceObserver implementation.
   void OnThemeChanged() override;
diff --git a/chrome/browser/web_applications/preinstalled_app_install_features.cc b/chrome/browser/web_applications/preinstalled_app_install_features.cc
index e9798f11..5bae94a 100644
--- a/chrome/browser/web_applications/preinstalled_app_install_features.cc
+++ b/chrome/browser/web_applications/preinstalled_app_install_features.cc
@@ -18,6 +18,9 @@
     &kMigrateDefaultChromeAppToWebAppsGSuite,
     &kMigrateDefaultChromeAppToWebAppsNonGSuite,
     &kDefaultCalculatorWebApp,
+#if BUILDFLAG(IS_CHROMEOS)
+    &kCursiveStylusPreinstall,
+#endif
 };
 
 bool g_always_enabled_for_testing = false;
@@ -75,6 +78,11 @@
     "AllowDefaultWebAppMigrationForChromeOsManagedUsers",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables installing the Cursive app on devices with a built-in stylus-capable
+// screen.
+const base::Feature kCursiveStylusPreinstall{"CursiveStylusPreinstall",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 bool IsPreinstalledAppInstallFeatureEnabled(base::StringPiece feature_name,
diff --git a/chrome/browser/web_applications/preinstalled_app_install_features.h b/chrome/browser/web_applications/preinstalled_app_install_features.h
index fee0792..83820ef8 100644
--- a/chrome/browser/web_applications/preinstalled_app_install_features.h
+++ b/chrome/browser/web_applications/preinstalled_app_install_features.h
@@ -22,6 +22,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 extern const base::Feature kAllowDefaultWebAppMigrationForChromeOsManagedUsers;
+
+extern const base::Feature kCursiveStylusPreinstall;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // Returns the base::Feature in |kPreinstalledAppInstallFeatures| that
diff --git a/chrome/browser/web_applications/web_app_offline_browsertest.cc b/chrome/browser/web_applications/web_app_offline_browsertest.cc
index eaa764c..96651c4 100644
--- a/chrome/browser/web_applications/web_app_offline_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_offline_browsertest.cc
@@ -13,21 +13,28 @@
 #include "content/public/test/browser_test_base.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/url_loader_interceptor.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/features.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/native_theme/native_theme.h"
 
 namespace web_app {
 
-enum class PageFlagParam {
-  kWithDefaultPageFlag = 0,
-  kWithoutDefaultPageFlag = 1,
-  kMaxValue = kWithoutDefaultPageFlag
+enum class FlagParam {
+  kWithFlag = 0,
+  kWithoutFlag = 1,
+  kMaxValue = kWithoutFlag
 };
 
-class WebAppOfflineTest : public InProcessBrowserTest {
+class WebAppOfflinePageTest : public InProcessBrowserTest,
+                              public ::testing::WithParamInterface<FlagParam> {
  public:
+  WebAppOfflinePageTest() {
+    if (GetParam() == FlagParam::kWithFlag) {
+      feature_list_.InitAndEnableFeature(
+          features::kDesktopPWAsDefaultOfflinePage);
+    } else {
+      feature_list_.InitAndDisableFeature(
+          features::kDesktopPWAsDefaultOfflinePage);
+    }
+  }
+
   // Start a web app without a service worker and disconnect.
   void StartWebAppAndDisconnect(content::WebContents* web_contents,
                                 std::string html) {
@@ -62,21 +69,6 @@
     web_contents->GetController().Reload(content::ReloadType::NORMAL, false);
     observer.Wait();
   }
-};
-
-class WebAppOfflinePageTest
-    : public WebAppOfflineTest,
-      public ::testing::WithParamInterface<PageFlagParam> {
- public:
-  WebAppOfflinePageTest() {
-    if (GetParam() == PageFlagParam::kWithDefaultPageFlag) {
-      feature_list_.InitAndEnableFeature(
-          features::kDesktopPWAsDefaultOfflinePage);
-    } else {
-      feature_list_.InitAndDisableFeature(
-          features::kDesktopPWAsDefaultOfflinePage);
-    }
-  }
 
  private:
   base::test::ScopedFeatureList feature_list_;
@@ -92,7 +84,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   StartWebAppAndDisconnect(web_contents, "/banners/no-sw-with-colors.html");
 
-  if (GetParam() == PageFlagParam::kWithDefaultPageFlag) {
+  if (GetParam() == FlagParam::kWithFlag) {
     // Expect that the default offline page is showing.
     EXPECT_TRUE(
         EvalJs(web_contents,
@@ -116,7 +108,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   StartPwaAndDisconnect(web_contents, "/banners/background-color.html");
 
-  if (GetParam() == PageFlagParam::kWithDefaultPageFlag) {
+  if (GetParam() == FlagParam::kWithFlag) {
     // Expect that the default offline page is showing.
     EXPECT_TRUE(
         EvalJs(web_contents,
@@ -145,124 +137,9 @@
                   .ExtractBool());
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    WebAppOfflinePageTest,
-    ::testing::Values(PageFlagParam::kWithDefaultPageFlag,
-                      PageFlagParam::kWithoutDefaultPageFlag));
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebAppOfflinePageTest,
+                         ::testing::Values(FlagParam::kWithFlag,
+                                           FlagParam::kWithoutFlag));
 
-class WebAppOfflineDarkModeTest
-    : public WebAppOfflineTest,
-      public testing::WithParamInterface<blink::mojom::PreferredColorScheme> {
- public:
-  WebAppOfflineDarkModeTest() {
-    feature_list_.InitWithFeatures({features::kDesktopPWAsDefaultOfflinePage,
-                                    blink::features::kWebAppEnableDarkMode},
-                                   {});
-  }
-
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // ShellContentBrowserClient::OverrideWebkitPrefs() overrides the
-    // prefers-color-scheme according to switches::kForceDarkMode
-    // command line.
-    if (GetParam() == blink::mojom::PreferredColorScheme::kDark)
-      command_line->AppendSwitch(switches::kForceDarkMode);
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// Testing offline page in dark mode for a web app with a manifest and no
-// service worker.
-IN_PROC_BROWSER_TEST_P(WebAppOfflineDarkModeTest,
-                       WebAppOfflineDarkModeNoServiceWorker) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // ui::NativeTheme::GetInstanceForNativeUi()->set_use_dark_colors(true);
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  StartWebAppAndDisconnect(
-      web_contents, "/web_apps/get_manifest.html?color_scheme_dark.json");
-
-  // Expect that the default offline page is showing with dark mode colors.
-
-  EXPECT_TRUE(
-      EvalJs(web_contents,
-             "window.matchMedia('(prefers-color-scheme: dark)').matches")
-          .ExtractBool());
-  EXPECT_EQ(
-      EvalJs(web_contents,
-             "window.getComputedStyle(document.querySelector('h2')).color")
-          .ExtractString(),
-      "rgb(255, 0, 0)");
-  EXPECT_EQ(EvalJs(web_contents,
-                   "window.getComputedStyle(document.querySelector('body'))."
-                   "backgroundColor")
-                .ExtractString(),
-            "rgb(255, 0, 0)");
-}
-
-// Testing offline page in dark mode for a web app with a manifest and service
-// worker that does not handle offline error.
-IN_PROC_BROWSER_TEST_P(WebAppOfflineDarkModeTest,
-                       WebAppOfflineDarkModeEmptyServiceWorker) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  StartWebAppAndDisconnect(web_contents,
-                           "/banners/manifest_test_page.html?manifest=../"
-                           "web_apps/color_scheme_dark.json");
-
-  // Expect that the default offline page is showing with dark mode colors.
-  EXPECT_TRUE(
-      EvalJs(web_contents,
-             "window.matchMedia('(prefers-color-scheme: dark)').matches")
-          .ExtractBool());
-  EXPECT_EQ(
-      EvalJs(web_contents,
-             "window.getComputedStyle(document.querySelector('h2')).color")
-          .ExtractString(),
-      "rgb(255, 0, 0)");
-  EXPECT_EQ(EvalJs(web_contents,
-                   "window.getComputedStyle(document.querySelector('body'))."
-                   "backgroundColor")
-                .ExtractString(),
-            "rgb(255, 0, 0)");
-}
-
-// Testing offline page in dark mode for a web app with a manifest that has not
-// provided dark mode colors.
-IN_PROC_BROWSER_TEST_P(WebAppOfflineDarkModeTest,
-                       WebAppOfflineNoDarkModeColorsProvided) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  StartWebAppAndDisconnect(web_contents, "/banners/no-sw-with-colors.html");
-
-  // Expect that the default offline page is showing with dark mode colors.
-  EXPECT_TRUE(
-      EvalJs(web_contents,
-             "window.matchMedia('(prefers-color-scheme: dark)').matches")
-          .ExtractBool());
-  EXPECT_EQ(
-      EvalJs(web_contents,
-             "window.getComputedStyle(document.querySelector('h2')).color")
-          .ExtractString(),
-      "rgb(0, 255, 0)");
-  EXPECT_EQ(EvalJs(web_contents,
-                   "window.getComputedStyle(document.querySelector('body'))."
-                   "backgroundColor")
-                .ExtractString(),
-            "rgb(255, 255, 0)");
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    /* no prefix */,
-    WebAppOfflineDarkModeTest,
-    ::testing::Values(blink::mojom::PreferredColorScheme::kDark));
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_utils.cc b/chrome/browser/web_applications/web_app_utils.cc
index 80e85de..2d4b1db4 100644
--- a/chrome/browser/web_applications/web_app_utils.cc
+++ b/chrome/browser/web_applications/web_app_utils.cc
@@ -141,38 +141,20 @@
       content::mojom::AlternativeErrorPageOverrideInfo::New();
   // TODO(crbug.com/1285128): Ensure sufficient contrast.
   base::Value dict(base::Value::Type::DICTIONARY);
-  SkColor theme_color =
-      web_app_registrar.GetAppThemeColor(*app_id).value_or(SK_ColorBLACK);
-  SkColor background_color =
-      web_app_registrar.GetAppBackgroundColor(*app_id).value_or(SK_ColorWHITE);
-  dict.SetStringKey(default_offline::kThemeColor,
-                    skia::SkColorToHexString(theme_color));
-  dict.SetStringKey(default_offline::kBackgroundColor,
-                    skia::SkColorToHexString(background_color));
+  dict.SetStringKey(
+      default_offline::kThemeColor,
+      skia::SkColorToHexString(
+          web_app_registrar.GetAppThemeColor(*app_id).value_or(SK_ColorBLACK)));
+  dict.SetStringKey(
+      default_offline::kBackgroundColor,
+      skia::SkColorToHexString(
+          web_app_registrar.GetAppBackgroundColor(*app_id).value_or(
+              SK_ColorWHITE)));
   dict.SetStringKey(default_offline::kAppShortName,
                     web_app_registrar.GetAppShortName(*app_id));
   dict.SetStringKey(
       default_offline::kMessage,
       l10n_util::GetStringUTF16(IDS_ERRORPAGES_HEADING_INTERNET_DISCONNECTED));
-  absl::optional<SkColor> dark_mode_theme_color =
-      web_app_registrar.GetAppDarkModeThemeColor(*app_id);
-  if (dark_mode_theme_color) {
-    dict.SetStringKey(default_offline::kDarkModeThemeColor,
-                      skia::SkColorToHexString(dark_mode_theme_color.value()));
-  } else {
-    dict.SetStringKey(default_offline::kDarkModeThemeColor,
-                      skia::SkColorToHexString(theme_color));
-  }
-  absl::optional<SkColor> dark_mode_background_color =
-      web_app_registrar.GetAppDarkModeThemeColor(*app_id);
-  if (dark_mode_background_color) {
-    dict.SetStringKey(
-        default_offline::kDarkModeBackgroundColor,
-        skia::SkColorToHexString(dark_mode_background_color.value()));
-  } else {
-    dict.SetStringKey(default_offline::kDarkModeBackgroundColor,
-                      skia::SkColorToHexString(background_color));
-  }
   alternative_error_page_info->alternative_error_page_params = std::move(dict);
   alternative_error_page_info->resource_id = IDR_WEBAPP_DEFAULT_OFFLINE_HTML;
   return alternative_error_page_info;
diff --git a/chrome/browser/web_applications/web_app_utils.h b/chrome/browser/web_applications/web_app_utils.h
index 62c1874..a895db6 100644
--- a/chrome/browser/web_applications/web_app_utils.h
+++ b/chrome/browser/web_applications/web_app_utils.h
@@ -37,8 +37,6 @@
 const char kAppShortName[] = "app_short_name";
 const char kThemeColor[] = "theme_color";
 const char kBackgroundColor[] = "customized_background_color";
-const char kDarkModeBackgroundColor[] = "dark_mode_background_color";
-const char kDarkModeThemeColor[] = "dark_mode_theme_color";
 }  // namespace default_offline
 
 // These functions return true if the WebApp System or its subset is allowed
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index b2e6fd07..784acc7 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1644118955-7e8d294844ae96817eadcc0770995e739334be21.profdata
+chrome-linux-main-1644235013-9a39b207e703942ce691ce65bc33f9f55a7e7bdf.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 5eaa1de..7f79016b 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1644163308-984567538b4fc27f0004c2fd2397b8422763b27a.profdata
+chrome-mac-arm-main-1644235013-3c57e87d75f00e968d00724f58555db724c63c35.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 58b8d5d..70c8bd7 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1644118955-05e676f529a4efca1a9a4e08337ebec4cd214d28.profdata
+chrome-mac-main-1644213352-7c32877691aefca58d04adaeeb2b65ee0a73d945.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 205377b..216b2a5 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1644118955-0ec0e7b6d7e110de3da69c7dc253de2e0a33b817.profdata
+chrome-win32-main-1644224319-d9713f1dc00d991a365b276dbeb8955bb5fd53ad.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 00978614..be65012 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1644163308-af270ed7ed4c8dbe3f2cebd5c1162801cc874a13.profdata
+chrome-win64-main-1644202718-d44b61313a46a6df96198aeb1e831627d61406eb.profdata
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index ff3c5d7e..9d66b77 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -41,6 +41,7 @@
 #include "components/dom_distiller/core/url_constants.h"
 #include "components/embedder_support/origin_trials/origin_trial_policy_impl.h"
 #include "components/services/heap_profiling/public/cpp/profiling_client.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/common/cdm_info.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_switches.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 0e00154e..ad8e272 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3136,6 +3136,7 @@
         "../browser/apps/app_service/notifications_browsertest.cc",
         "../browser/apps/app_service/webapk/webapk_policy_browsertest.cc",
         "../browser/apps/icon_standardizer_unittest.cc",
+        "../browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc",
         "../browser/apps/platform_apps/app_window_interactive_uitest_base.cc",
         "../browser/apps/platform_apps/app_window_interactive_uitest_base.h",
         "../browser/ash/accessibility/accessibility_common_browsertest.cc",
@@ -6649,9 +6650,11 @@
       "../browser/ui/app_list/search/files/file_result_unittest.cc",
       "../browser/ui/app_list/search/files/file_search_provider_unittest.cc",
       "../browser/ui/app_list/search/files/item_suggest_cache_unittest.cc",
+      "../browser/ui/app_list/search/files/justifications_unittest.cc",
       "../browser/ui/app_list/search/files/zero_state_drive_provider_unittest.cc",
       "../browser/ui/app_list/search/files/zero_state_file_provider_unittest.cc",
       "../browser/ui/app_list/search/help_app_provider_unittest.cc",
+      "../browser/ui/app_list/search/keyboard_shortcut_provider_unittest.cc",
       "../browser/ui/app_list/search/keyboard_shortcut_result_unittest.cc",
       "../browser/ui/app_list/search/mixer_unittest.cc",
       "../browser/ui/app_list/search/omnibox_result_unittest.cc",
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
index 8d1518c3..d03a7948 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_extension_test.js
@@ -219,37 +219,42 @@
       });
 
   test(
-      'Scrolling to an emoticon group should activate the corresponding ' +
-          'subcategory tab.',
+      'Scrolling to an emoticon group should activate the emoticon category ' +
+          'button.',
       async () => {
-        // TODO(b/216103506): Remove emoticon button click
+        const emojiCategoryButton = findInEmojiPicker(
+            'emoji-search', 'emoji-category-button', 'cr-icon-button');
         const emoticonCategoryButton = findInEmojiPicker(
             'emoji-search', 'emoji-category-button:last-of-type',
             'cr-icon-button');
-        emoticonCategoryButton.click();
-        await flush();
 
         const emoticonTestGroupId = '10';
-        const emoticonTabButton = findInEmojiPicker(
-            `.tab[data-group='${emoticonTestGroupId}']`, 'cr-button');
         emojiPicker.scrollToGroup(emoticonTestGroupId);
 
         await waitForCondition(
+            () => isCategoryButtonActive(emoticonCategoryButton) &&
+                !isCategoryButtonActive(emojiCategoryButton));
+      });
+  test(
+      'Scrolling to an emoticon group should activate the corresponding ' +
+          'subcategory tab.',
+      async () => {
+        const emoticonTestGroupId = '10';
+        emojiPicker.scrollToGroup(emoticonTestGroupId);
+        const emoticonTabButton = await waitForCondition(
+            () => findInEmojiPicker(
+                `.tab[data-group='${emoticonTestGroupId}']`, 'cr-button'));
+
+        await waitForCondition(
             () => isGroupButtonActive(emoticonTabButton),
             'The tab where the group is scrolled to failed to become active');
       });
 
   test('Scrolling to an emoticon group should update chevrons.', async () => {
-    const emoticonCategoryButton = findInEmojiPicker(
-        'emoji-search', 'emoji-category-button:last-of-type', 'cr-icon-button');
     const leftChevron = findInEmojiPicker('#left-chevron');
     const rightChevron = findInEmojiPicker('#right-chevron');
     const emoticonTestGroupId = '15';
 
-    // TODO(b/216103506): Remove emoticon button click
-    emoticonCategoryButton.click();
-    await flush();
-
     emojiPicker.scrollToGroup(emoticonTestGroupId);
     // when scrolling to the next page, the chevron display needs to be updated.
     await waitForCondition(
diff --git a/chrome/test/data/webui/history/BUILD.gn b/chrome/test/data/webui/history/BUILD.gn
index 1c88cae..3fa7c2bc 100644
--- a/chrome/test/data/webui/history/BUILD.gn
+++ b/chrome/test/data/webui/history/BUILD.gn
@@ -8,13 +8,13 @@
 # Test files that do not require preprocessing.
 non_preprocessed_files = [
   "history_clusters/utils.ts",
-  "history_drawer_test.js",
-  "history_item_focus_test.js",
+  "history_drawer_test.ts",
+  "history_item_focus_test.ts",
   "history_item_test.js",
   "history_list_focus_test.js",
   "history_list_test.js",
   "history_metrics_test.ts",
-  "history_overflow_menu_test.js",
+  "history_overflow_menu_test.ts",
   "history_routing_test.ts",
   "history_routing_with_query_param_test.ts",
   "history_supervised_user_test.js",
diff --git a/chrome/test/data/webui/history/history_drawer_test.js b/chrome/test/data/webui/history/history_drawer_test.js
deleted file mode 100644
index ee3b3b0..0000000
--- a/chrome/test/data/webui/history/history_drawer_test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
-import {flushTasks} from 'chrome://webui-test/test_util.js';
-
-import {TestBrowserService} from './test_browser_service.js';
-
-suite('drawer-test', function() {
-  let app;
-
-  setup(function() {
-    document.body.innerHTML = '';
-    const testService = new TestBrowserService();
-    BrowserServiceImpl.setInstance(testService);
-    app = document.createElement('history-app');
-    document.body.appendChild(app);
-    return Promise.all([
-      testService.whenCalled('queryHistory'),
-      ensureLazyLoaded(),
-    ]);
-  });
-
-  test('drawer has correct selection', function() {
-    app.selectedPage_ = 'syncedTabs';
-    app.hasDrawer_ = true;
-    return flushTasks().then(function() {
-      const drawer = /** @type {CrLazyRenderElement} */ (app.$.drawer);
-      let drawerSideBar = app.$$('#drawer-side-bar');
-
-      assertTrue(!!drawer);
-      // Drawer side bar doesn't exist until the first time the drawer is
-      // opened.
-      assertFalse(!!drawerSideBar);
-
-      const menuButton =
-          app.$.toolbar.$.mainToolbar.shadowRoot.querySelector('#menuButton');
-      assertTrue(!!menuButton);
-
-      menuButton.click();
-      assertTrue(drawer.getIfExists().open);
-      drawerSideBar = app.$$('#drawer-side-bar');
-      assertTrue(!!drawerSideBar);
-
-      assertEquals('syncedTabs', drawerSideBar.$.menu.selected);
-    });
-  });
-});
diff --git a/chrome/test/data/webui/history/history_drawer_test.ts b/chrome/test/data/webui/history/history_drawer_test.ts
new file mode 100644
index 0000000..e06d52f0
--- /dev/null
+++ b/chrome/test/data/webui/history/history_drawer_test.ts
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://history/history.js';
+
+import {BrowserServiceImpl, ensureLazyLoaded, HistoryAppElement, HistorySideBarElement} from 'chrome://history/history.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
+
+import {TestBrowserService} from './test_browser_service.js';
+import {navigateTo} from './test_util.js';
+
+suite('drawer-test', function() {
+  let app: HistoryAppElement;
+
+  setup(function() {
+    document.body.innerHTML = '';
+    const testService = new TestBrowserService();
+    BrowserServiceImpl.setInstance(testService);
+    app = document.createElement('history-app');
+    document.body.appendChild(app);
+    return Promise.all([
+      testService.whenCalled('queryHistory'),
+      ensureLazyLoaded(),
+    ]);
+  });
+
+  test('drawer has correct selection', function() {
+    navigateTo('/syncedTabs', app);
+    app.setHasDrawerForTesting(true);
+    return flushTasks().then(function() {
+      const drawerLazyRender = app.$.drawer;
+      assertTrue(!!drawerLazyRender);
+
+      // Drawer side bar doesn't exist until the first time the drawer is
+      // opened.
+      let drawerSideBar = app.shadowRoot!.querySelector<HistorySideBarElement>(
+          '#drawer-side-bar');
+      assertFalse(!!drawerSideBar);
+
+      const menuButton =
+          app.$.toolbar.$.mainToolbar.shadowRoot!.querySelector<HTMLElement>(
+              '#menuButton');
+      assertTrue(!!menuButton);
+
+      menuButton.click();
+      const drawer = drawerLazyRender.getIfExists();
+      assertTrue(!!drawer);
+      assertTrue(drawer.open);
+      drawerSideBar = app.shadowRoot!.querySelector<HistorySideBarElement>(
+          '#drawer-side-bar');
+      assertTrue(!!drawerSideBar);
+
+      assertEquals('syncedTabs', drawerSideBar.$.menu.selected);
+    });
+  });
+});
diff --git a/chrome/test/data/webui/history/history_item_focus_test.js b/chrome/test/data/webui/history/history_item_focus_test.ts
similarity index 71%
rename from chrome/test/data/webui/history/history_item_focus_test.js
rename to chrome/test/data/webui/history/history_item_focus_test.ts
index 74150c62..a9439a0 100644
--- a/chrome/test/data/webui/history/history_item_focus_test.js
+++ b/chrome/test/data/webui/history/history_item_focus_test.ts
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {BrowserServiceImpl} from 'chrome://history/history.js';
+import 'chrome://history/history.js';
+
+import {BrowserServiceImpl, HistoryItemElement} from 'chrome://history/history.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
 import {createHistoryEntry} from './test_util.js';
 
 suite('<history-item> focus test', function() {
-  let item;
+  let item: HistoryItemElement;
 
   setup(function() {
     document.body.innerHTML = '';
@@ -24,14 +27,14 @@
   test('refocus checkbox on click', async () => {
     await flushTasks();
     item.$['menu-button'].focus();
-    assertEquals(item.$['menu-button'], item.root.activeElement);
+    assertEquals(item.$['menu-button'], item.shadowRoot!.activeElement);
 
     const whenCheckboxSelected =
         eventToPromise('history-checkbox-select', item);
     item.$['time-accessed'].click();
 
     await whenCheckboxSelected;
-    assertEquals(item.$['checkbox'], item.root.activeElement);
+    assertEquals(item.$.checkbox, item.shadowRoot!.activeElement);
   });
 
   test('RemovingBookmarkMovesFocus', async () => {
@@ -43,15 +46,14 @@
     // the item. Otherwise, FocusRowBehavior will see that it newly received
     // focus and attempt to move the focus to the first focusable item since
     // the bookmark star is not in the focus order.
-    item.shadowRoot.querySelector('#checkbox').focus();
-    item.shadowRoot.querySelector('#link').focus();
-    item.shadowRoot.querySelector('#bookmark-star').focus();
-
-    item.shadowRoot.querySelector('#bookmark-star').click();
+    item.$.checkbox.focus();
+    item.$.link.focus();
+    const star = item.shadowRoot!.querySelector<HTMLElement>('#bookmark-star');
+    assertTrue(!!star);
+    star.focus();
+    star.click();
 
     // Check that focus is shifted to overflow menu icon.
-    assertEquals(
-        item.shadowRoot.activeElement,
-        item.shadowRoot.querySelector('#menu-button'));
+    assertEquals(item.shadowRoot!.activeElement, item.$['menu-button']);
   });
 });
diff --git a/chrome/test/data/webui/history/history_metrics_test.ts b/chrome/test/data/webui/history/history_metrics_test.ts
index 5798cb0..d0f5a2ee 100644
--- a/chrome/test/data/webui/history/history_metrics_test.ts
+++ b/chrome/test/data/webui/history/history_metrics_test.ts
@@ -12,7 +12,7 @@
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
 import {TestBrowserService} from './test_browser_service.js';
-import {createHistoryEntry, createHistoryInfo, createSession, createWindow, disableLinkClicks} from './test_util.js';
+import {createHistoryEntry, createHistoryInfo, createSession, createWindow, disableLinkClicks, navigateTo} from './test_util.js';
 
 suite('Metrics', function() {
   let testService: TestBrowserService;
@@ -57,14 +57,6 @@
         });
   }
 
-  function navigateTo(route: string) {
-    window.history.replaceState({}, '', route);
-    window.dispatchEvent(new CustomEvent('location-changed'));
-    // Update from the URL synchronously.
-    app.shadowRoot!.querySelector(
-                       'history-router')!.getDebouncerForTesting()!.flush();
-  }
-
   test('History.HistoryPageView', async () => {
     await finishSetup([]);
 
@@ -72,7 +64,7 @@
     assertTrue(!!histogram);
     assertEquals(1, histogram[HistoryPageViewHistogram.HISTORY]);
 
-    navigateTo('/syncedTabs');
+    navigateTo('/syncedTabs', app);
     assertEquals(1, histogram[HistoryPageViewHistogram.SIGNIN_PROMO]);
     await testService.whenCalled('otherDevicesInitialized');
 
@@ -81,7 +73,7 @@
     await testService.whenCalled('recordHistogram');
 
     assertEquals(1, histogram[HistoryPageViewHistogram.SYNCED_TABS]);
-    navigateTo('/history');
+    navigateTo('/history', app);
     assertEquals(2, histogram[HistoryPageViewHistogram.HISTORY]);
   });
 
@@ -200,7 +192,7 @@
     testService.setForeignSessions(sessionList);
     await finishSetup([]);
 
-    navigateTo('/syncedTabs');
+    navigateTo('/syncedTabs', app);
     await flushTasks();
 
     const histogram = histogramMap[SYNCED_TABS_HISTOGRAM_NAME];
diff --git a/chrome/test/data/webui/history/history_overflow_menu_test.js b/chrome/test/data/webui/history/history_overflow_menu_test.js
deleted file mode 100644
index 5ca82948..0000000
--- a/chrome/test/data/webui/history/history_overflow_menu_test.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {BrowserServiceImpl, ensureLazyLoaded} from 'chrome://history/history.js';
-import {TestBrowserService} from './test_browser_service.js';
-
-suite('#overflow-menu', function() {
-  let listContainer;
-  let sharedMenu;
-
-  let target1;
-  let target2;
-
-  setup(function() {
-    document.body.innerHTML = '';
-    const testService = new TestBrowserService();
-    BrowserServiceImpl.setInstance(testService);
-
-    const app = document.createElement('history-app');
-    document.body.appendChild(app);
-    return Promise
-        .all([
-          testService.whenCalled('queryHistory'),
-          ensureLazyLoaded(),
-        ])
-        .then(function() {
-          listContainer = app.$['history'];
-          const element1 = document.createElement('div');
-          const element2 = document.createElement('div');
-          document.body.appendChild(element1);
-          document.body.appendChild(element2);
-
-          target1 = element1;
-          target2 = element2;
-          sharedMenu = listContainer.$.sharedMenu.get();
-        });
-  });
-
-  test('opening and closing menu', function() {
-    const detail1 = {target: target1};
-    listContainer.dispatchEvent(new CustomEvent(
-        'open-menu', {bubbles: true, composed: true, detail: detail1}));
-    assertTrue(sharedMenu.open);
-    assertEquals(detail1, listContainer.actionMenuModel_);
-
-    sharedMenu.close();
-    assertFalse(sharedMenu.open);
-
-    const detail2 = {target: target2};
-    listContainer.dispatchEvent(new CustomEvent(
-        'open-menu', {bubbles: true, composed: true, detail: detail2}));
-    assertEquals(detail2, listContainer.actionMenuModel_);
-    assertTrue(sharedMenu.open);
-
-    sharedMenu.close();
-    assertFalse(sharedMenu.open);
-  });
-
-  teardown(function() {
-    sharedMenu.actionMenuModel_ = null;
-  });
-});
diff --git a/chrome/test/data/webui/history/history_overflow_menu_test.ts b/chrome/test/data/webui/history/history_overflow_menu_test.ts
new file mode 100644
index 0000000..024263e
--- /dev/null
+++ b/chrome/test/data/webui/history/history_overflow_menu_test.ts
@@ -0,0 +1,83 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {ActionMenuModel, BrowserServiceImpl, CrActionMenuElement, ensureLazyLoaded, HistoryListElement} from 'chrome://history/history.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+import {TestBrowserService} from './test_browser_service.js';
+import {createHistoryEntry} from './test_util.js';
+
+suite('#overflow-menu', function() {
+  let listContainer: HistoryListElement;
+  let sharedMenu: CrActionMenuElement;
+
+  let target1: HTMLElement;
+  let target2: HTMLElement;
+
+  setup(function() {
+    document.body.innerHTML = '';
+    const testService = new TestBrowserService();
+    BrowserServiceImpl.setInstance(testService);
+
+    const app = document.createElement('history-app');
+    document.body.appendChild(app);
+    return Promise
+        .all([
+          testService.whenCalled('queryHistory'),
+          ensureLazyLoaded(),
+        ])
+        .then(function() {
+          listContainer = app.$.history;
+          target1 = document.createElement('div');
+          target2 = document.createElement('div');
+          document.body.appendChild(target1);
+          document.body.appendChild(target2);
+          sharedMenu = listContainer.$.sharedMenu.get();
+        });
+  });
+
+  test('opening and closing menu', async function() {
+    const detail1: ActionMenuModel = {
+      index: 1,
+      item: createHistoryEntry(0, 'https://www.chromium.org'),
+      target: target1,
+    };
+    listContainer.dispatchEvent(new CustomEvent(
+        'open-menu', {bubbles: true, composed: true, detail: detail1}));
+    assertTrue(sharedMenu.open);
+
+    const moreButton = sharedMenu.querySelector<HTMLElement>('#menuMoreButton');
+    assertTrue(!!moreButton);
+
+    // Ensure that the menu corresponds to the clicked item.
+    let whenChangeQueryFired = eventToPromise('change-query', listContainer);
+    moreButton.click();
+    let e = await whenChangeQueryFired;
+
+    assertEquals('www.chromium.org', e.detail.search);
+
+    sharedMenu.close();
+    assertFalse(sharedMenu.open);
+
+    // Open the menu for a different item.
+    const detail2: ActionMenuModel = {
+      index: 2,
+      item: createHistoryEntry(0, 'https://www.wikipedia.org'),
+      target: target2,
+    };
+    listContainer.dispatchEvent(new CustomEvent(
+        'open-menu', {bubbles: true, composed: true, detail: detail2}));
+    assertTrue(sharedMenu.open);
+
+    // Ensure that the menu corresponds to the newly clicked item.
+    whenChangeQueryFired = eventToPromise('change-query', listContainer);
+    moreButton.click();
+    e = await whenChangeQueryFired;
+    assertEquals('www.wikipedia.org', e.detail.search);
+
+    sharedMenu.close();
+    assertFalse(sharedMenu.open);
+  });
+});
diff --git a/chrome/test/data/webui/history/history_routing_test.ts b/chrome/test/data/webui/history/history_routing_test.ts
index c09abaa..ad41693 100644
--- a/chrome/test/data/webui/history/history_routing_test.ts
+++ b/chrome/test/data/webui/history/history_routing_test.ts
@@ -12,6 +12,7 @@
 
 import {TestBrowserProxy, TestMetricsProxy} from './history_clusters/utils.js';
 import {TestBrowserService} from './test_browser_service.js';
+import {navigateTo} from './test_util.js';
 
 [true, false].forEach(isHistoryClustersEnabled => {
   const suitSuffix = isHistoryClustersEnabled ? 'enabled' : 'disabled';
@@ -21,14 +22,6 @@
     let testBrowserProxy: TestBrowserProxy;
     let testMetricsProxy: TestMetricsProxy;
 
-    function navigateTo(route: string) {
-      window.history.replaceState({}, '', route);
-      window.dispatchEvent(new CustomEvent('location-changed'));
-      // Update from the URL synchronously.
-      app.shadowRoot!.querySelector(
-                         'history-router')!.getDebouncerForTesting()!.flush();
-    }
-
     suiteSetup(() => {
       loadTimeData.overrideValues({
         disableHistoryClusters: 'Disable',
@@ -76,7 +69,7 @@
       assertEquals('history', app.$.content.selected);
       assertEquals(app.$.history, app.$['tabs-content'].selectedItem);
 
-      navigateTo('/syncedTabs');
+      navigateTo('/syncedTabs', app);
       return flushTasks().then(function() {
         assertEquals('chrome://history/syncedTabs', window.location.href);
 
@@ -93,7 +86,7 @@
           app.shadowRoot!.querySelector('#history'),
           app.$['tabs-content'].selectedItem);
 
-      navigateTo('/journeys');
+      navigateTo('/journeys', app);
       return flushTasks().then(function() {
         assertEquals('chrome://history/journeys', window.location.href);
 
@@ -113,7 +106,7 @@
       assertEquals('chrome://history/', sidebar.$.history.href);
       assertEquals('history', sidebar.$.history.getAttribute('path'));
 
-      navigateTo('/journeys');
+      navigateTo('/journeys', app);
       return flushTasks().then(function() {
         // Currently selected history view is preserved in sidebar menu item.
         assertEquals(
@@ -230,7 +223,7 @@
       assertEquals('chrome://history/', window.location.href);
       const searchTerm = 'Mei';
       assertEquals('history', app.$.content.selected);
-      navigateTo('/?q=' + searchTerm);
+      navigateTo('/?q=' + searchTerm, app);
       assertEquals(searchTerm, app.$.toolbar.searchTerm);
     });
 
@@ -246,7 +239,7 @@
     test('search is preserved across tabs and sidebar menu items', function() {
       const searchTerm = 'Soldier76';
       assertEquals('history', sidebar.$.menu.selected);
-      navigateTo('/?q=' + searchTerm);
+      navigateTo('/?q=' + searchTerm, app);
 
       sidebar.$.syncedTabs.click();
       assertEquals('syncedTabs', sidebar.$.menu.selected);
diff --git a/chrome/test/data/webui/history/test_util.ts b/chrome/test/data/webui/history/test_util.ts
index a4d0272..15ce8d9 100644
--- a/chrome/test/data/webui/history/test_util.ts
+++ b/chrome/test/data/webui/history/test_util.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryEntry, HistoryQuery} from 'chrome://history/history.js';
+import {ForeignSession, ForeignSessionTab, ForeignSessionWindow, HistoryAppElement, HistoryEntry, HistoryQuery} from 'chrome://history/history.js';
 import {middleOfNode} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 
 
@@ -176,3 +176,11 @@
 
   return {tabs: tabs, sessionId: 123, timestamp: 0};
 }
+
+export function navigateTo(route: string, app: HistoryAppElement) {
+  window.history.replaceState({}, '', route);
+  window.dispatchEvent(new CustomEvent('location-changed'));
+  // Update from the URL synchronously.
+  app.shadowRoot!.querySelector(
+                     'history-router')!.getDebouncerForTesting()!.flush();
+}
diff --git a/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js b/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js
index 37f511b..fc7b29b 100644
--- a/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_sync_controls_test.js
@@ -84,7 +84,6 @@
 suite('OsSyncControlsTest', function() {
   let browserProxy = null;
   let syncControls = null;
-  let syncIconContainer = null;
 
   setup(function() {
     browserProxy = new TestOsSyncBrowserProxy();
@@ -92,14 +91,7 @@
 
     PolymerTest.clearBody();
     syncControls = document.createElement('os-sync-controls');
-    syncControls.syncStatus = getDefaultSyncStatus();
-    syncControls.profileName = 'John Cena';
-    syncControls.profileEmail = 'john.cena@gmail.com';
-    syncControls.profileIconUrl = 'data:image/png;base64,abc123';
     document.body.appendChild(syncControls);
-
-    // Alias to help with line wrapping in test cases.
-    syncIconContainer = syncControls.$.syncIconContainer;
   });
 
   teardown(function() {
@@ -113,73 +105,6 @@
     assertFalse(syncControls.hidden);
   });
 
-  test('Avatar icon', function() {
-    assertEquals('data:image/png;base64,abc123', syncControls.$.avatarIcon.src);
-  });
-
-  test('Status icon is visible with feature enabled', function() {
-    setupSync();
-    assertFalse(syncControls.$.syncIconContainer.hidden);
-  });
-
-  test('Status icon with error', function() {
-    setupSync();
-    const status = getDefaultSyncStatus();
-    status.hasError = true;
-    syncControls.syncStatus = status;
-
-    assertTrue(syncIconContainer.classList.contains('sync-problem'));
-    assertTrue(!!syncControls.$$('[icon="settings:sync-problem"]'));
-  });
-
-  test('Status icon with sync paused for reauthentication', function() {
-    setupSync();
-    const status = getDefaultSyncStatus();
-    status.hasError = true;
-    status.statusAction = settings.StatusAction.REAUTHENTICATE;
-    syncControls.syncStatus = status;
-
-    assertTrue(syncIconContainer.classList.contains('sync-paused'));
-    assertTrue(!!syncControls.$$('[icon="settings:sync-disabled"]'));
-  });
-
-  test('Status icon with sync disabled', function() {
-    setupSync();
-    const status = getDefaultSyncStatus();
-    status.disabled = true;
-    syncControls.syncStatus = status;
-
-    assertTrue(syncIconContainer.classList.contains('sync-disabled'));
-    assertTrue(!!syncControls.$$('[icon="cr:sync"]'));
-  });
-
-  test('Account name and email with feature enabled', function() {
-    setupSync();
-    assertEquals('John Cena', syncControls.$.accountTitle.textContent.trim());
-    assertEquals(
-        'Syncing to john.cena@gmail.com',
-        syncControls.$.accountSubtitle.textContent.trim());
-  });
-
-  test('Account name and email with sync error', function() {
-    setupSync();
-    syncControls.syncStatus = {hasError: true};
-    Polymer.dom.flush();
-    assertEquals(
-        `Sync isn't working`, syncControls.$.accountTitle.textContent.trim());
-    assertEquals(
-        'john.cena@gmail.com',
-        syncControls.$.accountSubtitle.textContent.trim());
-  });
-
-  // Regression test for https://crbug.com/1076239
-  test('Handles undefined syncStatus', function() {
-    syncControls.syncStatus = undefined;
-    setupSync();
-    assertEquals('', syncControls.$.accountTitle.textContent.trim());
-    assertEquals('', syncControls.$.accountSubtitle.textContent.trim());
-  });
-
   test('SyncEnabled', function() {
     setupSync();
 
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
index 5c2e304f..22b440c 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.ts
@@ -16,6 +16,7 @@
         blocked: false,
         crashed: false,
         faviconUrl: undefined,
+        activeFaviconUrl: undefined,
         groupId: undefined,
         id: -1,
         index: -1,
diff --git a/chromeos/process_proxy/process_proxy_registry.h b/chromeos/process_proxy/process_proxy_registry.h
index 9b83c5b..1982794 100644
--- a/chromeos/process_proxy/process_proxy_registry.h
+++ b/chromeos/process_proxy/process_proxy_registry.h
@@ -54,7 +54,7 @@
   static scoped_refptr<base::SequencedTaskRunner> GetTaskRunner();
 
   // Starts new ProcessProxy (which starts new process).
-  // Returns true if the process is created sucessfully, false otherwise.
+  // Returns true if the process is created successfully, false otherwise.
   // The unique process id is passed back via |id|.
   bool OpenProcess(const base::CommandLine& cmdline,
                    const std::string& user_id_hash,
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 7dbd775..46594cd1 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-99-4827.0-1643629123-benchmark-99.0.4844.22-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-99-4827.0-1643629123-benchmark-99.0.4844.23-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 094db9b7..dbf73f9 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-99-4827.0-1643628001-benchmark-99.0.4844.22-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-99-4827.0-1643628001-benchmark-99.0.4844.23-r1-redacted.afdo.xz
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 4c3fea05..3553e68c 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -828,6 +828,8 @@
       deps += [
         "//components/autofill_assistant/browser",
         "//components/autofill_assistant/browser:proto",
+        "//components/autofill_assistant/content/common",
+        "//components/autofill_assistant/content/common:mojo_interfaces",
         "//components/autofill_assistant/content/renderer:browser_tests",
         "//components/browser_ui/client_certificate/android",
         "//components/browser_ui/client_certificate/android:java",
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 2bdd7a6..d84b2d0 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -324,6 +324,8 @@
     "//components/autofill_assistant/browser/devtools",
     "//components/autofill_assistant/browser/public:public",
     "//components/autofill_assistant/content/browser",
+    "//components/autofill_assistant/content/common",
+    "//components/autofill_assistant/content/common:mojo_interfaces",
     "//components/client_update_protocol",
     "//components/google/core/common:common",
     "//components/keyed_service/core",
diff --git a/components/autofill_assistant/browser/DEPS b/components/autofill_assistant/browser/DEPS
index 181aa07..db59c99 100644
--- a/components/autofill_assistant/browser/DEPS
+++ b/components/autofill_assistant/browser/DEPS
@@ -17,11 +17,13 @@
   "+components/strings/grit/components_strings.h",
   "+crypto",
   "+google_apis",
+  "+mojo/public/cpp/bindings",
   "+net",
   "+services/metrics/public",
   "+services/network/public/cpp",
   "+services/network/public/mojom",
   "+services/network/test",
+  "+third_party/blink/public/common",
   "+third_party/blink/public/common/switches.h",
   "+third_party/blink/public/mojom/payments/payment_request.mojom.h",
   "+third_party/icu/source/common/unicode",
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 92097d2..65630145 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -106,7 +106,7 @@
 WebController* Controller::GetWebController() {
   if (!web_controller_) {
     web_controller_ = WebController::CreateForWebContents(
-        web_contents(), &user_data_, &log_info_);
+        web_contents(), &user_data_, &log_info_, annotate_dom_model_service_);
   }
   return web_controller_.get();
 }
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 5d1b147..0146b3b 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1312,6 +1312,16 @@
 
   // If set, the document could not be resolved.
   optional bool get_document_failed = 5;
+
+  message PredictedElement {
+    // Whether the inference returned the expectation.
+    optional bool matches_css_element = 1;
+  }
+  message SemanticInferenceResult {
+    // If this is empty, the model inference did not find any results.
+    repeated PredictedElement predicted_elements = 1;
+  }
+  optional SemanticInferenceResult semantic_inference_result = 6;
 }
 
 // The pseudo type values come from
@@ -1547,6 +1557,17 @@
     optional int32 index = 1;
   }
 
+  // If set, and the model inference is enabled on the client, will run model
+  // inference and compare if the found element has been inferred along
+  // the expectation.
+  message SemanticInformation {
+    // The objective we expect this Selector to have.
+    optional int32 objective = 1;
+    // The role we expect this Selector to have.
+    optional int32 semantic_role = 2;
+  }
+  optional SemanticInformation semantic_information = 11;
+
   reserved 1 to 8;
 }
 
diff --git a/components/autofill_assistant/browser/starter.cc b/components/autofill_assistant/browser/starter.cc
index 2bb1c9aab..233eb903 100644
--- a/components/autofill_assistant/browser/starter.cc
+++ b/components/autofill_assistant/browser/starter.cc
@@ -593,9 +593,11 @@
                          .value();
   trigger_script_coordinator_ = std::make_unique<TriggerScriptCoordinator>(
       platform_delegate_, web_contents(),
-      WebController::CreateForWebContents(web_contents(),
-                                          /* user_data= */ nullptr,
-                                          /* log_info= */ nullptr),
+      WebController::CreateForWebContents(
+          web_contents(),
+          /* user_data= */ nullptr,
+          /* log_info= */ nullptr,
+          /* annotate_dom_model_service= */ nullptr),
       std::move(service_request_sender),
       url_fetcher.GetTriggerScriptsEndpoint(),
       std::make_unique<StaticTriggerConditions>(
diff --git a/components/autofill_assistant/browser/web/element_finder.cc b/components/autofill_assistant/browser/web/element_finder.cc
index dc73afe..3646efa 100644
--- a/components/autofill_assistant/browser/web/element_finder.cc
+++ b/components/autofill_assistant/browser/web/element_finder.cc
@@ -4,12 +4,16 @@
 
 #include "components/autofill_assistant/browser/web/element_finder.h"
 
+#include "base/barrier_callback.h"
 #include "components/autofill_assistant/browser/devtools/devtools_client.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/user_data_util.h"
 #include "components/autofill_assistant/browser/web/element.h"
 #include "components/autofill_assistant/browser/web/js_filter_builder.h"
 #include "components/autofill_assistant/browser/web/web_controller_util.h"
+#include "components/autofill_assistant/content/browser/content_autofill_assistant_driver.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 
@@ -74,6 +78,12 @@
   }
   return false;
 }
+
+void AddHostToList(std::vector<content::GlobalRenderFrameHostId>& host_ids,
+                   content::RenderFrameHost* host) {
+  host_ids.push_back(host->GetGlobalId());
+}
+
 }  // namespace
 
 ElementFinder::Result::Result() = default;
@@ -86,16 +96,19 @@
   return ElementFinder::Result();
 }
 
-ElementFinder::ElementFinder(content::WebContents* web_contents,
-                             DevtoolsClient* devtools_client,
-                             const UserData* user_data,
-                             ProcessedActionStatusDetailsProto* log_info,
-                             const Selector& selector,
-                             ResultType result_type)
+ElementFinder::ElementFinder(
+    content::WebContents* web_contents,
+    DevtoolsClient* devtools_client,
+    const UserData* user_data,
+    ProcessedActionStatusDetailsProto* log_info,
+    AnnotateDomModelService* annotate_dom_model_service,
+    const Selector& selector,
+    ResultType result_type)
     : web_contents_(web_contents),
       devtools_client_(devtools_client),
       user_data_(user_data),
       log_info_(log_info),
+      annotate_dom_model_service_(annotate_dom_model_service),
       selector_(selector),
       result_type_(result_type) {}
 
@@ -105,7 +118,7 @@
   callback_ = std::move(callback);
 
   if (selector_.empty()) {
-    SendErrorResult(ClientStatus(INVALID_SELECTOR));
+    SendResult(ClientStatus(INVALID_SELECTOR), Result::EmptyResult());
     return;
   }
 
@@ -113,10 +126,17 @@
   ClientStatus resolve_status =
       user_data::ResolveSelectorUserData(&selector_proto_, user_data_);
   if (!resolve_status.ok()) {
-    SendErrorResult(resolve_status);
+    SendResult(resolve_status, Result::EmptyResult());
     return;
   }
 
+  if (annotate_dom_model_service_ &&
+      selector_.proto.has_semantic_information()) {
+    RunAnnotateDomModel();
+  } else {
+    semantic_result_done_ = true;
+  }
+
   if (start_element.container_frame_host == nullptr) {
     current_frame_ = web_contents_->GetMainFrame();
   } else {
@@ -149,29 +169,67 @@
   if (selector_.proto.has_tracking_id()) {
     info->set_tracking_id(selector_.proto.tracking_id());
   }
+
+  if (selector_.proto.has_semantic_information()) {
+    auto* inference_result = info->mutable_semantic_inference_result();
+    for (const auto& node_data : semantic_node_data_) {
+      auto* predicted_element = inference_result->add_predicted_elements();
+      predicted_element->set_matches_css_element(
+          node_data.backend_node_id == result_backend_node_id_.value_or(-1));
+    }
+  }
 }
 
-void ElementFinder::SendErrorResult(const ClientStatus& status) {
-  if (!callback_)
+void ElementFinder::SendResult(const ClientStatus& status,
+                               const Result& result) {
+  if (!callback_) {
     return;
-
-  DCHECK(!status.ok());
+  }
   UpdateLogInfo(status);
-
-  std::move(callback_).Run(status, std::make_unique<Result>());
+  std::move(callback_).Run(
+      ClientStatus(status.proto_status(), status.details()),
+      std::make_unique<Result>(result));
 }
 
-void ElementFinder::SendSuccessResult(const std::string& object_id) {
+void ElementFinder::SendCollectedResultIfAny() {
+  if (!callback_) {
+    return;
+  }
+  if (!css_result_done_ || !semantic_result_done_) {
+    return;
+  }
+  SendResult(result_status_, result_);
+}
+
+void ElementFinder::GiveUpElementResolutionWithError(
+    const ClientStatus& status) {
+  DCHECK(!status.ok());
   if (!callback_)
     return;
 
-  UpdateLogInfo(OkClientStatus());
+  css_result_done_ = true;
+  result_status_ = status;
+  SendCollectedResultIfAny();
+}
 
-  // Fill in result and return
-  std::unique_ptr<Result> result =
-      std::make_unique<Result>(BuildResult(object_id));
-  result->dom_object.frame_stack = frame_stack_;
-  std::move(callback_).Run(OkClientStatus(), std::move(result));
+void ElementFinder::ResultFound(const std::string& object_id) {
+  if (!callback_)
+    return;
+
+  result_status_ = OkClientStatus();
+
+  // Fill in result.
+  result_ = BuildResult(object_id);
+  result_.dom_object.frame_stack = frame_stack_;
+
+  if (annotate_dom_model_service_ &&
+      selector_.proto.has_semantic_information()) {
+    DescribeNodeForAnnotateDom();
+    return;
+  }
+
+  css_result_done_ = true;
+  SendCollectedResultIfAny();
 }
 
 ElementFinder::Result ElementFinder::BuildResult(const std::string& object_id) {
@@ -182,6 +240,80 @@
   return result;
 }
 
+void ElementFinder::DescribeNodeForAnnotateDom() {
+  devtools_client_->GetDOM()->DescribeNode(
+      dom::DescribeNodeParams::Builder()
+          .SetObjectId(result_.object_id())
+          .Build(),
+      result_.node_frame_id(),
+      base::BindOnce(&ElementFinder::OnDescribeNodeForAnnotateDom,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ElementFinder::OnDescribeNodeForAnnotateDom(
+    const DevtoolsClient::ReplyStatus& reply_status,
+    std::unique_ptr<dom::DescribeNodeResult> node_result) {
+  if (node_result && node_result->GetNode()) {
+    result_backend_node_id_ = node_result->GetNode()->GetBackendNodeId();
+  }
+  css_result_done_ = true;
+  SendCollectedResultIfAny();
+}
+
+void ElementFinder::RunAnnotateDomModel() {
+  std::vector<content::GlobalRenderFrameHostId> host_ids;
+  web_contents_->GetMainFrame()->ForEachRenderFrameHost(
+      base::BindRepeating(&AddHostToList, std::ref(host_ids)));
+  const auto run_on_frame = base::BarrierCallback<std::vector<NodeData>>(
+      host_ids.size(), base::BindOnce(&ElementFinder::OnRunAnnotateDomModel,
+                                      weak_ptr_factory_.GetWeakPtr()));
+  for (const auto& host_id : host_ids) {
+    RunAnnotateDomModelOnFrame(host_id, run_on_frame);
+  }
+}
+
+void ElementFinder::RunAnnotateDomModelOnFrame(
+    const content::GlobalRenderFrameHostId& host_id,
+    base::OnceCallback<void(std::vector<NodeData>)> callback) {
+  content::RenderFrameHost* render_frame_host =
+      content::RenderFrameHost::FromID(host_id);
+  if (!render_frame_host) {
+    std::move(callback).Run(std::vector<NodeData>());
+    return;
+  }
+
+  auto* driver = ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
+      render_frame_host, annotate_dom_model_service_);
+  if (!driver) {
+    NOTREACHED();
+    std::move(callback).Run(std::vector<NodeData>());
+    return;
+  }
+
+  driver->GetAutofillAssistantAgent()->GetSemanticNodes(
+      selector_.proto.semantic_information().semantic_role(),
+      selector_.proto.semantic_information().objective(),
+      base::BindOnce(&ElementFinder::OnRunAnnotateDomModelOnFrame,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ElementFinder::OnRunAnnotateDomModelOnFrame(
+    base::OnceCallback<void(std::vector<NodeData>)> callback,
+    bool success,
+    const std::vector<NodeData>& node_data) {
+  std::move(callback).Run(node_data);
+}
+
+void ElementFinder::OnRunAnnotateDomModel(
+    const std::vector<std::vector<NodeData>>& all_node_data) {
+  for (const auto& node_data : all_node_data) {
+    semantic_node_data_.insert(semantic_node_data_.end(), node_data.begin(),
+                               node_data.end());
+  }
+  semantic_result_done_ = true;
+  SendCollectedResultIfAny();
+}
+
 void ElementFinder::ExecuteNextTask() {
   const auto& filters = selector_proto_.filters();
 
@@ -206,7 +338,7 @@
         }
         break;
     }
-    SendSuccessResult(object_id);
+    ResultFound(object_id);
     return;
   }
 
@@ -279,7 +411,7 @@
     case SelectorProto::Filter::FILTER_NOT_SET:
       VLOG(1) << __func__ << " Unset or unknown filter in " << filter << " in "
               << selector_;
-      SendErrorResult(ClientStatus(INVALID_SELECTOR));
+      GiveUpElementResolutionWithError(ClientStatus(INVALID_SELECTOR));
       return;
   }
 }
@@ -288,11 +420,11 @@
   if (current_matches_.size() > 1) {
     VLOG(1) << __func__ << " Got " << current_matches_.size() << " matches for "
             << selector_ << ", when only 1 was expected.";
-    SendErrorResult(ClientStatus(TOO_MANY_ELEMENTS));
+    GiveUpElementResolutionWithError(ClientStatus(TOO_MANY_ELEMENTS));
     return false;
   }
   if (current_matches_.empty()) {
-    SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+    GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return false;
   }
 
@@ -309,7 +441,7 @@
     return true;
   }
 
-  SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+  GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
   return false;
 }
 
@@ -320,7 +452,7 @@
     current_matches_.clear();
     return true;
   }
-  SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+  GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
   return false;
 }
 
@@ -332,7 +464,7 @@
   }
 
   if (current_matches_.empty()) {
-    SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+    GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return false;
   }
 
@@ -378,7 +510,7 @@
       CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
   if (!status.ok()) {
     VLOG(1) << __func__ << ": Failed to push value to JS array.";
-    SendErrorResult(status);
+    GiveUpElementResolutionWithError(status);
     return;
   }
 
@@ -387,7 +519,7 @@
   if (index == 0 &&
       !SafeGetObjectId(result->GetResult(), &current_matches_js_array_)) {
     VLOG(1) << __func__ << " Failed to get array ID.";
-    SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+    GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
   }
 
@@ -410,14 +542,14 @@
   if (!status.ok()) {
     VLOG(1) << __func__ << " Failed to get document root element.";
     get_document_failed_ = true;
-    SendErrorResult(status);
+    GiveUpElementResolutionWithError(status);
     return;
   }
   std::string object_id;
   if (!SafeGetObjectId(result->GetResult(), &object_id)) {
     VLOG(1) << __func__ << " Failed to get document root element.";
     get_document_failed_ = true;
-    SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+    GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
   }
 
@@ -456,7 +588,7 @@
     // call, it is expected.
     VLOG(1) << __func__ << ": Context doesn't exist yet to query frame "
             << frame_stack_.size() << " of " << selector_;
-    SendErrorResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+    GiveUpElementResolutionWithError(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
   }
   ClientStatus status =
@@ -464,7 +596,7 @@
   if (!status.ok()) {
     VLOG(1) << __func__ << ": Failed to query selector for frame "
             << frame_stack_.size() << " of " << selector_ << ": " << status;
-    SendErrorResult(status);
+    GiveUpElementResolutionWithError(status);
     return;
   }
 
@@ -493,7 +625,7 @@
   if (!ConvertPseudoType(proto_pseudo_type, &pseudo_type)) {
     VLOG(1) << __func__ << ": Unsupported pseudo-type "
             << PseudoTypeName(proto_pseudo_type);
-    SendErrorResult(ClientStatus(INVALID_ACTION));
+    GiveUpElementResolutionWithError(ClientStatus(INVALID_ACTION));
     return;
   }
 
@@ -517,7 +649,7 @@
     std::unique_ptr<dom::DescribeNodeResult> result) {
   if (!result || !result->GetNode()) {
     VLOG(1) << __func__ << " Failed to describe the node for pseudo element.";
-    SendErrorResult(
+    GiveUpElementResolutionWithError(
         UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
     return;
   }
@@ -568,7 +700,7 @@
     std::unique_ptr<dom::DescribeNodeResult> result) {
   if (!result || !result->GetNode()) {
     VLOG(1) << __func__ << " Failed to describe the node.";
-    SendErrorResult(
+    GiveUpElementResolutionWithError(
         UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
     return;
   }
@@ -580,7 +712,7 @@
     // See: b/206647825
     if (!node->HasFrameId()) {
       NOTREACHED() << "Frame without ID";  // Ensure all frames have an id.
-      SendErrorResult(ClientStatus(FRAME_HOST_NOT_FOUND));
+      GiveUpElementResolutionWithError(ClientStatus(FRAME_HOST_NOT_FOUND));
       return;
     }
 
@@ -590,7 +722,7 @@
         FindCorrespondingRenderFrameHost(node->GetFrameId(), web_contents_);
     if (!frame) {
       VLOG(1) << __func__ << " Failed to find corresponding owner frame.";
-      SendErrorResult(ClientStatus(FRAME_HOST_NOT_FOUND));
+      GiveUpElementResolutionWithError(ClientStatus(FRAME_HOST_NOT_FOUND));
       return;
     }
     current_frame_ = frame;
@@ -637,7 +769,7 @@
     std::unique_ptr<dom::ResolveNodeResult> result) {
   if (!result || !result->GetObject() || !result->GetObject()->HasObjectId()) {
     VLOG(1) << __func__ << " Failed to resolve object id from backend id.";
-    SendErrorResult(
+    GiveUpElementResolutionWithError(
         UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
     return;
   }
@@ -705,7 +837,7 @@
   if (!status.ok()) {
     VLOG(1) << __func__ << ": Failed to get element from array for "
             << selector_;
-    SendErrorResult(status);
+    GiveUpElementResolutionWithError(status);
     return;
   }
 
diff --git a/components/autofill_assistant/browser/web/element_finder.h b/components/autofill_assistant/browser/web/element_finder.h
index cd4e94c..dad92bdb 100644
--- a/components/autofill_assistant/browser/web/element_finder.h
+++ b/components/autofill_assistant/browser/web/element_finder.h
@@ -22,10 +22,14 @@
 #include "components/autofill_assistant/browser/web/element.h"
 #include "components/autofill_assistant/browser/web/js_filter_builder.h"
 #include "components/autofill_assistant/browser/web/web_controller_worker.h"
+#include "components/autofill_assistant/content/browser/annotate_dom_model_service.h"
+#include "components/autofill_assistant/content/common/node_data.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace content {
 class WebContents;
 class RenderFrameHost;
+struct GlobalRenderFrameHostId;
 }  // namespace content
 
 namespace autofill_assistant {
@@ -88,11 +92,13 @@
   };
 
   // |web_contents|, |devtools_client| and |user_data| must be valid for the
-  // lifetime of the instance.
+  // lifetime of the instance. If |annotate_dom_model_service| is not nullptr,
+  // must be valid for the lifetime of the instance.
   ElementFinder(content::WebContents* web_contents,
                 DevtoolsClient* devtools_client,
                 const UserData* user_data,
                 ProcessedActionStatusDetailsProto* log_info,
+                AnnotateDomModelService* annotate_dom_model_service,
                 const Selector& selector,
                 ResultType result_type);
   ~ElementFinder() override;
@@ -108,13 +114,21 @@
   // Update the log info with details about the current run.
   void UpdateLogInfo(const ClientStatus& status);
 
-  // Sends a result with the given status and no data. This expects an error
-  // status and will add details to |log_info_|.
-  void SendErrorResult(const ClientStatus& status);
+  // Eventually returns the given status and no element. This expects an error
+  // status.
+  void GiveUpElementResolutionWithError(const ClientStatus& status);
 
-  // Builds a result from the current state of the finder and returns it. This
-  // will add details to |log_info_|.
-  void SendSuccessResult(const std::string& object_id);
+  // Builds a result from the current state of the finder and eventually
+  // returns it with an ok status.
+  void ResultFound(const std::string& object_id);
+
+  // Call |callback_| with the |status| and |result|.
+  void SendResult(const ClientStatus& status, const Result& result);
+
+  // Calls |SendResult| with a the |result_status_| and |result_| if all tasks
+  // are complete. This includes waiting for the CSS selector resolution and
+  // the annotate DOM model inference (if applicable).
+  void SendCollectedResultIfAny();
 
   // Report |object_id| as result in |result| and initialize the frame-related
   // fields of |result| from the current state. Leaves the frame stack empty.
@@ -252,10 +266,28 @@
       const DevtoolsClient::ReplyStatus& reply_status,
       std::unique_ptr<runtime::CallFunctionOnResult> result);
 
+  // Helpers for running the annotate DOM model on all frames. The results will
+  // be compared against |result_| and logged to |log_info_|.
+  void DescribeNodeForAnnotateDom();
+  void OnDescribeNodeForAnnotateDom(
+      const DevtoolsClient::ReplyStatus& reply_status,
+      std::unique_ptr<dom::DescribeNodeResult> node_result);
+  void RunAnnotateDomModel();
+  void RunAnnotateDomModelOnFrame(
+      const content::GlobalRenderFrameHostId& host_id,
+      base::OnceCallback<void(std::vector<NodeData>)> callback);
+  void OnRunAnnotateDomModelOnFrame(
+      base::OnceCallback<void(std::vector<NodeData>)> callback,
+      bool success,
+      const std::vector<NodeData>& node_data);
+  void OnRunAnnotateDomModel(
+      const std::vector<std::vector<NodeData>>& all_node_data);
+
   const raw_ptr<content::WebContents> web_contents_;
   const raw_ptr<DevtoolsClient> devtools_client_;
   const raw_ptr<const UserData> user_data_;
   const raw_ptr<ProcessedActionStatusDetailsProto> log_info_;
+  const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_;
   const Selector selector_;
   const ResultType result_type_;
   Callback callback_;
@@ -299,6 +331,24 @@
 
   std::vector<JsObjectIdentifier> frame_stack_;
 
+  // The status of finding the element.
+  ClientStatus result_status_;
+
+  // The successful result when the element has been found. In the case where
+  // |selector_| contains |SemanticInformation| this is only filled once the
+  // backend node id has been resolved.
+  Result result_ = Result::EmptyResult();
+  // The backend node id (stable id of DevTools) for the |result_|. Only
+  // filled if the |selector_| contains |SemanticInformation|.
+  // TODO(b/217160707): Always fill this.
+  absl::optional<int> result_backend_node_id_;
+  bool css_result_done_ = false;
+
+  // Elements gathered through all frames. Unused if the |selector_| does not
+  // contain |SemanticInformation|.
+  std::vector<NodeData> semantic_node_data_;
+  bool semantic_result_done_ = false;
+
   // Finder for the target of the current proximity filter.
   std::unique_ptr<ElementFinder> proximity_target_filter_;
 
diff --git a/components/autofill_assistant/browser/web/mock_web_controller.cc b/components/autofill_assistant/browser/web/mock_web_controller.cc
index 6a1055e..05c205c 100644
--- a/components/autofill_assistant/browser/web/mock_web_controller.cc
+++ b/components/autofill_assistant/browser/web/mock_web_controller.cc
@@ -10,7 +10,8 @@
     : WebController(/* web_contents= */ nullptr,
                     /* devtools_client= */ nullptr,
                     /* user_data= */ nullptr,
-                    /* log_info= */ nullptr) {}
+                    /* log_info= */ nullptr,
+                    /* annotate_dom_model_service= */ nullptr) {}
 MockWebController::~MockWebController() {}
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/web/web_controller.cc b/components/autofill_assistant/browser/web/web_controller.cc
index 79ce36c..3d5f0c81 100644
--- a/components/autofill_assistant/browser/web/web_controller.cc
+++ b/components/autofill_assistant/browser/web/web_controller.cc
@@ -417,22 +417,26 @@
 std::unique_ptr<WebController> WebController::CreateForWebContents(
     content::WebContents* web_contents,
     const UserData* user_data,
-    ProcessedActionStatusDetailsProto* log_info) {
+    ProcessedActionStatusDetailsProto* log_info,
+    AnnotateDomModelService* annotate_dom_model_service) {
   return std::make_unique<WebController>(
       web_contents,
       std::make_unique<DevtoolsClient>(
           content::DevToolsAgentHost::GetOrCreateFor(web_contents)),
-      user_data, log_info);
+      user_data, log_info, annotate_dom_model_service);
 }
 
-WebController::WebController(content::WebContents* web_contents,
-                             std::unique_ptr<DevtoolsClient> devtools_client,
-                             const UserData* user_data,
-                             ProcessedActionStatusDetailsProto* log_info)
+WebController::WebController(
+    content::WebContents* web_contents,
+    std::unique_ptr<DevtoolsClient> devtools_client,
+    const UserData* user_data,
+    ProcessedActionStatusDetailsProto* log_info,
+    AnnotateDomModelService* annotate_dom_model_service)
     : web_contents_(web_contents),
       devtools_client_(std::move(devtools_client)),
       user_data_(user_data),
-      log_info_(log_info) {}
+      log_info_(log_info),
+      annotate_dom_model_service_(annotate_dom_model_service) {}
 
 WebController::~WebController() {}
 
@@ -855,8 +859,8 @@
                                      ElementFinder::ResultType result_type,
                                      ElementFinder::Callback callback) {
   auto finder = std::make_unique<ElementFinder>(
-      web_contents_, devtools_client_.get(), user_data_, log_info_, selector,
-      result_type);
+      web_contents_, devtools_client_.get(), user_data_, log_info_,
+      annotate_dom_model_service_, selector, result_type);
 
   auto* ptr = finder.get();
   pending_workers_.emplace_back(std::move(finder));
diff --git a/components/autofill_assistant/browser/web/web_controller.h b/components/autofill_assistant/browser/web/web_controller.h
index f952046..b39217e 100644
--- a/components/autofill_assistant/browser/web/web_controller.h
+++ b/components/autofill_assistant/browser/web/web_controller.h
@@ -32,6 +32,7 @@
 #include "components/autofill_assistant/browser/web/selector_observer.h"
 #include "components/autofill_assistant/browser/web/send_keyboard_input_worker.h"
 #include "components/autofill_assistant/browser/web/web_controller_worker.h"
+#include "components/autofill_assistant/content/browser/annotate_dom_model_service.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "third_party/icu/source/common/unicode/umachine.h"
 #include "url/gurl.h"
@@ -63,18 +64,22 @@
 // multiple operations, whether in sequence or in parallel.
 class WebController {
  public:
-  // Create web controller for a given |web_contents|. |user_data| must be valid
+  // Create web controller for a given |web_contents|. |user_data|, |log_info|
+  // and |annotate_dom_model_service| (if not nullptr) must be valid
   // for the lifetime of the controller.
   static std::unique_ptr<WebController> CreateForWebContents(
       content::WebContents* web_contents,
       const UserData* user_data,
-      ProcessedActionStatusDetailsProto* log_info);
+      ProcessedActionStatusDetailsProto* log_info,
+      AnnotateDomModelService* annotate_dom_model_service);
 
-  // |web_contents| and |user_data| must outlive this web controller.
+  // |web_contents|, |user_data|, |log_info| and |annotate_dom_model_service|
+  // (if not nullptr) must outlive this web controller.
   WebController(content::WebContents* web_contents,
                 std::unique_ptr<DevtoolsClient> devtools_client,
                 const UserData* user_data,
-                ProcessedActionStatusDetailsProto* log_info);
+                ProcessedActionStatusDetailsProto* log_info,
+                AnnotateDomModelService* annotate_dom_model_service);
 
   WebController(const WebController&) = delete;
   WebController& operator=(const WebController&) = delete;
@@ -519,6 +524,8 @@
   // Must not be |nullptr| and outlive this web controller.
   const raw_ptr<const UserData> user_data_;
   const raw_ptr<ProcessedActionStatusDetailsProto> log_info_;
+  // Can be |nullptr|, if not must outlive this web controller.
+  const raw_ptr<AnnotateDomModelService> annotate_dom_model_service_;
 
   // Currently running workers.
   std::vector<std::unique_ptr<WebControllerWorker>> pending_workers_;
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index a2e38a4..c10c100 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/time/time.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill_assistant/browser/action_value.pb.h"
@@ -29,22 +30,62 @@
 #include "components/autofill_assistant/browser/user_model.h"
 #include "components/autofill_assistant/browser/web/element_action_util.h"
 #include "components/autofill_assistant/browser/web/element_finder.h"
+#include "components/autofill_assistant/content/common/autofill_assistant_agent.mojom.h"
+#include "components/autofill_assistant/content/common/node_data.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/public/test/content_browser_test_utils.h"
 #include "content/shell/browser/shell.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/switches.h"
 
 namespace autofill_assistant {
+namespace {
 
+using ::base::test::RunOnceCallback;
+using ::testing::_;
 using ::testing::AnyOf;
 using ::testing::IsEmpty;
 using ::testing::Return;
 
+class MockAutofillAssistantAgent : public mojom::AutofillAssistantAgent {
+ public:
+  MockAutofillAssistantAgent() = default;
+  ~MockAutofillAssistantAgent() override = default;
+
+  void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
+    receivers_.Add(
+        this, mojo::PendingAssociatedReceiver<mojom::AutofillAssistantAgent>(
+                  std::move(handle)));
+  }
+
+  MOCK_METHOD(
+      void,
+      GetSemanticNodes,
+      (int32_t role,
+       int32_t objective,
+       base::OnceCallback<void(bool, const std::vector<NodeData>&)> callback),
+      (override));
+
+ private:
+  mojo::AssociatedReceiverSet<mojom::AutofillAssistantAgent> receivers_;
+};
+
+class FakeAnnotateDomModelService : public AnnotateDomModelService {
+ public:
+  FakeAnnotateDomModelService()
+      : AnnotateDomModelService(/* opt_guide= */ nullptr,
+                                /* background_task_runner= */ nullptr) {}
+  ~FakeAnnotateDomModelService() override = default;
+};
+
+}  // namespace
+
 // Flag to enable site per process to enforce OOPIFs.
 const char* kSitePerProcess = "site-per-process";
 const char* kTargetWebsitePath = "/autofill_assistant_target_website.html";
@@ -84,8 +125,22 @@
     ASSERT_TRUE(http_server_->Start(8080));
     ASSERT_TRUE(
         NavigateToURL(shell(), http_server_->GetURL(kTargetWebsitePath)));
+
+    // Register the same agent on all frames, such that the callback can be
+    // mocked.
+    shell()->web_contents()->GetMainFrame()->ForEachRenderFrameHost(
+        base::BindLambdaForTesting([this](content::RenderFrameHost* host) {
+          host->GetRemoteAssociatedInterfaces()->OverrideBinderForTesting(
+              mojom::AutofillAssistantAgent::Name_,
+              base::BindRepeating(
+                  &MockAutofillAssistantAgent::BindPendingReceiver,
+                  base::Unretained(&autofill_assistant_agent_)));
+        }));
+
     web_controller_ = WebController::CreateForWebContents(
-        shell()->web_contents(), &user_data_, &log_info_);
+        shell()->web_contents(), &user_data_, &log_info_,
+        &annotate_dom_model_service_);
+
     Observe(shell()->web_contents());
   }
 
@@ -947,6 +1002,8 @@
   UserData user_data_;
   UserModel user_model_;
   ProcessedActionStatusDetailsProto log_info_;
+  MockAutofillAssistantAgent autofill_assistant_agent_;
+  FakeAnnotateDomModelService annotate_dom_model_service_;
 
  private:
   std::unique_ptr<net::EmbeddedTestServer> http_server_;
@@ -3351,4 +3408,33 @@
   run_loop.Run();
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, RunWithDomAnnotation) {
+  Selector selector({"#input1"});
+  auto* semantic_information = selector.proto.mutable_semantic_information();
+  semantic_information->set_semantic_role(1);
+  semantic_information->set_objective(2);
+
+  NodeData node_frame_1;
+  node_frame_1.backend_node_id = 100;
+  NodeData node_frame_2;
+  node_frame_2.backend_node_id = 200;
+  EXPECT_CALL(autofill_assistant_agent_, GetSemanticNodes(1, 2, _))
+      .WillOnce(RunOnceCallback<2>(true, std::vector<NodeData>{node_frame_1}))
+      .WillOnce(RunOnceCallback<2>(true, std::vector<NodeData>{node_frame_2}))
+      // Capture any other frames.
+      .WillRepeatedly(RunOnceCallback<2>(true, std::vector<NodeData>()));
+
+  ClientStatus status;
+  ElementFinder::Result result;
+  FindElement(selector, &status, &result);
+
+  ASSERT_EQ(log_info_.element_finder_info().size(), 1);
+  EXPECT_EQ(log_info_.element_finder_info(0).status(), ACTION_APPLIED);
+  ASSERT_EQ(log_info_.element_finder_info(0)
+                .semantic_inference_result()
+                .predicted_elements()
+                .size(),
+            2);
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/website_login_manager_impl_unittest.cc b/components/autofill_assistant/browser/website_login_manager_impl_unittest.cc
index dfbd893a..b0ff929 100644
--- a/components/autofill_assistant/browser/website_login_manager_impl_unittest.cc
+++ b/components/autofill_assistant/browser/website_login_manager_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "components/password_manager/core/browser/mock_password_store_interface.h"
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/password_store_interface.h"
@@ -55,15 +56,23 @@
   MockPasswordManagerClient() = default;
   ~MockPasswordManagerClient() override = default;
 
-  MOCK_CONST_METHOD0(GetProfilePasswordStore,
-                     password_manager::PasswordStoreInterface*());
-  MOCK_CONST_METHOD0(GetAccountPasswordStore,
-                     password_manager::PasswordStoreInterface*());
-  MOCK_CONST_METHOD0(GetPasswordManager, PasswordManager*());
+  MOCK_METHOD(password_manager::PasswordStoreInterface*,
+              GetProfilePasswordStore,
+              (),
+              (const, override));
+  MOCK_METHOD(password_manager::PasswordStoreInterface*,
+              GetAccountPasswordStore,
+              (),
+              (const, override));
+  MOCK_METHOD(PasswordManager*, GetPasswordManager, (), (const, override));
   MOCK_METHOD(bool,
               IsSavingAndFillingEnabled,
               (const GURL&),
               (const, override));
+  MOCK_METHOD(password_manager::MockWebAuthnCredentialsDelegate*,
+              GetWebAuthnCredentialsDelegate,
+              (),
+              (override));
 };
 
 FormData MakeFormDataWithPasswordField() {
@@ -181,6 +190,10 @@
     password_manager_ = std::make_unique<PasswordManager>(&client_);
     ON_CALL(client_, GetPasswordManager())
         .WillByDefault(Return(password_manager_.get()));
+    ON_CALL(client_, GetWebAuthnCredentialsDelegate())
+        .WillByDefault(Return(&webauthn_credentials_delegate_));
+    ON_CALL(webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
+        .WillByDefault(Return(false));
   }
 
   password_manager::MockPasswordStoreInterface* store() {
@@ -197,6 +210,8 @@
   testing::NiceMock<MockPasswordManagerClient> client_;
   std::unique_ptr<WebsiteLoginManagerImpl> manager_;
   std::unique_ptr<PasswordManager> password_manager_;
+  testing::NiceMock<password_manager::MockWebAuthnCredentialsDelegate>
+      webauthn_credentials_delegate_;
   password_manager::StubPasswordManagerDriver driver_;
   scoped_refptr<password_manager::MockPasswordStoreInterface> profile_store_;
   scoped_refptr<password_manager::MockPasswordStoreInterface> account_store_;
diff --git a/components/autofill_assistant/content/browser/annotate_dom_model_service.cc b/components/autofill_assistant/content/browser/annotate_dom_model_service.cc
index 0bdec428..3c88e9a 100644
--- a/components/autofill_assistant/content/browser/annotate_dom_model_service.cc
+++ b/components/autofill_assistant/content/browser/annotate_dom_model_service.cc
@@ -43,9 +43,11 @@
     optimization_guide::OptimizationGuideModelProvider* opt_guide,
     const scoped_refptr<base::SequencedTaskRunner>& background_task_runner)
     : opt_guide_(opt_guide), background_task_runner_(background_task_runner) {
-  opt_guide_->AddObserverForOptimizationTargetModel(
-      optimization_guide::proto::OPTIMIZATION_TARGET_AUTOFILL_ASSISTANT,
-      /* model_metadata= */ absl::nullopt, this);
+  if (opt_guide_) {
+    opt_guide_->AddObserverForOptimizationTargetModel(
+        optimization_guide::proto::OPTIMIZATION_TARGET_AUTOFILL_ASSISTANT,
+        /* model_metadata= */ absl::nullopt, this);
+  }
 }
 
 AnnotateDomModelService::~AnnotateDomModelService() = default;
diff --git a/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc b/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
index ac4589a8..0ceb2ad 100644
--- a/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
+++ b/components/autofill_assistant/content/browser/content_autofill_assistant_driver.cc
@@ -32,6 +32,21 @@
   }
 }
 
+// static
+ContentAutofillAssistantDriver*
+ContentAutofillAssistantDriver::GetOrCreateForRenderFrameHost(
+    content::RenderFrameHost* render_frame_host,
+    AnnotateDomModelService* annotate_dom_model_service) {
+  ContentAutofillAssistantDriver* driver =
+      ContentAutofillAssistantDriver::GetOrCreateForCurrentDocument(
+          render_frame_host);
+  if (driver) {
+    DCHECK(annotate_dom_model_service);
+    driver->annotate_dom_model_service_ = annotate_dom_model_service;
+  }
+  return driver;
+}
+
 void ContentAutofillAssistantDriver::BindPendingReceiver(
     mojo::PendingAssociatedReceiver<mojom::AutofillAssistantDriver>
         pending_receiver) {
@@ -49,12 +64,6 @@
   return autofill_assistant_agent_;
 }
 
-void ContentAutofillAssistantDriver::SetAnnotateDomModelService(
-    AnnotateDomModelService* annotate_dom_model_service) {
-  DCHECK(annotate_dom_model_service);
-  annotate_dom_model_service_ = annotate_dom_model_service;
-}
-
 void ContentAutofillAssistantDriver::GetAnnotateDomModel(
     GetAnnotateDomModelCallback callback) {
   if (!annotate_dom_model_service_) {
diff --git a/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h b/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h
index 6850bdb..25f0f3d 100644
--- a/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h
+++ b/components/autofill_assistant/content/browser/content_autofill_assistant_driver.h
@@ -35,6 +35,10 @@
                              mojom::AutofillAssistantDriver> pending_receiver,
                          content::RenderFrameHost* render_frame_host);
 
+  static ContentAutofillAssistantDriver* GetOrCreateForRenderFrameHost(
+      content::RenderFrameHost* render_frame_host,
+      AnnotateDomModelService* annotate_dom_model_service);
+
   void BindPendingReceiver(
       mojo::PendingAssociatedReceiver<mojom::AutofillAssistantDriver>
           pending_receiver);
@@ -42,9 +46,6 @@
   const mojo::AssociatedRemote<mojom::AutofillAssistantAgent>&
   GetAutofillAssistantAgent();
 
-  void SetAnnotateDomModelService(
-      AnnotateDomModelService* annotate_dom_model_service);
-
   // autofill_assistant::mojom::AutofillAssistantDriver:
   void GetAnnotateDomModel(GetAnnotateDomModelCallback callback) override;
 
diff --git a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
index 00f7ce8..4d1d0a2 100644
--- a/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
+++ b/components/autofill_assistant/content/renderer/autofill_assistant_agent_browsertest.cc
@@ -14,11 +14,9 @@
 #include "content/public/renderer/render_frame.h"
 #include "content/public/test/render_view_test.h"
 #include "mojo/public/cpp/bindings/associated_receiver_set.h"
-#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
-#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 
 namespace autofill_assistant {
 namespace {
@@ -28,13 +26,8 @@
 
 class MockAutofillAssistantDriver : public mojom::AutofillAssistantDriver {
  public:
-  void BindHandle(mojo::ScopedMessagePipeHandle handle) {
-    receivers_.Add(this, mojo::PendingReceiver<mojom::AutofillAssistantDriver>(
-                             std::move(handle)));
-  }
-
   void BindPendingReceiver(mojo::ScopedInterfaceEndpointHandle handle) {
-    associated_receivers_.Add(
+    receivers_.Add(
         this, mojo::PendingAssociatedReceiver<mojom::AutofillAssistantDriver>(
                   std::move(handle)));
   }
@@ -45,9 +38,7 @@
               (override));
 
  private:
-  mojo::ReceiverSet<mojom::AutofillAssistantDriver> receivers_;
-  mojo::AssociatedReceiverSet<mojom::AutofillAssistantDriver>
-      associated_receivers_;
+  mojo::AssociatedReceiverSet<mojom::AutofillAssistantDriver> receivers_;
 };
 
 class AutofillAssistantAgentBrowserTest : public content::RenderViewTest {
@@ -57,16 +48,13 @@
   void SetUp() override {
     RenderViewTest::SetUp();
 
-    GetMainRenderFrame()->GetBrowserInterfaceBroker()->SetBinderForTesting(
-        mojom::AutofillAssistantDriver::Name_,
-        base::BindRepeating(&MockAutofillAssistantDriver::BindHandle,
-                            base::Unretained(&autofill_assistant_driver_)));
-    blink::AssociatedInterfaceProvider* remote_interfaces =
-        GetMainRenderFrame()->GetRemoteAssociatedInterfaces();
-    remote_interfaces->OverrideBinderForTesting(
-        mojom::AutofillAssistantDriver::Name_,
-        base::BindRepeating(&MockAutofillAssistantDriver::BindPendingReceiver,
-                            base::Unretained(&autofill_assistant_driver_)));
+    GetMainRenderFrame()
+        ->GetRemoteAssociatedInterfaces()
+        ->OverrideBinderForTesting(
+            mojom::AutofillAssistantDriver::Name_,
+            base::BindRepeating(
+                &MockAutofillAssistantDriver::BindPendingReceiver,
+                base::Unretained(&autofill_assistant_driver_)));
     autofill_assistant_agent_ = std::make_unique<AutofillAssistantAgent>(
         GetMainRenderFrame(), &associated_interfaces_);
 
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 61ceeb8e..41d73c6f 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -323,6 +323,7 @@
       <part file="print_media_strings.grdp" />
       <part file="printing_component_strings.grdp" />
       <part file="privacy_sandbox_strings.grdp" />
+      <part file="protocol_handler_strings.grdp" />
       <part file="reset_password_strings.grdp" />
       <part file="security_interstitials_strings.grdp" />
       <part file="send_tab_to_self_strings.grdp" />
diff --git a/components/exo/keyboard.cc b/components/exo/keyboard.cc
index 701e07a0f..bb41f47 100644
--- a/components/exo/keyboard.cc
+++ b/components/exo/keyboard.cc
@@ -31,6 +31,7 @@
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 #include "ui/views/widget/widget.h"
+#include "ui/wm/core/window_util.h"
 
 namespace exo {
 namespace {
@@ -90,7 +91,7 @@
 // to fix https://crbug.com/847500 without breaking ARC apps/Lacros browser.
 bool IsImeSupportedSurface(Surface* surface) {
   aura::Window* window = surface->window();
-  for (; window; window = window->parent()) {
+  while (window) {
     const auto app_type =
         static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType));
     switch (app_type) {
@@ -109,6 +110,12 @@
     // TODO(tetsui): find a way to remove this.
     if (window->GetProperty(aura::client::kSkipImeProcessing))
       return true;
+
+    if (aura::Window* transient_parent = wm::GetTransientParent(window)) {
+      window = transient_parent;
+    } else {
+      window = window->parent();
+    }
   }
   return false;
 }
diff --git a/components/exo/text_input.cc b/components/exo/text_input.cc
index 6441bb8f..7bb90d90 100644
--- a/components/exo/text_input.cc
+++ b/components/exo/text_input.cc
@@ -42,16 +42,12 @@
 }
 
 void TextInput::Activate(Surface* surface) {
-  DLOG_IF(ERROR, window_) << "Already activated with " << window_;
   DCHECK(surface);
-
-  window_ = surface->window();
-  AttachInputMethod();
+  AttachInputMethod(surface->window());
 }
 
 void TextInput::Deactivate() {
   DetachInputMethod();
-  window_ = nullptr;
 }
 
 void TextInput::ShowVirtualKeyboardIfEnabled() {
@@ -403,18 +399,25 @@
   delegate_->OnVirtualKeyboardVisibilityChanged(false);
 }
 
-void TextInput::AttachInputMethod() {
+void TextInput::AttachInputMethod(aura::Window* window) {
+  DCHECK(window);
+
+  if (window_) {
+    if (window == window_)
+      return;
+    DetachInputMethod();
+  }
   DCHECK(!input_method_);
 
-  ui::InputMethod* input_method = GetInputMethod(window_);
-  if (!input_method) {
+  window_ = window;
+  input_method_ = GetInputMethod(window_);
+  if (!input_method_) {
     LOG(ERROR) << "input method not found";
     return;
   }
 
   input_mode_ = ui::TEXT_INPUT_MODE_TEXT;
   input_type_ = ui::TEXT_INPUT_TYPE_TEXT;
-  input_method_ = input_method;
   if (auto* controller = input_method_->GetVirtualKeyboardController())
     controller->AddObserver(this);
   input_method_->SetFocusedTextInputClient(this);
@@ -437,6 +440,7 @@
   if (auto* controller = input_method_->GetVirtualKeyboardController())
     controller->RemoveObserver(this);
   input_method_ = nullptr;
+  window_ = nullptr;
   delegate_->Deactivated();
 }
 
diff --git a/components/exo/text_input.h b/components/exo/text_input.h
index 0c8c63e..f735690 100644
--- a/components/exo/text_input.h
+++ b/components/exo/text_input.h
@@ -181,7 +181,7 @@
   void OnKeyboardHidden() override;
 
  private:
-  void AttachInputMethod();
+  void AttachInputMethod(aura::Window* window);
   void DetachInputMethod();
 
   // Delegate to talk to actual its client.
diff --git a/components/exo/wayland/zwp_text_input_manager.cc b/components/exo/wayland/zwp_text_input_manager.cc
index 778a3f18..96922b84 100644
--- a/components/exo/wayland/zwp_text_input_manager.cc
+++ b/components/exo/wayland/zwp_text_input_manager.cc
@@ -387,7 +387,7 @@
       type = ui::TEXT_INPUT_TYPE_EMAIL;
       break;
     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_PASSWORD:
-      DCHECK(!should_do_learning);
+      should_do_learning = false;
       type = ui::TEXT_INPUT_TYPE_PASSWORD;
       break;
     case ZWP_TEXT_INPUT_V1_CONTENT_PURPOSE_DATE:
diff --git a/components/infobars/core/confirm_infobar_delegate.cc b/components/infobars/core/confirm_infobar_delegate.cc
index 954fba65..944097b 100644
--- a/components/infobars/core/confirm_infobar_delegate.cc
+++ b/components/infobars/core/confirm_infobar_delegate.cc
@@ -50,6 +50,15 @@
   return ui::ImageModel();
 }
 
+bool ConfirmInfoBarDelegate::GetButtonEnabled(InfoBarButton button) const {
+  return true;
+}
+
+std::u16string ConfirmInfoBarDelegate::GetButtonTooltip(
+    InfoBarButton button) const {
+  return std::u16string();
+}
+
 bool ConfirmInfoBarDelegate::OKButtonTriggersUACPrompt() const {
   return false;
 }
diff --git a/components/infobars/core/confirm_infobar_delegate.h b/components/infobars/core/confirm_infobar_delegate.h
index 5bf0232e4..afa7d17 100644
--- a/components/infobars/core/confirm_infobar_delegate.h
+++ b/components/infobars/core/confirm_infobar_delegate.h
@@ -61,6 +61,14 @@
   // returns an empty image.
   virtual ui::ImageModel GetButtonImage(InfoBarButton button) const;
 
+  // Returns whether the specified button is enabled. The default implementation
+  // returns true.
+  virtual bool GetButtonEnabled(InfoBarButton button) const;
+
+  // Returns the tooltip for the specified button. The default implementation
+  // returns an empty tooltip.
+  virtual std::u16string GetButtonTooltip(InfoBarButton button) const;
+
   // Returns whether or not the OK button will trigger a UAC elevation prompt on
   // Windows.
   virtual bool OKButtonTriggersUACPrompt() const;
diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h
index 18cce1f..386bf947 100644
--- a/components/infobars/core/infobar_delegate.h
+++ b/components/infobars/core/infobar_delegate.h
@@ -177,6 +177,7 @@
     AUTOFILL_ADDRESS_PROFILE_INFOBAR_DELEGATE_IOS = 106,
     ADD_TO_READING_LIST_IOS = 107,
     IOS_PERMISSIONS_INFOBAR_DELEGATE = 108,
+    SUPPORTED_LINKS_INFOBAR_DELEGATE_CHROMEOS = 109,
   };
 
   // Describes navigation events, used to decide whether infobars should be
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 7253694..7af7a18 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -140,6 +140,8 @@
     "password_form_manager_for_ui.h",
     "password_form_metrics_recorder.cc",
     "password_form_metrics_recorder.h",
+    "password_form_prediction_waiter.cc",
+    "password_form_prediction_waiter.h",
     "password_generation_frame_helper.cc",
     "password_generation_frame_helper.h",
     "password_generation_manager.cc",
@@ -325,6 +327,9 @@
     sources += [
       "built_in_backend_to_android_backend_migrator.cc",
       "built_in_backend_to_android_backend_migrator.h",
+      "capabilities_service.h",
+      "capabilities_service_impl.cc",
+      "capabilities_service_impl.h",
       "password_scripts_fetcher.h",
       "password_scripts_fetcher_impl.cc",
       "password_scripts_fetcher_impl.h",
@@ -333,6 +338,7 @@
       "password_store_proxy_backend.cc",
       "password_store_proxy_backend.h",
     ]
+    deps += [ "//components/autofill_assistant/browser/public" ]
   }
 
   if (!is_ios) {
@@ -528,6 +534,8 @@
     "mock_password_sync_metadata_store.h",
     "mock_smart_bubble_stats_store.cc",
     "mock_smart_bubble_stats_store.h",
+    "mock_webauthn_credentials_delegate.cc",
+    "mock_webauthn_credentials_delegate.h",
     "password_manager_test_utils.cc",
     "password_manager_test_utils.h",
     "site_affiliation/fake_affiliation_service.cc",
@@ -674,6 +682,7 @@
     "password_form_filling_unittest.cc",
     "password_form_manager_unittest.cc",
     "password_form_metrics_recorder_unittest.cc",
+    "password_form_prediction_waiter_unittest.cc",
     "password_generation_frame_helper_unittest.cc",
     "password_generation_manager_unittest.cc",
     "password_hash_data_unittest.cc",
@@ -716,6 +725,7 @@
   if (is_android) {
     sources += [
       "built_in_backend_to_android_backend_migrator_unittest.cc",
+      "capabilities_service_impl_unittest.cc",
       "password_scripts_fetcher_impl_unittests.cc",
       "password_store_backend_migration_decorator_unittest.cc",
       "password_store_proxy_backend_unittest.cc",
@@ -795,6 +805,10 @@
     "//ui/gfx:test_support",
     "//url",
   ]
+
+  if (is_android) {
+    deps += [ "//components/autofill_assistant/browser/public" ]
+  }
 }
 
 fuzzer_test("csv_reader_fuzzer") {
diff --git a/components/password_manager/core/browser/DEPS b/components/password_manager/core/browser/DEPS
index 54ebd78..923f54eb 100644
--- a/components/password_manager/core/browser/DEPS
+++ b/components/password_manager/core/browser/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/autofill/core/browser",
+  "+components/autofill_assistant/browser/public",
   "+components/device_reauth",
   "+components/favicon/core",
   "+components/keyed_service/core",
diff --git a/components/password_manager/core/browser/capabilities_service.h b/components/password_manager/core/browser/capabilities_service.h
new file mode 100644
index 0000000..f5e79d4
--- /dev/null
+++ b/components/password_manager/core/browser/capabilities_service.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CAPABILITIES_SERVICE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CAPABILITIES_SERVICE_H_
+
+#include <vector>
+
+#include "base/callback.h"
+#include "url/origin.h"
+
+namespace password_manager {
+
+// The class fetches capabilities for different origins.
+class CapabilitiesService {
+ public:
+  using ResponseCallback =
+      base::OnceCallback<void(const std::vector<url::Origin>&)>;
+
+  virtual ~CapabilitiesService() = default;
+
+  // Returns the subset of provided |origins| for which a password change script
+  // is available. In case of a network error while fetching capabilities, the
+  // result list will be empty.
+  virtual void QueryPasswordChangeScriptAvailability(
+      const std::vector<url::Origin>& origins,
+      ResponseCallback callback) = 0;
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CAPABILITIES_SERVICE_H_
diff --git a/components/password_manager/core/browser/capabilities_service_impl.cc b/components/password_manager/core/browser/capabilities_service_impl.cc
new file mode 100644
index 0000000..8da6ced
--- /dev/null
+++ b/components/password_manager/core/browser/capabilities_service_impl.cc
@@ -0,0 +1,103 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/capabilities_service_impl.h"
+
+#include "base/containers/flat_set.h"
+#include "base/hash/legacy_hash.h"
+#include "base/logging.h"
+#include "base/ranges/algorithm.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "net/http/http_status_code.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Number of leading bits of the domain url hashes to send to the server.
+constexpr uint32_t kHashPrefixSize = 15;
+
+constexpr char kRequestIntent[] = "PASSWORD_CHANGE";
+// Parameter that specifies the script's experiments.
+const char kExperimentsParameterName[] = "EXPERIMENT_IDS";
+// Server side experiment id that specifies when a script has only been released
+// to a small subset of clients.
+const char kScriptLiveExperiment[] = "3345172";
+
+bool ScriptInLiveExperiment(
+    const base::flat_map<std::string, std::string>& script_parameters) {
+  auto params_iter = script_parameters.find(kExperimentsParameterName);
+  if (params_iter == script_parameters.end()) {
+    return false;
+  }
+
+  const std::vector<std::string> experiments = base::SplitString(
+      params_iter->second, ",", base::WhitespaceHandling::TRIM_WHITESPACE,
+      base::SplitResult::SPLIT_WANT_NONEMPTY);
+
+  return base::ranges::count(experiments, kScriptLiveExperiment) > 0;
+}
+
+std::string CanonicalizeOrigin(const url::Origin& origin) {
+  std::string url_str = origin.GetURL().spec();
+  // Remove trailing '/' if exist.
+  base::TrimString(url_str, "/", &url_str);
+  return url_str;
+}
+
+uint64_t GetHashPrefix(const url::Origin& origin) {
+  const std::string& canonicalized_url = CanonicalizeOrigin(origin);
+  uint64_t hash = base::legacy::CityHash64(
+      base::as_bytes(base::make_span(canonicalized_url)));
+  return hash >> (64 - kHashPrefixSize);
+}
+}  // namespace
+
+CapabilitiesServiceImpl::CapabilitiesServiceImpl(
+    std::unique_ptr<autofill_assistant::AutofillAssistant> autofill_assistant)
+    : autofill_assistant_(std::move(autofill_assistant)) {}
+
+CapabilitiesServiceImpl::~CapabilitiesServiceImpl() = default;
+
+void CapabilitiesServiceImpl::QueryPasswordChangeScriptAvailability(
+    const std::vector<url::Origin>& origins,
+    ResponseCallback callback) {
+  std::vector<uint64_t> hash_prefixes;
+  base::ranges::transform(origins, std::back_inserter(hash_prefixes),
+                          GetHashPrefix);
+
+  autofill_assistant_->GetCapabilitiesByHashPrefix(
+      kHashPrefixSize, hash_prefixes, kRequestIntent,
+      base::BindOnce(&CapabilitiesServiceImpl::OnGetCapabilitiesResult,
+                     base::Unretained(this), origins, std::move(callback)));
+}
+
+void CapabilitiesServiceImpl::OnGetCapabilitiesResult(
+    const std::vector<url::Origin>& origins,
+    ResponseCallback callback,
+    int http_status,
+    const std::vector<CapabilitiesInfo>& infos) {
+  base::flat_set<url::Origin> origins_set(origins.begin(), origins.end());
+
+  std::vector<url::Origin> response;
+  if (http_status != net::HTTP_OK) {
+    // TODO(b/209429727) Record network failure metrics.
+    std::move(callback).Run(response);
+    return;
+  }
+
+  for (const CapabilitiesInfo& info : infos) {
+    // Checks if the script is visible to the client.
+    if (password_manager::features::kPasswordChangeLiveExperimentParam.Get() ||
+        !ScriptInLiveExperiment(info.script_parameters)) {
+      const auto cap_info_origin = url::Origin::Create(GURL(info.url));
+      if (origins_set.contains(cap_info_origin)) {
+        response.push_back(cap_info_origin);
+      }
+    }
+  }
+
+  std::move(callback).Run(response);
+}
diff --git a/components/password_manager/core/browser/capabilities_service_impl.h b/components/password_manager/core/browser/capabilities_service_impl.h
new file mode 100644
index 0000000..ed3c622f
--- /dev/null
+++ b/components/password_manager/core/browser/capabilities_service_impl.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CAPABILITIES_SERVICE_IMPL_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CAPABILITIES_SERVICE_IMPL_H_
+
+#include "components/autofill_assistant/browser/public/autofill_assistant.h"
+#include "components/password_manager/core/browser/capabilities_service.h"
+
+class CapabilitiesServiceImpl : public password_manager::CapabilitiesService {
+ public:
+  explicit CapabilitiesServiceImpl(
+      std::unique_ptr<autofill_assistant::AutofillAssistant>
+          autofill_assistant);
+  CapabilitiesServiceImpl(const CapabilitiesServiceImpl&) = delete;
+  CapabilitiesServiceImpl& operator=(const CapabilitiesServiceImpl&) = delete;
+
+  ~CapabilitiesServiceImpl() override;
+
+  // CapabilitiesService:
+  void QueryPasswordChangeScriptAvailability(
+      const std::vector<url::Origin>& origins,
+      ResponseCallback callback) override;
+
+ private:
+  using CapabilitiesInfo =
+      autofill_assistant::AutofillAssistant::CapabilitiesInfo;
+
+  void OnGetCapabilitiesResult(
+      const std::vector<url::Origin>& origins,
+      ResponseCallback callback,
+      int http_status,
+      const std::vector<
+          autofill_assistant::AutofillAssistant::CapabilitiesInfo>& infos);
+
+  // Used for capabilities requests.
+  std::unique_ptr<autofill_assistant::AutofillAssistant> autofill_assistant_;
+};
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_CAPABILITIES_SERVICE_IMPL_H_
diff --git a/components/password_manager/core/browser/capabilities_service_impl_unittest.cc b/components/password_manager/core/browser/capabilities_service_impl_unittest.cc
new file mode 100644
index 0000000..176b8112
--- /dev/null
+++ b/components/password_manager/core/browser/capabilities_service_impl_unittest.cc
@@ -0,0 +1,184 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/capabilities_service_impl.h"
+
+#include <vector>
+
+#include "base/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/autofill_assistant/browser/public/autofill_assistant.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "net/http/http_status_code.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace password_manager {
+namespace {
+
+using ::base::test::RunOnceCallback;
+using ::testing::_;
+using ::testing::NiceMock;
+using CapabilitiesInfo =
+    autofill_assistant::AutofillAssistant::CapabilitiesInfo;
+
+constexpr uint32_t kHashPrefixSize = 15;
+constexpr uint64_t kExampleDotComHash = 2170UL;
+constexpr uint64_t kTestDotComHash = 17534UL;
+constexpr uint64_t kDummyurlDotComHash = 15654UL;
+
+constexpr char kPasswordChangeIntent[] = "PASSWORD_CHANGE";
+
+class MockAutofillAssistant : public autofill_assistant::AutofillAssistant {
+ public:
+  MockAutofillAssistant() = default;
+  ~MockAutofillAssistant() override = default;
+
+  MOCK_METHOD4(GetCapabilitiesByHashPrefix,
+               void(uint32_t hash_prefix_length,
+                    const std::vector<uint64_t>& hash_prefix,
+                    const std::string& intent,
+                    GetCapabilitiesResponseCallback callback));
+};
+
+class CapabilitiesServiceImplTest : public ::testing::Test {
+ public:
+  CapabilitiesServiceImplTest() {
+    auto autofill_assistant =
+        std::make_unique<NiceMock<MockAutofillAssistant>>();
+    mock_autofill_assistant_ = autofill_assistant.get();
+
+    service_ = std::make_unique<CapabilitiesServiceImpl>(
+        std::move(autofill_assistant));
+  }
+  ~CapabilitiesServiceImplTest() override = default;
+
+ protected:
+  raw_ptr<NiceMock<MockAutofillAssistant>> mock_autofill_assistant_;
+  std::unique_ptr<CapabilitiesServiceImpl> service_;
+};
+
+TEST_F(CapabilitiesServiceImplTest, FetchCapabilitiesEmptyResponse) {
+  std::vector<url::Origin> origins = {
+      url::Origin::Create(GURL("https://example.com")),
+      url::Origin::Create(GURL("https://test.com"))};
+
+  EXPECT_CALL(*mock_autofill_assistant_,
+              GetCapabilitiesByHashPrefix(
+                  kHashPrefixSize,
+                  std::vector<uint64_t>{kExampleDotComHash, kTestDotComHash},
+                  kPasswordChangeIntent, _))
+      .WillOnce(
+          RunOnceCallback<3>(net::HTTP_OK, std::vector<CapabilitiesInfo>()));
+
+  base::MockCallback<password_manager::CapabilitiesService::ResponseCallback>
+      mock_response_callback;
+  EXPECT_CALL(mock_response_callback, Run(std::vector<url::Origin>()));
+
+  service_->QueryPasswordChangeScriptAvailability(origins,
+                                                  mock_response_callback.Get());
+}
+
+TEST_F(CapabilitiesServiceImplTest, BackendRequestFailed) {
+  EXPECT_CALL(*mock_autofill_assistant_, GetCapabilitiesByHashPrefix)
+      .WillOnce(RunOnceCallback<3>(net::HTTP_FORBIDDEN,
+                                   std::vector<CapabilitiesInfo>()));
+
+  base::MockCallback<password_manager::CapabilitiesService::ResponseCallback>
+      mock_response_callback;
+  EXPECT_CALL(mock_response_callback, Run(std::vector<url::Origin>()));
+
+  std::vector<url::Origin> origins = {
+      url::Origin::Create(GURL("https://example.com")),
+      url::Origin::Create(GURL("https://test.com"))};
+
+  service_->QueryPasswordChangeScriptAvailability(origins,
+                                                  mock_response_callback.Get());
+}
+
+TEST_F(CapabilitiesServiceImplTest, FetchCapabilitiesSuccess) {
+  std::vector<CapabilitiesInfo> capabilities_reponse = {
+      CapabilitiesInfo{"https://foo.test.com", {}},
+      CapabilitiesInfo{"https://bar.test.com", {}},
+      CapabilitiesInfo{"https://example.com", {}},
+      CapabilitiesInfo{"https://test.com", {}},
+      CapabilitiesInfo{"https://dummyurl.com",
+                       {{{"EXPERIMENT_IDS", "3345172"}}}},
+  };
+
+  EXPECT_CALL(*mock_autofill_assistant_,
+              GetCapabilitiesByHashPrefix(
+                  kHashPrefixSize,
+                  std::vector<uint64_t>{kExampleDotComHash, kTestDotComHash,
+                                        kDummyurlDotComHash},
+                  kPasswordChangeIntent, _))
+      .WillOnce(RunOnceCallback<3>(net::HTTP_OK, capabilities_reponse));
+
+  base::MockCallback<password_manager::CapabilitiesService::ResponseCallback>
+      mock_response_callback;
+
+  EXPECT_CALL(mock_response_callback,
+              Run(std::vector<url::Origin>{
+                  url::Origin::Create(GURL("https://example.com")),
+                  url::Origin::Create(GURL("https://test.com")),
+              }));
+
+  std::vector<url::Origin> origins = {
+      url::Origin::Create(GURL("https://example.com")),
+      url::Origin::Create(GURL("https://test.com")),
+      url::Origin::Create(GURL("https://dummyurl.com")),
+  };
+
+  service_->QueryPasswordChangeScriptAvailability(origins,
+                                                  mock_response_callback.Get());
+}
+
+TEST_F(CapabilitiesServiceImplTest, FetchCapabilitiesClientInLiveExperiment) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      password_manager::features::kPasswordDomainCapabilitiesFetching,
+      {{"live_experiment", "true"}});
+
+  std::vector<CapabilitiesInfo> capabilities_reponse = {
+      CapabilitiesInfo{"https://foo.test.com", {}},
+      CapabilitiesInfo{"https://bar.test.com", {}},
+      CapabilitiesInfo{"https://example.com", {}},
+      CapabilitiesInfo{"https://test.com", {}},
+      CapabilitiesInfo{"https://dummyurl.com",
+                       {{{"EXPERIMENT_IDS", "3345172"}}}},
+  };
+
+  EXPECT_CALL(*mock_autofill_assistant_,
+              GetCapabilitiesByHashPrefix(
+                  kHashPrefixSize,
+                  std::vector<uint64_t>{kExampleDotComHash, kTestDotComHash,
+                                        kDummyurlDotComHash},
+                  kPasswordChangeIntent, _))
+      .WillOnce(RunOnceCallback<3>(net::HTTP_OK, capabilities_reponse));
+
+  base::MockCallback<password_manager::CapabilitiesService::ResponseCallback>
+      mock_response_callback;
+  EXPECT_CALL(mock_response_callback,
+              Run(std::vector<url::Origin>{
+                  url::Origin::Create(GURL("https://example.com")),
+                  url::Origin::Create(GURL("https://test.com")),
+                  url::Origin::Create(GURL("https://dummyurl.com")),
+              }));
+
+  std::vector<url::Origin> origins = {
+      url::Origin::Create(GURL("https://example.com")),
+      url::Origin::Create(GURL("https://test.com")),
+      url::Origin::Create(GURL("https://dummyurl.com")),
+  };
+
+  service_->QueryPasswordChangeScriptAvailability(origins,
+                                                  mock_response_callback.Get());
+}
+
+}  // namespace
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper.cc b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
index d382f86a..818311df 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_helper.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
@@ -92,7 +92,8 @@
 
   IsSaved is_saved(
       base::ranges::any_of(partial_results_, [this](const auto& form) {
-        return form->url == url_ && form->username_value == username_;
+        return form->url == url_ && form->username_value == username_ &&
+               form->password_value == password_;
       }));
   IsReused is_reused(partial_results_.size() > (is_saved ? 1 : 0));
   HasChangeScript has_change_script(script_is_available_);
diff --git a/components/password_manager/core/browser/mock_webauthn_credentials_delegate.cc b/components/password_manager/core/browser/mock_webauthn_credentials_delegate.cc
new file mode 100644
index 0000000..ccdfe81
--- /dev/null
+++ b/components/password_manager/core/browser/mock_webauthn_credentials_delegate.cc
@@ -0,0 +1,13 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
+
+namespace password_manager {
+
+MockWebAuthnCredentialsDelegate::MockWebAuthnCredentialsDelegate() = default;
+
+MockWebAuthnCredentialsDelegate::~MockWebAuthnCredentialsDelegate() = default;
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h b/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h
new file mode 100644
index 0000000..f9e1abf
--- /dev/null
+++ b/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_WEBAUTHN_CREDENTIALS_DELEGATE_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_WEBAUTHN_CREDENTIALS_DELEGATE_H_
+
+#include "components/password_manager/core/browser/webauthn_credentials_delegate.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace password_manager {
+
+class MockWebAuthnCredentialsDelegate : public WebAuthnCredentialsDelegate {
+ public:
+  MockWebAuthnCredentialsDelegate();
+  ~MockWebAuthnCredentialsDelegate() override;
+
+  MockWebAuthnCredentialsDelegate(const MockWebAuthnCredentialsDelegate&) =
+      delete;
+  MockWebAuthnCredentialsDelegate& operator=(
+      const MockWebAuthnCredentialsDelegate&) = delete;
+
+  MOCK_METHOD(bool, IsWebAuthnAutofillEnabled, (), (const, override));
+  MOCK_METHOD(void,
+              SelectWebAuthnCredential,
+              (std::string backend_id),
+              (override));
+  MOCK_METHOD(const std::vector<autofill::Suggestion>&,
+              GetWebAuthnSuggestions,
+              (),
+              (const override));
+  MOCK_METHOD(void,
+              RetrieveWebAuthnSuggestions,
+              (base::OnceClosure),
+              (override));
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_WEBAUTHN_CREDENTIALS_DELEGATE_H_
\ No newline at end of file
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 7b7e4f1..e438754 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/device_reauth/mock_biometric_authenticator.h"
 #include "components/favicon/core/test/mock_favicon_service.h"
 #include "components/password_manager/core/browser/mock_password_feature_manager.h"
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
 #include "components/password_manager/core/browser/password_manager.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
@@ -84,6 +85,7 @@
 using testing::Invoke;
 using testing::NiceMock;
 using testing::Return;
+using testing::ReturnRef;
 using testing::SizeIs;
 using testing::Unused;
 
@@ -108,19 +110,6 @@
     "PasswordManager.CredentialsCountFromAccountStoreAfterUnlock";
 const gfx::Image kTestFavicon = gfx::test::CreateImage(16, 16);
 
-class MockWebAuthnCredentialsDelegate : public WebAuthnCredentialsDelegate {
- public:
-  MOCK_METHOD(bool, IsWebAuthnAutofillEnabled, (), (const, override));
-  MOCK_METHOD(void,
-              SelectWebAuthnCredential,
-              (std::string backend_id),
-              (override));
-  MOCK_METHOD(std::vector<autofill::Suggestion>,
-              GetWebAuthnSuggestions,
-              (),
-              (const override));
-};
-
 class MockPasswordManagerDriver : public StubPasswordManagerDriver {
  public:
   MOCK_METHOD(void,
@@ -323,6 +312,13 @@
     testing::Mock::VerifyAndClearExpectations(client);
     // Suppress the warnings in the tests.
     EXPECT_CALL(*client, GetFaviconService()).WillRepeatedly(Return(nullptr));
+
+    webauthn_credentials_delegate_ =
+        std::make_unique<MockWebAuthnCredentialsDelegate>();
+    ON_CALL(*client, GetWebAuthnCredentialsDelegate)
+        .WillByDefault(Return(webauthn_credentials_delegate_.get()));
+    ON_CALL(*webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
+        .WillByDefault(Return(false));
   }
 
   autofill::PasswordFormFillData CreateTestFormFillData() {
@@ -356,6 +352,9 @@
   scoped_refptr<device_reauth::MockBiometricAuthenticator> authenticator_ =
       base::MakeRefCounted<device_reauth::MockBiometricAuthenticator>();
 
+  std::unique_ptr<MockWebAuthnCredentialsDelegate>
+      webauthn_credentials_delegate_;
+
   std::u16string test_username_;
   std::u16string test_password_;
 
@@ -1803,8 +1802,10 @@
       .WillByDefault(Return(true));
   EXPECT_CALL(client, GetWebAuthnCredentialsDelegate)
       .WillRepeatedly(Return(&webauthn_credentials_delegate));
+  auto webauthn_credential_list =
+      std::vector<autofill::Suggestion>{webauthn_credential};
   EXPECT_CALL(webauthn_credentials_delegate, GetWebAuthnSuggestions)
-      .WillOnce(Return(std::vector<autofill::Suggestion>{webauthn_credential}));
+      .WillOnce(ReturnRef(webauthn_credential_list));
 
   // Show password suggestions including WebAuthn credentials.
   autofill::AutofillClient::PopupOpenArgs open_args;
diff --git a/components/password_manager/core/browser/password_form_filling_unittest.cc b/components/password_manager/core/browser/password_form_filling_unittest.cc
index d7ae67b2..d8e31315 100644
--- a/components/password_manager/core/browser/password_form_filling_unittest.cc
+++ b/components/password_manager/core/browser/password_form_filling_unittest.cc
@@ -17,11 +17,11 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/autofill/core/common/unique_ids.h"
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_metrics_recorder.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
-#include "components/password_manager/core/browser/webauthn_credentials_delegate.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -52,19 +52,6 @@
   MOCK_METHOD(void, InformNoSavedCredentials, (bool), (override));
 };
 
-class MockWebAuthnCredentialsDelegate : public WebAuthnCredentialsDelegate {
- public:
-  MOCK_METHOD(bool, IsWebAuthnAutofillEnabled, (), (const, override));
-  MOCK_METHOD(void,
-              SelectWebAuthnCredential,
-              (std::string backend_id),
-              (override));
-  MOCK_METHOD(std::vector<autofill::Suggestion>,
-              GetWebAuthnSuggestions,
-              (),
-              (const override));
-};
-
 class MockPasswordManagerClient : public StubPasswordManagerClient {
  public:
   MOCK_METHOD(void,
@@ -133,6 +120,11 @@
 
     metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
         true, client_.GetUkmSourceId(), /*pref_service=*/nullptr);
+
+    ON_CALL(client_, GetWebAuthnCredentialsDelegate)
+        .WillByDefault(Return(&webauthn_credentials_delegate_));
+    ON_CALL(webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
+        .WillByDefault(Return(false));
   }
 
  protected:
@@ -143,6 +135,7 @@
   PasswordForm psl_saved_match_;
   scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_;
   std::vector<const PasswordForm*> federated_matches_;
+  MockWebAuthnCredentialsDelegate webauthn_credentials_delegate_;
 };
 
 TEST_F(PasswordFormFillingTest, NoSavedCredentials) {
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index cb65bf2..a2ca715 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -10,7 +10,6 @@
 #include <string>
 #include <utility>
 
-#include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -148,6 +147,21 @@
   if (owned_form_fetcher_ &&
       !observed_form()->is_gaia_with_skip_save_password_form) {
     owned_form_fetcher_->Fetch();
+
+    WebAuthnCredentialsDelegate* delegate =
+        client_->GetWebAuthnCredentialsDelegate();
+    bool is_webauthn_autofill_enabled =
+        delegate && delegate->IsWebAuthnAutofillEnabled();
+
+    // The barrier closure is invoked by the WebAuthnCredentialsDelegate,
+    // if enabled, and by ProcessServerPredictions. A fill will trigger
+    // when both requests are satisfied.
+    async_predictions_waiter_.InitializeClosure(
+        is_webauthn_autofill_enabled ? 2 : 1);
+    if (is_webauthn_autofill_enabled) {
+      delegate->RetrieveWebAuthnSuggestions(
+          async_predictions_waiter_.closure());
+    }
   }
   votes_uploader_.StoreInitialFieldValues(*observed_form());
 }
@@ -643,13 +657,7 @@
 
 void PasswordFormManager::DelayFillForServerSidePredictions() {
   waiting_for_server_predictions_ = true;
-
-  weak_ptr_factory_.InvalidateWeakPtrs();
-  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&PasswordFormManager::Fill,
-                     weak_ptr_factory_.GetWeakPtr()),
-      kMaxFillingDelayForServerPredictions);
+  async_predictions_waiter_.StartTimer();
 }
 
 void PasswordFormManager::OnFetchCompleted() {
@@ -683,6 +691,10 @@
   }
 }
 
+void PasswordFormManager::OnWaitCompleted() {
+  Fill();
+}
+
 void PasswordFormManager::CreatePendingCredentials() {
   DCHECK(is_submitted_);
   if (!parsed_submitted_form_)
@@ -797,8 +809,15 @@
     return;
   }
   UpdatePredictionsForObservedForm(predictions);
-  if (parser_.predictions())
-    Fill();
+  if (parser_.predictions()) {
+    if (!async_predictions_waiter_.closure().is_null()) {
+      // Signals the availability of server predictions, but there might be
+      // other callbacks still outstanding.
+      async_predictions_waiter_.closure().Run();
+    } else {
+      Fill();
+    }
+  }
 }
 
 void PasswordFormManager::Fill() {
@@ -920,7 +939,8 @@
       password_save_manager_(std::move(password_save_manager)),
       // TODO(https://crbug.com/831123): set correctly
       // |is_possible_change_password_form| in |votes_uploader_| constructor
-      votes_uploader_(client, false /* is_possible_change_password_form */) {
+      votes_uploader_(client, false /* is_possible_change_password_form */),
+      async_predictions_waiter_(this) {
   form_fetcher_->AddConsumer(this);
   if (!metrics_recorder_) {
     metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h
index 571053e..62c596e 100644
--- a/components/password_manager/core/browser/password_form_manager.h
+++ b/components/password_manager/core/browser/password_form_manager.h
@@ -26,16 +26,13 @@
 #include "components/password_manager/core/browser/password_form_digest.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
 #include "components/password_manager/core/browser/password_form_metrics_recorder.h"
+#include "components/password_manager/core/browser/password_form_prediction_waiter.h"
 #include "components/password_manager/core/browser/password_save_manager.h"
 #include "components/password_manager/core/browser/votes_uploader.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace password_manager {
 
-// Filling timeout for waiting server predictions
-constexpr base::TimeDelta kMaxFillingDelayForServerPredictions =
-    base::Milliseconds(500);
-
 class PasswordFormMetricsRecorder;
 class PasswordManagerClient;
 class PasswordManagerDriver;
@@ -44,6 +41,7 @@
 // This class helps with filling the observed form and with saving/updating the
 // stored information about it.
 class PasswordFormManager : public PasswordFormManagerForUI,
+                            public PasswordFormPredictionWaiter::Client,
                             public FormFetcher::Consumer {
  public:
   // TODO(crbug.com/621355): So far, |form_fetcher| can be null. In that case
@@ -261,6 +259,9 @@
   // FormFetcher::Consumer:
   void OnFetchCompleted() override;
 
+  // PasswordFormPredictionWaiter::Client:
+  void OnWaitCompleted() override;
+
   // Create pending credentials from |parsed_submitted_form_| and forms received
   // from the password store.
   void CreatePendingCredentials();
@@ -337,8 +338,8 @@
       const autofill::FormData& observed_form_data,
       const std::map<autofill::FormSignature, FormPredictions>& predictions);
 
-  // Delays form filling by |kMaxFillingDelayForServerPredictions| while waiting
-  // for server-side predictions.
+  // Sets the timer on |async_predictions_waiter_| while waiting for
+  // server-side predictions.
   void DelayFillForServerSidePredictions();
 
   // The client which implements embedder-specific PasswordManager operations.
@@ -399,10 +400,10 @@
   // Time when stored credentials are received from the store. Used for metrics.
   base::TimeTicks received_stored_credentials_time_;
 
+  PasswordFormPredictionWaiter async_predictions_waiter_;
+
   // Used to transform FormData into PasswordForms.
   FormDataParser parser_;
-
-  base::WeakPtrFactory<PasswordFormManager> weak_ptr_factory_{this};
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 9424ef9..1a82c086 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -32,6 +32,7 @@
 #include "components/password_manager/core/browser/fake_form_fetcher.h"
 #include "components/password_manager/core/browser/field_info_manager.h"
 #include "components/password_manager/core/browser/mock_password_change_success_tracker.h"
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
@@ -171,6 +172,10 @@
   MOCK_METHOD(FieldInfoManager*, GetFieldInfoManager, (), (const, override));
   MOCK_METHOD(signin::IdentityManager*, GetIdentityManager, (), (override));
   MOCK_METHOD(PrefService*, GetPrefs, (), (const, override));
+  MOCK_METHOD(WebAuthnCredentialsDelegate*,
+              GetWebAuthnCredentialsDelegate,
+              (),
+              (override));
 };
 
 void CheckPendingCredentials(const PasswordForm& expected,
@@ -441,6 +446,11 @@
     ON_CALL(*client_.GetPasswordFeatureManager(), GetDefaultPasswordStore)
         .WillByDefault(Return(PasswordForm::Store::kProfileStore));
 
+    ON_CALL(client_, GetWebAuthnCredentialsDelegate)
+        .WillByDefault(Return(&webauthn_credentials_delegate_));
+    ON_CALL(webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
+        .WillByDefault(Return(false));
+
     fetcher_ = std::make_unique<FakeFormFetcher>();
     fetcher_->Fetch();
   }
@@ -464,6 +474,7 @@
   TestingPrefServiceSimple pref_service_;
   MockPasswordManagerClient client_;
   MockPasswordManagerDriver driver_;
+  MockWebAuthnCredentialsDelegate webauthn_credentials_delegate_;
 
   // Define |fetcher_| before |form_manager_|, because the former needs to
   // outlive the latter.
@@ -1821,14 +1832,14 @@
   EXPECT_CALL(driver_, FillPasswordForm).Times(0);
 
   // Wait half-delay time before updating form
-  task_environment_.FastForwardBy(kMaxFillingDelayForServerPredictions / 2);
+  task_environment_.FastForwardBy(kMaxFillingDelayForAsyncPredictions / 2);
 
   // Updating form should cancel previous task for fill and start a new delayed
   // fill task for waiting server-side predictions
   form_manager_->FillForm(changed_form, {});
 
   // Fire the cancelled fill task should do nothing
-  task_environment_.FastForwardBy(kMaxFillingDelayForServerPredictions / 2);
+  task_environment_.FastForwardBy(kMaxFillingDelayForAsyncPredictions / 2);
 
   PasswordFormFillData fill_data;
   EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
diff --git a/components/password_manager/core/browser/password_form_prediction_waiter.cc b/components/password_manager/core/browser/password_form_prediction_waiter.cc
new file mode 100644
index 0000000..ecb8307f
--- /dev/null
+++ b/components/password_manager/core/browser/password_form_prediction_waiter.cc
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/password_form_prediction_waiter.h"
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+
+namespace password_manager {
+
+PasswordFormPredictionWaiter::PasswordFormPredictionWaiter(Client* client)
+    : client_(client) {}
+
+PasswordFormPredictionWaiter::~PasswordFormPredictionWaiter() = default;
+
+void PasswordFormPredictionWaiter::StartTimer() {
+  // Unretained |this| is safe because the timer will be destroyed on
+  // destruction of this object.
+  timer_.Start(FROM_HERE, kMaxFillingDelayForAsyncPredictions, this,
+               &PasswordFormPredictionWaiter::OnTimeout);
+}
+
+void PasswordFormPredictionWaiter::InitializeClosure(size_t callback_count) {
+  // Invalidating the weak pointers serves to cancel outstanding callbacks
+  // on the BarrierClosure.
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  barrier_closure_ = base::BarrierClosure(
+      callback_count,
+      base::BindOnce(&PasswordFormPredictionWaiter::OnClosureComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PasswordFormPredictionWaiter::OnTimeout() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  barrier_closure_ = base::RepeatingClosure();
+  client_->OnWaitCompleted();
+}
+
+void PasswordFormPredictionWaiter::OnClosureComplete() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  barrier_closure_ = base::RepeatingClosure();
+  timer_.Stop();
+  client_->OnWaitCompleted();
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_form_prediction_waiter.h b/components/password_manager/core/browser/password_form_prediction_waiter.h
new file mode 100644
index 0000000..0d1332d7
--- /dev/null
+++ b/components/password_manager/core/browser/password_form_prediction_waiter.h
@@ -0,0 +1,67 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_PREDICTION_WAITER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_PREDICTION_WAITER_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace base {
+class OneShotTimer;
+}
+
+namespace password_manager {
+
+// Filling timeout for waiting for asynchronous predictions.
+constexpr base::TimeDelta kMaxFillingDelayForAsyncPredictions =
+    base::Milliseconds(500);
+
+// Helper class for PasswordFormManager to manage outstanding asynchronous
+// prediction fetches. This uses a barrier callback to wait on multiple
+// asynchronous events, signalling when all are complete, and also a timer
+// that will cause cause Client::OnWaitCompleted() to be called even if there
+// are still outstanding callbacks.
+class PasswordFormPredictionWaiter {
+ public:
+  class Client {
+   public:
+    virtual void OnWaitCompleted() = 0;
+  };
+
+  explicit PasswordFormPredictionWaiter(Client* client);
+
+  PasswordFormPredictionWaiter(const PasswordFormPredictionWaiter&) = delete;
+  PasswordFormPredictionWaiter& operator=(const PasswordFormPredictionWaiter&) =
+      delete;
+
+  ~PasswordFormPredictionWaiter();
+
+  void StartTimer();
+
+  void InitializeClosure(size_t callback_count);
+  const base::RepeatingClosure& closure() const { return barrier_closure_; }
+
+ private:
+  void OnTimeout();
+  void OnClosureComplete();
+
+  // The client owns the waiter so this pointer will survive this object's
+  // lifetime.
+  Client* client_;
+
+  base::OneShotTimer timer_;
+
+  // BarrierClosure is used to wait until predictions are obtained from
+  // all asynchronous sources.
+  base::RepeatingClosure barrier_closure_;
+
+  base::WeakPtrFactory<PasswordFormPredictionWaiter> weak_ptr_factory_{this};
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_PASSWORD_FORM_PREDICTION_WAITER_H_
diff --git a/components/password_manager/core/browser/password_form_prediction_waiter_unittest.cc b/components/password_manager/core/browser/password_form_prediction_waiter_unittest.cc
new file mode 100644
index 0000000..1e86b9f
--- /dev/null
+++ b/components/password_manager/core/browser/password_form_prediction_waiter_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/password_form_prediction_waiter.h"
+
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+class WaiterClient : public PasswordFormPredictionWaiter::Client {
+ public:
+  WaiterClient() = default;
+  ~WaiterClient() = default;
+
+  WaiterClient(const WaiterClient&) = delete;
+  WaiterClient& operator=(const WaiterClient&) = delete;
+
+  bool wait_completed() const { return wait_completed_; }
+  void Reset() { wait_completed_ = false; }
+
+ protected:
+  void OnWaitCompleted() override { wait_completed_ = true; }
+
+  bool wait_completed_ = false;
+};
+
+class PasswordFormPredictionWaiterTest : public testing::Test {
+ public:
+  PasswordFormPredictionWaiterTest() : prediction_waiter_(&client_) {}
+
+ protected:
+  WaiterClient client_;
+  PasswordFormPredictionWaiter prediction_waiter_;
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+};
+
+TEST_F(PasswordFormPredictionWaiterTest, WaitCompletedOnTimeout) {
+  prediction_waiter_.StartTimer();
+  task_environment_.FastForwardBy(kMaxFillingDelayForAsyncPredictions);
+  EXPECT_TRUE(client_.wait_completed());
+}
+
+TEST_F(PasswordFormPredictionWaiterTest,
+       WaitCompletedOnTimeoutWithOutstandingCallback) {
+  prediction_waiter_.StartTimer();
+  prediction_waiter_.InitializeClosure(2);
+  prediction_waiter_.closure().Run();
+  EXPECT_FALSE(client_.wait_completed());
+
+  task_environment_.FastForwardBy(kMaxFillingDelayForAsyncPredictions);
+  EXPECT_TRUE(client_.wait_completed());
+}
+
+TEST_F(PasswordFormPredictionWaiterTest, WaitCompletedOnSingleBarrierCallback) {
+  prediction_waiter_.InitializeClosure(1);
+  prediction_waiter_.closure().Run();
+
+  EXPECT_TRUE(prediction_waiter_.closure().is_null());
+  EXPECT_TRUE(client_.wait_completed());
+}
+
+TEST_F(PasswordFormPredictionWaiterTest,
+       WaitCompletedOnMultipleBarrierCallbacks) {
+  prediction_waiter_.InitializeClosure(3);
+  for (int i = 0; i < 3; ++i) {
+    prediction_waiter_.closure().Run();
+  }
+
+  EXPECT_TRUE(prediction_waiter_.closure().is_null());
+  EXPECT_TRUE(client_.wait_completed());
+}
+
+}  // namespace
+
+}  // namespace password_manager
\ No newline at end of file
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 0b5d5564..2ea3744 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -34,6 +34,7 @@
 #include "components/password_manager/core/browser/mock_password_reuse_manager.h"
 #include "components/password_manager/core/browser/mock_password_store_interface.h"
 #include "components/password_manager/core/browser/mock_smart_bubble_stats_store.h"
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
 #include "components/password_manager/core/browser/password_autofill_manager.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
@@ -150,6 +151,11 @@
     ON_CALL(filter_, IsSyncAccountEmail(_)).WillByDefault(Return(false));
     ON_CALL(*this, IsNewTabPage()).WillByDefault(Return(false));
     ON_CALL(*this, IsAutofillAssistantUIVisible()).WillByDefault(Return(false));
+
+    ON_CALL(*this, GetWebAuthnCredentialsDelegate)
+        .WillByDefault(Return(&webauthn_credentials_delegate_));
+    ON_CALL(webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
+        .WillByDefault(Return(false));
   }
 
   MOCK_METHOD(bool,
@@ -210,6 +216,10 @@
               (),
               (const, override));
   MOCK_METHOD(version_info::Channel, GetChannel, (), (const override));
+  MOCK_METHOD(WebAuthnCredentialsDelegate*,
+              GetWebAuthnCredentialsDelegate,
+              (),
+              (override));
 
   // Workaround for std::unique_ptr<> lacking a copy constructor.
   bool PromptUserToSaveOrUpdatePassword(
@@ -240,6 +250,7 @@
  private:
   mutable FakeNetworkContext network_context_;
   testing::NiceMock<MockStoreResultFilter> filter_;
+  MockWebAuthnCredentialsDelegate webauthn_credentials_delegate_;
 };
 
 class MockPasswordManagerDriver : public StubPasswordManagerDriver {
@@ -3880,7 +3891,9 @@
     EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordPtr);
     EXPECT_CALL(client_, IsSavingAndFillingEnabled)
         .WillRepeatedly(Return(true));
+
     manager()->OnPasswordFormsParsed(&driver_, {form2.form_data});
+
     OnPasswordFormSubmitted(form2.form_data);
     manager()->OnPasswordFormsRendered(&driver_, {} /* observed */,
                                        true /* did stop loading */);
diff --git a/components/password_manager/core/browser/sync_credentials_filter_unittest.cc b/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
index db3be069..4e4c29e 100644
--- a/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
+++ b/components/password_manager/core/browser/sync_credentials_filter_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "components/password_manager/core/browser/fake_form_fetcher.h"
 #include "components/password_manager/core/browser/mock_password_store_interface.h"
+#include "components/password_manager/core/browser/mock_webauthn_credentials_delegate.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
@@ -56,6 +57,9 @@
     prefs_->registry()->RegisterListPref(prefs::kPasswordProtectionLoginURLs);
     prefs_->SetString(prefs::kPasswordProtectionChangePasswordURL,
                       kEnterpriseURL);
+
+    ON_CALL(webauthn_credentials_delegate_, IsWebAuthnAutofillEnabled)
+        .WillByDefault(testing::Return(false));
   }
 
   FakePasswordManagerClient(const FakePasswordManagerClient&) = delete;
@@ -76,6 +80,9 @@
   signin::IdentityManager* GetIdentityManager() override {
     return identity_manager_;
   }
+  MockWebAuthnCredentialsDelegate* GetWebAuthnCredentialsDelegate() override {
+    return &webauthn_credentials_delegate_;
+  }
 
   void set_last_committed_entry_url(base::StringPiece url_spec) {
     last_committed_origin_ = url::Origin::Create(GURL(url_spec));
@@ -91,6 +98,7 @@
   url::Origin last_committed_origin_;
   scoped_refptr<testing::NiceMock<MockPasswordStoreInterface>> password_store_ =
       new testing::NiceMock<MockPasswordStoreInterface>;
+  MockWebAuthnCredentialsDelegate webauthn_credentials_delegate_;
   bool is_incognito_ = false;
   raw_ptr<signin::IdentityManager> identity_manager_;
   std::unique_ptr<TestingPrefServiceSimple> prefs_;
diff --git a/components/password_manager/core/browser/webauthn_credentials_delegate.h b/components/password_manager/core/browser/webauthn_credentials_delegate.h
index 23aa024c..ebd27d6 100644
--- a/components/password_manager/core/browser/webauthn_credentials_delegate.h
+++ b/components/password_manager/core/browser/webauthn_credentials_delegate.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 
 namespace password_manager {
@@ -27,7 +28,13 @@
 
   // Returns the list of eligible WebAuthn credentials to fulfill an ongoing
   // WebAuthn request.
-  virtual std::vector<autofill::Suggestion> GetWebAuthnSuggestions() const = 0;
+  virtual const std::vector<autofill::Suggestion>& GetWebAuthnSuggestions()
+      const = 0;
+
+  // Initiates retrieval of discoverable WebAuthn credentials from the platform
+  // authenticator. |callback| is invoked upon completion.
+  virtual void RetrieveWebAuthnSuggestions(
+      base::OnceCallback<void()> callback) = 0;
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 0b1b1c8e..33f6cf8 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -230,6 +230,12 @@
     "UsernameFirstFlowFallbackCrowdsourcing",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Returns true if the client is part of the live_experiment group for
+// |kPasswordDomainCapabilitiesFetching|, otherwise, the client is assumed to be
+// in the regular launch group.
+extern const base::FeatureParam<bool> kPasswordChangeLiveExperimentParam = {
+    &kPasswordDomainCapabilitiesFetching, "live_experiment", false};
+
 #if BUILDFLAG(IS_ANDROID)
 // Current migration version to Google Mobile Services. If version saved in pref
 // is lower than 'kMigrationVersion' passwords will be re-uploaded.
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 09d3daf..3e760b7 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -70,7 +70,7 @@
 extern const base::Feature kUsernameFirstFlowFallbackCrowdsourcing;
 
 // All features parameters are in alphabetical order.
-
+extern const base::FeatureParam<bool> kPasswordChangeLiveExperimentParam;
 #if BUILDFLAG(IS_ANDROID)
 extern const base::FeatureParam<int> kMigrationVersion;
 #endif
diff --git a/components/performance_manager/features.cc b/components/performance_manager/features.cc
index 5455d67..193c27e 100644
--- a/components/performance_manager/features.cc
+++ b/components/performance_manager/features.cc
@@ -11,8 +11,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 
-namespace performance_manager {
-namespace features {
+namespace performance_manager::features {
 
 const base::Feature kRunOnMainThread{"RunOnMainThread",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
@@ -55,23 +54,4 @@
 const base::Feature kBFCachePerformanceManagerPolicy{
     "BFCachePerformanceManagerPolicy", base::FEATURE_DISABLED_BY_DEFAULT};
 
-constexpr base::FeatureParam<bool>
-    BFCachePerformanceManagerPolicyParams::kFlushOnModeratePressure;
-
-constexpr base::FeatureParam<int>
-    BFCachePerformanceManagerPolicyParams::kDelayToFlushBackgroundTabInSeconds;
-
-// static
-BFCachePerformanceManagerPolicyParams
-BFCachePerformanceManagerPolicyParams::GetParams() {
-  BFCachePerformanceManagerPolicyParams params;
-  params.flush_on_moderate_pressure_ =
-      BFCachePerformanceManagerPolicyParams::kFlushOnModeratePressure.Get();
-  params.delay_to_flush_background_tab_ = base::Seconds(
-      BFCachePerformanceManagerPolicyParams::kDelayToFlushBackgroundTabInSeconds
-          .Get());
-  return params;
-}
-
-}  // namespace features
-}  // namespace performance_manager
+}  // namespace performance_manager::features
diff --git a/components/performance_manager/graph/policies/bfcache_policy.cc b/components/performance_manager/graph/policies/bfcache_policy.cc
index 0428b49..2f45dbd3 100644
--- a/components/performance_manager/graph/policies/bfcache_policy.cc
+++ b/components/performance_manager/graph/policies/bfcache_policy.cc
@@ -18,11 +18,33 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
 
-namespace performance_manager {
-namespace policies {
+namespace performance_manager::policies {
 
 namespace {
 
+// Whether or not the BFCache of all pages should be flushed when the system
+// is under *moderate* memory pressure. The policy always flushes the bfcache
+// under critical pressure.
+bool IsFlushOnModeratePressureEnabled() {
+  static constexpr base::FeatureParam<bool> flush_on_moderate_pressure{
+      &features::kBFCachePerformanceManagerPolicy, "flush_on_moderate_pressure",
+      false};
+
+  return flush_on_moderate_pressure.Get();
+}
+
+// The back forward cache should be flushed after the tab goes to background
+// and elapses this delay. If the value is negative (such as -1), the back
+// forward cache in the background tabs will not be flushed.
+base::TimeDelta DelayToFlushBackgroundTab() {
+  static constexpr base::FeatureParam<int>
+      delay_to_flush_background_tab_in_seconds{
+          &features::kBFCachePerformanceManagerPolicy,
+          "delay_to_flush_background_tab_in_seconds", -1};
+
+  return base::Seconds(delay_to_flush_background_tab_in_seconds.Get());
+}
+
 bool PageMightHaveFramesInBFCache(const PageNode* page_node) {
   // TODO(crbug.com/1211368): Use PageState when that actually works.
   auto main_frame_nodes = page_node->GetMainFrameNodes();
@@ -52,13 +74,8 @@
 }  // namespace
 
 BFCachePolicy::BFCachePolicy()
-    : flush_on_moderate_pressure_{features::
-                                      BFCachePerformanceManagerPolicyParams::
-                                          GetParams()
-                                              .flush_on_moderate_pressure()},
-      delay_to_flush_background_tab_{
-          features::BFCachePerformanceManagerPolicyParams::GetParams()
-              .delay_to_flush_background_tab()} {}
+    : flush_on_moderate_pressure_{IsFlushOnModeratePressureEnabled()},
+      delay_to_flush_background_tab_{DelayToFlushBackgroundTab()} {}
 
 BFCachePolicy::~BFCachePolicy() = default;
 
@@ -154,5 +171,4 @@
   }
 }
 
-}  // namespace policies
-}  // namespace performance_manager
+}  // namespace performance_manager::policies
diff --git a/components/performance_manager/public/features.h b/components/performance_manager/public/features.h
index 4b881f2..5e9402f2 100644
--- a/components/performance_manager/public/features.h
+++ b/components/performance_manager/public/features.h
@@ -13,8 +13,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 
-namespace performance_manager {
-namespace features {
+namespace performance_manager::features {
 
 // The feature that gates whether or not the PM runs on the main (UI) thread.
 extern const base::Feature kRunOnMainThread;
@@ -67,52 +66,6 @@
 // BFCache of all pages when the system is under memory pressure.
 extern const base::Feature kBFCachePerformanceManagerPolicy;
 
-// Parameters allowing to control some aspects of the
-// |kBFCachePerformanceManagerPolicy|.
-class BFCachePerformanceManagerPolicyParams {
- public:
-  BFCachePerformanceManagerPolicyParams(
-      BFCachePerformanceManagerPolicyParams&&) = default;
-  BFCachePerformanceManagerPolicyParams& operator=(
-      BFCachePerformanceManagerPolicyParams&&) = default;
-  BFCachePerformanceManagerPolicyParams(
-      const BFCachePerformanceManagerPolicyParams&) = delete;
-  BFCachePerformanceManagerPolicyParams& operator=(
-      const BFCachePerformanceManagerPolicyParams&) = delete;
-  ~BFCachePerformanceManagerPolicyParams() = default;
-
-  static BFCachePerformanceManagerPolicyParams GetParams();
-
-  // Whether or not the BFCache of all pages should be flushed when the system
-  // is under *moderate* memory pressure. The policy always flushes the bfcache
-  // under critical pressure.
-  bool flush_on_moderate_pressure() const {
-    return flush_on_moderate_pressure_;
-  }
-
-  base::TimeDelta delay_to_flush_background_tab() const {
-    return delay_to_flush_background_tab_;
-  }
-
-  static constexpr base::FeatureParam<bool> kFlushOnModeratePressure{
-      &features::kBFCachePerformanceManagerPolicy, "flush_on_moderate_pressure",
-      false};
-
-  // The back forward cache should be flushed after the tab goes to background
-  // and elapses this delay. If the value is negative (such as -1), the back
-  // forward cache in the background tabs will not be flushed.
-  static constexpr base::FeatureParam<int> kDelayToFlushBackgroundTabInSeconds{
-      &features::kBFCachePerformanceManagerPolicy,
-      "delay_to_flush_background_tab_in_seconds", -1};
-
- private:
-  BFCachePerformanceManagerPolicyParams() = default;
-
-  bool flush_on_moderate_pressure_;
-  base::TimeDelta delay_to_flush_background_tab_;
-};
-
-}  // namespace features
-}  // namespace performance_manager
+}  // namespace performance_manager::features
 
 #endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_FEATURES_H_
diff --git a/components/policy/core/common/policy_switches.cc b/components/policy/core/common/policy_switches.cc
index 7b6560f..ff69ab4 100644
--- a/components/policy/core/common/policy_switches.cc
+++ b/components/policy/core/common/policy_switches.cc
@@ -17,10 +17,6 @@
 // Specifies the URL at which to upload encrypted reports.
 const char kEncryptedReportingUrl[] = "encrypted-reporting-url";
 
-// Always treat user as affiliated.
-// TODO(antrim): Remove once test servers correctly produce affiliation ids.
-const char kUserAlwaysAffiliated[] = "user-always-affiliated";
-
 // Set policy value by command line.
 const char kChromePolicy[] = "policy";
 
diff --git a/components/policy/core/common/policy_switches.h b/components/policy/core/common/policy_switches.h
index 129adf5..46b42ef 100644
--- a/components/policy/core/common/policy_switches.h
+++ b/components/policy/core/common/policy_switches.h
@@ -16,7 +16,6 @@
 POLICY_EXPORT extern const char kDeviceManagementUrl[];
 POLICY_EXPORT extern const char kRealtimeReportingUrl[];
 POLICY_EXPORT extern const char kEncryptedReportingUrl[];
-POLICY_EXPORT extern const char kUserAlwaysAffiliated[];
 POLICY_EXPORT extern const char kChromePolicy[];
 POLICY_EXPORT extern const char kSecureConnectApiUrl[];
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 7daa533..21717e0 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -100,6 +100,8 @@
 
   // Enumerates different flavors of registration.
   enum Flavor {
+    reserved 12;
+
     // User manually enrolls a device for device management.
     FLAVOR_ENROLLMENT_MANUAL = 0;
     // User re-starts enrollment manually to recover from loss of policy.
@@ -135,8 +137,6 @@
     // mandatory, but it failed and we are doing a fallback to manual
     // enrollment.
     FLAVOR_ENROLLMENT_ATTESTATION_MANUAL_FALLBACK = 11;
-    // Enrollment triggered by USB pre-configuration
-    FLAVOR_ENROLLMENT_ATTESTATION_USB_ENROLLMENT = 12;
     // Device state downloaded from the server during OOBE indicates that
     // initial enrollment is mandatory.
     FLAVOR_ENROLLMENT_INITIAL_SERVER_FORCED = 13;
diff --git a/components/protocol_handler_strings.grdp b/components/protocol_handler_strings.grdp
new file mode 100644
index 0000000..71a3eaf6
--- /dev/null
+++ b/components/protocol_handler_strings.grdp
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+   <!-- Protocol Handler -->
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_TOOLTIP" desc="Location bar icon tooltip text when a page wants to use registerProtocolHandler.">
+     page wants to install a service handler.
+  </message>
+  <!-- Register Protocol Handler Strings -->
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME" desc="A more user friendly way of describing mailto: links.">
+    email
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME" desc="A more user friendly way of describing webcal: links.">
+    web calendar
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM" desc="The message to display when asking a user to confirm the registration of a protocol handler.">
+    Allow <ph name="HANDLER_HOSTNAME">$1<ex>google.com</ex></ph> to open all <ph name="PROTOCOL">$2<ex>search</ex></ph> links?
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE" desc="The message to display when asking a user to confirm the registration of a protocol handler.">
+    Allow <ph name="HANDLER_HOSTNAME">$1<ex>google.com</ex></ph> to open all <ph name="PROTOCOL">$2<ex>search</ex></ph> links instead of <ph name="REPLACED_HANDLER_TITLE">$3<ex>Elgoog Search</ex></ph>?
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_FRAGMENT" desc="The permission fragment to display when asking a user to confirm the registration of a protocol handler in a permission bubble. Follows a prompt 'This site would like to:'.">
+    Open <ph name="PROTOCOL">$1<ex>search</ex></ph> links
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_CONFIRM_REPLACE_FRAGMENT" desc="The permission fragment to display when asking a user to confirm the registration of a protocol handler. Follows a prompt 'This site would like to:'.">
+    Open <ph name="PROTOCOL">$1<ex>search</ex></ph> links instead of <ph name="REPLACED_HANDLER_TITLE">$2<ex>Elgoog Search</ex></ph>
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_ACCEPT" desc="Text to show for the accept button for the register protocol handler request infobar.">
+    Allow
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_DENY" desc="Text to show for the deny button for the register protocol handler request infobar.">
+    Deny
+  </message>
+  <message name="IDS_REGISTER_PROTOCOL_HANDLER_IGNORE" desc="Text to show for an ignore prompt for a register protocol handler registration request.">
+    Ignore
+  </message>
+  <!-- Protocol Handling intent prompt -->
+  <if expr="not is_android">
+    <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_QUESTION" desc="Label on the protocol handler intent picker.">
+      Allow app to open <ph name="PROTOCOL_SCHEME">$1<ex>mailto</ex></ph> links?
+    </message>
+  </if>
+</grit-part>
diff --git a/chrome/app/generated_resources_grd/IDS_PROTOCOL_HANDLER_INTENT_PICKER_QUESTION.png.sha1 b/components/protocol_handler_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_QUESTION.png.sha1
similarity index 100%
rename from chrome/app/generated_resources_grd/IDS_PROTOCOL_HANDLER_INTENT_PICKER_QUESTION.png.sha1
rename to components/protocol_handler_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_QUESTION.png.sha1
diff --git a/components/sync/base/ordinal.h b/components/sync/base/ordinal.h
index c73b07c..7314f1fe 100644
--- a/components/sync/base/ordinal.h
+++ b/components/sync/base/ordinal.h
@@ -226,7 +226,7 @@
 const unsigned int Ordinal<Traits>::kRadix;
 
 template <typename Traits>
-Ordinal<Traits>::LessThanFn::LessThanFn() {}
+Ordinal<Traits>::LessThanFn::LessThanFn() = default;
 
 template <typename Traits>
 bool Ordinal<Traits>::LessThanFn::operator()(const Ordinal<Traits>& lhs,
@@ -235,7 +235,7 @@
 }
 
 template <typename Traits>
-Ordinal<Traits>::EqualsFn::EqualsFn() {}
+Ordinal<Traits>::EqualsFn::EqualsFn() = default;
 
 template <typename Traits>
 bool Ordinal<Traits>::EqualsFn::operator()(const Ordinal<Traits>& lhs,
diff --git a/components/sync/base/pref_names.h b/components/sync/base/pref_names.h
index dff8998..cdb17210 100644
--- a/components/sync/base/pref_names.h
+++ b/components/sync/base/pref_names.h
@@ -8,9 +8,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 
-namespace syncer {
-
-namespace prefs {
+namespace syncer::prefs {
 
 // Boolean specifying whether the user finished setting up sync at least once.
 constexpr inline char kSyncFirstSetupComplete[] = "sync.has_setup_completed";
@@ -82,8 +80,6 @@
 // flag is present.
 constexpr inline char kLocalSyncBackendDir[] = "sync.local_sync_backend_dir";
 
-}  // namespace prefs
-
-}  // namespace syncer
+}  // namespace syncer::prefs
 
 #endif  // COMPONENTS_SYNC_BASE_PREF_NAMES_H_
diff --git a/components/sync/base/unique_position_unittest.cc b/components/sync/base/unique_position_unittest.cc
index 61134d49..8cecc77 100644
--- a/components/sync/base/unique_position_unittest.cc
+++ b/components/sync/base/unique_position_unittest.cc
@@ -387,7 +387,7 @@
 class SuffixGenerator {
  public:
   explicit SuffixGenerator(const std::string& cache_guid)
-      : cache_guid_(cache_guid), next_id_(-65535) {}
+      : cache_guid_(cache_guid) {}
 
   std::string NextSuffix() {
     // This is not entirely realistic, but that should be OK.  The current
@@ -401,7 +401,7 @@
 
  private:
   const std::string cache_guid_;
-  int64_t next_id_;
+  int64_t next_id_ = -65535;
 };
 
 // Cache guids generated in the same style as real clients.
diff --git a/components/sync/base/weak_handle.cc b/components/sync/base/weak_handle.cc
index b9dbc5b18..80df60b 100644
--- a/components/sync/base/weak_handle.cc
+++ b/components/sync/base/weak_handle.cc
@@ -10,9 +10,7 @@
 #include "base/logging.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 
-namespace syncer {
-
-namespace internal {
+namespace syncer::internal {
 
 WeakHandleCoreBase::WeakHandleCoreBase()
     : owner_loop_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
@@ -30,6 +28,4 @@
   }
 }
 
-}  // namespace internal
-
-}  // namespace syncer
+}  // namespace syncer::internal
diff --git a/components/sync/base/weak_handle.h b/components/sync/base/weak_handle.h
index 6c34773..6d4a0d7 100644
--- a/components/sync/base/weak_handle.h
+++ b/components/sync/base/weak_handle.h
@@ -121,7 +121,7 @@
   friend class base::RefCountedThreadSafe<WeakHandleCore<T>>;
 
   // May be destroyed on any thread.
-  ~WeakHandleCore() {}
+  ~WeakHandleCore() = default;
 
   // Must be dereferenced only on the owner thread.  May be destroyed
   // from any thread.
@@ -136,7 +136,7 @@
 class WeakHandle {
  public:
   // Creates an uninitialized WeakHandle.
-  WeakHandle() {}
+  WeakHandle() = default;
 
   // Creates an initialized WeakHandle from |ptr|.
   explicit WeakHandle(const base::WeakPtr<T>& ptr)
diff --git a/components/sync/engine/sync_manager_impl.cc b/components/sync/engine/sync_manager_impl.cc
index 7164bb98..d2024ec9 100644
--- a/components/sync/engine/sync_manager_impl.cc
+++ b/components/sync/engine/sync_manager_impl.cc
@@ -441,7 +441,7 @@
   const ModelTypeSet types_to_refresh =
       Intersection(types, model_type_registry_->GetConnectedTypes());
 
-  if (!types.Empty()) {
+  if (!types_to_refresh.Empty()) {
     scheduler_->ScheduleLocalRefreshRequest(types_to_refresh);
   }
 }
diff --git a/components/web_app_resources/web_app_default_offline.css b/components/web_app_resources/web_app_default_offline.css
index ce3e4899..7729554f 100644
--- a/components/web_app_resources/web_app_default_offline.css
+++ b/components/web_app_resources/web_app_default_offline.css
@@ -1,31 +1,16 @@
 /* Copyright 2022 The Chromium Authors. All rights reserved.
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file. */
-
-@media (prefers-color-scheme: light) {
-  body {
+ body {
     background-color: var(--customized-background-color);
   }
   h2 {
     color: var(--theme-color);
-  }
- }
-
-@media (prefers-color-scheme: dark) {
-  body {
-    background-color: var(--dark-mode-background-color);
-  }
-  h2 {
-    color: var(--dark-mode-theme-color);
-  }
-}
-
-h2 {
-  height: 200px;
-  left: 50%;
-  margin-inline-start: -200px;
-  margin-top: -100px;
-  position: fixed;
-  top: 50%;
-  width: 400px;
-}
+    height: 200px;
+    left: 50%;
+    margin-inline-start: -200px;
+    margin-top: -100px;
+    position: fixed;
+    top: 50%;
+    width: 400px;
+  }
\ No newline at end of file
diff --git a/components/web_app_resources/web_app_default_offline.html b/components/web_app_resources/web_app_default_offline.html
index f56add9..2659984 100644
--- a/components/web_app_resources/web_app_default_offline.html
+++ b/components/web_app_resources/web_app_default_offline.html
@@ -10,14 +10,12 @@
     :root {
       --customized-background-color: $i18n{customized_background_color};
       --theme-color: $i18n{theme_color};
-      --dark-mode-background-color: $i18n{dark_mode_background_color};
-      --dark-mode-theme-color: $i18n{dark_mode_theme_color};
     }
   </style>
 </head>
   <body style="font-family: $i18n{fontfamily};font-size:$i18n{fontsize}">
     <h1>$i18n{app_short_name}</h1>
-    <h2 id="default-web-app-msg">$i18n{web_app_default_offline_message}</h2>
-    <!--TODO(crbug.com/1285723: Add web app icon.)-->
+    <h2  id="default-web-app-msg">$i18n{web_app_default_offline_message}</h2>
+    <!-- TODO(crbug.com/1285723): Add web app icon. -->
   </body>
 </html>
\ No newline at end of file
diff --git a/components/webapps/browser/banners/app_banner_manager.cc b/components/webapps/browser/banners/app_banner_manager.cc
index 2e0739a..a7fb28d 100644
--- a/components/webapps/browser/banners/app_banner_manager.cc
+++ b/components/webapps/browser/banners/app_banner_manager.cc
@@ -35,6 +35,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/manifest/manifest_util.h"
 #include "third_party/blink/public/mojom/installation/installation.mojom.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom.h"
@@ -622,17 +623,28 @@
   // only allow the page to enter the cache if we know for sure that no
   // installation is needed. Note: this check must happen before calling
   // Terminate as it might set the installable_web_app_check_result_ to kNo.
-  if (installable_web_app_check_result_ != InstallableWebAppCheckResult::kNo &&
-      state_ != State::INACTIVE) {
-    content::BackForwardCache::DisableForRenderFrameHost(
-        handle->GetPreviousRenderFrameHostId(),
-        back_forward_cache::DisabledReason(
-            back_forward_cache::DisabledReasonId::kAppBannerManager));
+  if (!base::FeatureList::IsEnabled(
+          blink::features::kBackForwardCacheAppBanner)) {
+    if (installable_web_app_check_result_ !=
+            InstallableWebAppCheckResult::kNo &&
+        state_ != State::INACTIVE) {
+      content::BackForwardCache::DisableForRenderFrameHost(
+          handle->GetPreviousRenderFrameHostId(),
+          back_forward_cache::DisabledReason(
+              back_forward_cache::DisabledReasonId::kAppBannerManager));
+    }
   }
 
-  if (state_ != State::COMPLETE && state_ != State::INACTIVE)
-    Terminate();
-  ResetCurrentPageData();
+  if (base::FeatureList::IsEnabled(
+          blink::features::kBackForwardCacheAppBanner) &&
+      handle->IsServedFromBackForwardCache()) {
+    UpdateState(State::INACTIVE);
+    RequestAppBanner(validated_url_);
+  } else {
+    if (state_ != State::COMPLETE && state_ != State::INACTIVE)
+      Terminate();
+    ResetCurrentPageData();
+  }
 }
 
 void AppBannerManager::DidFinishLoad(
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.cc b/content/browser/blob_storage/chrome_blob_storage_context.cc
index df1a7ae..a74178f 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.cc
+++ b/content/browser/blob_storage/chrome_blob_storage_context.cc
@@ -34,6 +34,7 @@
 #include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/blob/blob_url_loader_factory.h"
 #include "storage/browser/blob/blob_url_registry.h"
+#include "storage/browser/file_system/file_system_context.h"
 
 using base::FilePath;
 using base::UserDataAdapter;
@@ -212,6 +213,37 @@
   return blob_handle;
 }
 
+void ChromeBlobStorageContext::CreateFileSystemBlob(
+    scoped_refptr<storage::FileSystemContext> file_system_context,
+    mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
+    const storage::FileSystemURL& url,
+    const std::string& blob_uuid,
+    const std::string& content_type,
+    const uint64_t file_size,
+    const base::Time& file_modification_time) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  auto blob_builder = std::make_unique<storage::BlobDataBuilder>(blob_uuid);
+  if (file_size > 0) {
+    // Use AppendFileSystemFile here, since we're streaming the file directly
+    // from the file system backend, and the file thus might not actually be
+    // backed by a file on disk.
+    blob_builder->AppendFileSystemFile(url, 0, file_size,
+                                       file_modification_time,
+                                       std::move(file_system_context));
+  }
+  blob_builder->set_content_type(content_type);
+
+  std::unique_ptr<storage::BlobDataHandle> blob_handle =
+      context_->AddFinishedBlob(std::move(blob_builder));
+
+  // Since the blob we're creating doesn't depend on other blobs, and doesn't
+  // require blob memory/disk quota, creating the blob can't fail.
+  DCHECK(!blob_handle->IsBroken());
+
+  storage::BlobImpl::Create(std::move(blob_handle), std::move(blob_receiver));
+}
+
 // static
 scoped_refptr<network::SharedURLLoaderFactory>
 ChromeBlobStorageContext::URLLoaderFactoryForToken(
diff --git a/content/browser/blob_storage/chrome_blob_storage_context.h b/content/browser/blob_storage/chrome_blob_storage_context.h
index 3f1765c..ebb37f3 100644
--- a/content/browser/blob_storage/chrome_blob_storage_context.h
+++ b/content/browser/blob_storage/chrome_blob_storage_context.h
@@ -13,6 +13,7 @@
 
 #include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner_helpers.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
@@ -27,6 +28,8 @@
 
 namespace storage {
 class BlobStorageContext;
+class FileSystemContext;
+class FileSystemURL;
 namespace mojom {
 class BlobStorageContext;
 }
@@ -74,6 +77,17 @@
       base::span<const uint8_t> data,
       const std::string& content_type);
 
+  // Creates a FileSystem File blob accessible by the renderer via the blob
+  // remote corresponding to `blob_receiver`.
+  void CreateFileSystemBlob(
+      scoped_refptr<storage::FileSystemContext> file_system_context,
+      mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
+      const storage::FileSystemURL& url,
+      const std::string& blob_uuid,
+      const std::string& content_type,
+      const uint64_t file_size,
+      const base::Time& file_modification_time);
+
   // Returns a SharedURLLoaderFactory capable of creating URLLoaders for exactly
   // the one URL associated with the passed in |token|. Attempting to load any
   // other URL through the factory will result in an error. If the |token|
diff --git a/content/browser/file_system/file_system_url_drag_drop_browsertest.cc b/content/browser/file_system/file_system_url_drag_drop_browsertest.cc
new file mode 100644
index 0000000..1ea6c918
--- /dev/null
+++ b/content/browser/file_system/file_system_url_drag_drop_browsertest.cc
@@ -0,0 +1,309 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "storage/browser/file_system/external_mount_points.h"
+
+namespace content {
+
+// End-to-end tests for drag and drop, including correct behavior of the
+// DataTransferItem's getAsFile method.
+
+class FileSystemURLDragDropBrowserTest : public ContentBrowserTest {
+ public:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(embedded_test_server()->Start());
+    ContentBrowserTest::SetUp();
+  }
+
+  void TearDown() override {
+    ContentBrowserTest::TearDown();
+    ASSERT_TRUE(temp_dir_.Delete());
+  }
+
+  base::FilePath CreateTestFileInDirectory(const base::FilePath& directory_path,
+                                           const std::string& contents) {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    base::FilePath result;
+    EXPECT_TRUE(base::CreateTemporaryFileInDir(directory_path, &result));
+    EXPECT_TRUE(base::WriteFile(result, contents));
+    return result;
+  }
+
+  base::FilePath CreateTestDir() {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    base::FilePath result;
+    EXPECT_TRUE(base::CreateTemporaryDirInDir(
+        temp_dir_.GetPath(), FILE_PATH_LITERAL("test"), &result));
+    return result;
+  }
+
+  RenderWidgetHostImpl* GetRenderWidgetHostImplForMainFrame() {
+    WebContentsImpl* web_contents_impl =
+        static_cast<WebContentsImpl*>(shell()->web_contents());
+    return web_contents_impl->GetMainFrame()->GetRenderWidgetHost();
+  }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+};
+
+IN_PROC_BROWSER_TEST_F(FileSystemURLDragDropBrowserTest, FileSystemFileDrop) {
+  // Get the RenderWidgetHostImpl for the main (and only) frame.
+  RenderWidgetHostImpl* render_widget_host_impl =
+      GetRenderWidgetHostImplForMainFrame();
+  DCHECK(render_widget_host_impl);
+
+  // Prepare the window for dragging and dropping.
+  GURL url = embedded_test_server()->GetURL("/title1.html");
+  ASSERT_TRUE(NavigateToURL(shell(), url));
+
+  // Prevent defaults of drag operations and create a promise that will resolve
+  // with the text from a dropped file after window.ondrop is called. The text
+  // is retrieved using the DataTransferItem getAsFile function. The promise
+  // will reject if zero/multiples items are dropped or if the item is not a
+  // file. This test will also ensure that certain drag handlers get an event
+  // with the DataTransferItemList populated (in the language of the spec,
+  // handlers where the expected "drag data store mode" is "protected mode").
+  ASSERT_TRUE(
+      ExecJs(shell(),
+             "const checkDataTransfer = (caller, event, reject) => {"
+             "  if (event.dataTransfer.items.length !== 1) {"
+             "    reject('There were ' + event.dataTransfer.items.length"
+             "            + ' DataTransferItems in the list passed to the '"
+             "            + caller + ' handler. Expected 1.');"
+             "  }"
+             "  if (event.dataTransfer.items[0].kind != 'file') {"
+             "    reject('The DataTransferItem was of kind: '"
+             "            + event.dataTransfer.items[0].kind + ' (in the '"
+             "            + caller + ' handler). Expected file.');"
+             "  }"
+             "};"
+             "const handled_events = [];"
+             "const expected_events = ["
+             "  'ondragenter',"
+             "  'ondragover',"
+             "  'ondrop',"
+             "];"
+             "var p = new Promise((resolve, reject) => {"
+             "  window.ondragenter = async (event) => {"
+             "    event.preventDefault();"
+             "    checkDataTransfer('ondragenter', event, reject);"
+             "    handled_events.push('ondragenter');"
+             "  };"
+             "  window.ondragover = async (event) => {"
+             "    event.preventDefault();"
+             "    checkDataTransfer('ondragover', event, reject);"
+             "    handled_events.push('ondragover');"
+             "  };"
+             "  window.ondrop = async (event) => {"
+             "    event.preventDefault();"
+             "    checkDataTransfer('ondrop', event, reject);"
+             "    handled_events.push('ondrop');"
+             "    if (handled_events.length != expected_events.length) {"
+             "      reject('Unexpected number of events handled: ' +"
+             "              handled_events.length);"
+             "    }"
+             "    for (var i = 0; i < handled_events.length; i++) {"
+             "      if (handled_events[i] != expected_events[i]) {"
+             "        reject('Unexpected order of drag/drop handlers');"
+             "      }"
+             "    }"
+             "    const fileItem = event.dataTransfer.items[0];"
+             "    const file = fileItem.getAsFile();"
+             "    var text = await file.text();"
+             "    resolve(text);"
+             "  }"
+             "});"));
+
+  // Create a directory and create a file inside the directory.
+  const base::FilePath test_dir_path = CreateTestDir();
+  std::string test_contents = "Debugged code is the best code.";
+  const base::FilePath file_inside_dir =
+      CreateTestFileInDirectory(test_dir_path, test_contents);
+
+  // Create a File System File from this local file
+  storage::ExternalMountPoints* external_mount_points =
+      storage::ExternalMountPoints::GetSystemInstance();
+  constexpr char testMountName[] = "DropFileSystemFileTestMount";
+
+  EXPECT_TRUE(external_mount_points->RegisterFileSystem(
+      testMountName, storage::kFileSystemTypeLocal,
+      storage::FileSystemMountOption(), test_dir_path));
+
+  storage::FileSystemURL original_file =
+      external_mount_points->CreateExternalFileSystemURL(
+          blink::StorageKey(url::Origin::Create(url)), testMountName,
+          file_inside_dir.BaseName());
+  EXPECT_TRUE(original_file.is_valid());
+
+  // Get the points corresponding to the center of the browser window in
+  // both screen coordinates and window coordinates.
+  const gfx::Rect window_in_screen_coords =
+      render_widget_host_impl->GetView()->GetBoundsInRootWindow();
+  const gfx::PointF screen_point =
+      gfx::PointF(window_in_screen_coords.CenterPoint());
+  const gfx::PointF client_point =
+      gfx::PointF(window_in_screen_coords.width() / 2,
+                  window_in_screen_coords.height() / 2);
+
+  // Drop the test file.
+  DropData::FileSystemFileInfo filesystem_file_info;
+  filesystem_file_info.url = original_file.ToGURL();
+  filesystem_file_info.size = test_contents.size();
+  filesystem_file_info.filesystem_id = original_file.filesystem_id();
+  DropData drop_data;
+  drop_data.file_system_files.push_back(filesystem_file_info);
+
+  render_widget_host_impl->FilterDropData(&drop_data);
+  render_widget_host_impl->DragTargetDragEnter(
+      drop_data, client_point, screen_point,
+      blink::DragOperationsMask::kDragOperationEvery,
+      /*key_modifiers=*/0, base::DoNothing());
+  render_widget_host_impl->DragTargetDragOver(
+      client_point, screen_point,
+      blink::DragOperationsMask::kDragOperationEvery,
+      /*key_modifiers=*/0, base::DoNothing());
+  render_widget_host_impl->DragTargetDrop(drop_data, client_point, screen_point,
+                                          /*key_modifiers=*/0,
+                                          base::DoNothing());
+
+  // Expect the promise to resolve with `test_contents`.
+  EXPECT_EQ(test_contents, EvalJs(shell(), "p"));
+
+  EXPECT_TRUE(external_mount_points->RevokeFileSystem(testMountName));
+}
+
+IN_PROC_BROWSER_TEST_F(FileSystemURLDragDropBrowserTest, FileSystemFileLeave) {
+  // Get the RenderWidgetHostImpl for the main (and only) frame.
+  RenderWidgetHostImpl* render_widget_host_impl =
+      GetRenderWidgetHostImplForMainFrame();
+  DCHECK(render_widget_host_impl);
+
+  // Prepare the window for dragging and dropping.
+  GURL url = embedded_test_server()->GetURL("/title1.html");
+  ASSERT_TRUE(NavigateToURL(shell(), url));
+
+  // Prevent defaults of drag operations and create a promise that will resolve
+  // once window.ondragleave is called. The promise will reject if the
+  // DataTransferItemList in the event object passed to the ondragenter,
+  // ondragover, and ondragleave handlers contains zero/multiple items, or if
+  // the sole DataTransferItem has a `kind` that is not 'file'. This ensures
+  // that sufficient state is preserved across drag events to provide this data
+  // to JS.
+  ASSERT_TRUE(
+      ExecJs(shell(),
+             "const checkDataTransfer = (caller, event, reject) => {"
+             "  if (event.dataTransfer.items.length !== 1) {"
+             "    reject('There were ' + event.dataTransfer.items.length"
+             "            + ' DataTransferItems in the list passed to the '"
+             "            + caller + ' handler. Expected 1.');"
+             "  }"
+             "  if (event.dataTransfer.items[0].kind != 'file') {"
+             "    reject('The DataTransferItem was of kind: '"
+             "            + event.dataTransfer.items[0].kind + ' (in the '"
+             "            + caller + ' handler). Expected file.');"
+             "  }"
+             "};"
+             "const handled_events = [];"
+             "const expected_events = ["
+             "  'ondragenter',"
+             "  'ondragover',"
+             "  'ondragleave',"
+             "];"
+             "var p = new Promise((resolve, reject) => {"
+             "  window.ondragenter = async (event) => {"
+             "    event.preventDefault();"
+             "    checkDataTransfer('ondragenter', event, reject);"
+             "    handled_events.push('ondragenter');"
+             "  };"
+             "  window.ondragover = async (event) => {"
+             "    event.preventDefault();"
+             "    checkDataTransfer('ondragover', event, reject);"
+             "    handled_events.push('ondragover');"
+             "  };"
+             "  window.ondragleave = async (event) => {"
+             "    event.preventDefault();"
+             "    checkDataTransfer('ondragleave', event, reject);"
+             "    handled_events.push('ondragleave');"
+             "    if (handled_events.length != expected_events.length) {"
+             "      reject('Unexpected number of events handled: ' +"
+             "              handled_events.length);"
+             "    }"
+             "    for (var i = 0; i < handled_events.length; i++) {"
+             "      if (handled_events[i] != expected_events[i]) {"
+             "        reject('Unexpected order of drag/drop handlers');"
+             "      }"
+             "    }"
+             "    resolve('done');"
+             "  }"
+             "});"));
+
+  // Create a directory and create a file inside the directory.
+  const base::FilePath test_dir_path = CreateTestDir();
+  std::string test_contents = "Irrelevant contents.";
+  const base::FilePath file_inside_dir =
+      CreateTestFileInDirectory(test_dir_path, test_contents);
+
+  // Create a File System File from this local file
+  storage::ExternalMountPoints* external_mount_points =
+      storage::ExternalMountPoints::GetSystemInstance();
+  constexpr char testMountName[] = "LeaveFileSystemFileTestMount";
+
+  EXPECT_TRUE(external_mount_points->RegisterFileSystem(
+      testMountName, storage::kFileSystemTypeLocal,
+      storage::FileSystemMountOption(), test_dir_path));
+
+  storage::FileSystemURL original_file =
+      external_mount_points->CreateExternalFileSystemURL(
+          blink::StorageKey(url::Origin::Create(url)), testMountName,
+          file_inside_dir.BaseName());
+  EXPECT_TRUE(original_file.is_valid());
+
+  // Get the points corresponding to the center of the browser window in
+  // both screen coordinates and window coordinates.
+  const gfx::Rect window_in_screen_coords =
+      render_widget_host_impl->GetView()->GetBoundsInRootWindow();
+  const gfx::PointF screen_point =
+      gfx::PointF(window_in_screen_coords.CenterPoint());
+  const gfx::PointF client_point =
+      gfx::PointF(window_in_screen_coords.width() / 2,
+                  window_in_screen_coords.height() / 2);
+
+  DropData::FileSystemFileInfo filesystem_file_info;
+  filesystem_file_info.url = original_file.ToGURL();
+  filesystem_file_info.size = test_contents.size();
+  filesystem_file_info.filesystem_id = original_file.filesystem_id();
+  DropData drop_data;
+  drop_data.file_system_files.push_back(filesystem_file_info);
+
+  render_widget_host_impl->FilterDropData(&drop_data);
+  render_widget_host_impl->DragTargetDragEnter(
+      drop_data, client_point, screen_point,
+      blink::DragOperationsMask::kDragOperationEvery,
+      /*key_modifiers=*/0, base::DoNothing());
+  render_widget_host_impl->DragTargetDragOver(
+      client_point, screen_point,
+      blink::DragOperationsMask::kDragOperationEvery,
+      /*key_modifiers=*/0, base::DoNothing());
+  render_widget_host_impl->DragTargetDragLeave(client_point, screen_point);
+
+  // Expect the promise to resolve with `done`.
+  EXPECT_EQ("done", EvalJs(shell(), "p"));
+
+  EXPECT_TRUE(external_mount_points->RevokeFileSystem(testMountName));
+}
+}  // namespace content
diff --git a/content/browser/file_system_access/file_system_access_file_handle_impl.cc b/content/browser/file_system_access/file_system_access_file_handle_impl.cc
index 3418645..a3fcc79 100644
--- a/content/browser/file_system_access/file_system_access_file_handle_impl.cc
+++ b/content/browser/file_system_access/file_system_access_file_handle_impl.cc
@@ -55,37 +55,6 @@
 
 namespace {
 
-void CreateBlobOnIOThread(
-    scoped_refptr<storage::FileSystemContext> file_system_context,
-    const scoped_refptr<ChromeBlobStorageContext>& blob_context,
-    mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
-    const storage::FileSystemURL& url,
-    const std::string& blob_uuid,
-    const std::string& content_type,
-    const base::File::Info& info) {
-  DCHECK_CURRENTLY_ON(BrowserThread::IO);
-
-  auto blob_builder = std::make_unique<storage::BlobDataBuilder>(blob_uuid);
-  // Only append if the file has data.
-  if (info.size > 0) {
-    // Use AppendFileSystemFile here, since we're streaming the file directly
-    // from the file system backend, and the file thus might not actually be
-    // backed by a file on disk.
-    blob_builder->AppendFileSystemFile(url, 0, info.size, info.last_modified,
-                                       std::move(file_system_context));
-  }
-  blob_builder->set_content_type(content_type);
-
-  std::unique_ptr<BlobDataHandle> blob_handle =
-      blob_context->context()->AddFinishedBlob(std::move(blob_builder));
-
-  // Since the blob we're creating doesn't depend on other blobs, and doesn't
-  // require blob memory/disk quota, creating the blob can't fail.
-  DCHECK(!blob_handle->IsBroken());
-
-  BlobImpl::Create(std::move(blob_handle), std::move(blob_receiver));
-}
-
 std::pair<base::File, base::FileErrorOr<int64_t>> GetFileLengthOnBlockingThread(
     base::File file) {
   int64_t file_length = file.GetLength();
@@ -473,11 +442,11 @@
 
   GetIOThreadTaskRunner({})->PostTask(
       FROM_HERE,
-      base::BindOnce(&CreateBlobOnIOThread,
-                     base::WrapRefCounted(file_system_context()),
+      base::BindOnce(&ChromeBlobStorageContext::CreateFileSystemBlob,
                      base::WrapRefCounted(manager()->blob_context()),
+                     base::WrapRefCounted(file_system_context()),
                      std::move(blob_receiver), url(), std::move(uuid),
-                     std::move(content_type), info));
+                     std::move(content_type), info.size, info.last_modified));
 }
 
 void FileSystemAccessFileHandleImpl::CreateFileWriterImpl(
diff --git a/content/browser/renderer_host/data_transfer_util.cc b/content/browser/renderer_host/data_transfer_util.cc
index a70ffef..2157570 100644
--- a/content/browser/renderer_host/data_transfer_util.cc
+++ b/content/browser/renderer_host/data_transfer_util.cc
@@ -11,13 +11,19 @@
 #include "base/check.h"
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
+#include "base/guid.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/chromeos_buildflags.h"
+#include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/file_system_access/file_system_access_manager_impl.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "net/base/mime_util.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
+#include "storage/browser/blob/blob_storage_context.h"
 #include "storage/browser/file_system/external_mount_points.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "storage/browser/file_system/file_system_context.h"
+#include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h"
 #include "third_party/blink/public/mojom/drag/drag.mojom.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom.h"
 #include "ui/base/clipboard/clipboard_constants.h"
@@ -73,10 +79,75 @@
   return result;
 }
 
+std::vector<blink::mojom::DragItemFileSystemFilePtr>
+FileSystemFileInfosToDragItemFileSystemFilePtr(
+    std::vector<DropData::FileSystemFileInfo> file_system_file_infos,
+    FileSystemAccessManagerImpl* file_system_access_manager,
+    scoped_refptr<content::ChromeBlobStorageContext> context) {
+  std::vector<blink::mojom::DragItemFileSystemFilePtr> result;
+  for (const content::DropData::FileSystemFileInfo& file_system_file :
+       file_system_file_infos) {
+    blink::mojom::DragItemFileSystemFilePtr item =
+        blink::mojom::DragItemFileSystemFile::New();
+    item->url = file_system_file.url;
+    item->size = file_system_file.size;
+    item->file_system_id = file_system_file.filesystem_id;
+
+    storage::FileSystemURL file_system_url =
+        file_system_access_manager->context()->CrackURLInFirstPartyContext(
+            file_system_file.url);
+    DCHECK(file_system_url.type() != storage::kFileSystemTypePersistent);
+    DCHECK(file_system_url.type() != storage::kFileSystemTypeTemporary);
+
+    std::string uuid = base::GenerateGUID();
+
+    std::string content_type;
+
+    base::FilePath::StringType extension = file_system_url.path().Extension();
+    if (!extension.empty()) {
+      std::string mime_type;
+      // TODO(https://crbug.com/155455): Historically for blobs created from
+      // file system URLs we've only considered well known content types to
+      // avoid leaking the presence of locally installed applications when
+      // creating blobs from files in the sandboxed file system. However, since
+      // this code path should only deal with real/"trusted" paths, we could
+      // consider taking platform defined mime type mappings into account here
+      // as well. Note that the approach used here must not block or else it
+      // can't be called from the UI thread (for example, calls to
+      // GetMimeTypeFromExtension can block).
+      if (net::GetWellKnownMimeTypeFromExtension(extension.substr(1),
+                                                 &mime_type))
+        content_type = std::move(mime_type);
+    }
+    // TODO(https://crbug.com/962306): Consider some kind of fallback type when
+    // the above mime type detection fails.
+
+    mojo::PendingRemote<blink::mojom::Blob> blob_remote;
+    mojo::PendingReceiver<blink::mojom::Blob> blob_receiver =
+        blob_remote.InitWithNewPipeAndPassReceiver();
+
+    item->serialized_blob = blink::mojom::SerializedBlob::New(
+        uuid, content_type, item->size, std::move(blob_remote));
+
+    GetIOThreadTaskRunner({})->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &ChromeBlobStorageContext::CreateFileSystemBlob, context,
+            base::WrapRefCounted(file_system_access_manager->context()),
+            std::move(blob_receiver), std::move(file_system_url),
+            std::move(uuid), std::move(content_type), item->size,
+            base::Time()));
+
+    result.push_back(std::move(item));
+  }
+  return result;
+}
+
 blink::mojom::DragDataPtr DropDataToDragData(
     const DropData& drop_data,
     FileSystemAccessManagerImpl* file_system_access_manager,
-    int child_id) {
+    int child_id,
+    scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context) {
   // These fields are currently unused when dragging into Blink.
   DCHECK(drop_data.download_metadata.empty());
   DCHECK(drop_data.file_contents_content_disposition.empty());
@@ -105,17 +176,17 @@
   std::vector<blink::mojom::DataTransferFilePtr> files =
       FileInfosToDataTransferFiles(drop_data.filenames,
                                    file_system_access_manager, child_id);
-  for (size_t i = 0; i < files.size(); ++i) {
-    items.push_back(blink::mojom::DragItem::NewFile(std::move(files[i])));
+  for (auto& file : files) {
+    items.push_back(blink::mojom::DragItem::NewFile(std::move(file)));
   }
-  for (const content::DropData::FileSystemFileInfo& file_system_file :
-       drop_data.file_system_files) {
-    blink::mojom::DragItemFileSystemFilePtr item =
-        blink::mojom::DragItemFileSystemFile::New();
-    item->url = file_system_file.url;
-    item->size = file_system_file.size;
-    item->file_system_id = file_system_file.filesystem_id;
-    items.push_back(blink::mojom::DragItem::NewFileSystemFile(std::move(item)));
+
+  std::vector<blink::mojom::DragItemFileSystemFilePtr> file_system_files =
+      FileSystemFileInfosToDragItemFileSystemFilePtr(
+          drop_data.file_system_files, file_system_access_manager,
+          std::move(chrome_blob_storage_context));
+  for (auto& file_system_file : file_system_files) {
+    items.push_back(
+        blink::mojom::DragItem::NewFileSystemFile(std::move(file_system_file)));
   }
   if (drop_data.file_contents_source_url.is_valid()) {
     blink::mojom::DragItemBinaryPtr item = blink::mojom::DragItemBinary::New();
diff --git a/content/browser/renderer_host/data_transfer_util.h b/content/browser/renderer_host/data_transfer_util.h
index 77125e94..ce3b0afe 100644
--- a/content/browser/renderer_host/data_transfer_util.h
+++ b/content/browser/renderer_host/data_transfer_util.h
@@ -14,6 +14,7 @@
 
 namespace content {
 class FileSystemAccessManagerImpl;
+class ChromeBlobStorageContext;
 
 // Convert ui::FileInfos to mojo DataTransferFiles. Creates
 // DataTransferAccessTokens and remaps paths if needed.
@@ -27,7 +28,8 @@
 blink::mojom::DragDataPtr DropDataToDragData(
     const DropData& drop_data,
     FileSystemAccessManagerImpl* file_system_access_manager,
-    int child_id);
+    int child_id,
+    scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context);
 
 CONTENT_EXPORT
 blink::mojom::DragDataPtr DropMetaDataToDragData(
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 7436238..c129b16a 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -2034,7 +2034,9 @@
     blink_frame_widget_->DragTargetDrop(
         DropDataToDragData(drop_data_with_permissions,
                            storage_partition->GetFileSystemAccessManager(),
-                           GetProcess()->GetID()),
+                           GetProcess()->GetID(),
+                           ChromeBlobStorageContext::GetFor(
+                               GetProcess()->GetBrowserContext())),
         ConvertWindowPointToViewport(client_point), screen_point, key_modifiers,
         std::move(callback));
   }
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 4f3c1524..9d71949 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -26,6 +26,7 @@
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/test/begin_frame_args_test.h"
+#include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/gpu/compositor_util.h"
 #include "content/browser/renderer_host/agent_scheduling_group_host.h"
 #include "content/browser/renderer_host/data_transfer_util.h"
@@ -2092,7 +2093,9 @@
           ->GetFileSystemAccessManager();
   blink::DragOperationsMask drag_operation = blink::kDragOperationEvery;
   host_->StartDragging(
-      DropDataToDragData(drop_data, file_system_manager, process_->GetID()),
+      DropDataToDragData(
+          drop_data, file_system_manager, process_->GetID(),
+          ChromeBlobStorageContext::GetFor(process_->GetBrowserContext())),
       drag_operation, SkBitmap(), gfx::Vector2d(),
       blink::mojom::DragEventSourceInfo::New());
   EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1);
@@ -2101,7 +2104,9 @@
   host_->RendererExited();
   EXPECT_FALSE(host_->GetView());
   host_->StartDragging(
-      DropDataToDragData(drop_data, file_system_manager, process_->GetID()),
+      DropDataToDragData(
+          drop_data, file_system_manager, process_->GetID(),
+          ChromeBlobStorageContext::GetFor(process_->GetBrowserContext())),
       drag_operation, SkBitmap(), gfx::Vector2d(),
       blink::mojom::DragEventSourceInfo::New());
   EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 41c9e09d..47ef40d 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1142,6 +1142,7 @@
     "../browser/download/save_package_browsertest.cc",
     "../browser/fenced_frame/fenced_frame_browsertest.cc",
     "../browser/file_system/file_system_browsertest.cc",
+    "../browser/file_system/file_system_url_drag_drop_browsertest.cc",
     "../browser/file_system/file_system_url_loader_factory_browsertest.cc",
     "../browser/file_system/fileapi_browsertest.cc",
     "../browser/find_request_manager_browsertest.cc",
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc
index edaf36e83..e55fa4b 100644
--- a/content/test/test_render_view_host.cc
+++ b/content/test/test_render_view_host.cc
@@ -11,6 +11,7 @@
 #include "build/build_config.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/host/host_frame_sink_manager.h"
+#include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/compositor/image_transport_factory.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
@@ -452,9 +453,10 @@
   StoragePartitionImpl* storage_partition =
       static_cast<StoragePartitionImpl*>(GetProcess()->GetStoragePartition());
   GetWidget()->StartDragging(
-      DropDataToDragData(drop_data,
-                         storage_partition->GetFileSystemAccessManager(),
-                         GetProcess()->GetID()),
+      DropDataToDragData(
+          drop_data, storage_partition->GetFileSystemAccessManager(),
+          GetProcess()->GetID(),
+          ChromeBlobStorageContext::GetFor(GetProcess()->GetBrowserContext())),
       blink::kDragOperationEvery, std::move(bitmap), gfx::Vector2d(),
       blink::mojom::DragEventSourceInfo::New());
 }
diff --git a/ios/chrome/app/enterprise_app_agent.mm b/ios/chrome/app/enterprise_app_agent.mm
index 9c012997..665ab3b 100644
--- a/ios/chrome/app/enterprise_app_agent.mm
+++ b/ios/chrome/app/enterprise_app_agent.mm
@@ -21,6 +21,12 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+constexpr CGFloat kTimeout = 30;
+
+}  // namespace
+
 @interface EnterpriseAppAgent () <
     ChromeBrowserCloudManagementControllerObserver,
     CloudPolicyClientObserver,
@@ -38,6 +44,9 @@
 // Browser policy connector for iOS.
 @property(nonatomic, assign) BrowserPolicyConnectorIOS* policyConnector;
 
+// YES if enterprise launch screen has been dismissed.
+@property(nonatomic, assign) BOOL launchScreenDismissed;
+
 @end
 
 @implementation EnterpriseAppAgent
@@ -83,11 +92,22 @@
       _cloudPolicyClientObserver =
           std::make_unique<CloudPolicyClientObserverBridge>(self, client);
 
+      self.launchScreenDismissed = NO;
       for (SceneState* scene in appState.connectedScenes) {
         if (scene.activationLevel > SceneActivationLevelBackground) {
           [self showUIInScene:scene];
         }
       }
+
+      // Ensure to never stay stuck on enterprise launch screen.
+      __weak EnterpriseAppAgent* weakSelf = self;
+      dispatch_after(
+          dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kTimeout * NSEC_PER_SEC)),
+          dispatch_get_main_queue(), ^{
+            if (!weakSelf.launchScreenDismissed) {
+              [weakSelf cloudPolicyDidError:nullptr];
+            }
+          });
     } else {
       [self.appState queueTransitionToNextInitStage];
     }
@@ -116,7 +136,8 @@
 #pragma mark - ChromeBrowserCloudManagementControllerObserverBridge
 
 - (void)policyRegistrationDidCompleteSuccessfuly:(BOOL)succeeded {
-  if (!succeeded) {
+  if (!succeeded && !self.launchScreenDismissed) {
+    self.launchScreenDismissed = YES;
     [self.appState queueTransitionToNextInitStage];
   }
 }
@@ -124,11 +145,17 @@
 #pragma mark - CloudPolicyClientObserverBridge
 
 - (void)cloudPolicyWasFetched:(policy::CloudPolicyClient*)client {
-  [self.appState queueTransitionToNextInitStage];
+  if (!self.launchScreenDismissed) {
+    self.launchScreenDismissed = YES;
+    [self.appState queueTransitionToNextInitStage];
+  }
 }
 
 - (void)cloudPolicyDidError:(policy::CloudPolicyClient*)client {
-  [self.appState queueTransitionToNextInitStage];
+  if (!self.launchScreenDismissed) {
+    self.launchScreenDismissed = YES;
+    [self.appState queueTransitionToNextInitStage];
+  }
 }
 
 - (void)cloudPolicyRegistrationChanged:(policy::CloudPolicyClient*)client {
diff --git a/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_view_controller.mm b/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_view_controller.mm
index f5d2e2e..10c2ab78 100644
--- a/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signin_sync/signin_sync_view_controller.mm
@@ -333,7 +333,7 @@
   int textID = IDS_IOS_FIRST_RUN_SYNC_SCREEN_CONTENT;
   [self.delegate signinSyncViewController:self addConsentStringID:textID];
   label.text = l10n_util::GetNSString(textID);
-  label.textColor = [UIColor colorNamed:kGrey600Color];
+  label.textColor = [UIColor colorNamed:kTextSecondaryColor];
   label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
   return label;
 }
diff --git a/ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.mm b/ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.mm
index c973eb2b..cfe82cb 100644
--- a/ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.mm
@@ -8,6 +8,7 @@
 #include "base/i18n/rtl.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
+#import "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "base/notreached.h"
@@ -21,6 +22,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/first_run/first_run_configuration.h"
+#include "ios/chrome/browser/first_run/first_run_metrics.h"
 #include "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service.h"
 #import "ios/chrome/browser/signin/chrome_account_manager_service_factory.h"
@@ -159,6 +161,7 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
   [self.navigationController setNavigationBarHidden:YES];
+  base::UmaHistogramEnumeration("FirstRun.Stage", first_run::kStart);
 }
 
 - (void)viewDidLayoutSubviews {
@@ -265,6 +268,7 @@
       };
 
   [self.coordinator start];
+  base::UmaHistogramEnumeration("FirstRun.Stage", first_run::kSyncScreenStart);
 }
 
 // Handles the sign-in completion and proceeds to complete the first run
@@ -274,6 +278,20 @@
   [self.coordinator stop];
   self.coordinator = nil;
 
+  switch (signinResult) {
+    case SigninCoordinatorResultCanceledByUser:
+    case SigninCoordinatorResultInterrupted: {
+      base::UmaHistogramEnumeration(
+          "FirstRun.Stage", first_run::kSyncScreenCompletionWithoutSync);
+      break;
+    }
+    case SigninCoordinatorResultSuccess: {
+      base::UmaHistogramEnumeration("FirstRun.Stage",
+                                    first_run::kSyncScreenCompletionWithSync);
+      break;
+    }
+  }
+
   [self completeFirstRunWithSigninCompletionInfo:signinCompletionInfo];
 }
 
@@ -304,6 +322,7 @@
             (UIViewController*)presentingViewController
                                  signinCompletionInfo:
                                      (SigninCompletionInfo*)completionInfo {
+  base::UmaHistogramEnumeration("FirstRun.Stage", first_run::kComplete);
   FirstRunDismissed();
   switch (completionInfo.signinCompletionAction) {
     case SigninCompletionActionNone:
diff --git a/ios/chrome/browser/ui/menu/browser_action_factory.mm b/ios/chrome/browser/ui/menu/browser_action_factory.mm
index bb5f176..d9bb559 100644
--- a/ios/chrome/browser/ui/menu/browser_action_factory.mm
+++ b/ios/chrome/browser/ui/menu/browser_action_factory.mm
@@ -338,10 +338,12 @@
 
         UrlLoadingBrowserAgent::FromBrowser(strongSelf.browser)->Load(params);
       };
-  // TODO(crbug.com/1285015): Add the image.
+
   return [self actionWithTitle:l10n_util::GetNSString(
                                    IDS_IOS_TOOLS_MENU_SEARCH_COPIED_IMAGE)
-                         image:nil
+                         image:[self configuredSymbolNamed:@"doc.on.clipboard"
+                                              systemSymbol:YES]
+
                           type:MenuActionType::SearchCopiedImage
                          block:^{
                            ClipboardRecentContent::GetInstance()
@@ -365,10 +367,10 @@
         });
       };
 
-  // TODO(crbug.com/1285015): Add the image.
   return [self actionWithTitle:l10n_util::GetNSString(
                                    IDS_IOS_TOOLS_MENU_VISIT_COPIED_LINK)
-                         image:nil
+                         image:[self configuredSymbolNamed:@"doc.on.clipboard"
+                                              systemSymbol:YES]
                           type:MenuActionType::VisitCopiedLink
                          block:^{
                            ClipboardRecentContent::GetInstance()
@@ -392,10 +394,10 @@
         });
       };
 
-  // TODO(crbug.com/1285015): Add the image.
   return [self actionWithTitle:l10n_util::GetNSString(
                                    IDS_IOS_TOOLS_MENU_SEARCH_COPIED_TEXT)
-                         image:nil
+                         image:[self configuredSymbolNamed:@"doc.on.clipboard"
+                                              systemSymbol:YES]
                           type:MenuActionType::SearchCopiedText
                          block:^{
                            ClipboardRecentContent::GetInstance()
diff --git a/ios/chrome/browser/ui/menu/browser_action_factory_unittest.mm b/ios/chrome/browser/ui/menu/browser_action_factory_unittest.mm
index 033e153..03e7f87 100644
--- a/ios/chrome/browser/ui/menu/browser_action_factory_unittest.mm
+++ b/ios/chrome/browser/ui/menu/browser_action_factory_unittest.mm
@@ -405,7 +405,8 @@
       [[BrowserActionFactory alloc] initWithBrowser:test_browser_.get()
                                            scenario:kTestMenuScenario];
 
-  UIImage* expectedImage = nil;
+  UIImage* expectedImage = [factory configuredSymbolNamed:@"doc.on.clipboard"
+                                             systemSymbol:YES];
   NSString* expectedTitle =
       l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SEARCH_COPIED_IMAGE);
 
@@ -421,7 +422,8 @@
       [[BrowserActionFactory alloc] initWithBrowser:test_browser_.get()
                                            scenario:kTestMenuScenario];
 
-  UIImage* expectedImage = nil;
+  UIImage* expectedImage = [factory configuredSymbolNamed:@"doc.on.clipboard"
+                                             systemSymbol:YES];
   NSString* expectedTitle =
       l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_VISIT_COPIED_LINK);
 
@@ -437,7 +439,8 @@
       [[BrowserActionFactory alloc] initWithBrowser:test_browser_.get()
                                            scenario:kTestMenuScenario];
 
-  UIImage* expectedImage = nil;
+  UIImage* expectedImage = [factory configuredSymbolNamed:@"doc.on.clipboard"
+                                             systemSymbol:YES];
   NSString* expectedTitle =
       l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_SEARCH_COPIED_TEXT);
 
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
index 2f079b7..ce53944 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_mediator.h"
 
 #include "base/files/scoped_temp_dir.h"
+#include "base/ios/ios_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/time/default_clock.h"
@@ -308,10 +309,16 @@
     number_of_action_items++;
   }
 
+  // Stop/Reload, New Tab, New Incognito Tab.
+  NSUInteger number_of_tab_actions = 3;
+  if (base::ios::IsMultipleScenesSupported()) {
+    // New Window option is added in this case.
+    number_of_tab_actions++;
+  }
+
   // Checks that Tools Menu has the right number of items in each section.
   CheckMediatorSetItems(@[
-    // Stop/Reload, New Tab, New Incognito Tab.
-    @(3),
+    @(number_of_tab_actions),
     // 4 collections, Downloads, Settings.
     @(6),
     // Other actions, depending on configuration.
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index a10250d..cc72902 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-3343a839b59bdca29ae44afe5bb9f82ca951b757
\ No newline at end of file
+5323b8723f4bfb6e4ac4cfa71362d3876d89b347
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index fa4a652c..0829d36 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-e8890c91117a1887057d06e6af2c59fc822e3f21
\ No newline at end of file
+10ee10a4126e91957a994c27c751b3027dc57cbd
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 2f8e676..43e48a3 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-adb0101a8caba8a3600db4e14fdfbc4decea7bd3
\ No newline at end of file
+24ff8abdae794bddf80b9eaf58827eb15a93f900
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index c8e07830..98024c8b 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-67eccd63a45f6d9992bbd3ae98ba3d1cde082a18
\ No newline at end of file
+29291014b09800078bc8097debaf88a3d5f496ea
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index 8adbd9d..a9d82f57 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-dd902dc476249b350620c4c498da33ecd8794de3
\ No newline at end of file
+70b426fd753738dbd992009246cc6d8d8d29ff0a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index eff58a6b..608869e 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-88f070706f962c0bce177dc1ce84171bdb723972
\ No newline at end of file
+fcc0e34cb93055250840d6e29bc732b184a8c402
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 1a861ce..ed6fe4ef 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-33fb3927f5840b596f3c2074be418c416f90f132
\ No newline at end of file
+376827318d7d8ce8f1f0abf986e4af3e33336085
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 97e753d..0dd4a15d 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9caacbfda2e62f20256c92cd7c1d026631edc879
\ No newline at end of file
+bba6a112fe50d780d93dc989572004d0d01fb19f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 040b08f1..34950f8 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-581a52488ed93faed2587a2e39ae61122502c60a
\ No newline at end of file
+45f6098b0dcaf04d45e3983c0ea9a3016b0a16f8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 5a23540b..071a987 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f5c5107e00a86d6b3c067cc6b09a2603f1a155be
\ No newline at end of file
+15531cf827b6532f1640b0913d5c5ce5e99c88fc
\ No newline at end of file
diff --git a/mojo/core/ports/node.cc b/mojo/core/ports/node.cc
index 965cbc5..25ad1c9 100644
--- a/mojo/core/ports/node.cc
+++ b/mojo/core/ports/node.cc
@@ -1759,6 +1759,8 @@
   {
     SinglePortLocker locker(&port_ref);
     auto* port = locker.port();
+    if (port->state == Port::kClosed)
+      return;
     peer_node_name = port->peer_node_name;
     peer_port_name = port->peer_port_name;
     sequence_num = port->next_control_sequence_num_to_send++;
@@ -1784,6 +1786,8 @@
   {
     SinglePortLocker locker(&port_ref);
     auto* port = locker.port();
+    if (port->state == Port::kClosed)
+      return;
     DCHECK_EQ(port->state, Port::kProxying);
 
     // Make sure we have seen ObserveProxyAck before removing the port.
diff --git a/net/spdy/spdy_proxy_client_socket_unittest.cc b/net/spdy/spdy_proxy_client_socket_unittest.cc
index 21ac1e6..89fe987a 100644
--- a/net/spdy/spdy_proxy_client_socket_unittest.cc
+++ b/net/spdy/spdy_proxy_client_socket_unittest.cc
@@ -92,7 +92,6 @@
 base::WeakPtr<SpdySession> CreateSpdyProxySession(
     const url::SchemeHostPort& destination,
     HttpNetworkSession* http_session,
-    SpdySessionDependencies* session_deps,
     const SpdySessionKey& key,
     const CommonConnectJobParams* common_connect_job_params) {
   EXPECT_FALSE(http_session->spdy_session_pool()->FindAvailableSession(
@@ -269,8 +268,8 @@
 
   // Creates the SPDY session and stream.
   spdy_session_ = CreateSpdyProxySession(
-      url::SchemeHostPort(url_), session_.get(), &session_deps_,
-      endpoint_spdy_session_key_, common_connect_job_params_.get());
+      url::SchemeHostPort(url_), session_.get(), endpoint_spdy_session_key_,
+      common_connect_job_params_.get());
 
   base::WeakPtr<SpdyStream> spdy_stream(
       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, spdy_session_, url_,
diff --git a/net/websockets/websocket_basic_stream.cc b/net/websockets/websocket_basic_stream.cc
index 8b87a0b..558de4e 100644
--- a/net/websockets/websocket_basic_stream.cc
+++ b/net/websockets/websocket_basic_stream.cc
@@ -249,6 +249,10 @@
 
 std::string WebSocketBasicStream::GetExtensions() const { return extensions_; }
 
+const NetLogWithSource& WebSocketBasicStream::GetNetLogWithSource() const {
+  return net_log_;
+}
+
 /*static*/
 std::unique_ptr<WebSocketBasicStream>
 WebSocketBasicStream::CreateWebSocketBasicStreamForTesting(
diff --git a/net/websockets/websocket_basic_stream.h b/net/websockets/websocket_basic_stream.h
index 2327e58..b672aba 100644
--- a/net/websockets/websocket_basic_stream.h
+++ b/net/websockets/websocket_basic_stream.h
@@ -130,6 +130,8 @@
 
   std::string GetExtensions() const override;
 
+  const NetLogWithSource& GetNetLogWithSource() const override;
+
   ////////////////////////////////////////////////////////////////////////////
   // Methods for testing only.
 
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc
index 90a1e8f..b9fd94c 100644
--- a/net/websockets/websocket_channel_test.cc
+++ b/net/websockets/websocket_channel_test.cc
@@ -282,12 +282,18 @@
   // Returns the string passed to the constructor.
   std::string GetExtensions() const override { return extensions_; }
 
+  const NetLogWithSource& GetNetLogWithSource() const override {
+    return net_log_;
+  }
+
  private:
   // The string to return from GetSubProtocol().
   std::string protocol_;
 
   // The string to return from GetExtensions().
   std::string extensions_;
+
+  NetLogWithSource net_log_;
 };
 
 // To make the static initialisers easier to read, we use enums rather than
@@ -718,6 +724,7 @@
   MOCK_METHOD0(Close, void());
   MOCK_CONST_METHOD0(GetSubProtocol, std::string());
   MOCK_CONST_METHOD0(GetExtensions, std::string());
+  MOCK_CONST_METHOD0(GetNetLogWithSource, NetLogWithSource&());
   MOCK_METHOD0(AsWebSocketStream, WebSocketStream*());
 };
 
diff --git a/net/websockets/websocket_deflate_stream.cc b/net/websockets/websocket_deflate_stream.cc
index 9863396..229cdd0 100644
--- a/net/websockets/websocket_deflate_stream.cc
+++ b/net/websockets/websocket_deflate_stream.cc
@@ -98,6 +98,10 @@
   return stream_->GetExtensions();
 }
 
+const NetLogWithSource& WebSocketDeflateStream::GetNetLogWithSource() const {
+  return stream_->GetNetLogWithSource();
+}
+
 void WebSocketDeflateStream::OnReadComplete(
     std::vector<std::unique_ptr<WebSocketFrame>>* frames,
     int result) {
diff --git a/net/websockets/websocket_deflate_stream.h b/net/websockets/websocket_deflate_stream.h
index 909d20d..fecd09569 100644
--- a/net/websockets/websocket_deflate_stream.h
+++ b/net/websockets/websocket_deflate_stream.h
@@ -13,6 +13,7 @@
 
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_export.h"
+#include "net/log/net_log_with_source.h"
 #include "net/websockets/websocket_deflater.h"
 #include "net/websockets/websocket_frame.h"
 #include "net/websockets/websocket_inflater.h"
@@ -56,6 +57,7 @@
   void Close() override;
   std::string GetSubProtocol() const override;
   std::string GetExtensions() const override;
+  const NetLogWithSource& GetNetLogWithSource() const override;
 
  private:
   enum ReadingState {
diff --git a/net/websockets/websocket_deflate_stream_fuzzer.cc b/net/websockets/websocket_deflate_stream_fuzzer.cc
index 95f74867..0720424 100644
--- a/net/websockets/websocket_deflate_stream_fuzzer.cc
+++ b/net/websockets/websocket_deflate_stream_fuzzer.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_piece.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/log/net_log_with_source.h"
 #include "net/websockets/websocket_deflate_parameters.h"
 #include "net/websockets/websocket_deflate_predictor.h"
 #include "net/websockets/websocket_deflate_predictor_impl.h"
@@ -64,6 +65,9 @@
   void Close() override {}
   std::string GetSubProtocol() const override { return std::string(); }
   std::string GetExtensions() const override { return std::string(); }
+  const NetLogWithSource& GetNetLogWithSource() const override {
+    return net_log_;
+  }
 
  private:
   std::unique_ptr<WebSocketFrame> CreateFrame() {
@@ -96,6 +100,8 @@
   std::vector<scoped_refptr<IOBufferWithSize>> buffers_;
 
   FuzzedDataProvider* fuzzed_data_provider_;
+
+  NetLogWithSource net_log_;
 };
 
 void WebSocketDeflateStreamFuzz(const uint8_t* data, size_t size) {
diff --git a/net/websockets/websocket_deflate_stream_test.cc b/net/websockets/websocket_deflate_stream_test.cc
index 2f42dfb9..2e828cd 100644
--- a/net/websockets/websocket_deflate_stream_test.cc
+++ b/net/websockets/websocket_deflate_stream_test.cc
@@ -21,6 +21,7 @@
 #include "base/test/mock_callback.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
+#include "net/log/net_log_with_source.h"
 #include "net/test/gtest_util.h"
 #include "net/websockets/websocket_deflate_parameters.h"
 #include "net/websockets/websocket_deflate_predictor.h"
@@ -84,6 +85,7 @@
   MOCK_METHOD0(Close, void());
   MOCK_CONST_METHOD0(GetSubProtocol, std::string());
   MOCK_CONST_METHOD0(GetExtensions, std::string());
+  MOCK_CONST_METHOD0(GetNetLogWithSource, NetLogWithSource&());
 };
 
 // This mock class relies on some assumptions.
diff --git a/net/websockets/websocket_stream.h b/net/websockets/websocket_stream.h
index e4a705a4..14db067 100644
--- a/net/websockets/websocket_stream.h
+++ b/net/websockets/websocket_stream.h
@@ -16,6 +16,7 @@
 #include "net/base/isolation_info.h"
 #include "net/base/net_export.h"
 #include "net/cookies/site_for_cookies.h"
+#include "net/log/net_log_with_source.h"
 #include "net/websockets/websocket_event_interface.h"
 #include "net/websockets/websocket_handshake_request_info.h"
 #include "net/websockets/websocket_handshake_response_info.h"
@@ -270,6 +271,8 @@
   // extensions were negotiated, the empty string is returned.
   virtual std::string GetExtensions() const = 0;
 
+  virtual const NetLogWithSource& GetNetLogWithSource() const = 0;
+
  protected:
   WebSocketStream();
 };
diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom
index 80f26b62..4c4cc16d 100644
--- a/services/network/public/mojom/url_response_head.mojom
+++ b/services/network/public/mojom/url_response_head.mojom
@@ -7,6 +7,7 @@
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "services/network/public/mojom/fetch_api.mojom";
+import "services/network/public/mojom/ip_address_space.mojom";
 import "services/network/public/mojom/ip_endpoint.mojom";
 import "services/network/public/mojom/load_timing_info.mojom";
 import "services/network/public/mojom/network_param.mojom";
@@ -81,6 +82,14 @@
   // Remote address of the socket which fetched this resource.
   IPEndPoint remote_endpoint;
 
+  // The IP address space of the request client/the request's policy container.
+  // https://wicg.github.io/private-network-access/#policy-container-ip-address-space
+  IPAddressSpace client_address_space = IPAddressSpace.kUnknown;
+
+  // The IP address space of the response, derived from the remote IP endpoint.
+  // https://wicg.github.io/private-network-access/#response-ip-address-space
+  IPAddressSpace response_address_space = IPAddressSpace.kUnknown;
+
   // True if the response was fetched from the cache and validated over the
   // network.
   bool is_validated = false;
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 0621f51..df55e69f 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -110,77 +110,6 @@
 // mojo::core::Core::CreateDataPipe
 constexpr size_t kBlockedBodyAllocationSize = 1;
 
-void PopulateResourceResponse(net::URLRequest* request,
-                              bool is_load_timing_enabled,
-                              int32_t options,
-                              network::mojom::URLResponseHead* response) {
-  response->request_time = request->request_time();
-  response->response_time = request->response_time();
-  response->headers = request->response_headers();
-  response->parsed_headers =
-      PopulateParsedHeaders(response->headers.get(), request->url());
-
-  request->GetCharset(&response->charset);
-  response->content_length = request->GetExpectedContentSize();
-  request->GetMimeType(&response->mime_type);
-  net::HttpResponseInfo response_info = request->response_info();
-  response->was_fetched_via_spdy = response_info.was_fetched_via_spdy;
-  response->was_alpn_negotiated = response_info.was_alpn_negotiated;
-  response->alpn_negotiated_protocol = response_info.alpn_negotiated_protocol;
-  response->connection_info = response_info.connection_info;
-  response->remote_endpoint = response_info.remote_endpoint;
-  response->was_fetched_via_cache = request->was_cached();
-  response->is_validated = (response_info.cache_entry_status ==
-                            net::HttpResponseInfo::ENTRY_VALIDATED);
-  response->proxy_server = request->proxy_server();
-  response->network_accessed = response_info.network_accessed;
-  response->async_revalidation_requested =
-      response_info.async_revalidation_requested;
-  response->was_in_prefetch_cache =
-      !(request->load_flags() & net::LOAD_PREFETCH) &&
-      response_info.unused_since_prefetch;
-
-  response->was_cookie_in_request = false;
-  for (const auto& cookie_with_access_result : request->maybe_sent_cookies()) {
-    if (cookie_with_access_result.access_result.status.IsInclude()) {
-      // IsInclude() true means the cookie was sent.
-      response->was_cookie_in_request = true;
-      break;
-    }
-  }
-
-  if (is_load_timing_enabled)
-    request->GetLoadTimingInfo(&response->load_timing);
-
-  if (request->ssl_info().cert.get()) {
-    response->ct_policy_compliance = request->ssl_info().ct_policy_compliance;
-    response->cert_status = request->ssl_info().cert_status;
-    net::SSLVersion ssl_version = net::SSLConnectionStatusToVersion(
-        request->ssl_info().connection_status);
-    response->is_legacy_tls_version =
-        ssl_version == net::SSLVersion::SSL_CONNECTION_VERSION_TLS1 ||
-        ssl_version == net::SSLVersion::SSL_CONNECTION_VERSION_TLS1_1;
-
-    if ((options & mojom::kURLLoadOptionSendSSLInfoWithResponse) ||
-        (net::IsCertStatusError(request->ssl_info().cert_status) &&
-         (options & mojom::kURLLoadOptionSendSSLInfoForCertificateError))) {
-      response->ssl_info = request->ssl_info();
-    }
-  }
-
-  response->request_start = request->creation_time();
-  response->response_start = base::TimeTicks::Now();
-  response->encoded_data_length = request->GetTotalReceivedBytes();
-  response->auth_challenge_info = request->auth_challenge_info();
-  response->has_range_requested = request->extra_request_headers().HasHeader(
-      net::HttpRequestHeaders::kRange);
-  base::ranges::copy(request->response_info().dns_aliases,
-                     std::back_inserter(response->dns_aliases));
-  // [spec]: https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
-  // 13. Set response’s request-includes-credentials to includeCredentials.
-  response->request_include_credentials = request->allow_credentials();
-}
-
 // A subclass of net::UploadBytesElementReader which owns
 // ResourceRequestBody.
 class BytesElementReader : public net::UploadBytesElementReader {
@@ -1116,7 +1045,7 @@
 
 PrivateNetworkAccessCheckResult URLLoader::PrivateNetworkAccessCheck(
     const net::TransportInfo& transport_info) {
-  resource_ip_address_space_ = TransportInfoToIPAddressSpace(transport_info);
+  response_ip_address_space_ = TransportInfoToIPAddressSpace(transport_info);
 
   const mojom::ClientSecurityState* security_state = GetClientSecurityState();
 
@@ -1124,7 +1053,7 @@
   // `URLLoader::PrivateNetworkAccessCheck()` and fails to compile.
   PrivateNetworkAccessCheckResult result = network::PrivateNetworkAccessCheck(
       security_state, target_ip_address_space_, options_,
-      resource_ip_address_space_);
+      response_ip_address_space_);
 
   url_request_->net_log().AddEvent(
       net::NetLogEventType::PRIVATE_NETWORK_ACCESS_CHECK, [&] {
@@ -1138,7 +1067,7 @@
                           IPAddressSpaceToStringPiece(client_address_space));
         dict.SetStringKey(
             "resource_address_space",
-            IPAddressSpaceToStringPiece(resource_ip_address_space_));
+            IPAddressSpaceToStringPiece(response_ip_address_space_));
         dict.SetStringKey("result",
                           PrivateNetworkAccessCheckResultToStringPiece(result));
         return dict;
@@ -1164,7 +1093,7 @@
   if (devtools_observer_) {
     devtools_observer_->OnPrivateNetworkRequest(
         devtools_request_id(), url_request_->url(), is_warning,
-        resource_ip_address_space_, security_state->Clone());
+        response_ip_address_space_, security_state->Clone());
   }
 
   return result;
@@ -1188,7 +1117,7 @@
     // with it later, then fail the request with the same net error code as
     // other CORS errors.
     cors_error_status_ = CorsErrorStatus(*cors_error, target_ip_address_space_,
-                                         resource_ip_address_space_);
+                                         response_ip_address_space_);
     return net::ERR_FAILED;
   }
 
@@ -1216,6 +1145,87 @@
   return net::OK;
 }
 
+mojom::URLResponseHeadPtr URLLoader::BuildResponseHead() const {
+  auto response = mojom::URLResponseHead::New();
+
+  response->request_time = url_request_->request_time();
+  response->response_time = url_request_->response_time();
+  response->headers = url_request_->response_headers();
+  response->parsed_headers =
+      PopulateParsedHeaders(response->headers.get(), url_request_->url());
+
+  url_request_->GetCharset(&response->charset);
+  response->content_length = url_request_->GetExpectedContentSize();
+  url_request_->GetMimeType(&response->mime_type);
+  net::HttpResponseInfo response_info = url_request_->response_info();
+  response->was_fetched_via_spdy = response_info.was_fetched_via_spdy;
+  response->was_alpn_negotiated = response_info.was_alpn_negotiated;
+  response->alpn_negotiated_protocol = response_info.alpn_negotiated_protocol;
+  response->connection_info = response_info.connection_info;
+  response->remote_endpoint = response_info.remote_endpoint;
+  response->was_fetched_via_cache = url_request_->was_cached();
+  response->is_validated = (response_info.cache_entry_status ==
+                            net::HttpResponseInfo::ENTRY_VALIDATED);
+  response->proxy_server = url_request_->proxy_server();
+  response->network_accessed = response_info.network_accessed;
+  response->async_revalidation_requested =
+      response_info.async_revalidation_requested;
+  response->was_in_prefetch_cache =
+      !(url_request_->load_flags() & net::LOAD_PREFETCH) &&
+      response_info.unused_since_prefetch;
+
+  response->was_cookie_in_request = false;
+  for (const auto& cookie_with_access_result :
+       url_request_->maybe_sent_cookies()) {
+    if (cookie_with_access_result.access_result.status.IsInclude()) {
+      // IsInclude() true means the cookie was sent.
+      response->was_cookie_in_request = true;
+      break;
+    }
+  }
+
+  if (is_load_timing_enabled_)
+    url_request_->GetLoadTimingInfo(&response->load_timing);
+
+  if (url_request_->ssl_info().cert.get()) {
+    response->ct_policy_compliance =
+        url_request_->ssl_info().ct_policy_compliance;
+    response->cert_status = url_request_->ssl_info().cert_status;
+    net::SSLVersion ssl_version = net::SSLConnectionStatusToVersion(
+        url_request_->ssl_info().connection_status);
+    response->is_legacy_tls_version =
+        ssl_version == net::SSLVersion::SSL_CONNECTION_VERSION_TLS1 ||
+        ssl_version == net::SSLVersion::SSL_CONNECTION_VERSION_TLS1_1;
+
+    if ((options_ & mojom::kURLLoadOptionSendSSLInfoWithResponse) ||
+        (net::IsCertStatusError(url_request_->ssl_info().cert_status) &&
+         (options_ & mojom::kURLLoadOptionSendSSLInfoForCertificateError))) {
+      response->ssl_info = url_request_->ssl_info();
+    }
+  }
+
+  response->request_start = url_request_->creation_time();
+  response->response_start = base::TimeTicks::Now();
+  response->encoded_data_length = url_request_->GetTotalReceivedBytes();
+  response->auth_challenge_info = url_request_->auth_challenge_info();
+  response->has_range_requested =
+      url_request_->extra_request_headers().HasHeader(
+          net::HttpRequestHeaders::kRange);
+  base::ranges::copy(url_request_->response_info().dns_aliases,
+                     std::back_inserter(response->dns_aliases));
+  // [spec]: https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
+  // 13. Set response’s request-includes-credentials to includeCredentials.
+  response->request_include_credentials = url_request_->allow_credentials();
+
+  response->response_address_space = response_ip_address_space_;
+
+  const mojom::ClientSecurityState* state = GetClientSecurityState();
+  response->client_address_space =
+      state ? state->ip_address_space : mojom::IPAddressSpace::kUnknown;
+
+  return response;
+}
+
 void URLLoader::OnReceivedRedirect(net::URLRequest* url_request,
                                    const net::RedirectInfo& redirect_info,
                                    bool* defer_redirect) {
@@ -1228,9 +1238,7 @@
   // optionally follow the redirect.
   *defer_redirect = true;
 
-  auto response = network::mojom::URLResponseHead::New();
-  PopulateResourceResponse(url_request_.get(), is_load_timing_enabled_,
-                           options_, response.get());
+  mojom::URLResponseHeadPtr response = BuildResponseHead();
   DispatchOnRawResponse();
   ReportFlaggedResponseCookies();
 
@@ -1390,9 +1398,7 @@
     return;
   }
 
-  response_ = network::mojom::URLResponseHead::New();
-  PopulateResourceResponse(url_request_.get(), is_load_timing_enabled_,
-                           options_, response_.get());
+  response_ = BuildResponseHead();
   DispatchOnRawResponse();
 
   // Parse and remove the Trust Tokens response headers, if any are expected,
@@ -2181,7 +2187,7 @@
   emitted_devtools_raw_response_ = true;
   devtools_observer_->OnRawResponse(
       devtools_request_id().value(), url_request_->maybe_stored_cookies(),
-      std::move(header_array), raw_response_headers, resource_ip_address_space_,
+      std::move(header_array), raw_response_headers, response_ip_address_space_,
       response_headers->response_code());
 
   return true;
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 959a22d..272efd7 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -427,6 +427,13 @@
   PrivateNetworkAccessCheckResult PrivateNetworkAccessCheck(
       const net::TransportInfo& info);
 
+  mojom::DevToolsObserver* GetDevToolsObserver() const;
+  mojom::CookieAccessObserver* GetCookieAccessObserver() const;
+
+  // Builds a response struct based on the data received so far.
+  // Never returns nullptr.
+  mojom::URLResponseHeadPtr BuildResponseHead() const;
+
   // Determine given the |url|, whether the |url_request_| should include
   // credentials and client certificates.
   void SetRequestCredentials(const GURL& url);
@@ -562,10 +569,11 @@
   mojom::IPAddressSpace target_ip_address_space_ =
       mojom::IPAddressSpace::kUnknown;
 
-  // The resource's address space, as computed using the |net::TransportInfo|
+  // The response's address space, as computed using the |net::TransportInfo|
   // argument to the |OnConnected()| callback. This info is only available then,
   // so the computation result is stored for later use in this member.
-  mojom::IPAddressSpace resource_ip_address_space_ =
+  // https://wicg.github.io/private-network-access/#response-ip-address-space
+  mojom::IPAddressSpace response_ip_address_space_ =
       mojom::IPAddressSpace::kUnknown;
 
   mojo::Remote<mojom::TrustedHeaderClient> header_client_;
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index d816188d..301eb0537 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -1751,6 +1751,16 @@
              << net::ErrorToString(params.expected_result) << " }";
 }
 
+mojom::IPAddressSpace ResponseAddressSpace(
+    const URLLoaderFakeTransportInfoTestParams& params) {
+  switch (params.transport_type) {
+    case net::TransportType::kDirect:
+      return params.endpoint_address_space;
+    case net::TransportType::kProxied:
+      return mojom::IPAddressSpace::kUnknown;
+  }
+}
+
 class URLLoaderFakeTransportInfoTest
     : public URLLoaderTest,
       public testing::WithParamInterface<URLLoaderFakeTransportInfoTestParams> {
@@ -1805,7 +1815,15 @@
     EXPECT_THAT(client()->completion_status().cors_error_status,
                 Optional(InsecurePrivateNetworkCorsErrorStatus(
                     params.endpoint_address_space)));
+    return;
   }
+
+  // Check that the right address spaces are reported in `URLResponseHead`.
+  ASSERT_FALSE(client()->response_head().is_null());
+  EXPECT_EQ(client()->response_head()->client_address_space,
+            params.client_address_space);
+  EXPECT_EQ(client()->response_head()->response_address_space,
+            ResponseAddressSpace(params));
 }
 
 // Lists all combinations we want to test in URLLoaderFakeTransportInfoTest.
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index f635815..96255be 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -5703,7 +5703,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -5967,7 +5967,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 827be02..e6f476b 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -43369,7 +43369,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43633,7 +43633,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -43972,7 +43972,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -44236,7 +44236,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -44575,7 +44575,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -44839,7 +44839,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M99",
-              "revision": "version:99.0.4844.22"
+              "revision": "version:99.0.4844.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 59456cf..0d49e7de 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -435,7 +435,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.22',
+          'revision': 'version:99.0.4844.23',
         }
       ],
     },
@@ -507,7 +507,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.22',
+          'revision': 'version:99.0.4844.23',
         }
       ],
     },
@@ -579,7 +579,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M99',
-          'revision': 'version:99.0.4844.22',
+          'revision': 'version:99.0.4844.23',
         }
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 64fc65f..808fadd 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1082,10 +1082,7 @@
                         "cache_size": "6",
                         "check_eligibility_after_pagehide": "true",
                         "enable_same_site": "true",
-                        "file_system_api_supported": "true",
                         "foreground_cache_size": "2",
-                        "grace_period_to_finish_loading_in_seconds": "60",
-                        "max_buffered_bytes": "200000",
                         "max_buffered_bytes_per_process": "1024000",
                         "supported_features": "MediaSessionImplOnServiceCreated"
                     },
@@ -1116,9 +1113,7 @@
                         "content_injection_supported": "true",
                         "enable_same_site": "false",
                         "extension_message_supported": "true",
-                        "file_system_api_supported": "true",
                         "foreground_cache_size": "2",
-                        "grace_period_to_finish_loading_in_seconds": "60",
                         "max_buffered_bytes_per_process": "1024000",
                         "unload_support": "opt_in_header_required"
                     },
@@ -1131,6 +1126,25 @@
             ]
         }
     ],
+    "BackForwardCacheAppBanner": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "BackForwardCacheAppBanner"
+                    ]
+                }
+            ]
+        }
+    ],
     "BackForwardCacheMemoryControls": [
         {
             "platforms": [
diff --git a/third_party/blink/public/mojom/drag/drag.mojom b/third_party/blink/public/mojom/drag/drag.mojom
index f79084f..8e089d4 100644
--- a/third_party/blink/public/mojom/drag/drag.mojom
+++ b/third_party/blink/public/mojom/drag/drag.mojom
@@ -12,6 +12,7 @@
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "url/mojom/url.mojom";
 import "third_party/blink/public/mojom/data_transfer/data_transfer.mojom";
+import "third_party/blink/public/mojom/blob/serialized_blob.mojom";
 
 // This struct encodes what drag-and-drop operations are allowed. It's
 // typemapped to blink::DragOperationsMask.
@@ -58,6 +59,8 @@
   url.mojom.Url url;
   int64 size;
   string? file_system_id;
+  // `serialized_blob` is only set for drop operations.
+  SerializedBlob? serialized_blob;
 };
 
 union DragItem {
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 393aa9f..99342cb 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3479,6 +3479,9 @@
   kV8UDPSocket_Writable_AttributeGetter = 4158,
   kAbortSignalTimeout = 4159,
   kClientHintsPartitionedCookies = 4160,
+  kV8Document_Prerendering_AttributeGetter = 4161,
+  kV8Document_Onprerenderingchange_AttributeGetter = 4162,
+  kV8Document_Onprerenderingchange_AttributeSetter = 4163,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_drag_data.h b/third_party/blink/public/platform/web_drag_data.h
index 2f539a7..922a8d94 100644
--- a/third_party/blink/public/platform/web_drag_data.h
+++ b/third_party/blink/public/platform/web_drag_data.h
@@ -35,6 +35,7 @@
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom-shared.h"
 #include "third_party/blink/public/platform/cross_variant_mojo_util.h"
+#include "third_party/blink/public/platform/web_blob_info.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_data.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -97,6 +98,7 @@
     WebURL file_system_url;
     int64_t file_system_file_size;
     WebString file_system_id;
+    WebBlobInfo file_system_blob_info;
   };
 
   WebDragData() = default;
diff --git a/third_party/blink/renderer/core/clipboard/data_object.cc b/third_party/blink/renderer/core/clipboard/data_object.cc
index d3fcc09..5214b10 100644
--- a/third_party/blink/renderer/core/clipboard/data_object.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object.cc
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/core/clipboard/dragged_isolated_file_system.h"
 #include "third_party/blink/renderer/core/clipboard/paste_mode.h"
 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
 #include "third_party/blink/renderer/platform/file_metadata.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
@@ -313,7 +314,7 @@
 }
 
 // static
-DataObject* DataObject::Create(WebDragData data) {
+DataObject* DataObject::Create(const WebDragData& data) {
   DataObject* data_object = Create();
   bool has_file_system = false;
 
@@ -342,13 +343,30 @@
       case WebDragData::Item::kStorageTypeFileSystemFile: {
         // TODO(http://crbug.com/429077): The file system URL may refer a user
         // visible file.
+        scoped_refptr<BlobDataHandle> blob_data_handle =
+            item.file_system_blob_info.GetBlobHandle();
+
+        // If the browser process has provided a BlobDataHandle to use for
+        // building the File object (as a result of a drop operation being
+        // performed) then use it to create the file here (instead of creating
+        // a File object without one and requiring a call to
+        // BlobRegistry::Register in the browser process to hook up the Blob
+        // remote/receiver pair). If no BlobDataHandle was provided, create a
+        // BlobDataHandle to an empty blob since the File object contents
+        // won't be needed (for example, because this DataObject will be used
+        // for the DragEnter case where the spec only indicates that basic file
+        // metadata should be retrievable via the corresponding
+        // DataTransferItem).
+        if (!blob_data_handle) {
+          blob_data_handle = BlobDataHandle::Create();
+        }
         has_file_system = true;
         FileMetadata file_metadata;
         file_metadata.length = item.file_system_file_size;
-
         data_object->Add(
             File::CreateForFileSystemFile(item.file_system_url, file_metadata,
-                                          File::kIsNotUserVisible),
+                                          File::kIsNotUserVisible,
+                                          std::move(blob_data_handle)),
             item.file_system_id);
       } break;
     }
diff --git a/third_party/blink/renderer/core/clipboard/data_object.h b/third_party/blink/renderer/core/clipboard/data_object.h
index 7c2523d..2575e0a 100644
--- a/third_party/blink/renderer/core/clipboard/data_object.h
+++ b/third_party/blink/renderer/core/clipboard/data_object.h
@@ -64,7 +64,7 @@
   static DataObject* CreateFromClipboard(SystemClipboard*, PasteMode);
   static DataObject* CreateFromString(const String&);
   static DataObject* Create();
-  static DataObject* Create(WebDragData);
+  static DataObject* Create(const WebDragData&);
 
   DataObject();
   virtual ~DataObject();
diff --git a/third_party/blink/renderer/core/css/css_property_value_set_test.cc b/third_party/blink/renderer/core/css/css_property_value_set_test.cc
index fff96310..821245e2 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set_test.cc
+++ b/third_party/blink/renderer/core/css/css_property_value_set_test.cc
@@ -65,4 +65,27 @@
   EXPECT_EQ("bar", set1.GetPropertyValue(AtomicString("--y")));
 }
 
+// https://crbug.com/1292163
+TEST_F(CSSPropertyValueSetTest, ConflictingLonghandAndShorthand) {
+  auto* context = MakeGarbageCollected<CSSParserContext>(GetDocument());
+  auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
+
+  String sheet_text = R"CSS(
+    #first {
+      offset: none reverse 2turn;
+      offset-path: initial;
+    }
+  )CSS";
+
+  CSSParser::ParseSheet(context, style_sheet, sheet_text,
+                        CSSDeferPropertyParsing::kNo);
+  StyleRule* rule = RuleAt(style_sheet, 0);
+
+  EXPECT_EQ(
+      "offset-position: initial; offset-distance: initial; "
+      "offset-rotate: reverse 2turn; offset-anchor: initial; "
+      "offset-path: initial;",
+      rule->Properties().AsText());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
index 2e9dcdc..37a67d0 100644
--- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
+++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.cc
@@ -180,54 +180,54 @@
   // information through as output parameters on functions involved in length
   // resolution.
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return viewport_size_.Width() / 100;
 }
 double CSSToLengthConversionData::ViewportHeightPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return viewport_size_.Height() / 100;
 }
 double CSSToLengthConversionData::ViewportInlineSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.Width()
                                     : viewport_size_.Height()) /
          100;
 }
 double CSSToLengthConversionData::ViewportBlockSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.Height()
                                     : viewport_size_.Width()) /
          100;
 }
 double CSSToLengthConversionData::ViewportMinPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return std::min(viewport_size_.Width(), viewport_size_.Height()) / 100;
 }
 double CSSToLengthConversionData::ViewportMaxPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return std::max(viewport_size_.Width(), viewport_size_.Height()) / 100;
 }
 
 double CSSToLengthConversionData::SmallViewportWidthPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return viewport_size_.SmallWidth() / 100;
 }
 
 double CSSToLengthConversionData::SmallViewportHeightPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return viewport_size_.SmallHeight() / 100;
 }
 
 double CSSToLengthConversionData::SmallViewportInlineSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.SmallWidth()
                                     : viewport_size_.SmallHeight()) /
          100;
@@ -235,7 +235,7 @@
 
 double CSSToLengthConversionData::SmallViewportBlockSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.SmallHeight()
                                     : viewport_size_.SmallWidth()) /
          100;
@@ -243,33 +243,33 @@
 
 double CSSToLengthConversionData::SmallViewportMinPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return std::min(viewport_size_.SmallWidth(), viewport_size_.SmallHeight()) /
          100;
 }
 
 double CSSToLengthConversionData::SmallViewportMaxPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return std::max(viewport_size_.SmallWidth(), viewport_size_.SmallHeight()) /
          100;
 }
 
 double CSSToLengthConversionData::LargeViewportWidthPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return viewport_size_.LargeWidth() / 100;
 }
 
 double CSSToLengthConversionData::LargeViewportHeightPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return viewport_size_.LargeHeight() / 100;
 }
 
 double CSSToLengthConversionData::LargeViewportInlineSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.LargeWidth()
                                     : viewport_size_.LargeHeight()) /
          100;
@@ -277,7 +277,7 @@
 
 double CSSToLengthConversionData::LargeViewportBlockSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.LargeHeight()
                                     : viewport_size_.LargeWidth()) /
          100;
@@ -285,33 +285,33 @@
 
 double CSSToLengthConversionData::LargeViewportMinPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return std::min(viewport_size_.LargeWidth(), viewport_size_.LargeHeight()) /
          100;
 }
 
 double CSSToLengthConversionData::LargeViewportMaxPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasStaticViewportUnits();
   return std::max(viewport_size_.LargeWidth(), viewport_size_.LargeHeight()) /
          100;
 }
 
 double CSSToLengthConversionData::DynamicViewportWidthPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasDynamicViewportUnits();
   return viewport_size_.DynamicWidth() / 100;
 }
 
 double CSSToLengthConversionData::DynamicViewportHeightPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasDynamicViewportUnits();
   return viewport_size_.DynamicHeight() / 100;
 }
 
 double CSSToLengthConversionData::DynamicViewportInlineSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasDynamicViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.DynamicWidth()
                                     : viewport_size_.DynamicHeight()) /
          100;
@@ -319,7 +319,7 @@
 
 double CSSToLengthConversionData::DynamicViewportBlockSizePercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasDynamicViewportUnits();
   return (IsHorizontalWritingMode() ? viewport_size_.DynamicHeight()
                                     : viewport_size_.DynamicWidth()) /
          100;
@@ -327,7 +327,7 @@
 
 double CSSToLengthConversionData::DynamicViewportMinPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasDynamicViewportUnits();
   return std::min(viewport_size_.DynamicWidth(),
                   viewport_size_.DynamicHeight()) /
          100;
@@ -335,7 +335,7 @@
 
 double CSSToLengthConversionData::DynamicViewportMaxPercent() const {
   if (style_)
-    const_cast<ComputedStyle*>(style_)->SetHasViewportUnits(true);
+    const_cast<ComputedStyle*>(style_)->SetHasDynamicViewportUnits();
   return std::max(viewport_size_.DynamicWidth(),
                   viewport_size_.DynamicHeight()) /
          100;
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 ebf1e5db3..5107d7b 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -928,8 +928,7 @@
   if (Element* animating_element = state.GetAnimatingElement())
     SetAnimationUpdateIfNeeded(style_recalc_context, state, *animating_element);
 
-  if (state.Style()->HasViewportUnits())
-    GetDocument().SetHasViewportUnits();
+  GetDocument().AddViewportUnitFlags(state.StyleRef().ViewportUnitFlags());
 
   if (state.Style()->HasContainerRelativeUnits()) {
     state.Style()->SetDependsOnContainerQueries(true);
@@ -1155,7 +1154,7 @@
     if (collector.MatchedResult().DependsOnContainerQueries())
       state.Style()->SetDependsOnContainerQueries(true);
     if (collector.MatchedResult().DependsOnViewportContainerQueries())
-      state.Style()->SetHasViewportUnits(true);
+      state.Style()->SetHasStaticViewportUnits();
     if (collector.MatchedResult().DependsOnRemContainerQueries())
       state.Style()->SetHasRemUnits();
     if (collector.MatchedResult().ConditionallyAffectsAnimations())
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index e157b5b..75334480 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -2325,7 +2325,7 @@
   RecalcStyle(change, StyleRecalcContext::FromAncestors(container));
 }
 
-void StyleEngine::RecalcStyleForContainerDescendantsInLegacyLayoutTree(
+void StyleEngine::RecalcStyleForNonLayoutNGContainerDescendants(
     Element& container) {
   if (!RuntimeEnabledFeatures::CSSContainerQueriesEnabled())
     return;
@@ -2337,15 +2337,15 @@
   // subtree. Style recalc will not be resumed during layout for legacy layout.
   // Instead, finish recalc for the subtree when it is discovered that the
   // container is in legacy layout.
-
+  // Also, this method is called to complete a skipped style recalc where we
+  // could not predict that the LayoutObject would not be created, like if the
+  // parent LayoutObject returns false for IsChildAllowed.
   auto* cq_data = container.GetContainerQueryData();
   if (!cq_data)
     return;
 
   if (cq_data->SkippedStyleRecalc())
     RecalcStyleForContainer(container, {});
-
-  cq_data->SetContainerQueryEvaluator(nullptr);
 }
 
 void StyleEngine::UpdateStyleAndLayoutTreeForContainer(
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 2c43f9f..00eb287 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -494,7 +494,7 @@
   void UpdateStyleAndLayoutTreeForContainer(Element& container,
                                             const LogicalSize&,
                                             LogicalAxes contained_axes);
-  void RecalcStyleForContainerDescendantsInLegacyLayoutTree(Element& container);
+  void RecalcStyleForNonLayoutNGContainerDescendants(Element& container);
   void RecalcStyle();
 
   void ClearEnsuredDescendantStyles(Element& element);
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index a2d3549b..1733cb5 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -3514,6 +3514,76 @@
   }
 }
 
+TEST_F(StyleEngineTest, HasViewportUnitFlags) {
+  ScopedCSSViewportUnits4ForTest flag(true);
+
+  struct {
+    const char* value;
+    bool has_static;
+    bool has_dynamic;
+  } test_data[] = {
+      {"1px", false, false},
+      {"1em", false, false},
+      {"1rem", false, false},
+
+      {"1vw", true, false},
+      {"1vh", true, false},
+      {"1vi", true, false},
+      {"1vb", true, false},
+      {"1vmin", true, false},
+      {"1vmax", true, false},
+
+      {"1svw", true, false},
+      {"1svh", true, false},
+      {"1svi", true, false},
+      {"1svb", true, false},
+      {"1svmin", true, false},
+      {"1svmax", true, false},
+
+      {"1lvw", true, false},
+      {"1lvh", true, false},
+      {"1lvi", true, false},
+      {"1lvb", true, false},
+      {"1lvmin", true, false},
+      {"1lvmax", true, false},
+
+      {"1dvw", false, true},
+      {"1dvh", false, true},
+      {"1dvi", false, true},
+      {"1dvb", false, true},
+      {"1dvmin", false, true},
+      {"1dvmax", false, true},
+
+      {"calc(1vh)", true, false},
+      {"calc(1dvh)", false, true},
+      {"calc(1vh + 1dvh)", true, true},
+  };
+
+  for (const auto& data : test_data) {
+    SCOPED_TRACE(data.value);
+    auto holder = std::make_unique<DummyPageHolder>(gfx::Size(800, 600));
+    Document& document = holder->GetDocument();
+    document.body()->setInnerHTML(String::Format(R"HTML(
+      <style>
+        div { width: %s; }
+      </style>
+      <div id=target></div>
+    )HTML",
+                                                 data.value));
+    document.View()->UpdateAllLifecyclePhasesForTest();
+
+    Element* target = document.getElementById("target");
+    ASSERT_TRUE(target);
+
+    EXPECT_EQ(data.has_static,
+              target->GetComputedStyle()->HasStaticViewportUnits());
+    EXPECT_EQ(data.has_dynamic,
+              target->GetComputedStyle()->HasDynamicViewportUnits());
+    EXPECT_EQ(data.has_static, document.HasStaticViewportUnits());
+    EXPECT_EQ(data.has_dynamic, document.HasDynamicViewportUnits());
+  }
+}
+
 class StyleEngineSimTest : public SimTest {};
 
 TEST_F(StyleEngineSimTest, OwnerColorScheme) {
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc
index 16ac976e..d0adffd 100644
--- a/third_party/blink/renderer/core/css/style_property_serializer.cc
+++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -933,8 +933,10 @@
       result.Append(rotate->CssText());
     }
   } else {
-    DCHECK(distance->IsInitialValue());
-    DCHECK(rotate->IsInitialValue());
+    // The longhand values cannot be serialized as a valid shorthand value.
+    // Serialize them as individual longhands instead.
+    if (!distance->IsInitialValue() || !rotate->IsInitialValue())
+      return String();
   }
   if (RuntimeEnabledFeatures::CSSOffsetPositionAnchorEnabled()) {
     const CSSValue* anchor =
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index bdfc91c2..10c9616 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -714,6 +714,7 @@
       xml_version_("1.0"),
       xml_standalone_(kStandaloneUnspecified),
       has_xml_declaration_(0),
+      viewport_unit_flags_(0),
       design_mode_(false),
       is_running_exec_command_(false),
       has_annotated_regions_(false),
@@ -754,7 +755,6 @@
           GetTaskRunner(TaskType::kInternalLoading),
           this,
           &Document::DidAssociateFormControlsTimerFired),
-      has_viewport_units_(false),
       parser_sync_policy_(kAllowDeferredParsing),
       node_count_(0),
       // Use the source id from the document initializer if it is available.
@@ -4573,7 +4573,7 @@
     if (GetFrame()->IsMainFrame() && !Printing())
       probe::DidResizeMainFrame(GetFrame());
   }
-  if (!HasViewportUnits())
+  if (!HasStaticViewportUnits())
     return;
   GetStyleResolver().SetResizedForViewportUnits();
   SetNeedsStyleRecalcForViewportUnits();
@@ -4587,9 +4587,9 @@
   MediaQueryAffectingValueChanged(MediaValueChange::kSize);
   if (media_query_matcher_)
     media_query_matcher_->ViewportChanged();
-  // TODO(crbug.com/1093055): Target dv* specifically.
-  if (!HasViewportUnits())
+  if (!HasDynamicViewportUnits())
     return;
+  // TODO(crbug.com/1093055): Target dv* specifically.
   GetStyleResolver().SetResizedForViewportUnits();
   SetNeedsStyleRecalcForViewportUnits();
 }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index ce32dcd..a24323e 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1471,8 +1471,19 @@
   void MaybeHandleHttpRefresh(const String&, HttpRefreshType);
   bool IsHttpRefreshScheduledWithin(base::TimeDelta interval);
 
-  void SetHasViewportUnits() { has_viewport_units_ = true; }
-  bool HasViewportUnits() const { return has_viewport_units_; }
+  // Marks the Document has having at least one Element which depends
+  // on the specified ViewportUnitFlags.
+  void AddViewportUnitFlags(unsigned flags) { viewport_unit_flags_ |= flags; }
+
+  bool HasStaticViewportUnits() const {
+    return viewport_unit_flags_ &
+           static_cast<unsigned>(ViewportUnitFlag::kStatic);
+  }
+  bool HasDynamicViewportUnits() const {
+    return viewport_unit_flags_ &
+           static_cast<unsigned>(ViewportUnitFlag::kDynamic);
+  }
+
   void LayoutViewportWasResized();
   // dv*
   void DynamicViewportUnitsChanged();
@@ -2133,6 +2144,8 @@
   String xml_version_;
   unsigned xml_standalone_ : 2;
   unsigned has_xml_declaration_ : 1;
+  // See enum ViewportUnitFlags.
+  unsigned viewport_unit_flags_ : kViewportUnitFlagBits;
 
   AtomicString content_language_;
 
@@ -2230,8 +2243,6 @@
 
   HeapHashSet<Member<SVGUseElement>> use_elements_needing_update_;
 
-  bool has_viewport_units_;
-
   ParserSynchronizationPolicy parser_sync_policy_;
 
   Member<CanvasFontCache> canvas_font_cache_;
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index a2411dd..65188cc 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -178,7 +178,7 @@
     readonly attribute boolean wasDiscarded;
 
     // https://wicg.github.io/nav-speculation/prerendering.html#dom-document-prerendering
-    [RuntimeEnabled=Prerender2RelatedFeatures] readonly attribute boolean prerendering;
+    [RuntimeEnabled=Prerender2RelatedFeatures, Measure] readonly attribute boolean prerendering;
 
     // CORS and RFC1918
     // https://wicg.github.io/cors-rfc1918/#feature-detect
@@ -212,7 +212,7 @@
     attribute EventHandler onbeforecut;
     attribute EventHandler onbeforepaste;
     attribute EventHandler onfreeze;
-    [RuntimeEnabled=Prerender2RelatedFeatures] attribute EventHandler onprerenderingchange;
+    [RuntimeEnabled=Prerender2RelatedFeatures, Measure] attribute EventHandler onprerenderingchange;
     attribute EventHandler onresume;
     attribute EventHandler onsearch;
     attribute EventHandler onvisibilitychange;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index fa16d1b..e31fbb2 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -606,6 +606,20 @@
   top_document.EnqueueAutofocusCandidate(element);
 }
 
+bool MaySkipNGBlockNodeLayout(const LayoutObject& layout_object) {
+  // Return true if we are not guaranteed that the layout_object will hit the
+  // LayoutNG code path during layout, which is a problem for container queries.
+  //
+  // Out-of-flow positioned replaced elements take the legacy path for layout
+  // if the container for positioning is a legacy object. That is the case for
+  // LayoutView, which is a legacy object but does not otherwise force
+  // legacy layout objects.
+  return layout_object.ForceLegacyLayout() ||
+         (!RuntimeEnabledFeatures::LayoutNGViewEnabled() &&
+          layout_object.IsOutOfFlowPositioned() &&
+          layout_object.IsLayoutReplaced());
+}
+
 }  // namespace
 
 Element::Element(const QualifiedName& tag_name,
@@ -2679,12 +2693,6 @@
   if (being_rendered) {
     AdjustForceLegacyLayout(style, &children_context.force_legacy_layout);
 
-    if (children_context.force_legacy_layout) {
-      GetDocument()
-          .GetStyleEngine()
-          .RecalcStyleForContainerDescendantsInLegacyLayoutTree(*this);
-    }
-
     LegacyLayout legacy = children_context.force_legacy_layout
                               ? LegacyLayout::kForce
                               : LegacyLayout::kAuto;
@@ -2714,6 +2722,19 @@
   }
   children_context.use_previous_in_flow = true;
 
+  if (children_context.force_legacy_layout ||
+      (being_rendered && !children_context.parent) ||
+      (layout_object && MaySkipNGBlockNodeLayout(*layout_object))) {
+    // If the created LayoutObject is forced into a legacy object, or if a
+    // LayoutObject was not created, even if we thought it should have been, for
+    // instance because the parent LayoutObject returns false for
+    // IsChildAllowed, we need to complete the skipped style recalc for size
+    // query containers as we would not have an NGBlockNode to resume from.
+    GetDocument()
+        .GetStyleEngine()
+        .RecalcStyleForNonLayoutNGContainerDescendants(*this);
+  }
+
   bool skip_container_descendants = SkippedContainerStyleRecalc();
   bool skip_lock_descendants = ChildStyleRecalcBlockedByDisplayLock();
   if (skip_container_descendants || skip_lock_descendants) {
@@ -2916,7 +2937,7 @@
     LayoutObject* layout_object = GetLayoutObject();
     if (!layout_object || !layout_object->SelfNeedsLayout() ||
         !layout_object->IsEligibleForSizeContainment() ||
-        layout_object->ForceLegacyLayout()) {
+        MaySkipNGBlockNodeLayout(*layout_object)) {
       return false;
     }
   }
diff --git a/third_party/blink/renderer/core/fileapi/file.cc b/third_party/blink/renderer/core/fileapi/file.cc
index 57c1c28..14cd17e 100644
--- a/third_party/blink/renderer/core/fileapi/file.cc
+++ b/third_party/blink/renderer/core/fileapi/file.cc
@@ -262,6 +262,21 @@
 
 File::File(const KURL& file_system_url,
            const FileMetadata& metadata,
+           UserVisibility user_visibility,
+           scoped_refptr<BlobDataHandle> blob_data_handle)
+    : Blob(std::move(blob_data_handle)),
+      has_backing_file_(false),
+      user_visibility_(user_visibility),
+      name_(DecodeURLEscapeSequences(file_system_url.LastPathComponent(),
+                                     DecodeURLMode::kUTF8OrIsomorphic)),
+      file_system_url_(file_system_url),
+      snapshot_size_(metadata.length),
+      snapshot_modification_time_(metadata.modification_time) {
+  DCHECK_GE(metadata.length, 0);
+}
+
+File::File(const KURL& file_system_url,
+           const FileMetadata& metadata,
            UserVisibility user_visibility)
     : Blob(BlobDataHandle::Create(
           CreateBlobDataForFileSystemURL(file_system_url, metadata),
diff --git a/third_party/blink/renderer/core/fileapi/file.h b/third_party/blink/renderer/core/fileapi/file.h
index e23ab93..e9e98c77 100644
--- a/third_party/blink/renderer/core/fileapi/file.h
+++ b/third_party/blink/renderer/core/fileapi/file.h
@@ -119,9 +119,18 @@
     return MakeGarbageCollected<File>(url, metadata, user_visibility);
   }
 
-  File(const String& path,
-       ContentTypeLookupPolicy = kWellKnownContentTypes,
-       UserVisibility = File::kIsUserVisible);
+  static File* CreateForFileSystemFile(
+      const KURL& url,
+      const FileMetadata& metadata,
+      UserVisibility user_visibility,
+      scoped_refptr<BlobDataHandle> blob_data_handle) {
+    return MakeGarbageCollected<File>(url, metadata, user_visibility,
+                                      std::move(blob_data_handle));
+  }
+
+  explicit File(const String& path,
+                ContentTypeLookupPolicy = kWellKnownContentTypes,
+                UserVisibility = File::kIsUserVisible);
   File(const String& path,
        const String& name,
        ContentTypeLookupPolicy,
@@ -139,6 +148,11 @@
        scoped_refptr<BlobDataHandle>);
   File(const String& name, const FileMetadata&, UserVisibility);
   File(const KURL& file_system_url, const FileMetadata&, UserVisibility);
+  File(const KURL& file_system_url,
+       const FileMetadata& metadata,
+       UserVisibility user_visibility,
+       scoped_refptr<BlobDataHandle> blob_data_handle);
+
   File(const File&);
 
   KURL FileSystemURL() const {
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index 06abca1..b0fbc0e 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -120,13 +120,7 @@
   explicit HTMLDocumentParserState(ParserSynchronizationPolicy mode)
       : state_(DeferredParserState::kNotScheduled),
         meta_csp_state_(MetaCSPTokenState::kNotSeen),
-        mode_(mode),
-        end_if_delayed_forbidden_(0),
-        should_complete_(0),
-        should_attempt_to_end_on_eof_(0),
-        times_yielded_(0),
-        needs_link_header_dispatch_(true),
-        have_seen_first_byte_(false) {}
+        mode_(mode) {}
 
   void Trace(Visitor* v) const {}
 
@@ -153,20 +147,33 @@
   }
   void DispatchedLinkHeaderPreloads() { needs_link_header_dispatch_ = false; }
 
-  bool HaveSeenFirstByte() const { return have_seen_first_byte_; }
-  void SetHaveSeenFirstByte() { have_seen_first_byte_ = true; }
+  bool SeenFirstByte() const { return have_seen_first_byte_; }
+  void MarkSeenFirstByte() { have_seen_first_byte_ = true; }
+
+  bool EndWasDelayed() const { return end_was_delayed_; }
+  void SetEndWasDelayed(bool new_value) { end_was_delayed_ = new_value; }
+
+  bool AddedPendingParserBlockingStylesheet() const {
+    return added_pending_parser_blocking_stylesheet_;
+  }
+  void SetAddedPendingParserBlockingStylesheet(bool new_value) {
+    added_pending_parser_blocking_stylesheet_ = new_value;
+  }
+
+  bool WaitingForStylesheets() const { return is_waiting_for_stylesheets_; }
+  void SetWaitingForStylesheets(bool new_value) {
+    is_waiting_for_stylesheets_ = new_value;
+  }
 
   // Keeps track of whether Document::Finish has been called whilst parsing.
   // ShouldAttemptToEndOnEOF() means that the parser should close when there's
   // no more input.
-  bool ShouldAttemptToEndOnEOF() const {
-    return should_attempt_to_end_on_eof_ > 0;
-  }
+  bool ShouldAttemptToEndOnEOF() const { return should_attempt_to_end_on_eof_; }
   void SetAttemptToEndOnEOF() {
-    // This method should only be called from ::Finish.
-    should_attempt_to_end_on_eof_++;
     // Should only ever call ::Finish once.
-    DCHECK(should_attempt_to_end_on_eof_ < 2);
+    DCHECK(!should_attempt_to_end_on_eof_);
+    // This method should only be called from ::Finish.
+    should_attempt_to_end_on_eof_ = true;
   }
 
   bool ShouldEndIfDelayed() const { return end_if_delayed_forbidden_ == 0; }
@@ -181,6 +188,12 @@
   void MarkYield() { times_yielded_++; }
   int TimesYielded() const { return times_yielded_; }
 
+  NestingLevelIncrementer ScopedPumpSession() {
+    return NestingLevelIncrementer(pump_session_nesting_level_);
+  }
+  bool InPumpSession() const { return pump_session_nesting_level_; }
+  bool InNestedPumpSession() const { return pump_session_nesting_level_ > 1; }
+
   void SetSeenCSPMetaTag(const bool seen) {
     if (meta_csp_state_ == MetaCSPTokenState::kUnenforceable)
       return;
@@ -200,32 +213,37 @@
  private:
   void EnterEndIfDelayedForbidden() { end_if_delayed_forbidden_++; }
   void ExitEndIfDelayedForbidden() {
+    DCHECK(end_if_delayed_forbidden_);
     end_if_delayed_forbidden_--;
-    DCHECK_GE(end_if_delayed_forbidden_, 0);
   }
 
   void EnterAttemptToEndForbidden() {
-    DCHECK(should_attempt_to_end_on_eof_ > 0);
-    should_attempt_to_end_on_eof_ = 0;
+    DCHECK(should_attempt_to_end_on_eof_);
+    should_attempt_to_end_on_eof_ = false;
   }
 
   void EnterShouldComplete() { should_complete_++; }
   void ExitShouldComplete() {
+    DCHECK(should_complete_);
     should_complete_--;
-    DCHECK_GE(should_complete_, 0);
   }
 
   DeferredParserState state_;
   MetaCSPTokenState meta_csp_state_;
   ParserSynchronizationPolicy mode_;
-  int end_if_delayed_forbidden_;
-  int should_complete_;
+  unsigned end_if_delayed_forbidden_ = 0;
+  unsigned should_complete_ = 0;
+  unsigned times_yielded_ = 0;
+  unsigned pump_session_nesting_level_ = 0;
+
   // Set to non-zero if Document::Finish has been called and we're operating
   // asynchronously.
-  int should_attempt_to_end_on_eof_;
-  int times_yielded_;
-  bool needs_link_header_dispatch_;
-  bool have_seen_first_byte_;
+  bool should_attempt_to_end_on_eof_ = false;
+  bool needs_link_header_dispatch_ = true;
+  bool have_seen_first_byte_ = false;
+  bool end_was_delayed_ = false;
+  bool added_pending_parser_blocking_stylesheet_ = false;
+  bool is_waiting_for_stylesheets_ = false;
 };
 
 class EndIfDelayedForbiddenScope {
@@ -384,11 +402,7 @@
           MakeGarbageCollected<HTMLDocumentParserState>(sync_policy)),
       scheduler_(sync_policy == kAllowDeferredParsing
                      ? Thread::Current()->Scheduler()
-                     : nullptr),
-      pump_session_nesting_level_(0),
-      end_was_delayed_(false),
-      added_pending_parser_blocking_stylesheet_(false),
-      is_waiting_for_stylesheets_(false) {
+                     : nullptr) {
   // Report metrics for async document parsing or forced synchronous parsing.
   // The document must be main frame to meet UKM requirements, and must have a
   // high resolution clock for high quality data.
@@ -497,6 +511,10 @@
   AttemptToRunDeferredScriptsAndEnd();
 }
 
+bool HTMLDocumentParser::IsPaused() const {
+  return IsWaitingForScripts() || task_runner_state_->WaitingForStylesheets();
+}
+
 bool HTMLDocumentParser::IsParsingFragment() const {
   return tree_builder_->IsParsingFragment();
 }
@@ -614,13 +632,13 @@
   DCHECK(tokenizer_);
   DCHECK(token_);
 
-  NestingLevelIncrementer session(pump_session_nesting_level_);
+  NestingLevelIncrementer session = task_runner_state_->ScopedPumpSession();
 
   // If we're in kForceSynchronousParsing, always run until all available input
   // is consumed.
   bool should_run_until_completion = task_runner_state_->ShouldComplete() ||
                                      task_runner_state_->IsSynchronous() ||
-                                     pump_session_nesting_level_ > 1;
+                                     task_runner_state_->InNestedPumpSession();
   TRACE_EVENT2("blink", "HTMLDocumentParser::PumpTokenizer", "should_complete",
                should_run_until_completion, "parser", (void*)this);
 
@@ -716,7 +734,7 @@
 void HTMLDocumentParser::SchedulePumpTokenizer() {
   TRACE_EVENT0("blink", "HTMLDocumentParser::SchedulePumpTokenizer");
   DCHECK(!IsStopped());
-  DCHECK(!InPumpSession());
+  DCHECK(!task_runner_state_->InPumpSession());
   DCHECK(!task_runner_state_->ShouldComplete());
   if (task_runner_state_->IsScheduled()) {
     // If the parser is already scheduled, there's no need to do anything.
@@ -734,7 +752,7 @@
 void HTMLDocumentParser::ScheduleEndIfDelayed() {
   TRACE_EVENT0("blink", "HTMLDocumentParser::ScheduleEndIfDelayed");
   DCHECK(!IsStopped());
-  DCHECK(!InPumpSession());
+  DCHECK(!task_runner_state_->InPumpSession());
   DCHECK(!task_runner_state_->ShouldComplete());
 
   // Schedule a pump callback if needed.
@@ -863,7 +881,7 @@
   if (preload_scanner_ && preloader_) {
     preload_scanner_->AppendToEnd(source);
     if (task_runner_state_->GetMode() == kAllowDeferredParsing &&
-        (IsPaused() || !task_runner_state_->HaveSeenFirstByte())) {
+        (IsPaused() || !task_runner_state_->SeenFirstByte())) {
       // Should scan and preload if the parser's paused waiting for a resource,
       // or if we're starting a document for the first time (we want to at least
       // prefetch anything that's in the <head> section).
@@ -872,13 +890,13 @@
   }
 
   input_.AppendToEnd(source);
-  task_runner_state_->SetHaveSeenFirstByte();
+  task_runner_state_->MarkSeenFirstByte();
 
   // Add input_source.length() to "file size" metric.
   if (metrics_reporter_)
     metrics_reporter_->AddInput(input_source.length());
 
-  if (InPumpSession()) {
+  if (task_runner_state_->InPumpSession()) {
     // We've gotten data off the network in a nested write. We don't want to
     // consume any more of the input stream now.  Do not worry.  We'll consume
     // this data in a less-nested write().
@@ -911,7 +929,7 @@
     return;
 
   SetIsPreloading(false);
-  if (task_runner_state_->HaveSeenFirstByte() && !IsStopped())
+  if (task_runner_state_->SeenFirstByte() && !IsStopped())
     FinishAppend();
 }
 
@@ -937,8 +955,8 @@
 }
 
 bool HTMLDocumentParser::ShouldDelayEnd() const {
-  return InPumpSession() || IsPaused() || IsExecutingScript() ||
-         task_runner_state_->IsScheduled();
+  return task_runner_state_->InPumpSession() || IsPaused() ||
+         IsExecutingScript() || task_runner_state_->IsScheduled();
 }
 
 void HTMLDocumentParser::AttemptToEnd() {
@@ -952,7 +970,7 @@
   // If there are pending scripts, future control flow should pass to
   // EndIfDelayed.
   if (ShouldDelayEnd()) {
-    end_was_delayed_ = true;
+    task_runner_state_->SetEndWasDelayed(true);
     return;
   }
   PrepareToStopParsing();
@@ -967,10 +985,10 @@
   if (IsDetached())
     return;
 
-  if (!end_was_delayed_ || ShouldDelayEnd())
+  if (!task_runner_state_->EndWasDelayed() || ShouldDelayEnd())
     return;
 
-  end_was_delayed_ = false;
+  task_runner_state_->SetEndWasDelayed(false);
   PrepareToStopParsing();
 }
 
@@ -1059,7 +1077,8 @@
 
   insertion_preload_scanner_.reset();
   if (task_runner_state_->GetMode() == kAllowDeferredParsing &&
-      !task_runner_state_->ShouldComplete() && !InPumpSession()) {
+      !task_runner_state_->ShouldComplete() &&
+      !task_runner_state_->InPumpSession()) {
     SchedulePumpTokenizer();
   } else {
     ShouldCompleteScope should_complete(task_runner_state_);
@@ -1109,8 +1128,8 @@
 
   DCHECK(GetDocument()->IsScriptExecutionReady());
 
-  if (is_waiting_for_stylesheets_)
-    is_waiting_for_stylesheets_ = false;
+  if (task_runner_state_->WaitingForStylesheets())
+    task_runner_state_->SetWaitingForStylesheets(false);
 
   // Document only calls this when the Document owns the DocumentParser so this
   // will not be called in the DocumentFragment case.
@@ -1127,7 +1146,7 @@
   // token so don't actually set the bit to block parsing here, just track
   // the state of the added sheet in case it does persist beyond a single
   // token.
-  added_pending_parser_blocking_stylesheet_ = true;
+  task_runner_state_->SetAddedPendingParserBlockingStylesheet(true);
 }
 
 void HTMLDocumentParser::DidLoadAllPendingParserBlockingStylesheets() {
@@ -1135,13 +1154,13 @@
   // The document will also call into executeScriptsWaitingForResources
   // which is when the parser will re-start, otherwise it will attempt to
   // resume twice which could cause state machine issues.
-  added_pending_parser_blocking_stylesheet_ = false;
+  task_runner_state_->SetAddedPendingParserBlockingStylesheet(false);
 }
 
 void HTMLDocumentParser::CheckIfBlockingStylesheetAdded() {
-  if (added_pending_parser_blocking_stylesheet_) {
-    added_pending_parser_blocking_stylesheet_ = false;
-    is_waiting_for_stylesheets_ = true;
+  if (task_runner_state_->AddedPendingParserBlockingStylesheet()) {
+    task_runner_state_->SetAddedPendingParserBlockingStylesheet(false);
+    task_runner_state_->SetWaitingForStylesheets(true);
   }
 }
 
@@ -1277,7 +1296,7 @@
 std::string HTMLDocumentParser::GetPreloadHistogramSuffix() {
   bool is_main_frame = GetDocument() && GetDocument()->GetFrame() &&
                        GetDocument()->GetFrame()->IsMainFrame();
-  bool have_seen_first_byte = task_runner_state_->HaveSeenFirstByte();
+  bool have_seen_first_byte = task_runner_state_->SeenFirstByte();
   return base::StrCat({is_main_frame ? ".MainFrame" : ".Subframe",
                        have_seen_first_byte ? ".NonInitial" : ".Initial"});
 }
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index 9b4a25a4..e604ffc6 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -135,9 +135,7 @@
   bool HasInsertionPoint() final;
   void PrepareToStopParsing() final;
   void StopParsing() final;
-  bool IsPaused() const {
-    return IsWaitingForScripts() || is_waiting_for_stylesheets_;
-  }
+  bool IsPaused() const;
   bool IsWaitingForScripts() const final;
   bool IsExecutingScript() const final;
   void ExecuteScriptsWaitingForResources() final;
@@ -175,7 +173,6 @@
   void end();
 
   bool IsParsingFragment() const;
-  bool InPumpSession() const { return pump_session_nesting_level_ > 0; }
   // ShouldDelayEnd assesses whether any resources, scripts or nested pumps are
   // delaying the end of parsing.
   bool ShouldDelayEnd() const;
@@ -218,10 +215,6 @@
   std::unique_ptr<base::ElapsedTimer> yield_timer_;
 
   ThreadScheduler* scheduler_;
-  unsigned pump_session_nesting_level_;
-  bool end_was_delayed_;
-  bool added_pending_parser_blocking_stylesheet_;
-  bool is_waiting_for_stylesheets_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index b1a18cd..16b8cb3 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -110,7 +110,7 @@
  private:
   void* data_refs[8];
   void* pointers[1];
-  unsigned bitfields[5];
+  unsigned bitfields[6];
 };
 
 struct SameSizeAsComputedStyle : public SameSizeAsComputedStyleBase,
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index f02ccd8..0e94e0e 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2764,6 +2764,24 @@
 
   bool ForceDark() const { return DarkColorScheme() && ColorSchemeForced(); }
 
+  void SetHasStaticViewportUnits() {
+    SetViewportUnitFlags(ViewportUnitFlags() |
+                         static_cast<unsigned>(ViewportUnitFlag::kStatic));
+  }
+  void SetHasDynamicViewportUnits() {
+    SetViewportUnitFlags(ViewportUnitFlags() |
+                         static_cast<unsigned>(ViewportUnitFlag::kDynamic));
+  }
+  bool HasStaticViewportUnits() const {
+    return ViewportUnitFlags() &
+           static_cast<unsigned>(ViewportUnitFlag::kStatic);
+  }
+  bool HasDynamicViewportUnits() const {
+    return ViewportUnitFlags() &
+           static_cast<unsigned>(ViewportUnitFlag::kDynamic);
+  }
+  bool HasViewportUnits() const { return ViewportUnitFlags(); }
+
  private:
   EClear Clear() const { return ClearInternal(); }
   EFloat Floating() const { return FloatingInternal(); }
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 84feb83..3925a1d 100644
--- a/third_party/blink/renderer/core/style/computed_style_constants.h
+++ b/third_party/blink/renderer/core/style/computed_style_constants.h
@@ -388,6 +388,14 @@
   kAncestorsAffectedByFocusVisibleInHas = 1 << 4,
 };
 
+constexpr size_t kViewportUnitFlagBits = 2;
+enum class ViewportUnitFlag {
+  // v*, sv*, lv*
+  kStatic = 0x1,
+  // dv*
+  kDynamic = 0x2,
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_COMPUTED_STYLE_CONSTANTS_H_
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 f103331..fc01417 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
@@ -204,13 +204,12 @@
       default_value: "false",
     },
     // These are set if we used viewport or rem units when resolving a length.
-    // FIXME: HasViewportUnits should be a monotonic_flag.
     {
-      name: "HasViewportUnits",
+      name: "ViewportUnitFlags",
       field_template: "primitive",
-      default_value: "false",
-      type_name: "bool",
-      custom_compare: true,
+      field_size: 2,
+      default_value: "0",
+      type_name: "unsigned",
     },
     {
       name: "HasRemUnits",
diff --git a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc
index d838396..a0c514d 100644
--- a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc
+++ b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/renderer/platform/mojo/drag_mojom_traits.h"
 
 #include <string>
-
 #include "base/check.h"
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
@@ -15,8 +14,10 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom-blink.h"
 #include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/web_drag_data.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -209,6 +210,13 @@
 }
 
 //  static
+scoped_refptr<blink::BlobDataHandle> StructTraits<
+    blink::mojom::DragItemFileSystemFileDataView,
+    blink::WebDragData::Item>::serialized_blob(const blink::WebDragData::Item&
+                                                   item) {
+  return item.file_system_blob_info.GetBlobHandle();
+}
+
 mojo::PendingRemote<blink::mojom::blink::FileSystemAccessDataTransferToken>
 StructTraits<blink::mojom::DataTransferFileDataView, blink::WebDragData::Item>::
     file_system_access_token(const blink::WebDragData::Item& item) {
@@ -223,17 +231,27 @@
                   blink::WebDragData::Item>::
     Read(blink::mojom::DragItemFileSystemFileDataView data,
          blink::WebDragData::Item* out) {
-  blink::WebDragData::Item item;
   blink::KURL file_system_url;
   WTF::String file_system_id;
+
   if (!data.ReadUrl(&file_system_url) ||
       !data.ReadFileSystemId(&file_system_id))
     return false;
 
+  scoped_refptr<blink::BlobDataHandle> blob_data_handle;
+
+  if (!data.ReadSerializedBlob(&blob_data_handle))
+    return false;
+
+  blink::WebDragData::Item item;
   item.storage_type = blink::WebDragData::Item::kStorageTypeFileSystemFile;
   item.file_system_url = file_system_url;
   item.file_system_file_size = data.size();
   item.file_system_id = file_system_id;
+  if (blob_data_handle) {
+    item.file_system_blob_info =
+        blink::WebBlobInfo(std::move(blob_data_handle));
+  }
   *out = std::move(item);
   return true;
 }
diff --git a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h
index 58153f6..600229d 100644
--- a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h
@@ -19,6 +19,7 @@
 #include "third_party/blink/public/mojom/drag/drag.mojom-shared.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_data_transfer_token.mojom-blink.h"
 #include "third_party/blink/public/platform/web_drag_data.h"
+#include "third_party/blink/renderer/platform/blob/serialized_blob_mojom_traits.h"
 #include "third_party/blink/renderer/platform/mojo/kurl_mojom_traits.h"
 #include "third_party/blink/renderer/platform/mojo/string16_mojom_traits.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
@@ -76,6 +77,8 @@
   static blink::KURL url(const blink::WebDragData::Item& item);
   static int64_t size(const blink::WebDragData::Item& item);
   static WTF::String file_system_id(const blink::WebDragData::Item& item);
+  static scoped_refptr<blink::BlobDataHandle> serialized_blob(
+      const blink::WebDragData::Item& item);
   static bool Read(blink::mojom::DragItemFileSystemFileDataView data,
                    blink::WebDragData::Item* out);
 };
diff --git a/third_party/blink/renderer/platform/peerconnection/metronome_source_test.cc b/third_party/blink/renderer/platform/peerconnection/metronome_source_test.cc
index 8afc565b..ecc365dd 100644
--- a/third_party/blink/renderer/platform/peerconnection/metronome_source_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/metronome_source_test.cc
@@ -4,12 +4,18 @@
 
 #include "third_party/webrtc_overrides/metronome_source.h"
 
+#include "base/bind.h"
+#include "base/callback_forward.h"
 #include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/notreached.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/thread_pool.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/webrtc/api/task_queue/queued_task.h"
+#include "third_party/webrtc/api/task_queue/task_queue_base.h"
 
 namespace blink {
 
@@ -31,6 +37,60 @@
   scoped_refptr<MetronomeSource> metronome_source_;
 };
 
+// Fake task queue for testing WebRtcMetronomeAdapter.
+class FakeTaskQueue : public webrtc::TaskQueueBase {
+ public:
+  explicit FakeTaskQueue(bool retain_tasks)
+      : retain_tasks_(retain_tasks),
+        runner_(base::ThreadPool::CreateSequencedTaskRunner({})) {}
+
+  void Delete() override { NOTREACHED(); }
+
+  void PostDelayedTask(std::unique_ptr<webrtc::QueuedTask> task,
+                       uint32_t milliseconds) override {
+    NOTREACHED();
+  }
+
+  void PostTask(std::unique_ptr<webrtc::QueuedTask> task) override {
+    last_task_ = std::move(task);
+    if (!retain_tasks_)
+      PostLastTask();
+  }
+
+  void PostLastTask() {
+    if (last_task_) {
+      runner_->PostTask(FROM_HERE,
+                        base::BindOnce(
+                            [](FakeTaskQueue* thiz,
+                               std::unique_ptr<webrtc::QueuedTask> task) {
+                              if (!task->Run())
+                                task.release();
+                            },
+                            base::Unretained(this), std::move(last_task_)));
+    }
+  }
+
+ private:
+  const bool retain_tasks_;
+  scoped_refptr<base::SequencedTaskRunner> runner_;
+  std::unique_ptr<webrtc::QueuedTask> last_task_;
+};
+
+class FakeTickListener : public webrtc::Metronome::TickListener {
+ public:
+  explicit FakeTickListener(base::RepeatingCallback<void()> cb,
+                            FakeTaskQueue* queue)
+      : cb_(std::move(cb)), queue_(queue) {}
+
+  void OnTick() override { cb_.Run(); }
+
+  webrtc::TaskQueueBase* OnTickTaskQueue() override { return queue_; }
+
+ private:
+  base::RepeatingCallback<void()> cb_;
+  FakeTaskQueue* queue_;
+};
+
 // Helper class to signal to a bool that it was destroyed. Used to test lifetime
 // of base::RepatingCallback<> functions.
 class DestructorSetter {
@@ -260,4 +320,72 @@
   metronome_source_->RemoveListener(listener_handle);
 }
 
+TEST_F(MetronomeSourceTest, WebRtcMetronomeAdapterTickPeriod) {
+  EXPECT_EQ(kMetronomeTick.InMicroseconds(),
+            metronome_source_->CreateWebRtcMetronome()->TickPeriod().us());
+}
+
+TEST_F(MetronomeSourceTest, WebRtcMetronomeAdapterAddsHandler) {
+  int callback_count = 0;
+  FakeTaskQueue fake_queue(false);
+  auto tick_listener = FakeTickListener(
+      base::BindRepeating([](int* callback_count) { ++(*callback_count); },
+                          base::Unretained(&callback_count)),
+      &fake_queue);
+  auto metronome_adapter = metronome_source_->CreateWebRtcMetronome();
+
+  EXPECT_FALSE(metronome_source_->IsActive());
+  metronome_adapter->AddListener(&tick_listener);
+  EXPECT_TRUE(metronome_source_->IsActive());
+
+  // Next tick should trigger callback.
+  task_environment_.FastForwardBy(kMetronomeTick);
+  EXPECT_EQ(1, callback_count);
+
+  // Each tick should trigger callback.
+  task_environment_.FastForwardBy(kMetronomeTick * 10);
+  EXPECT_EQ(11, callback_count);
+
+  // Removing should not fire callback.
+  metronome_adapter->RemoveListener(&tick_listener);
+  task_environment_.FastForwardBy(kMetronomeTick);
+  EXPECT_EQ(11, callback_count);
+
+  // Removing should inactivate metronome source.
+  EXPECT_FALSE(metronome_source_->IsActive());
+}
+
+TEST_F(MetronomeSourceTest,
+       WebRtcMetronomeAdapterDoesNotExecuteListenerAfterRemoval) {
+  int callback_count = 0;
+
+  FakeTaskQueue fake_queue(true);
+  auto tick_listener = FakeTickListener(
+      base::BindRepeating([](int* callback_count) { ++(*callback_count); },
+                          base::Unretained(&callback_count)),
+      &fake_queue);
+  auto metronome_adapter = metronome_source_->CreateWebRtcMetronome();
+
+  metronome_adapter->AddListener(&tick_listener);
+  task_environment_.FastForwardBy(kMetronomeTick);
+  EXPECT_EQ(0, callback_count);
+  // Now task should have been posted to fake queue. Running it should increase
+  // the callback count.
+  fake_queue.PostLastTask();
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(1, callback_count);
+
+  // Simulate scenario where fake_queue will run it's task after
+  // `RemoveListener` has been called. This occurs when `RemoveListener` is
+  // called after the metronome has ticked but before the task has been run on
+  // the queue.
+  task_environment_.FastForwardBy(kMetronomeTick);
+  EXPECT_EQ(1, callback_count);
+
+  metronome_adapter->RemoveListener(&tick_listener);
+  fake_queue.PostLastTask();
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(1, callback_count);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/peerconnection/metronome_task_queue_factory_test.cc b/third_party/blink/renderer/platform/peerconnection/metronome_task_queue_factory_test.cc
index 528ef3f..b61f295 100644
--- a/third_party/blink/renderer/platform/peerconnection/metronome_task_queue_factory_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/metronome_task_queue_factory_test.cc
@@ -25,20 +25,13 @@
 
 constexpr base::TimeDelta kMetronomeTick = base::Hertz(64);
 
-// Wrapper needed for the TaskQueueTest and MetronomeLikeTaskQueueTest suites.
+// Test-only factory needed for the TaskQueueTest suite.
 class TestMetronomeTaskQueueFactory final : public webrtc::TaskQueueFactory {
  public:
-  static std::unique_ptr<TestMetronomeTaskQueueFactory>
-  CreateWithTestEnvironment() {
-    return std::unique_ptr<TestMetronomeTaskQueueFactory>(
-        new TestMetronomeTaskQueueFactory(
-            std::make_unique<base::test::TaskEnvironment>()));
-  }
-  static std::unique_ptr<TestMetronomeTaskQueueFactory>
-  CreateWithoutTestEnvironment() {
-    return std::unique_ptr<TestMetronomeTaskQueueFactory>(
-        new TestMetronomeTaskQueueFactory(nullptr));
-  }
+  TestMetronomeTaskQueueFactory()
+      : metronome_source_(
+            base::MakeRefCounted<blink::MetronomeSource>(kMetronomeTick)),
+        factory_(CreateWebRtcMetronomeTaskQueueFactory(metronome_source_)) {}
 
   std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter>
   CreateTaskQueue(absl::string_view name, Priority priority) const override {
@@ -46,14 +39,7 @@
   }
 
  private:
-  explicit TestMetronomeTaskQueueFactory(
-      std::unique_ptr<base::test::TaskEnvironment> task_environment)
-      : task_environment_(std::move(task_environment)),
-        metronome_source_(
-            base::MakeRefCounted<blink::MetronomeSource>(kMetronomeTick)),
-        factory_(CreateWebRtcMetronomeTaskQueueFactory(metronome_source_)) {}
-
-  std::unique_ptr<base::test::TaskEnvironment> task_environment_;
+  base::test::TaskEnvironment task_environment_;
   scoped_refptr<blink::MetronomeSource> metronome_source_;
   std::unique_ptr<webrtc::TaskQueueFactory> factory_;
 };
@@ -63,21 +49,35 @@
 INSTANTIATE_TEST_SUITE_P(
     WebRtcMetronomeTaskQueue,
     TaskQueueTest,
-    ::testing::Values([]() {
-      return TestMetronomeTaskQueueFactory::CreateWithTestEnvironment();
-    }));
+    ::testing::Values(std::make_unique<TestMetronomeTaskQueueFactory>));
+
+// Provider needed for the MetronomeLikeTaskQueueTest suite.
+class MetronomeTaskQueueProvider : public MetronomeLikeTaskQueueProvider {
+ public:
+  void Initialize() override {
+    scoped_refptr<blink::MetronomeSource> metronome_source =
+        base::MakeRefCounted<blink::MetronomeSource>(kMetronomeTick);
+    task_queue_ =
+        CreateWebRtcMetronomeTaskQueueFactory(metronome_source)
+            ->CreateTaskQueue("MetronomeTestTaskQueue",
+                              webrtc::TaskQueueFactory::Priority::NORMAL);
+  }
+
+  base::TimeDelta MetronomeTick() const override { return kMetronomeTick; }
+  webrtc::TaskQueueBase* TaskQueue() const override {
+    return task_queue_.get();
+  }
+
+ private:
+  std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter> task_queue_;
+};
 
 // Instantiate suite to run all tests defined in
 // third_party/webrtc_overrides/test/metronome_like_task_queue_test.h
-INSTANTIATE_TEST_SUITE_P(WebRtcMetronomeTaskQueue,
-                         MetronomeLikeTaskQueueTest,
-                         ::testing::Values(MetronomeLikeTaskQueueTestParams{
-                             .task_queue_factory =
-                                 []() {
-                                   return TestMetronomeTaskQueueFactory::
-                                       CreateWithoutTestEnvironment();
-                                 },
-                             .metronome_tick = kMetronomeTick}));
+INSTANTIATE_TEST_SUITE_P(
+    WebRtcMetronomeTaskQueue,
+    MetronomeLikeTaskQueueTest,
+    ::testing::Values(std::make_unique<MetronomeTaskQueueProvider>));
 
 }  // namespace
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c54e2bb..0273e9d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1914,7 +1914,6 @@
 
 
 # @container
-crbug.com/1289850 external/wpt/css/css-contain/container-queries/canvas-as-container-002.html [ Failure ]
 crbug.com/1289850 external/wpt/css/css-contain/container-queries/canvas-as-container-004.html [ Failure ]
 crbug.com/1289850 external/wpt/css/css-contain/container-queries/canvas-as-container-006.html [ Failure ]
 crbug.com/1273913 external/wpt/css/css-contain/container-queries/pseudo-elements-002.tentative.html [ Failure ]
@@ -3203,7 +3202,7 @@
 crbug.com/626703 [ Mac10.15 ] external/wpt/css/filter-effects/filter-function/filter-function-007.html [ Failure ]
 crbug.com/626703 [ Mac11-arm64 ] external/wpt/css/filter-effects/filter-function/filter-function-007.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/filter-effects/filter-function/filter-function-007.html [ Failure ]
-crbug.com/626703 [ Mac11 ] external/wpt/css/filter-effects/filter-function/filter-function-007.html [ Timeout Failure ]
+crbug.com/626703 [ Mac11 ] external/wpt/css/filter-effects/filter-function/filter-function-007.html [ Failure Timeout ]
 crbug.com/626703 virtual/scalefactor200/external/wpt/css/filter-effects/filter-function/filter-function-001.html [ Failure ]
 crbug.com/626703 virtual/scalefactor200/external/wpt/css/filter-effects/filter-function/filter-function-002.html [ Failure ]
 crbug.com/626703 virtual/scalefactor200/external/wpt/css/filter-effects/filter-function/filter-function-003.html [ Failure ]
@@ -7612,3 +7611,6 @@
 crbug.com/1293965 virtual/scroll-unification/fast/forms/suggestion-picker/datetimelocal-suggestion-picker-appearance-with-scroll-bar.html [ Skip ]
 crbug.com/1293965 [ Mac ] fast/forms/month/month-picker-appearance-step.html [ Skip ]
 crbug.com/1293965 [ Mac ] fast/forms/color-scheme/week-picker/week-picker-appearance-highlight-es.html [ Skip ]
+
+# Sheriff 2022-02-07
+crbug.com/1289759 [ Linux ] http/tests/loading/slow-parsing-subframe.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index e1e2d6a..34cc282 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 7f8942c429a66fa5225d2b9cebcf72d09afd9b14
+Version: 1c1e48b75ebf98fd3db4af8a330fcb6109942ce5
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index c1d1a7b0..7d67b45 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -324536,10 +324536,6 @@
        []
       ]
      },
-     "shared-worker-import-meta-expected.txt": [
-      "229c339fe84726d41b1e1ee9b92d1a8428cef90a",
-      []
-     ],
      "shared-worker-options-credentials.html.headers": [
       "8da851ab7363870014a32121a3cd87979aa2099d",
       []
@@ -549938,7 +549934,7 @@
       ]
      ],
      "shared-worker-import-meta.html": [
-      "44cd9df9ee7097abfee68b01a8868249bee63f3b",
+      "e8e9b51f6a50bf2f362cb3d66d2940d402474838",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/canvas-as-container-crash.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/canvas-as-container-crash.html
new file mode 100644
index 0000000..8d82560
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/canvas-as-container-crash.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>CSS Container Queries Test: Absolute positioned canvas container crash</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1289850">
+<p>Pass if there is no crash.</p>
+<canvas id="canv" style="display:block;position:absolute;container:inline-size"></canvas>
+<script>
+  canv.offsetTop;
+  canv.appendChild(document.createElement("span"));
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/math-block-container-child-crash.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/math-block-container-child-crash.html
new file mode 100644
index 0000000..00b68366
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/math-block-container-child-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>CSS Container Queries Test: Math block container child crash</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1294268">
+<p>Pass if there is no crash.</p>
+<math id="m" style="display:block math"></math>
+<script>
+  let div = document.createElement("div");
+  div.style.containerType = "size";
+  m.appendChild(div);
+  div.appendChild(document.createElement("span"));
+  document.body.offsetTop;
+  div.style.color = "green";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta.html b/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta.html
index 44cd9df9e..e8e9b51 100644
--- a/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta.html
+++ b/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta.html
@@ -43,7 +43,7 @@
         worker.port.postMessage('./' + script_url + '#1');
         return new Promise(resolve => worker.port.onmessage = resolve);
       })
-      .then(msg_event => assert_true(msg_event.data.endsWith(script_url)));
+      .then(msg_event => assert_true(msg_event.data.endsWith(script_url + "#1")));
 }, 'Test import.meta.url on the imported module script with a fragment.');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
similarity index 63%
rename from third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
rename to third_party/blink/web_tests/flag-specific/highdpi/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
index 229c339..9706a58f 100644
--- a/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS Test import.meta.url on the top-level module script.
 PASS Test import.meta.url on the imported module script.
-FAIL Test import.meta.url on the imported module script with a fragment. assert_true: expected true got false
+PASS Test import.meta.url on the imported module script with a fragment.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/virtual/plz-dedicated-worker/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
similarity index 63%
copy from third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
copy to third_party/blink/web_tests/flag-specific/highdpi/virtual/plz-dedicated-worker/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
index 229c339..9706a58f 100644
--- a/third_party/blink/web_tests/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/highdpi/virtual/plz-dedicated-worker/external/wpt/workers/modules/shared-worker-import-meta-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS Test import.meta.url on the top-level module script.
 PASS Test import.meta.url on the imported module script.
-FAIL Test import.meta.url on the imported module script with a fragment. assert_true: expected true got false
+PASS Test import.meta.url on the imported module script with a fragment.
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win7/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-window-move-expected.txt b/third_party/blink/web_tests/platform/win7/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-window-move-expected.txt
deleted file mode 100644
index 8d85af6..0000000
--- a/third_party/blink/web_tests/platform/win7/virtual/prerender/external/wpt/speculation-rules/prerender/restriction-window-move-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL a prerendering page cannot move its window by executing moveTo. assert_equals: expected "PASS" but got "FAIL: Error: assert_equals: y position for primary expected 1 but got 0"
-PASS a prerendering page cannot move its window by executing moveBy.
-Harness: the test ran to completion.
-
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn
index e0b4440..073efc50 100644
--- a/third_party/webrtc_overrides/BUILD.gn
+++ b/third_party/webrtc_overrides/BUILD.gn
@@ -48,6 +48,7 @@
   "//third_party/webrtc/api/audio_codecs/opus:audio_decoder_opus",
   "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_multiopus",
   "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_opus",
+  "//third_party/webrtc/api/metronome",
   "//third_party/webrtc/api/rtc_event_log:rtc_event_log_factory",
   "//third_party/webrtc/api/task_queue:task_queue",
   "//third_party/webrtc/api/transport:enums",
@@ -114,7 +115,6 @@
   webrtc_public_deps += [
     "//third_party/webrtc/api:network_state_predictor_api",
     "//third_party/webrtc/api/audio:audio_frame_api",
-    "//third_party/webrtc/api/task_queue",
     "//third_party/webrtc/api/transport:goog_cc",
     "//third_party/webrtc/api/transport:network_control",
     "//third_party/webrtc/api/units:time_delta",
@@ -216,8 +216,10 @@
   configs += [ "//third_party/webrtc:library_impl_config" ]
   deps = [
     "//base",
+    "//third_party/webrtc/api/metronome",
     "//third_party/webrtc/api/task_queue",
     "//third_party/webrtc/rtc_base/system:rtc_export",
+    "//third_party/webrtc/rtc_base/task_utils:to_queued_task",
   ]
 }
 
diff --git a/third_party/webrtc_overrides/metronome_source.cc b/third_party/webrtc_overrides/metronome_source.cc
index fc4f6f1..ad67f53 100644
--- a/third_party/webrtc_overrides/metronome_source.cc
+++ b/third_party/webrtc_overrides/metronome_source.cc
@@ -3,14 +3,135 @@
 // found in the LICENSE file.
 
 #include "third_party/webrtc_overrides/metronome_source.h"
+#include <memory>
+#include <utility>
 
+#include "base/bind.h"
+#include "base/containers/flat_set.h"
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/lock.h"
+#include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
+#include "base/thread_annotations.h"
+#include "base/time/time.h"
 #include "base/trace_event/typed_macros.h"
+#include "third_party/webrtc/api/metronome/metronome.h"
+#include "third_party/webrtc/rtc_base/task_utils/to_queued_task.h"
 #include "third_party/webrtc_overrides/task_queue_factory.h"
 
 namespace blink {
 
+namespace {
+
+// Wraps a webrtc::Metronome::TickListener to ensure that OnTick is not called
+// after it is removed from the WebRtcMetronomeAdapter, using the Deactive
+// method.
+class WebRtcMetronomeListenerWrapper
+    : public base::RefCountedThreadSafe<WebRtcMetronomeListenerWrapper> {
+ public:
+  explicit WebRtcMetronomeListenerWrapper(
+      webrtc::Metronome::TickListener* listener)
+      : listener_(listener) {}
+
+  void Deactivate() {
+    base::AutoLock auto_lock(lock_);
+    active_ = false;
+  }
+
+  webrtc::Metronome::TickListener* listener() { return listener_; }
+
+  void OnTick() {
+    base::AutoLock auto_lock(lock_);
+    if (!active_)
+      return;
+    listener_->OnTick();
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<WebRtcMetronomeListenerWrapper>;
+  ~WebRtcMetronomeListenerWrapper() = default;
+
+  webrtc::Metronome::TickListener* const listener_;
+
+  base::Lock lock_;
+  bool active_ GUARDED_BY(lock_) = true;
+};
+
+class WebRtcMetronomeAdapter : public webrtc::Metronome {
+ public:
+  explicit WebRtcMetronomeAdapter(
+      scoped_refptr<MetronomeSource> metronome_source)
+      : metronome_source_(std::move(metronome_source)) {
+    DCHECK(metronome_source_);
+  }
+
+  ~WebRtcMetronomeAdapter() override { DCHECK(listeners_.empty()); }
+
+  // Adds a tick listener to the metronome. Once this method has returned
+  // OnTick will be invoked on each metronome tick. A listener may
+  // only be added to the metronome once.
+  void AddListener(TickListener* listener) override {
+    DCHECK(listener);
+    base::AutoLock auto_lock(lock_);
+    auto [it, inserted] = listeners_.emplace(
+        listener,
+        base::MakeRefCounted<WebRtcMetronomeListenerWrapper>(listener));
+    DCHECK(inserted);
+    if (listeners_.size() == 1) {
+      DCHECK(!tick_handle_);
+      tick_handle_ = metronome_source_->AddListener(
+          nullptr,
+          base::BindRepeating(&WebRtcMetronomeAdapter::OnTick,
+                              base::Unretained(this)),
+          base::TimeTicks::Min());
+    }
+  }
+
+  // Removes the tick listener from the metronome. Once this method has returned
+  // OnTick will never be called again. This method must not be called from
+  // within OnTick.
+  void RemoveListener(TickListener* listener) override {
+    DCHECK(listener);
+    base::AutoLock auto_lock(lock_);
+    auto it = listeners_.find(listener);
+    if (it == listeners_.end()) {
+      DLOG(WARNING) << __FUNCTION__ << " called with unregistered listener.";
+      return;
+    }
+    it->second->Deactivate();
+    listeners_.erase(it);
+    if (listeners_.size() == 0) {
+      metronome_source_->RemoveListener(std::move(tick_handle_));
+    }
+  }
+
+  // Returns the current tick period of the metronome.
+  webrtc::TimeDelta TickPeriod() const override {
+    return webrtc::TimeDelta::Micros(
+        metronome_source_->metronome_tick().InMicroseconds());
+  }
+
+ private:
+  void OnTick() {
+    base::AutoLock auto_lock(lock_);
+    for (auto [listener, wrapper] : listeners_) {
+      listener->OnTickTaskQueue()->PostTask(webrtc::ToQueuedTask(
+          [wrapper = std::move(wrapper)] { wrapper->OnTick(); }));
+    }
+  }
+
+  const scoped_refptr<MetronomeSource> metronome_source_;
+  base::Lock lock_;
+  base::flat_map<TickListener*, scoped_refptr<WebRtcMetronomeListenerWrapper>>
+      listeners_ GUARDED_BY(lock_);
+  scoped_refptr<MetronomeSource::ListenerHandle> tick_handle_ GUARDED_BY(lock_);
+};
+
+}  // namespace
+
 MetronomeSource::ListenerHandle::ListenerHandle(
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     base::RepeatingCallback<void()> callback,
@@ -44,15 +165,19 @@
       wakeup_time_ = base::TimeTicks::Max();
     }
   }
-  task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &MetronomeSource::ListenerHandle::MaybeRunCallbackOnTaskRunner,
-          this));
+  if (task_runner_ == nullptr) {
+    // Run the task directly if task_runner_ is null.
+    MaybeRunCallback();
+  } else {
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&MetronomeSource::ListenerHandle::MaybeRunCallback,
+                       this));
+  }
 }
 
-void MetronomeSource::ListenerHandle::MaybeRunCallbackOnTaskRunner() {
-  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+void MetronomeSource::ListenerHandle::MaybeRunCallback() {
+  DCHECK(task_runner_ == nullptr || task_runner_->RunsTasksInCurrentSequence());
   base::AutoLock auto_lock(is_active_lock_);
   if (!is_active_)
     return;
@@ -163,4 +288,8 @@
   }
 }
 
+std::unique_ptr<webrtc::Metronome> MetronomeSource::CreateWebRtcMetronome() {
+  return std::make_unique<WebRtcMetronomeAdapter>(base::WrapRefCounted(this));
+}
+
 }  // namespace blink
diff --git a/third_party/webrtc_overrides/metronome_source.h b/third_party/webrtc_overrides/metronome_source.h
index 7f0efd7..057a916 100644
--- a/third_party/webrtc_overrides/metronome_source.h
+++ b/third_party/webrtc_overrides/metronome_source.h
@@ -16,6 +16,7 @@
 #include "base/thread_annotations.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "third_party/webrtc/api/metronome/metronome.h"
 #include "third_party/webrtc/rtc_base/system/rtc_export.h"
 
 namespace blink {
@@ -60,7 +61,7 @@
     ~ListenerHandle();
 
     void OnMetronomeTick();
-    void MaybeRunCallbackOnTaskRunner();
+    void MaybeRunCallback();
 
     void Inactivate();
 
@@ -96,6 +97,11 @@
   // until it has been deactivated by removing all listeners.
   bool IsActive();
 
+  base::TimeDelta metronome_tick() const { return metronome_tick_; }
+
+  // Creates a webrtc::Metronome which is backed by this metronome.
+  std::unique_ptr<webrtc::Metronome> CreateWebRtcMetronome();
+
  private:
   friend class base::RefCountedThreadSafe<MetronomeSource>;
 
diff --git a/third_party/webrtc_overrides/test/metronome_like_task_queue_test.cc b/third_party/webrtc_overrides/test/metronome_like_task_queue_test.cc
index ddbec8e..325e80d6 100644
--- a/third_party/webrtc_overrides/test/metronome_like_task_queue_test.cc
+++ b/third_party/webrtc_overrides/test/metronome_like_task_queue_test.cc
@@ -10,111 +10,99 @@
 #include "base/time/time.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/webrtc/api/task_queue/queued_task.h"
 #include "third_party/webrtc/api/task_queue/task_queue_factory.h"
+#include "third_party/webrtc/rtc_base/task_utils/to_queued_task.h"
 
 namespace blink {
 
 using ::testing::ElementsAre;
+using ::testing::MockFunction;
+
+namespace {
+
+std::unique_ptr<webrtc::QueuedTask> MockFunctionAsQueuedTask(
+    MockFunction<void()>& mock_function) {
+  return webrtc::ToQueuedTask(mock_function.AsStdFunction());
+}
+
+class MockCallback {
+ public:
+  MockCallback() {
+    EXPECT_CALL(callback_, Call()).WillRepeatedly([&]() { ++callback_count_; });
+  }
+
+  size_t callback_count() const { return callback_count_; }
+  bool was_called() const { return callback_count_ > 0; }
+
+  std::unique_ptr<webrtc::QueuedTask> ToQueuedTask() {
+    return MockFunctionAsQueuedTask(callback_);
+  }
+
+ private:
+  MockFunction<void()> callback_;
+  size_t callback_count_ = 0;
+};
+
+}  // namespace
 
 TEST_P(MetronomeLikeTaskQueueTest, PostTaskRunsPriorToTick) {
-  auto task_queue = task_queue_factory_->CreateTaskQueue(
-      "MetronomeTestQueue", webrtc::TaskQueueFactory::Priority::NORMAL);
+  auto* task_queue = provider_->TaskQueue();
 
-  bool did_run = false;
-  task_queue->PostTask(webrtc::ToQueuedTask([&did_run]() { did_run = true; }));
+  MockCallback callback;
+  task_queue->PostTask(callback.ToQueuedTask());
 
-  EXPECT_FALSE(did_run);
+  EXPECT_FALSE(callback.was_called());
   task_environment_.FastForwardBy(base::Nanoseconds(1));
-  EXPECT_TRUE(did_run);
+  EXPECT_TRUE(callback.was_called());
 }
 
 TEST_P(MetronomeLikeTaskQueueTest, NormalPriorityDelayedTasksRunOnTicks) {
-  auto task_queue = task_queue_factory_->CreateTaskQueue(
-      "MetronomeTestQueue", webrtc::TaskQueueFactory::Priority::NORMAL);
-
-  int task_run_counter = 0;
+  auto* task_queue = provider_->TaskQueue();
 
   // Delay task until next tick.
-  task_queue->PostDelayedTask(
-      webrtc::ToQueuedTask([&task_run_counter]() { ++task_run_counter; }),
-      metronome_tick_.InMilliseconds());
-  EXPECT_EQ(task_run_counter, 0);
-  task_environment_.FastForwardBy(metronome_tick_);
-  EXPECT_EQ(task_run_counter, 1);
+  MockCallback callback;
+  task_queue->PostDelayedTask(callback.ToQueuedTask(),
+                              provider_->MetronomeTick().InMilliseconds());
+  EXPECT_EQ(callback.callback_count(), 0u);
+  task_environment_.FastForwardBy(provider_->MetronomeTick());
+  EXPECT_EQ(callback.callback_count(), 1u);
 
   // Delay half a tick. A full tick must pass before it runs.
   task_queue->PostDelayedTask(
-      webrtc::ToQueuedTask([&task_run_counter]() { ++task_run_counter; }),
-      (metronome_tick_ / 2).InMilliseconds());
-  task_environment_.FastForwardBy(metronome_tick_ - base::Milliseconds(1));
-  EXPECT_EQ(task_run_counter, 1);
+      callback.ToQueuedTask(),
+      (provider_->MetronomeTick() / 2).InMilliseconds());
+  task_environment_.FastForwardBy(provider_->MetronomeTick() -
+                                  base::Milliseconds(1));
+  EXPECT_EQ(callback.callback_count(), 1u);
   task_environment_.FastForwardBy(base::Milliseconds(1));
-  EXPECT_EQ(task_run_counter, 2);
+  EXPECT_EQ(callback.callback_count(), 2u);
 
   // Delay several ticks.
   task_queue->PostDelayedTask(
-      webrtc::ToQueuedTask([&task_run_counter]() { ++task_run_counter; }),
-      (metronome_tick_ * 3).InMilliseconds());
-  task_environment_.FastForwardBy(metronome_tick_ * 2);
-  EXPECT_EQ(task_run_counter, 2);
-  task_environment_.FastForwardBy(metronome_tick_);
-  EXPECT_EQ(task_run_counter, 3);
+      callback.ToQueuedTask(),
+      (provider_->MetronomeTick() * 3).InMilliseconds());
+  task_environment_.FastForwardBy(provider_->MetronomeTick() * 2);
+  EXPECT_EQ(callback.callback_count(), 2u);
+  task_environment_.FastForwardBy(provider_->MetronomeTick());
+  EXPECT_EQ(callback.callback_count(), 3u);
 }
 
 TEST_P(MetronomeLikeTaskQueueTest,
        NormalPriorityHighPrecisionDelayedTasksRunOutsideTicks) {
-  auto task_queue = task_queue_factory_->CreateTaskQueue(
-      "MetronomeTestQueue", webrtc::TaskQueueFactory::Priority::NORMAL);
+  auto* task_queue = provider_->TaskQueue();
 
-  bool did_run = false;
-  task_queue->PostDelayedHighPrecisionTask(
-      webrtc::ToQueuedTask([&did_run]() { did_run = true; }),
-      /*milliseconds=*/1);
+  MockCallback callback;
+  task_queue->PostDelayedHighPrecisionTask(callback.ToQueuedTask(),
+                                           /*milliseconds=*/1);
 
-  EXPECT_FALSE(did_run);
+  EXPECT_FALSE(callback.was_called());
   task_environment_.FastForwardBy(base::Milliseconds(1));
-  EXPECT_TRUE(did_run);
-}
-
-TEST_P(MetronomeLikeTaskQueueTest, CanDeleteTaskQueueWhileTasksAreInFlight) {
-  auto task_queue = task_queue_factory_->CreateTaskQueue(
-      "MetronomeTestQueue", webrtc::TaskQueueFactory::Priority::NORMAL);
-
-  bool did_run_post_task = false;
-  task_queue->PostTask(webrtc::ToQueuedTask(
-      [&did_run_post_task]() { did_run_post_task = true; }));
-  bool did_run_low_precision_delay_task = false;
-  task_queue->PostDelayedTask(
-      webrtc::ToQueuedTask([&did_run_low_precision_delay_task]() {
-        did_run_low_precision_delay_task = true;
-      }),
-      /*milliseconds=*/1);
-  bool did_run_high_precision_delay_task = false;
-  task_queue->PostDelayedHighPrecisionTask(
-      webrtc::ToQueuedTask([&did_run_high_precision_delay_task]() {
-        did_run_high_precision_delay_task = true;
-      }),
-      /*milliseconds=*/1);
-
-  EXPECT_FALSE(did_run_post_task);
-  EXPECT_FALSE(did_run_low_precision_delay_task);
-  EXPECT_FALSE(did_run_high_precision_delay_task);
-  task_queue.reset();
-
-  // The PostTask is executed, but not the delayed tasks.
-  EXPECT_TRUE(did_run_post_task);
-  EXPECT_FALSE(did_run_low_precision_delay_task);
-  EXPECT_FALSE(did_run_high_precision_delay_task);
-
-  // The delayed tasks never run because they have been cancelled.
-  task_environment_.FastForwardBy(metronome_tick_);
-  EXPECT_FALSE(did_run_low_precision_delay_task);
-  EXPECT_FALSE(did_run_high_precision_delay_task);
+  EXPECT_TRUE(callback.was_called());
 }
 
 TEST_P(MetronomeLikeTaskQueueTest, DelayedTasksRunInOrder) {
-  auto task_queue = task_queue_factory_->CreateTaskQueue(
-      "MetronomeTestQueue", webrtc::TaskQueueFactory::Priority::NORMAL);
+  auto* task_queue = provider_->TaskQueue();
 
   constexpr uint32_t kTime0Ms = 0;
   constexpr uint32_t kTime1Ms = 1;
@@ -144,7 +132,7 @@
                                 run_tasks.emplace_back("Time1_Third");
                               }),
                               kTime1Ms);
-  task_environment_.FastForwardBy(metronome_tick_);
+  task_environment_.FastForwardBy(provider_->MetronomeTick());
 
   EXPECT_THAT(run_tasks,
               ElementsAre("Time0_First", "Time0_Second", "Time0_Third",
@@ -152,27 +140,27 @@
 }
 
 TEST_P(MetronomeLikeTaskQueueTest, DelayedTaskCanPostDelayedTask) {
-  auto task_queue = task_queue_factory_->CreateTaskQueue(
-      "MetronomeTestQueue", webrtc::TaskQueueFactory::Priority::NORMAL);
+  auto* task_queue = provider_->TaskQueue();
 
   bool task0_ran = false;
   bool task1_ran = false;
 
   task_queue->PostDelayedTask(
       webrtc::ToQueuedTask(
-          [tick = metronome_tick_, &task_queue, &task0_ran, &task1_ran]() {
+          [tick = provider_->MetronomeTick(), &task_queue, &task0_ran,
+           &task1_ran]() {
             task0_ran = true;
             // Inception!
             task_queue->PostDelayedTask(
                 webrtc::ToQueuedTask([&task1_ran]() { task1_ran = true; }),
                 tick.InMilliseconds());
           }),
-      metronome_tick_.InMilliseconds());
+      provider_->MetronomeTick().InMilliseconds());
 
-  task_environment_.FastForwardBy(metronome_tick_);
+  task_environment_.FastForwardBy(provider_->MetronomeTick());
   EXPECT_TRUE(task0_ran);
   EXPECT_FALSE(task1_ran);
-  task_environment_.FastForwardBy(metronome_tick_);
+  task_environment_.FastForwardBy(provider_->MetronomeTick());
   EXPECT_TRUE(task0_ran);
   EXPECT_TRUE(task1_ran);
 }
diff --git a/third_party/webrtc_overrides/test/metronome_like_task_queue_test.h b/third_party/webrtc_overrides/test/metronome_like_task_queue_test.h
index f844cb8..e9f15e5 100644
--- a/third_party/webrtc_overrides/test/metronome_like_task_queue_test.h
+++ b/third_party/webrtc_overrides/test/metronome_like_task_queue_test.h
@@ -17,25 +17,31 @@
 
 namespace blink {
 
-struct RTC_EXPORT MetronomeLikeTaskQueueTestParams {
-  std::function<std::unique_ptr<webrtc::TaskQueueFactory>()> task_queue_factory;
-  base::TimeDelta metronome_tick;
+class RTC_EXPORT MetronomeLikeTaskQueueProvider {
+ public:
+  virtual ~MetronomeLikeTaskQueueProvider() = default;
+
+  virtual void Initialize() = 0;
+  virtual base::TimeDelta MetronomeTick() const = 0;
+  virtual webrtc::TaskQueueBase* TaskQueue() const = 0;
 };
 
 class RTC_EXPORT MetronomeLikeTaskQueueTest
-    : public ::testing::TestWithParam<MetronomeLikeTaskQueueTestParams> {
+    : public ::testing::TestWithParam<
+          std::function<std::unique_ptr<MetronomeLikeTaskQueueProvider>()>> {
  public:
   MetronomeLikeTaskQueueTest()
       : task_environment_(
             base::test::TaskEnvironment::ThreadingMode::MULTIPLE_THREADS,
             base::test::TaskEnvironment::TimeSource::MOCK_TIME),
-        task_queue_factory_(GetParam().task_queue_factory()),
-        metronome_tick_(GetParam().metronome_tick) {}
+        provider_(GetParam()()) {}
+
+  void SetUp() override { provider_->Initialize(); }
+  void TearDown() override { provider_.reset(); }
 
  protected:
   base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<webrtc::TaskQueueFactory> task_queue_factory_;
-  const base::TimeDelta metronome_tick_;
+  std::unique_ptr<MetronomeLikeTaskQueueProvider> provider_;
 };
 
 }  // namespace blink
diff --git a/third_party/zlib/google/BUILD.gn b/third_party/zlib/google/BUILD.gn
index c29e892..1d5c74f 100644
--- a/third_party/zlib/google/BUILD.gn
+++ b/third_party/zlib/google/BUILD.gn
@@ -18,6 +18,7 @@
     ]
     deps = [
       "//base",
+      "//base:i18n",
       "//third_party/zlib:minizip",
     ]
   }
diff --git a/third_party/zlib/google/test/data/SJIS Bug 846195.zip b/third_party/zlib/google/test/data/SJIS Bug 846195.zip
new file mode 100644
index 0000000..0783002
--- /dev/null
+++ b/third_party/zlib/google/test/data/SJIS Bug 846195.zip
Binary files differ
diff --git a/third_party/zlib/google/zip_reader.cc b/third_party/zlib/google/zip_reader.cc
index f546a90d..9209b6b 100644
--- a/third_party/zlib/google/zip_reader.cc
+++ b/third_party/zlib/google/zip_reader.cc
@@ -8,7 +8,9 @@
 
 #include "base/bind.h"
 #include "base/files/file.h"
+#include "base/i18n/icu_string_conversions.h"
 #include "base/logging.h"
+#include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -65,12 +67,9 @@
 
 StringWriterDelegate::StringWriterDelegate(size_t max_read_bytes,
                                            std::string* output)
-    : max_read_bytes_(max_read_bytes),
-      output_(output) {
-}
+    : max_read_bytes_(max_read_bytes), output_(output) {}
 
-StringWriterDelegate::~StringWriterDelegate() {
-}
+StringWriterDelegate::~StringWriterDelegate() {}
 
 bool StringWriterDelegate::PrepareOutput() {
   return true;
@@ -116,38 +115,23 @@
 
 }  // namespace
 
-// TODO(satorux): The implementation assumes that file names in zip files
-// are encoded in UTF-8. This is true for zip files created by Zip()
-// function in zip.h, but not true for user-supplied random zip files.
-ZipReader::EntryInfo::EntryInfo(const std::string& file_name_in_zip,
+ZipReader::EntryInfo::EntryInfo(base::FilePath file_path,
+                                std::string file_path_in_original_encoding,
                                 const unz_file_info& raw_file_info)
-    : file_path_(base::FilePath::FromUTF8Unsafe(file_name_in_zip)),
-      is_directory_(false),
-      is_unsafe_(false),
-      is_encrypted_(false),
+    : file_path_(std::move(file_path)),
+      file_path_in_original_encoding_(
+          std::move(file_path_in_original_encoding)),
       posix_mode_(0) {
   original_size_ = raw_file_info.uncompressed_size;
 
   // Directory entries in zip files end with "/".
-  is_directory_ = base::EndsWith(file_name_in_zip, "/",
-                                 base::CompareCase::INSENSITIVE_ASCII);
+  is_directory_ = base::EndsWith(file_path_in_original_encoding_, "/");
 
   // Check the file name here for directory traversal issues.
-  is_unsafe_ = file_path_.ReferencesParent();
-
-  // We also consider that the file name is unsafe, if it's invalid UTF-8.
-  std::u16string file_name_utf16;
-  if (!base::UTF8ToUTF16(file_name_in_zip.data(), file_name_in_zip.size(),
-                         &file_name_utf16)) {
-    is_unsafe_ = true;
-  }
-
   // We also consider that the file name is unsafe, if it's absolute.
   // On Windows, IsAbsolute() returns false for paths starting with "/".
-  if (file_path_.IsAbsolute() ||
-      base::StartsWith(file_name_in_zip, "/",
-                       base::CompareCase::INSENSITIVE_ASCII))
-    is_unsafe_ = true;
+  is_unsafe_ = file_path_.ReferencesParent() || file_path_.IsAbsolute() ||
+               base::StartsWith(file_path_in_original_encoding_, "/");
 
   // Whether the file is encrypted is bit 0 of the flag.
   is_encrypted_ = raw_file_info.flag & 1;
@@ -256,21 +240,30 @@
   DCHECK(zip_file_);
 
   unz_file_info raw_file_info = {};
-  char raw_file_name_in_zip[internal::kZipMaxPath] = {};
-  const int result = unzGetCurrentFileInfo(zip_file_,
-                                           &raw_file_info,
-                                           raw_file_name_in_zip,
-                                           sizeof(raw_file_name_in_zip) - 1,
-                                           NULL,  // extraField.
-                                           0,  // extraFieldBufferSize.
-                                           NULL,  // szComment.
-                                           0);  // commentBufferSize.
+  char file_path_in_zip[internal::kZipMaxPath] = {};
+  const int result = unzGetCurrentFileInfo(
+      zip_file_, &raw_file_info, file_path_in_zip, sizeof(file_path_in_zip) - 1,
+      nullptr,  // extraField.
+      0,        // extraFieldBufferSize.
+      nullptr,  // szComment.
+      0);       // commentBufferSize.
   if (result != UNZ_OK)
     return false;
-  if (raw_file_name_in_zip[0] == '\0')
+
+  // Convert file path from original encoding to Unicode.
+  const base::StringPiece file_path_in_original_encoding(file_path_in_zip);
+  std::u16string file_path_in_utf16;
+  const char* const encoding = encoding_.empty() ? "UTF-8" : encoding_.c_str();
+  if (!base::CodepageToUTF16(file_path_in_original_encoding, encoding,
+                             base::OnStringConversionError::SUBSTITUTE,
+                             &file_path_in_utf16)) {
+    LOG(ERROR) << "Cannot convert file path from encoding " << encoding;
     return false;
-  current_entry_info_.reset(
-      new EntryInfo(raw_file_name_in_zip, raw_file_info));
+  }
+
+  current_entry_info_.reset(new EntryInfo(
+      base::FilePath::FromUTF16Unsafe(file_path_in_utf16),
+      std::string(file_path_in_original_encoding), raw_file_info));
   return true;
 }
 
@@ -440,7 +433,7 @@
 }
 
 void ZipReader::Reset() {
-  zip_file_ = NULL;
+  zip_file_ = nullptr;
   num_entries_ = 0;
   reached_end_ = false;
   current_entry_info_.reset();
@@ -453,9 +446,8 @@
                              const int64_t offset) {
   char buffer[internal::kZipBufSize];
 
-  const int num_bytes_read = unzReadCurrentFile(zip_file_,
-                                                buffer,
-                                                internal::kZipBufSize);
+  const int num_bytes_read =
+      unzReadCurrentFile(zip_file_, buffer, internal::kZipBufSize);
 
   if (num_bytes_read == 0) {
     unzCloseCurrentFile(zip_file_);
diff --git a/third_party/zlib/google/zip_reader.h b/third_party/zlib/google/zip_reader.h
index c2d3bea8..2199357 100644
--- a/third_party/zlib/google/zip_reader.h
+++ b/third_party/zlib/google/zip_reader.h
@@ -80,7 +80,8 @@
   // a zip file.
   class EntryInfo {
    public:
-    EntryInfo(const std::string& filename_in_zip,
+    EntryInfo(base::FilePath file_path,
+              std::string file_path_in_original_encoding,
               const unz_file_info& raw_file_info);
 
     EntryInfo(const EntryInfo&) = delete;
@@ -90,6 +91,14 @@
     // "foo/bar.txt", but if it's absolute, is_unsafe() returns true.
     const base::FilePath& file_path() const { return file_path_; }
 
+    // Returns the file path in its original encoding as it is stored in the ZIP
+    // archive. The encoding is not specified here. It might not be UTF-8, and
+    // the caller needs to use other means to determine the encoding if it wants
+    // to interpret this file path correctly.
+    const std::string& file_path_in_original_encoding() const {
+      return file_path_in_original_encoding_;
+    }
+
     // Returns the size of the original file (i.e. after uncompressed).
     // Returns 0 if the entry is a directory.
     // Note: this value should not be trusted, because it is stored as metadata
@@ -121,7 +130,8 @@
     int posix_mode() const { return posix_mode_; }
 
    private:
-    const base::FilePath file_path_;
+    base::FilePath file_path_;
+    std::string file_path_in_original_encoding_;
     int64_t original_size_;
     base::Time last_modified_;
     bool is_directory_;
@@ -154,6 +164,10 @@
   // destructor of the class, so you usually don't need to call this.
   void Close();
 
+  // Sets the encoding of file paths in the ZIP archive.
+  // By default, file paths are assumed to be in UTF-8.
+  void SetEncoding(std::string encoding) { encoding_ = std::move(encoding); }
+
   // Returns true if there is at least one entry to read. This function is
   // used to scan entries with AdvanceToNextEntry(), like:
   //
@@ -167,12 +181,12 @@
   bool AdvanceToNextEntry();
 
   // Opens the current entry in the zip file. On success, returns true and
-  // updates the the current entry state (i.e. current_entry_info() is
-  // updated). This function should be called before operations over the
-  // current entry like ExtractCurrentEntryToFile().
+  // updates the current entry state (i.e. current_entry_info() is updated).
+  // This function should be called before operations over the current entry
+  // like ExtractCurrentEntryToFile().
   //
-  // Note that there is no CloseCurrentEntryInZip(). The the current entry
-  // state is reset automatically as needed.
+  // Note that there is no CloseCurrentEntryInZip(). The current entry state is
+  // reset automatically as needed.
   bool OpenCurrentEntryInZip();
 
   // Extracts |num_bytes_to_extract| bytes of the current entry to |delegate|,
@@ -210,9 +224,7 @@
 
   // Returns the current entry info. Returns NULL if the current entry is
   // not yet opened. OpenCurrentEntryInZip() must be called beforehand.
-  EntryInfo* current_entry_info() const {
-    return current_entry_info_.get();
-  }
+  EntryInfo* current_entry_info() const { return current_entry_info_.get(); }
 
   // Returns the number of entries in the zip file.
   // Open() must be called beforehand.
@@ -233,6 +245,7 @@
                     const ProgressCallback& progress_callback,
                     const int64_t offset);
 
+  std::string encoding_;
   unzFile zip_file_;
   int num_entries_;
   bool reached_end_;
diff --git a/third_party/zlib/google/zip_reader_unittest.cc b/third_party/zlib/google/zip_reader_unittest.cc
index a15193b4..f4ecfbdb 100644
--- a/third_party/zlib/google/zip_reader_unittest.cc
+++ b/third_party/zlib/google/zip_reader_unittest.cc
@@ -8,13 +8,14 @@
 #include <stdint.h>
 #include <string.h>
 
-#include <set>
 #include <string>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/cxx17_backports.h"
 #include "base/files/file.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/hash/md5.h"
@@ -32,8 +33,10 @@
 #include "testing/platform_test.h"
 #include "third_party/zlib/google/zip_internal.h"
 
-using ::testing::Return;
 using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
 
 namespace {
 
@@ -41,10 +44,7 @@
 
 class FileWrapper {
  public:
-  typedef enum {
-    READ_ONLY,
-    READ_WRITE
-  } AccessMode;
+  typedef enum { READ_ONLY, READ_WRITE } AccessMode;
 
   FileWrapper(const base::FilePath& path, AccessMode mode) {
     int flags = base::File::FLAG_READ;
@@ -75,18 +75,13 @@
       : success_calls_(0),
         failure_calls_(0),
         progress_calls_(0),
-        current_progress_(0) {
-  }
+        current_progress_(0) {}
 
   // Success callback for async functions.
-  void OnUnzipSuccess() {
-    success_calls_++;
-  }
+  void OnUnzipSuccess() { success_calls_++; }
 
   // Failure callback for async functions.
-  void OnUnzipFailure() {
-    failure_calls_++;
-  }
+  void OnUnzipFailure() { failure_calls_++; }
 
   // Progress callback for async functions.
   void OnUnzipProgress(int64_t progress) {
@@ -137,87 +132,66 @@
   return false;
 }
 
-}   // namespace
+using Paths = std::vector<base::FilePath>;
+
+}  // namespace
 
 namespace zip {
 
 // Make the test a PlatformTest to setup autorelease pools properly on Mac.
 class ZipReaderTest : public PlatformTest {
  protected:
-  virtual void SetUp() {
+  void SetUp() override {
     PlatformTest::SetUp();
 
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     test_dir_ = temp_dir_.GetPath();
-
-    ASSERT_TRUE(GetTestDataDirectory(&test_data_dir_));
-
-    test_zip_file_ = test_data_dir_.AppendASCII("test.zip");
-    encrypted_zip_file_ = test_data_dir_.AppendASCII("test_encrypted.zip");
-    evil_zip_file_ = test_data_dir_.AppendASCII("evil.zip");
-    evil_via_invalid_utf8_zip_file_ = test_data_dir_.AppendASCII(
-        "evil_via_invalid_utf8.zip");
-    evil_via_absolute_file_name_zip_file_ = test_data_dir_.AppendASCII(
-        "evil_via_absolute_file_name.zip");
-
-    test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/")));
-    test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo/bar/")));
-    test_zip_contents_.insert(
-        base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt")));
-    test_zip_contents_.insert(
-        base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt")));
-    test_zip_contents_.insert(
-        base::FilePath(FILE_PATH_LITERAL("foo/bar.txt")));
-    test_zip_contents_.insert(base::FilePath(FILE_PATH_LITERAL("foo.txt")));
-    test_zip_contents_.insert(
-        base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden")));
   }
 
-  virtual void TearDown() {
-    PlatformTest::TearDown();
+  static base::FilePath GetTestDataDirectory() {
+    base::FilePath path;
+    CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &path));
+    return path.AppendASCII("third_party")
+        .AppendASCII("zlib")
+        .AppendASCII("google")
+        .AppendASCII("test")
+        .AppendASCII("data");
   }
 
-  bool GetTestDataDirectory(base::FilePath* path) {
-    bool success = base::PathService::Get(base::DIR_SOURCE_ROOT, path);
-    EXPECT_TRUE(success);
-    if (!success)
-      return false;
-    *path = path->AppendASCII("third_party");
-    *path = path->AppendASCII("zlib");
-    *path = path->AppendASCII("google");
-    *path = path->AppendASCII("test");
-    *path = path->AppendASCII("data");
-    return true;
-  }
+  static Paths GetPaths(const base::FilePath& zip_path,
+                        base::StringPiece encoding = {}) {
+    Paths paths;
 
-  bool CompareFileAndMD5(const base::FilePath& path,
-                         const std::string expected_md5) {
-    // Read the output file and compute the MD5.
-    std::string output;
-    if (!base::ReadFileToString(path, &output))
-      return false;
-    const std::string md5 = base::MD5String(output);
-    return expected_md5 == md5;
+    if (ZipReader reader; reader.Open(zip_path)) {
+      if (!encoding.empty())
+        reader.SetEncoding(std::string(encoding));
+
+      while (reader.HasMore()) {
+        if (reader.OpenCurrentEntryInZip())
+          paths.push_back(reader.current_entry_info()->file_path());
+        reader.AdvanceToNextEntry();
+      }
+    }
+
+    return paths;
   }
 
   // The path to temporary directory used to contain the test operations.
   base::FilePath test_dir_;
   // The path to the test data directory where test.zip etc. are located.
-  base::FilePath test_data_dir_;
+  const base::FilePath data_dir_ = GetTestDataDirectory();
   // The path to test.zip in the test data directory.
-  base::FilePath test_zip_file_;
-  // The path to test_encrypted.zip in the test data directory.
-  base::FilePath encrypted_zip_file_;
-  // The path to evil.zip in the test data directory.
-  base::FilePath evil_zip_file_;
-  // The path to evil_via_invalid_utf8.zip in the test data directory.
-  base::FilePath evil_via_invalid_utf8_zip_file_;
-  // The path to evil_via_absolute_file_name.zip in the test data directory.
-  base::FilePath evil_via_absolute_file_name_zip_file_;
-  std::set<base::FilePath> test_zip_contents_;
-
+  const base::FilePath test_zip_file_ = data_dir_.AppendASCII("test.zip");
+  const Paths test_zip_contents_ = {
+      base::FilePath(FILE_PATH_LITERAL("foo/")),
+      base::FilePath(FILE_PATH_LITERAL("foo/bar/")),
+      base::FilePath(FILE_PATH_LITERAL("foo/bar/baz.txt")),
+      base::FilePath(FILE_PATH_LITERAL("foo/bar/quux.txt")),
+      base::FilePath(FILE_PATH_LITERAL("foo/bar.txt")),
+      base::FilePath(FILE_PATH_LITERAL("foo.txt")),
+      base::FilePath(FILE_PATH_LITERAL("foo/bar/.hidden")),
+  };
   base::ScopedTempDir temp_dir_;
-
   base::test::TaskEnvironment task_environment_;
 };
 
@@ -234,49 +208,47 @@
 
 TEST_F(ZipReaderTest, Open_NonExistentFile) {
   ZipReader reader;
-  ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("nonexistent.zip")));
+  ASSERT_FALSE(reader.Open(data_dir_.AppendASCII("nonexistent.zip")));
 }
 
 TEST_F(ZipReaderTest, Open_ExistentButNonZipFile) {
   ZipReader reader;
-  ASSERT_FALSE(reader.Open(test_data_dir_.AppendASCII("create_test_zip.sh")));
+  ASSERT_FALSE(reader.Open(data_dir_.AppendASCII("create_test_zip.sh")));
 }
 
 // Iterate through the contents in the test zip file, and compare that the
 // contents collected from the zip reader matches the expected contents.
 TEST_F(ZipReaderTest, Iteration) {
-  std::set<base::FilePath> actual_contents;
+  Paths actual_contents;
   ZipReader reader;
   ASSERT_TRUE(reader.Open(test_zip_file_));
   while (reader.HasMore()) {
     ASSERT_TRUE(reader.OpenCurrentEntryInZip());
-    actual_contents.insert(reader.current_entry_info()->file_path());
+    actual_contents.push_back(reader.current_entry_info()->file_path());
     ASSERT_TRUE(reader.AdvanceToNextEntry());
   }
   EXPECT_FALSE(reader.AdvanceToNextEntry());  // Shouldn't go further.
   EXPECT_EQ(test_zip_contents_.size(),
             static_cast<size_t>(reader.num_entries()));
-  EXPECT_EQ(test_zip_contents_.size(), actual_contents.size());
-  EXPECT_EQ(test_zip_contents_, actual_contents);
+  EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_));
 }
 
 // Open the test zip file from a file descriptor, iterate through its contents,
 // and compare that they match the expected contents.
 TEST_F(ZipReaderTest, PlatformFileIteration) {
-  std::set<base::FilePath> actual_contents;
+  Paths actual_contents;
   ZipReader reader;
   FileWrapper zip_fd_wrapper(test_zip_file_, FileWrapper::READ_ONLY);
   ASSERT_TRUE(reader.OpenFromPlatformFile(zip_fd_wrapper.platform_file()));
   while (reader.HasMore()) {
     ASSERT_TRUE(reader.OpenCurrentEntryInZip());
-    actual_contents.insert(reader.current_entry_info()->file_path());
+    actual_contents.push_back(reader.current_entry_info()->file_path());
     ASSERT_TRUE(reader.AdvanceToNextEntry());
   }
   EXPECT_FALSE(reader.AdvanceToNextEntry());  // Shouldn't go further.
   EXPECT_EQ(test_zip_contents_.size(),
             static_cast<size_t>(reader.num_entries()));
-  EXPECT_EQ(test_zip_contents_.size(), actual_contents.size());
-  EXPECT_EQ(test_zip_contents_, actual_contents);
+  EXPECT_THAT(actual_contents, ElementsAreArray(test_zip_contents_));
 }
 
 TEST_F(ZipReaderTest, current_entry_info_RegularFile) {
@@ -306,7 +278,7 @@
 
 TEST_F(ZipReaderTest, current_entry_info_DotDotFile) {
   ZipReader reader;
-  ASSERT_TRUE(reader.Open(evil_zip_file_));
+  ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil.zip")));
   base::FilePath target_path(FILE_PATH_LITERAL(
       "../levilevilevilevilevilevilevilevilevilevilevilevil"));
   ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
@@ -320,22 +292,73 @@
 
 TEST_F(ZipReaderTest, current_entry_info_InvalidUTF8File) {
   ZipReader reader;
-  ASSERT_TRUE(reader.Open(evil_via_invalid_utf8_zip_file_));
+  ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("evil_via_invalid_utf8.zip")));
   // The evil file is the 2nd file in the zip file.
   // We cannot locate by the file name ".\x80.\\evil.txt",
   // as FilePath may internally convert the string.
   ASSERT_TRUE(reader.AdvanceToNextEntry());
   ASSERT_TRUE(reader.OpenCurrentEntryInZip());
-  ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
+  const ZipReader::EntryInfo* const entry = reader.current_entry_info();
 
-  // This file is unsafe because of invalid UTF-8 in the file name.
-  EXPECT_TRUE(current_entry_info->is_unsafe());
-  EXPECT_FALSE(current_entry_info->is_directory());
+  ASSERT_TRUE(entry);
+  EXPECT_FALSE(entry->is_unsafe());
+  EXPECT_FALSE(entry->is_directory());
+  EXPECT_EQ(entry->file_path(),
+            base::FilePath::FromUTF8Unsafe(".�.\\evil.txt"));
+}
+
+// By default, file paths in ZIPs are interpreted as UTF-8. But in this test,
+// the ZIP archive contains file paths that are actually encoded in Shift JIS.
+// The SJIS-encoded paths are thus wrongly interpreted as UTF-8, resulting in
+// garbled paths. Invalid UTF-8 sequences are safely converted to the
+// replacement character �.
+TEST_F(ZipReaderTest, EncodingSjisAsUtf8) {
+  EXPECT_THAT(
+      GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip")),
+      ElementsAre(
+          base::FilePath::FromUTF8Unsafe("�V�����t�H���_/SJIS_835C_�\\.txt"),
+          base::FilePath::FromUTF8Unsafe(
+              "�V�����t�H���_/�V�����e�L�X�g �h�L�������g.txt")));
+}
+
+// In this test, SJIS-encoded paths are interpreted as Code Page 1252. This
+// results in garbled paths. Note the presence of C1 control codes U+0090 and
+// U+0081 in the garbled paths.
+TEST_F(ZipReaderTest, EncodingSjisAs1252) {
+  EXPECT_THAT(
+      GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "windows-1252"),
+      ElementsAre(base::FilePath::FromUTF8Unsafe(
+                      "\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/SJIS_835C_ƒ\\.txt"),
+                  base::FilePath::FromUTF8Unsafe(
+                      "\u0090V‚µ‚¢ƒtƒHƒ‹ƒ_/\u0090V‚µ‚¢ƒeƒLƒXƒg "
+                      "ƒhƒLƒ…ƒ\u0081ƒ“ƒg.txt")));
+}
+
+// In this test, SJIS-encoded paths are interpreted as Code Page 866. This
+// results in garbled paths.
+TEST_F(ZipReaderTest, EncodingSjisAsIbm866) {
+  EXPECT_THAT(
+      GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "IBM866"),
+      ElementsAre(
+          base::FilePath::FromUTF8Unsafe("РVВ╡ВвГtГHГЛГ_/SJIS_835C_Г\\.txt"),
+          base::FilePath::FromUTF8Unsafe(
+              "РVВ╡ВвГtГHГЛГ_/РVВ╡ВвГeГLГXГg ГhГLГЕГБГУГg.txt")));
+}
+
+// Tests that SJIS-encoded paths are correctly converted to Unicode.
+TEST_F(ZipReaderTest, EncodingSjis) {
+  EXPECT_THAT(
+      GetPaths(data_dir_.AppendASCII("SJIS Bug 846195.zip"), "Shift_JIS"),
+      ElementsAre(
+          base::FilePath::FromUTF8Unsafe("新しいフォルダ/SJIS_835C_ソ.txt"),
+          base::FilePath::FromUTF8Unsafe(
+              "新しいフォルダ/新しいテキスト ドキュメント.txt")));
 }
 
 TEST_F(ZipReaderTest, current_entry_info_AbsoluteFile) {
   ZipReader reader;
-  ASSERT_TRUE(reader.Open(evil_via_absolute_file_name_zip_file_));
+  ASSERT_TRUE(
+      reader.Open(data_dir_.AppendASCII("evil_via_absolute_file_name.zip")));
   base::FilePath target_path(FILE_PATH_LITERAL("/evil.txt"));
   ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
   ZipReader::EntryInfo* current_entry_info = reader.current_entry_info();
@@ -377,7 +400,7 @@
   ZipReader reader;
   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
 
-  ASSERT_TRUE(reader.Open(encrypted_zip_file_));
+  ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("test_encrypted.zip")));
   ASSERT_TRUE(LocateAndOpenEntry(&reader, target_path));
   EXPECT_TRUE(reader.current_entry_info()->is_encrypted());
   reader.Close();
@@ -416,8 +439,8 @@
                                             test_dir_.AppendASCII("test.txt")));
 
   std::string actual;
-  ASSERT_TRUE(base::ReadFileToString(
-      test_dir_.AppendASCII("test.txt"), &actual));
+  ASSERT_TRUE(
+      base::ReadFileToString(test_dir_.AppendASCII("test.txt"), &actual));
   EXPECT_EQ(std::string("This is a test.\n"), actual);
 }
 
@@ -448,8 +471,8 @@
   EXPECT_LE(1, listener.progress_calls());
 
   std::string output;
-  ASSERT_TRUE(base::ReadFileToString(test_dir_.AppendASCII("quux.txt"),
-                                     &output));
+  ASSERT_TRUE(
+      base::ReadFileToString(test_dir_.AppendASCII("quux.txt"), &output));
   const std::string md5 = base::MD5String(output);
   EXPECT_EQ(kQuuxExpectedMD5, md5);
 
@@ -493,7 +516,7 @@
   // sizes from 0 to 7 bytes respectively, being the contents of each file a
   // substring of "0123456" starting at '0'.
   base::FilePath test_zip_file =
-      test_data_dir_.AppendASCII("test_mismatch_size.zip");
+      data_dir_.AppendASCII("test_mismatch_size.zip");
 
   ZipReader reader;
   std::string contents;
@@ -529,7 +552,7 @@
   // sizes from 0 to 7 bytes respectively, being the contents of each file a
   // substring of "0123456" starting at '0'.
   base::FilePath test_zip_file =
-      test_data_dir_.AppendASCII("test_mismatch_size.zip");
+      data_dir_.AppendASCII("test_mismatch_size.zip");
 
   ZipReader reader;
   std::string contents;
@@ -572,8 +595,7 @@
   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
 
   ZipReader reader;
-  ASSERT_TRUE(
-      reader.Open(test_data_dir_.AppendASCII("test_posix_permissions.zip")));
+  ASSERT_TRUE(reader.Open(data_dir_.AppendASCII("test_posix_permissions.zip")));
   for (auto entry : {"0.txt", "1.txt", "2.txt", "3.txt"}) {
     ASSERT_TRUE(LocateAndOpenEntry(&reader, base::FilePath::FromASCII(entry)));
     FilePathWriterDelegate delegate(temp_dir.GetPath().AppendASCII(entry));
@@ -613,8 +635,7 @@
 TEST_F(ZipReaderTest, ExtractCurrentEntryPrepareFailure) {
   testing::StrictMock<MockWriterDelegate> mock_writer;
 
-  EXPECT_CALL(mock_writer, PrepareOutput())
-      .WillOnce(Return(false));
+  EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(false));
 
   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
   ZipReader reader;
@@ -630,10 +651,8 @@
 TEST_F(ZipReaderTest, ExtractCurrentEntryWriteBytesFailure) {
   testing::StrictMock<MockWriterDelegate> mock_writer;
 
-  EXPECT_CALL(mock_writer, PrepareOutput())
-      .WillOnce(Return(true));
-  EXPECT_CALL(mock_writer, WriteBytes(_, _))
-      .WillOnce(Return(false));
+  EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(true));
+  EXPECT_CALL(mock_writer, WriteBytes(_, _)).WillOnce(Return(false));
 
   base::FilePath target_path(FILE_PATH_LITERAL("foo/bar/quux.txt"));
   ZipReader reader;
@@ -648,10 +667,8 @@
 TEST_F(ZipReaderTest, ExtractCurrentEntrySuccess) {
   testing::StrictMock<MockWriterDelegate> mock_writer;
 
-  EXPECT_CALL(mock_writer, PrepareOutput())
-      .WillOnce(Return(true));
-  EXPECT_CALL(mock_writer, WriteBytes(_, _))
-      .WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_writer, PrepareOutput()).WillOnce(Return(true));
+  EXPECT_CALL(mock_writer, WriteBytes(_, _)).WillRepeatedly(Return(true));
   EXPECT_CALL(mock_writer, SetPosixFilePermissions(_));
   EXPECT_CALL(mock_writer, SetTimeModified(_));
 
diff --git a/third_party/zlib/google/zip_unittest.cc b/third_party/zlib/google/zip_unittest.cc
index 944930f..53f76eb 100644
--- a/third_party/zlib/google/zip_unittest.cc
+++ b/third_party/zlib/google/zip_unittest.cc
@@ -346,16 +346,14 @@
 TEST_F(ZipTest, UnzipEvil2) {
   base::FilePath path;
   ASSERT_TRUE(GetTestDataDirectory(&path));
-  // The zip file contains an evil file with invalid UTF-8 in its file
-  // name.
+  // The ZIP file contains a file with invalid UTF-8 in its file name.
   path = path.AppendASCII("evil_via_invalid_utf8.zip");
   // See the comment at UnzipEvil() for why we do this.
   base::FilePath output_dir = test_dir_.AppendASCII("out");
-  // This should fail as it contains an evil file.
-  ASSERT_FALSE(zip::Unzip(path, output_dir));
-  base::FilePath evil_file = output_dir;
-  evil_file = evil_file.AppendASCII("../evil.txt");
-  ASSERT_FALSE(base::PathExists(evil_file));
+  ASSERT_TRUE(zip::Unzip(path, output_dir));
+  ASSERT_TRUE(base::PathExists(
+      output_dir.Append(base::FilePath::FromUTF8Unsafe(".�.\\evil.txt"))));
+  ASSERT_FALSE(base::PathExists(output_dir.AppendASCII("../evil.txt")));
 }
 
 TEST_F(ZipTest, UnzipWithFilter) {
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 9eb4d5e..08f63c7 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -646,7 +646,7 @@
 
     'client.devtools-frontend.integration': {
       'DevTools Linux': 'release_trybot_blink_do_typecheck',
-      'DevTools Linux Fastbuild': 'release_trybot_blink_skip_typecheck',
+      'DevTools Linux Fastbuild': 'release_trybot_blink',
     },
 
     'client.openscreen.chromium': {
@@ -1218,7 +1218,7 @@
     'tryserver.devtools-frontend': {
       # Align devtools blink builders with chromium linux-rel
       'devtools_frontend_linux_blink_light_rel': 'release_trybot_blink_do_typecheck',
-      'devtools_frontend_linux_blink_light_rel_fastbuild': 'release_trybot_blink_skip_typecheck',
+      'devtools_frontend_linux_blink_light_rel_fastbuild': 'release_trybot_blink',
       'devtools_frontend_linux_blink_rel': 'release_trybot_blink_do_typecheck',
     },
 
@@ -2995,11 +2995,6 @@
       'release_trybot_blink',
     ],
 
-    # TODO(crbug.com/1278663): remove this when disable typecheck by default.
-    'release_trybot_blink_skip_typecheck': [
-      'release_trybot_blink', 'devtools_skip_typecheck',
-    ],
-
     'release_trybot_blink_do_typecheck': [
       'release_trybot_blink', 'devtools_do_typecheck',
     ],
@@ -3443,11 +3438,6 @@
       'mixins': ['debug', 'static', 'minimal_symbols', 'reclient'],
     },
 
-    # TODO(crbug.com/1278663): remove this when disable typecheck by default.
-    'devtools_skip_typecheck': {
-      'gn_args': 'devtools_skip_typecheck=true',
-    },
-
     'devtools_do_typecheck': {
       'gn_args': 'devtools_skip_typecheck=false',
     },
diff --git a/tools/mb/mb_config_expectations/client.devtools-frontend.integration.json b/tools/mb/mb_config_expectations/client.devtools-frontend.integration.json
index ae2deb2b..3316fc22 100644
--- a/tools/mb/mb_config_expectations/client.devtools-frontend.integration.json
+++ b/tools/mb/mb_config_expectations/client.devtools-frontend.integration.json
@@ -14,7 +14,6 @@
   "DevTools Linux Fastbuild": {
     "gn_args": {
       "dcheck_always_on": true,
-      "devtools_skip_typecheck": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
diff --git a/tools/mb/mb_config_expectations/tryserver.devtools-frontend.json b/tools/mb/mb_config_expectations/tryserver.devtools-frontend.json
index 4539edc..4ce549b2 100644
--- a/tools/mb/mb_config_expectations/tryserver.devtools-frontend.json
+++ b/tools/mb/mb_config_expectations/tryserver.devtools-frontend.json
@@ -14,7 +14,6 @@
   "devtools_frontend_linux_blink_light_rel_fastbuild": {
     "gn_args": {
       "dcheck_always_on": true,
-      "devtools_skip_typecheck": true,
       "ffmpeg_branding": "Chrome",
       "is_component_build": false,
       "is_debug": false,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6333489..97f1642 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -37021,6 +37021,9 @@
   <int value="4158" label="V8UDPSocket_Writable_AttributeGetter"/>
   <int value="4159" label="AbortSignalTimeout"/>
   <int value="4160" label="ClientHintsPartitionedCookies"/>
+  <int value="4161" label="V8Document_Prerendering_AttributeGetter"/>
+  <int value="4162" label="V8Document_Onprerenderingchange_AttributeGetter"/>
+  <int value="4163" label="V8Document_Onprerenderingchange_AttributeSetter"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -46026,6 +46029,9 @@
   <int value="104" label="WEBID_PERMISSION_INFOBAR_DELEGATE (Obsolete)"/>
   <int value="105" label="AUTOFILL_OFFER_NOTIFICATION_INFOBAR_DELEGATE"/>
   <int value="106" label="AUTOFILL_ADDRESS_PROFILE_INFOBAR_DELEGATE_IOS"/>
+  <int value="107" label="ADD_TO_READING_LIST_IOS"/>
+  <int value="108" label="IOS_PERMISSIONS_INFOBAR_DELEGATE"/>
+  <int value="109" label="SUPPORTED_LINKS_INFOBAR_DELEGATE_CHROMEOS"/>
 </enum>
 
 <enum name="InfoBarResponse">
diff --git a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc
index f04d49f..9bbfdf8 100644
--- a/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc
+++ b/ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_unittest.cc
@@ -292,6 +292,175 @@
   EXPECT_EQ(actual_cancelled, expected_cancelled);
 }
 
+TEST_F(NeuralStylusPalmDetectionFilterTest, CallFilterTestWithAdaptiveHold) {
+  std::bitset<kNumTouchEvdevSlots> actual_held, actual_cancelled;
+  std::bitset<kNumTouchEvdevSlots> expected_held, expected_cancelled;
+
+  // Enable early stage predictions to support adaptive hold.
+  model_config_.nn_delay_start_if_palm = true;
+  model_config_.early_stage_sample_counts = {2};
+
+  // Only one touch in slot 0, nothing happens.
+  touch_[0].touching = true;
+  touch_[0].tracking_id = 500;
+  touch_[0].major = 15;
+  touch_[0].minor = 10;
+  touch_[0].x = 15;
+  touch_[0].y = 10;
+  touch_[0].slot = 0;
+  base::TimeTicks touch_time =
+      base::TimeTicks::UnixEpoch() + base::Milliseconds(10.0);
+  palm_detection_filter_->Filter(touch_, touch_time, &actual_held,
+                                 &actual_cancelled);
+  EXPECT_TRUE(actual_held.none());
+  EXPECT_TRUE(actual_cancelled.none());
+
+  // And now, let's add touches 1 and 2.
+  touch_[0].x = 17;
+  touch_[0].major = 14;
+  touch_[0].was_touching = true;
+
+  touch_[1].touching = true;
+  touch_[1].major = 11;
+  touch_[1].minor = 9;
+  touch_[1].x = 30;
+  touch_[1].y = 25;
+  touch_[1].tracking_id = 501;
+  touch_[1].slot = 1;
+
+  touch_[2].touching = true;
+  touch_[2].major = 10;
+  touch_[2].minor = 8;
+  touch_[2].x = 5500;
+  touch_[2].y = 2942;
+  touch_[2].tracking_id = 502;
+  touch_[2].slot = 2;
+
+  // Slot 0 now has 2 reports, ready for an early stage prediction.
+  std::vector<float> features = {
+      15, 10, 0, 0.25, 1, 14, 10, 0.05, 0.25, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+      0,  0,  0, 0,    0, 0,  0,  0.4,  0.05, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+      0,  0,  0, 0,    0, 0,  0,  0,    0,    0, 0, 0, 0, 0, 0, 0, 0, 0,
+      0,  0,  0, 0,    0, 0,  0,  0,    0,    0, 0, 0, 0, 0, 0, 0, 0, 0,
+      0,  0,  0, 0,    0, 0,  0,  0,    0,    0, 0, 0, 0, 0, 0, 0};
+  EXPECT_CALL(*model_,
+              Inference(testing::Pointwise(testing::FloatEq(), features)))
+      .Times(1)
+      .WillOnce(testing::Return(0.5));
+  touch_time += base::Milliseconds(8.0f);
+  palm_detection_filter_->Filter(touch_, touch_time, &actual_held,
+                                 &actual_cancelled);
+  // Slot 0 is held.
+  expected_held.set(0, true);
+  EXPECT_EQ(actual_held, expected_held);
+  EXPECT_TRUE(actual_cancelled.none());
+
+  // Slot 1 and 2 have 2 reports now, do early stage prediction on them.
+  // Slot 0 ends and have 3 reports (more than min_sample_count), do final stage
+  // prediction on it.
+  touch_[3] = touch_[2];
+  touch_[3].slot = 3;
+  touch_[3].x = 8000;
+  touch_[3].tracking_id = 504;
+  touch_[1].was_touching = true;
+  touch_[2].was_touching = true;
+  touch_[0].touching = false;
+  touch_[0].tracking_id = -1;
+  // Early stage for slot 1.
+  features = {11, 9, 0, 0.625,    1,    11, 9, 0,    0.625, 1,  0,  0,    0,
+              0,  0, 0, 0,        0,    0,  0, 0,    0,     0,  0,  0,    0.4,
+              0,  0, 1, 0.512957, 15,   10, 0, 0.25, 1,     14, 10, 0.05, 0.25,
+              1,  0, 0, 0,        0,    0,  0, 0,    0,     0,  0,  0,    0,
+              0,  0, 0, 0.4,      0.05, 0,  0, 0,    0,     0,  0,  0,    0,
+              0,  0, 0, 0,        0,    0,  0, 0,    0,     0,  0,  0,    0,
+              0,  0, 0, 0,        0,    0,  0, 0,    0,     0};
+
+  EXPECT_CALL(*model_,
+              Inference(testing::Pointwise(testing::FloatEq(), features)))
+      .Times(1)
+      .WillOnce(testing::Return(0.5));
+  // Early stage for slot 2.
+  features = {10, 8, 0, 73.55, 1, 10, 8, 0,   73.55, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0.4, 0,     0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0,   0,     0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0,   0,     0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0,   0,     0, 0, 0, 0, 0, 0, 0};
+  EXPECT_CALL(*model_,
+              Inference(testing::Pointwise(testing::FloatEq(), features)))
+      .Times(1)
+      .WillOnce(testing::Return(0.5));
+
+  // Final stage for slot 0.
+  features = {15,   10, 0, 0.25,     1,  14, 10, 0.05,  0.25, 1,  0, 0, 0,
+              0,    0,  0, 0,        0,  0,  0,  0,     0,    0,  0, 0, 0.4,
+              0.05, 0,  1, 0.512957, 11, 9,  0,  0.625, 1,    11, 9, 0, 0.625,
+              1,    0,  0, 0,        0,  0,  0,  0,     0,    0,  0, 0, 0,
+              0,    0,  0, 0.4,      0,  0,  0,  0,     0,    0,  0, 0, 0,
+              0,    0,  0, 0,        0,  0,  0,  0,     0,    0,  0, 0, 0,
+              0,    0,  0, 0,        0,  0,  0,  0,     0,    0};
+  EXPECT_CALL(*model_,
+              Inference(testing::Pointwise(testing::FloatEq(), features)))
+      .Times(1)
+      .WillOnce(testing::Return(0.5));
+
+  touch_time += base::Milliseconds(8.0f);
+  palm_detection_filter_->Filter(touch_, touch_time, &actual_held,
+                                 &actual_cancelled);
+
+  // Slot 1 and 2 are held, slot 0 is cancelled.
+  expected_held.reset();
+  expected_held.set(1, true);
+  expected_held.set(2, true);
+  EXPECT_EQ(actual_held, expected_held);
+
+  expected_cancelled.set(0, true);
+  EXPECT_EQ(actual_cancelled, expected_cancelled);
+
+  // At this update, slot 3 has 2 reports, do an early prediction on it.
+  // Slot 2 ends and have 3 reports, do final prediction.
+  touch_[0].was_touching = false;
+  touch_[2].tracking_id = -1;
+  touch_[3].was_touching = true;
+  // Early stage for slot 3.
+  features = {10, 8, 0, 60.1, 1, 10, 8, 0,   60.1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,    0, 0,  0, 0.4, 0,    0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,    0, 0,  0, 0,   0,    0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,    0, 0,  0, 0,   0,    0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,    0, 0,  0, 0,   0,    0, 0, 0, 0, 0, 0, 0};
+  EXPECT_CALL(*model_,
+              Inference(testing::Pointwise(testing::FloatEq(), features)))
+      .Times(1)
+      .WillOnce(testing::Return(0.5));
+
+  // Final stage for slot 2.
+  features = {10, 8, 0, 73.55, 1, 10, 8, 0,   73.55, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0.4, 0,     0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0,   0,     0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0,   0,     0, 0, 0, 0, 0, 0, 0, 0, 0,
+              0,  0, 0, 0,     0, 0,  0, 0,   0,     0, 0, 0, 0, 0, 0, 0};
+  EXPECT_CALL(*model_,
+              Inference(testing::Pointwise(testing::FloatEq(), features)))
+      .Times(1)
+      .WillOnce(testing::Return(0.5));
+  touch_time += base::Milliseconds(8.0f);
+  palm_detection_filter_->Filter(touch_, touch_time, &actual_held,
+                                 &actual_cancelled);
+
+  // Slot 1 was held and at this update it neither ends nor reaches
+  // max_sample_count, so it keeps held. Slot 3 is newly held.
+  expected_held.reset();
+  expected_held.set(1, true);
+  expected_held.set(3, true);
+  EXPECT_EQ(actual_held, expected_held);
+
+  // Slot 0 was cancelled and no new touch comes to this slot, it keeps
+  // cancelled. Slot 2 is newly cancelled.
+  expected_cancelled.reset();
+  expected_cancelled.set(0, true);
+  expected_cancelled.set(2, true);
+  EXPECT_EQ(actual_cancelled, expected_cancelled);
+}
+
 TEST_F(NeuralStylusPalmDetectionFilterTest, InferenceOnceNotPalm) {
   std::bitset<kNumTouchEvdevSlots> actual_held, actual_cancelled;
   base::TimeTicks touch_time =
diff --git a/ui/file_manager/file_manager/foreground/css/common.css b/ui/file_manager/file_manager/foreground/css/common.css
index 699858cd..915cb7df 100644
--- a/ui/file_manager/file_manager/foreground/css/common.css
+++ b/ui/file_manager/file_manager/foreground/css/common.css
@@ -51,40 +51,6 @@
   background-position: left 10px center;
 }
 
-cr-menu.chrome-menu > .menuitem-button {
-  background-position: center;
-  background-repeat: no-repeat;
-  border: 1px solid rgb(235, 235, 235);
-  height: 42px;
-  margin: -36px -1px -1px 0;
-  min-width: 60px;
-  padding: 0;
-  position: absolute;
-  width: 60px;
-}
-
-cr-menu.chrome-menu > .menuitem-button[checked] {
-  background-color: rgb(235, 235, 235);
-}
-
-cr-menu.chrome-menu > .menuitem-button.left {
-  right: 59px;
-}
-
-html[dir='rtl'] cr-menu.chrome-menu > .menuitem-button.left {
-  left: 59px;
-  right: auto;
-}
-
-cr-menu.chrome-menu > .menuitem-button.right {
-  right: 0;
-}
-
-html[dir='rtl'] cr-menu.chrome-menu > .menuitem-button.right {
-  left: 0;
-  right: auto;
-}
-
 cr-menu.chrome-menu > cr-menu-item[disabled] {
   color: var(--cros-text-color-disabled);
   pointer-events: none;
@@ -99,11 +65,6 @@
   opacity: 0.20;
 }
 
-cr-menu.chrome-menu > cr-menu-item:not([disabled])[selected],
-cr-menu.chrome-menu > cr-menu-item:not([disabled]):active {
-  background-color: rgba(0, 0, 0, 0.08);
-}
-
 cr-menu.chrome-menu > hr {
   background: var(--cros-separator-color);
   height: 1px;
@@ -157,7 +118,7 @@
   z-index: 0;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button {
+.cr-dialog-container .cr-dialog-buttons > button {
   background-color: var(--cros-button-background-color-secondary);
   border: 0;
   border-radius: 4px;
@@ -179,52 +140,52 @@
   z-index: 0;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-ok,
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-ok:hover {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-ok,
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-ok:hover {
   background-color: var(--cros-button-background-color-primary);
   color: var(--cros-button-label-color-primary);
   order: 1;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-ok:hover {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-ok:hover {
   background: var(--cros-button-background-color-primary-hover-preblended);
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-ok:active {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-ok:active {
   box-shadow: 0 1px 2px var(--cros-button-active-shadow-color-key-primary),
               0 1px 3px var(--cros-button-active-shadow-color-ambient-primary);
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-ok[disabled],
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-ok[disabled]:hover {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-ok[disabled],
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-ok[disabled]:hover {
   background-color:
       var(--cros-button-background-color-primary-disabled);
   color: var(--cros-button-label-color-primary-disabled);
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-cancel {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-cancel {
   border: 1px solid var(--cros-button-stroke-color-secondary);
   color: var(--cros-button-label-color-secondary);
   order: 0;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-cancel:hover {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-cancel:hover {
   background: var(--cros-button-background-color-secondary-hover);
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-cancel:active {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-cancel:active {
   box-shadow: 0 1px 2px var(--cros-button-active-shadow-color-key-secondary),
               0 1px 3px var(--cros-button-active-shadow-color-ambient-secondary);
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-cancel[disabled],
-.cr-dialog-container.files-ng .cr-dialog-buttons > button.cr-dialog-cancel[disabled]:hover  {
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-cancel[disabled],
+.cr-dialog-container .cr-dialog-buttons > button.cr-dialog-cancel[disabled]:hover  {
   border: 1px solid var(--cros-button-stroke-color-secondary-disabled);
   color: var(--cros-button-label-color-secondary-disabled);
 }
 
-:root.focus-outline-visible .cr-dialog-container.files-ng .cr-dialog-buttons > button:not(:active):focus,
-:host-context(:root.focus-outline-visible) .cr-dialog-container.files-ng .cr-dialog-buttons > button:not(:active):focus {
+:root.focus-outline-visible .cr-dialog-container .cr-dialog-buttons > button:not(:active):focus,
+:host-context(:root.focus-outline-visible) .cr-dialog-container .cr-dialog-buttons > button:not(:active):focus {
   outline: 2px solid var(--cros-focus-ring-color);
   outline-offset: 2px;
 }
@@ -232,7 +193,7 @@
 /* Some files confirm dialogs need to focus their 'Cancel' button by default,
    rather than the 'OK' button. The dialog buttons should have focus rings in
    this case (regardless of mouse, pointer, keyboard interaction). */
-.cr-dialog-container.files-ng .cr-dialog-frame.files-confirm-dialog-cancel-default .cr-dialog-buttons > button:not(:active):focus {
+.cr-dialog-container .cr-dialog-frame.files-confirm-dialog-cancel-default .cr-dialog-buttons > button:not(:active):focus {
   outline: 2px solid var(--cros-focus-ring-color);
   outline-offset: 2px;
 }
@@ -241,10 +202,8 @@
   margin-inline-start: 12px;
 }
 
-/* Gray progress bar. Background color google grey 700 with 30% opacity */
 .progress-bar {
-  /* TODO(crbug.com/1212771): Update when semantic color is available. */
-  background-color: rgba(95, 104, 106, .3);
+  background-color: var(--cros-slider-track-color-inactive);
   border-radius: 3px;
   height: 6px;
 }
@@ -265,11 +224,11 @@
 }
 
 /* Pop-up dialogs. */
-.cr-dialog-container.files-ng.shown {
+.cr-dialog-container.shown {
   background-color: var(--cros-app-shield-60);
 }
 
-.cr-dialog-container.files-ng {
+.cr-dialog-container {
   display: flex;
   height: 100%;
   left: 0;
@@ -281,7 +240,7 @@
   z-index: 9999;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-frame {
+.cr-dialog-container .cr-dialog-frame {
   background-color: var(--cros-bg-color-elevation-3);
   border-radius: 12px;
   box-shadow: var(--cros-elevation-3-shadow);
@@ -298,7 +257,7 @@
   position: relative;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-frame:focus {
+.cr-dialog-container .cr-dialog-frame:focus {
   outline: none;
 }
 
@@ -324,16 +283,16 @@
   animation-timing-function: ease-in-out;
 }
 
-.cr-dialog-container.files-ng.shown > .cr-dialog-frame {
+.cr-dialog-container.shown > .cr-dialog-frame {
   opacity: 1;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-frame {
+.cr-dialog-container .cr-dialog-frame {
   opacity: 0;
   transition: opacity 100ms;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-title {
+.cr-dialog-container .cr-dialog-title {
   color: var(--cros-text-color-primary);
   display: block;
   font-size: 16px;
@@ -341,36 +300,36 @@
   margin-bottom: 16px;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-text {
+.cr-dialog-container .cr-dialog-text {
   font-size: 14px;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-text,
-.cr-dialog-container.files-ng .cr-dialog-title {
+.cr-dialog-container .cr-dialog-text,
+.cr-dialog-container .cr-dialog-title {
   overflow-wrap: break-word;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-text {
+.cr-dialog-container .cr-dialog-text {
   color: var(--cros-text-color-secondary);
 }
 
-.cr-dialog-container.files-ng .no-title .cr-dialog-text {
+.cr-dialog-container .no-title .cr-dialog-text {
   color: var(--cros-text-color-primary);
 }
 
-.cr-dialog-container.files-ng .cr-dialog-buttons {
+.cr-dialog-container .cr-dialog-buttons {
   display: flex;
   flex-direction: row;
   justify-content: flex-end;
   padding-top: 32px;
 }
 
-.cr-dialog-container.files-ng .cr-dialog-close {
+.cr-dialog-container .cr-dialog-close {
   display: none;
 }
 
-.cr-dialog-container.files-ng #suggest-app-dialog .cr-dialog-close,
-.cr-dialog-container.files-ng #default-task-dialog .cr-dialog-close {
+.cr-dialog-container #suggest-app-dialog .cr-dialog-close,
+.cr-dialog-container #default-task-dialog .cr-dialog-close {
   -webkit-mask-image: url(../images/common/ic_close.svg);
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
@@ -384,8 +343,8 @@
   width: 32px;
 }
 
-:root[dir='rtl'] .cr-dialog-container.files-ng .cr-dialog-close,
-:host-context(:root[dir='rtl']) .cr-dialog-container.files-ng .cr-dialog-close {
+:root[dir='rtl'] .cr-dialog-container .cr-dialog-close,
+:host-context(:root[dir='rtl']) .cr-dialog-container .cr-dialog-close {
   left: 6px;
   right: unset !important;
 }
@@ -431,7 +390,7 @@
 }
 
 /* Common typography styles for ChromeOS. */
-body.files-ng .headline2 {
+.headline2 {
   color: var(--cros-text-color-primary);
   font-family: Roboto;
   font-size: 15px;
@@ -439,7 +398,7 @@
   line-height: 22px;
 }
 
-body.files-ng .button2 {
+.button2 {
   color: var(--cros-text-color-primary);
   font-family: Roboto;
   font-size: 13px;
@@ -447,31 +406,16 @@
   line-height: 20px;
 }
 
-body.files-ng .body2-primary {
+.body2-primary {
   color: var(--cros-text-color-primary);
   font-family: Roboto;
   font-size: 13px;
   line-height: 20px;
 }
 
-body.files-ng .annotation1-primary {
+.annotation1-primary {
   color: var(--cros-text-color-primary);
   font-family: Roboto;
   font-size: 12px;
   line-height: 18px;
 }
-
- /* Modification to cr-button to comply with ChromeOS GM2 rules. */
-body.files-ng cr-button.text-button {
-  color: var(--google-blue-600);
-  cursor: pointer;
-  font-family: Roboto;
-  font-size: 13px;
-  font-weight: 500;
-  line-height: 20px;
-}
-
-body.files-ng cr-button.text-button:focus {
-  outline-color: rgba(var(--google-blue-600-rgb), 40%);
-  outline-width: 2px;
-}
diff --git a/ui/file_manager/file_manager/foreground/css/menu.css b/ui/file_manager/file_manager/foreground/css/menu.css
index 567517f..3b4582b 100644
--- a/ui/file_manager/file_manager/foreground/css/menu.css
+++ b/ui/file_manager/file_manager/foreground/css/menu.css
@@ -135,7 +135,8 @@
   color: var(--cros-ripple-color);
 }
 
-html.files-ng cr-menu.files-menu > cr-menu-item:not([disabled])[selected] {
+html.files-ng cr-menu.files-menu > cr-menu-item:not([disabled])[selected],
+html.files-ng cr-menu.files-menu > cr-menu-item:not([disabled]):active {
   background-color: var(--cros-menu-item-background-hover);
 }
 
diff --git a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
index e197393..521e8ac 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_format_dialog.html
@@ -21,11 +21,11 @@
   }
 
   #warning-icon {
-    --iron-icon-fill-color: var(--google-red-600);
+    --iron-icon-fill-color: var(--cros-icon-color-alert);
   }
 
   #warning-message {
-    color: var(--google-red-600);
+    color: var(--cros-text-color-alert);
     display: inline-block;
     margin-inline-start: 8px;
   }
diff --git a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
index 92a21c8..2002c84 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
+++ b/ui/file_manager/file_manager/foreground/elements/files_quick_view.css
@@ -32,8 +32,7 @@
 }
 
 dialog::backdrop {
-  background-color: #000;
-  opacity: 0.6;
+  background-color: var(--cros-app-shield-80);
 }
 
 dialog#delete-confirm-dialog::backdrop {
@@ -132,15 +131,12 @@
 
 #file-path {
   flex: 1;
+  margin-inline-start: 8px;
   max-width: 100%;
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
-:host([files-ng]) #file-path {
-  margin-inline-start: 8px;
-}
-
 .buttons-group {
   display: flex;
 }
@@ -161,22 +157,22 @@
   width: 36px;
 }
 
-:host-context(html.pointer-active.files-ng) cr-button:not(:active):hover {
+:host-context(html.pointer-active) cr-button:not(:active):hover {
   --hover-bg-color: none;
   cursor: unset;
 }
 
-:host([files-ng]) cr-button::after {
+cr-button::after {
   content: '';
   height: 48px;
   position: absolute;
   width: 48px;
 }
 
-:host([files-ng]) #back-button > .icon,
-:host([files-ng]) #open-button > .icon,
-:host([files-ng]) #delete-button > .icon,
-:host([files-ng]) #info-button > .icon {
+#back-button > .icon,
+#open-button > .icon,
+#delete-button > .icon,
+#info-button > .icon {
   -webkit-mask-position: center;
   -webkit-mask-repeat: no-repeat;
   background-color: currentColor;
@@ -190,15 +186,15 @@
   width: 16px;
 }
 
-:host([files-ng]) #back-button > iron-icon {
+#back-button > iron-icon {
   display: none;
 }
 
-:host([files-ng]) #back-button > .icon {
+#back-button > .icon {
   -webkit-mask-image: url(../images/files/ui/back.svg);
 }
 
-:host([files-ng]) #open-button > .icon {
+#open-button > .icon {
   -webkit-mask-image: url(../images/files/ui/external_link.svg);
 }
 
@@ -216,7 +212,7 @@
   -webkit-mask-image: url(../images/files/ui/info.svg);
 }
 
-:host-context(html.focus-outline-visible.files-ng) cr-button:not(:active):focus {
+:host-context(html.focus-outline-visible) cr-button:not(:active):focus {
   border: 2px solid var(--cros-focus-ring-color);
 }
 
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc
index 6addcba..9d883389 100644
--- a/ui/message_center/views/message_view.cc
+++ b/ui/message_center/views/message_view.cc
@@ -34,6 +34,10 @@
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/widget/widget.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/time/time.h"
+#endif
+
 #if BUILDFLAG(IS_WIN)
 #include "ui/base/win/shell.h"
 #endif
@@ -465,6 +469,13 @@
     observer.OnSnoozeButtonPressed(notification_id_);
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+base::TimeDelta MessageView::GetBoundsAnimationDuration(
+    const Notification& notification) const {
+  return base::Milliseconds(0);
+}
+#endif
+
 bool MessageView::ShouldShowControlButtons() const {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Users on ChromeOS are used to the Settings and Close buttons not being
diff --git a/ui/message_center/views/message_view.h b/ui/message_center/views/message_view.h
index 6a49d359..c51ca93 100644
--- a/ui/message_center/views/message_view.h
+++ b/ui/message_center/views/message_view.h
@@ -11,6 +11,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
+#include "build/chromeos_buildflags.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/insets.h"
@@ -26,6 +27,10 @@
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/view.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "base/time/time.h"
+#endif
+
 namespace views {
 class ScrollView;
 }  // namespace views
@@ -131,6 +136,12 @@
   virtual void OnSettingsButtonPressed(const ui::Event& event);
   virtual void OnSnoozeButtonPressed(const ui::Event& event);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Gets the animation duration for a recent bounds change.
+  virtual base::TimeDelta GetBoundsAnimationDuration(
+      const Notification& notification) const;
+#endif
+
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index bfbaf16..080b071 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -2626,7 +2626,14 @@
 
 void Textfield::OnCursorBlinkTimerFired() {
   DCHECK(ShouldBlinkCursor());
-  DCHECK_EQ(CalculateCursorViewBounds(), cursor_view_->bounds());
+  // TODO(crbug.com/1294712): The cursor position is not updated appropriately
+  // when locale changes from a left-to-right script to a right-to-left script.
+  // Thus the cursor is displayed at a wrong position immediately after the
+  // locale change. As a band-aid solution, we update the cursor here, so that
+  // the cursor can be at the wrong position only up until the next blink. It
+  // would be better to detect locale change explicitly (how?) and update
+  // there.
+  UpdateCursorViewPosition();
   cursor_view_->SetVisible(!cursor_view_->GetVisible());
 }
 
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html b/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html
index e7efc1d9..dac62b6 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.html
@@ -11,11 +11,19 @@
     <style include="cr-radio-button-style">
       :host {
         background-color: var(--cr-card-background-color);
-        border: solid 2px;
         border-radius: 8px;
         box-shadow: var(--cr-elevation-1);
         margin: var(--cr-card-radio-button-margin, 8px);
         width: var(--cr-card-radio-button-width, 200px);
+        --focus-shadow-color: rgba(var(--google-blue-600-rgb), .4);
+        --hover-bg-color: rgba(var(--google-blue-500-rgb), .04);
+      }
+
+      @media (prefers-color-scheme: dark) {
+        :host {
+          --focus-shadow-color: rgba(var(--google-blue-300-rgb), .5);
+          --hover-bg-color: rgba(var(--google-blue-300-rgb), .08);
+        }
       }
 
       /* Overwrite paper-ripple defined in cr-radio-button-style
@@ -34,14 +42,6 @@
         width: var(--paper-ripple-width);
       }
 
-      :host([checked]) {
-        border-color: var(--cr-checked-color);
-      }
-
-      :host(:not([checked])) {
-        border-color: var(--cr-card-background-color);
-      }
-
       #button {
         height: var(--cr-card-radio-button-height, auto);
         padding: var(--cr-card-radio-button-padding, 24px);
@@ -49,6 +49,14 @@
         width: 100%;
       }
 
+      :host-context(.focus-outline-visible) #button:focus {
+        box-shadow: 0 0 0 2px var(--focus-shadow-color);
+      }
+
+      #button:hover {
+        background-color: var(--hover-bg-color);
+      }
+
       #checkMark {
         left: var(--cr-card-radio-button-checkmark-left, auto);
         position: absolute;
diff --git a/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js b/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js
index 2de0052..6d19ac71 100644
--- a/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js
+++ b/ui/webui/resources/cr_elements/cr_radio_button/cr_card_radio_button.js
@@ -13,4 +13,6 @@
   behaviors: [
     CrRadioButtonBehavior,
   ],
+
+  onFocus_() {},
 });