diff --git a/DEPS b/DEPS
index 8243395..c22fb81 100644
--- a/DEPS
+++ b/DEPS
@@ -300,7 +300,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '225d51031ad0737ecf355cd985fa33041a4e9331',
+  'skia_revision': '0af809b0e7ceb3961adc0bb1110d0c58f825f447',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -351,7 +351,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '562f34819229080abb05b4e7ae7e9e47fc84c6eb',
+  'freetype_revision': '80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -387,7 +387,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': '9a6376f97c074646b6f55f0b3002bee3ae14b1f1',
+  'devtools_frontend_revision': 'b8427abadb0fdc9a141b64573836a17dae7651ca',
   # 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.
@@ -447,7 +447,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libavif
   # and whatever else without interference from each other.
-  'libavif_revision': 'd78c0db95b1afe85a66b41c066f8327165a8d567',
+  'libavif_revision': '094e6166339bc317d54b42460232c28193ea4daf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
@@ -525,10 +525,15 @@
 ]
 
 deps = {
+  # TODO(crbug.com/1447924): remove buildtools/clang_format path
   'src/buildtools/clang_format/script':
     Var('chromium_git') +
     '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' +
     Var('clang_format_revision'),
+  'src/third_party/clang_format/script':
+    Var('chromium_git') +
+    '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' +
+    Var('clang_format_revision'),
   'src/buildtools/linux64': {
     'packages': [
       {
@@ -803,7 +808,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '7f50ed3ce8355b5b15ffb381be7a98ca5556e03d',
+    '4b1699a325c46130c900f9b628622bb41ae11542',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1000,7 +1005,7 @@
   },
 
   'src/third_party/androidx_javascriptengine/src': {
-      'url': Var('chromium_git') + '/aosp/platform/frameworks/support/javascriptengine/javascriptengine/src.git' + '@' + '32d6b492f879811b741c863b8ca2701b25a5651c',
+      'url': Var('chromium_git') + '/aosp/platform/frameworks/support/javascriptengine/javascriptengine/src.git' + '@' + '5afa1411d4818e3598a03fb7a3aaf43eecfd00fb',
       'condition': 'checkout_android',
   },
 
@@ -1233,7 +1238,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'a6a5427d09aa7e34decfed0679a121dd9453f7f9',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'ba4eb2e1187d26c611c4885b7cb0cf627eb87059',
     'condition': 'checkout_src_internal',
   },
 
@@ -1700,7 +1705,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '9d82cb852512318a848e2050551eb305bf0872ac',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '86ba039eec4889624d5c44a4ebf19b18d8df91cc',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1882,10 +1887,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '062e548df1d873fb6b98459eb3d3391b8c3c9702',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9e1c8491ce514c0c65e01f5b8bec509d88fdd536',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e3441ec4569f4a91543a6ee44df49a1546f2d25d',
+    Var('webrtc_git') + '/src.git' + '@' + '2eacbbc03a4a41ea658661225eb1c8fc07884c33',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1975,7 +1980,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@3344f9c2c2257cc376520c63fd2d60edba5d8a72',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@4d6e6def44e5a6c65a319c537567bb4659e2f63f',
     'condition': 'checkout_src_internal',
   },
 
@@ -2005,7 +2010,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'Rue7PaOjH2iBrltijkX5UYgknSeT11RyJbKHS4K4CJ0C',
+        'version': '3rrVAFgdA4taMSvM2Jcn3k9nZEbxTs5t-Iesu46PBHsC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3961,7 +3966,7 @@
   'src/ui/gl/resources/angle-metal': {
     'packages': [{
        'package': 'chromium/gpu/angle-metal-shader-libraries',
-       'version': '3RCJyE5Qw5kRXMUDeD7t0PHlkhdpzUWwy1B6N4D_ZhEC',
+       'version': 'pYQO5nzpAu-R6lyIIAKqLkYr99Bru6y99j3TLz0_m7EC',
     }],
     'dep_type': 'cipd',
     'condition': 'checkout_mac or checkout_ios',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index a1375ab..266ef71 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -390,8 +390,6 @@
     "clipboard/clipboard_history_resource_manager.h",
     "clipboard/clipboard_history_util.cc",
     "clipboard/clipboard_history_util.h",
-    "clipboard/clipboard_manager_bubble_view.cc",
-    "clipboard/clipboard_manager_bubble_view.h",
     "clipboard/clipboard_nudge.cc",
     "clipboard/clipboard_nudge.h",
     "clipboard/clipboard_nudge_constants.h",
diff --git a/ash/clipboard/clipboard_history_controller_impl.cc b/ash/clipboard/clipboard_history_controller_impl.cc
index 4aa27d8..5e7a134 100644
--- a/ash/clipboard/clipboard_history_controller_impl.cc
+++ b/ash/clipboard/clipboard_history_controller_impl.cc
@@ -14,7 +14,6 @@
 #include "ash/clipboard/clipboard_history_menu_model_adapter.h"
 #include "ash/clipboard/clipboard_history_resource_manager.h"
 #include "ash/clipboard/clipboard_history_util.h"
-#include "ash/clipboard/clipboard_manager_bubble_view.h"
 #include "ash/clipboard/clipboard_nudge_constants.h"
 #include "ash/clipboard/clipboard_nudge_controller.h"
 #include "ash/clipboard/scoped_clipboard_history_pause_impl.h"
@@ -66,9 +65,7 @@
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/controls/menu/menu_controller.h"
-#include "ui/views/widget/widget.h"
 
 namespace ash {
 
@@ -220,14 +217,12 @@
   ~AcceleratorTarget() override = default;
 
   void OnMenuShown() {
-    CHECK(!chromeos::features::IsClipboardHistoryRefreshEnabled());
     Shell::Get()->accelerator_controller()->Register(
         {delete_selected_, tab_navigation_, shift_tab_navigation_},
         /*accelerator_target=*/this);
   }
 
   void OnMenuClosed() {
-    CHECK(!chromeos::features::IsClipboardHistoryRefreshEnabled());
     Shell::Get()->accelerator_controller()->Unregister(
         delete_selected_, /*accelerator_target=*/this);
     Shell::Get()->accelerator_controller()->Unregister(
@@ -331,37 +326,25 @@
 
 void ClipboardHistoryControllerImpl::Shutdown() {
   if (IsMenuShowing()) {
-    if (chromeos::features::IsClipboardHistoryRefreshEnabled()) {
-      clipboard_manager_->CancelDialog();
-    } else {
-      context_menu_->Cancel(/*will_paste_item=*/false);
-    }
+    context_menu_->Cancel(/*will_paste_item=*/false);
   }
   nudge_controller_.reset();
 }
 
 bool ClipboardHistoryControllerImpl::IsMenuShowing() const {
-  return chromeos::features::IsClipboardHistoryRefreshEnabled()
-             ? clipboard_manager_
-             : context_menu_ && context_menu_->IsRunning();
+  return context_menu_ && context_menu_->IsRunning();
 }
 
 void ClipboardHistoryControllerImpl::ToggleMenuShownByAccelerator(
     bool is_plain_text_paste) {
   if (IsMenuShowing()) {
-    if (chromeos::features::IsClipboardHistoryRefreshEnabled()) {
-      // TODO(b/267694484): Paste rather than just closing here.
-      clipboard_manager_->CancelDialog();
-    } else {
-      // Before hiding the menu, paste the selected menu item, or the first item
-      // if none is selected.
-      PasteClipboardItemByCommandId(
-          context_menu_->GetSelectedMenuItemCommand().value_or(
-              clipboard_history_util::kFirstItemCommandId),
-          is_plain_text_paste
-              ? ClipboardHistoryPasteType::kPlainTextAccelerator
-              : ClipboardHistoryPasteType::kRichTextAccelerator);
-    }
+    // Before hiding the menu, paste the selected menu item, or the first item
+    // if none is selected.
+    PasteClipboardItemByCommandId(
+        context_menu_->GetSelectedMenuItemCommand().value_or(
+            clipboard_history_util::kFirstItemCommandId),
+        is_plain_text_paste ? ClipboardHistoryPasteType::kPlainTextAccelerator
+                            : ClipboardHistoryPasteType::kRichTextAccelerator);
     return;
   }
 
@@ -412,47 +395,39 @@
     active_menu_instance->Cancel(views::MenuController::ExitType::kAll);
   }
 
-  last_menu_show_time_ = base::TimeTicks::Now();
   last_menu_source_ = show_source;
 
-  if (chromeos::features::IsClipboardHistoryRefreshEnabled()) {
-    clipboard_manager_ = ClipboardManagerBubbleView::Create(anchor_rect);
-    clipboard_manager_->GetWidget()->AddObserver(this);
-    clipboard_manager_->GetWidget()->Show();
-  } else {
-    // `Unretained()` is safe because `this` owns `context_menu_`.
-    context_menu_ = ClipboardHistoryMenuModelAdapter::Create(
-        menu_delegate_.get(), std::move(callback),
-        base::BindRepeating(&ClipboardHistoryControllerImpl::OnMenuClosed,
-                            base::Unretained(this)),
-        clipboard_history_.get());
-    context_menu_->Run(anchor_rect, source_type, show_source);
+  // `Unretained()` is safe because `this` owns `context_menu_`.
+  context_menu_ = ClipboardHistoryMenuModelAdapter::Create(
+      menu_delegate_.get(), std::move(callback),
+      base::BindRepeating(&ClipboardHistoryControllerImpl::OnMenuClosed,
+                          base::Unretained(this)),
+      clipboard_history_.get());
+  context_menu_->Run(anchor_rect, source_type, show_source);
 
-    DCHECK(IsMenuShowing());
-    accelerator_target_->OnMenuShown();
+  CHECK(IsMenuShowing());
+  accelerator_target_->OnMenuShown();
 
-    // The first menu item should be selected as default after the clipboard
-    // history menu shows. Note that the menu item is selected asynchronously
-    // to avoid interference from synthesized mouse events.
-    menu_task_timer_.Start(
-        FROM_HERE, base::TimeDelta(),
-        base::BindOnce(
-            [](const base::WeakPtr<ClipboardHistoryControllerImpl>&
-                   controller_weak_ptr) {
-              if (!controller_weak_ptr) {
-                return;
-              }
+  // The first menu item should be selected by default after the clipboard
+  // history menu shows. Note that the menu item is selected asynchronously
+  // to avoid the interference from synthesized mouse events.
+  menu_task_timer_.Start(
+      FROM_HERE, base::TimeDelta(),
+      base::BindOnce(
+          [](const base::WeakPtr<ClipboardHistoryControllerImpl>&
+                 controller_weak_ptr) {
+            if (!controller_weak_ptr) {
+              return;
+            }
 
-              controller_weak_ptr->context_menu_->SelectMenuItemWithCommandId(
-                  clipboard_history_util::kFirstItemCommandId);
-              if (controller_weak_ptr
-                      ->initial_item_selected_callback_for_test_) {
-                controller_weak_ptr->initial_item_selected_callback_for_test_
-                    .Run();
-              }
-            },
-            weak_ptr_factory_.GetWeakPtr()));
-  }
+            controller_weak_ptr->context_menu_->SelectMenuItemWithCommandId(
+                clipboard_history_util::kFirstItemCommandId);
+            if (controller_weak_ptr->initial_item_selected_callback_for_test_) {
+              controller_weak_ptr->initial_item_selected_callback_for_test_
+                  .Run();
+            }
+          },
+          weak_ptr_factory_.GetWeakPtr()));
 
   base::UmaHistogramEnumeration("Ash.ClipboardHistory.ContextMenu.ShowMenu",
                                 show_source);
@@ -672,11 +647,7 @@
   if (!IsMenuShowing())
     return;
 
-  if (chromeos::features::IsClipboardHistoryRefreshEnabled()) {
-    clipboard_manager_->CancelDialog();
-  } else {
-    context_menu_->Cancel(/*will_paste_item=*/false);
-  }
+  context_menu_->Cancel(/*will_paste_item=*/false);
 }
 
 void ClipboardHistoryControllerImpl::OnOperationConfirmed(bool copy) {
@@ -737,15 +708,6 @@
   PostItemUpdateNotificationTask();
 }
 
-void ClipboardHistoryControllerImpl::OnWidgetClosing(views::Widget* widget) {
-  CHECK_EQ(clipboard_manager_->GetWidget(), widget);
-  widget->RemoveObserver(this);
-  // When `widget` is destroyed, it will clean up `clipboard_manager_` as well.
-  clipboard_manager_ = nullptr;
-  base::UmaHistogramTimes("Ash.ClipboardHistory.ContextMenu.UserJourneyTime",
-                          base::TimeTicks::Now() - last_menu_show_time_);
-}
-
 void ClipboardHistoryControllerImpl::OnSessionStateChanged(
     session_manager::SessionState state) {
   PostItemUpdateNotificationTask();
diff --git a/ash/clipboard/clipboard_history_controller_impl.h b/ash/clipboard/clipboard_history_controller_impl.h
index 84898cc..10e76b4c 100644
--- a/ash/clipboard/clipboard_history_controller_impl.h
+++ b/ash/clipboard/clipboard_history_controller_impl.h
@@ -17,7 +17,6 @@
 #include "ash/public/cpp/session/session_observer.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
-#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/one_shot_event.h"
@@ -26,7 +25,6 @@
 #include "base/unguessable_token.h"
 #include "base/values.h"
 #include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
-#include "ui/views/widget/widget_observer.h"
 
 namespace aura {
 class Window;
@@ -41,7 +39,6 @@
 class ClipboardHistoryItem;
 class ClipboardHistoryMenuModelAdapter;
 class ClipboardHistoryResourceManager;
-class ClipboardManagerBubbleView;
 class ClipboardNudgeController;
 enum class LoginStatus;
 class ScopedClipboardHistoryPause;
@@ -52,7 +49,6 @@
     : public ClipboardHistoryController,
       public ClipboardHistory::Observer,
       public ClipboardHistoryResourceManager::Observer,
-      public views::WidgetObserver,
       public SessionObserver {
  public:
   // Source and plain vs. rich text info for each paste. These values are used
@@ -185,9 +181,6 @@
   void OnCachedImageModelUpdated(
       const std::vector<base::UnguessableToken>& menu_item_ids) override;
 
-  // views::WidgetObserver:
-  void OnWidgetClosing(views::Widget* widget) override;
-
   // SessionObserver:
   void OnSessionStateChanged(session_manager::SessionState state) override;
   void OnLoginStatusChanged(LoginStatus login_status) override;
@@ -270,20 +263,13 @@
   std::unique_ptr<AcceleratorTarget> accelerator_target_;
   // Controller that shows contextual nudges for multipaste.
   std::unique_ptr<ClipboardNudgeController> nudge_controller_;
-
-  // Context menu displayed by `ShowMenu()` when the clipboard history refresh
-  // feature is disabled. Null when `MenuIsShowing()` is false.
+  // Context menu displayed by `ShowMenu()`. Null when `MenuIsShowing()` is
+  // false.
   std::unique_ptr<ClipboardHistoryMenuModelAdapter> context_menu_;
   // Handles events on the `context_menu_`.
   std::unique_ptr<MenuDelegate> menu_delegate_;
-  // Bubble view displayed by `ShowMenu()` when the clipboard history refresh
-  // feature is enabled. Null when `MenuIsShowing()` is false.
-  raw_ptr<ClipboardManagerBubbleView> clipboard_manager_ = nullptr;
 
-  // The timestamp when the clipboard history menu was last shown.
-  base::TimeTicks last_menu_show_time_;
-
-  // How the user last caused the clipboard history menu to show.
+  // How the user last caused the `context_menu_` to show.
   crosapi::mojom::ClipboardHistoryControllerShowSource last_menu_source_;
 
   // Whether a paste is currently being performed.
diff --git a/ash/clipboard/clipboard_history_controller_unittest.cc b/ash/clipboard/clipboard_history_controller_unittest.cc
index 4e3863fd..f3899d74 100644
--- a/ash/clipboard/clipboard_history_controller_unittest.cc
+++ b/ash/clipboard/clipboard_history_controller_unittest.cc
@@ -42,6 +42,7 @@
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
 #include "ui/base/models/image_model.h"
 #include "ui/events/event_constants.h"
+#include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/image/image_unittest_util.h"
 #include "ui/gfx/skia_util.h"
@@ -245,8 +246,231 @@
   std::unique_ptr<MockClipboardImageModelFactory> mock_image_factory_;
 };
 
+// Tests that Search + V with no history fails to show a menu.
+TEST_F(ClipboardHistoryControllerTest, NoHistoryNoMenu) {
+  base::HistogramTester histogram_tester;
+  ShowMenu();
+
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 0);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 0);
+}
+
+// Tests that Search + V shows a menu when there is something to show.
+TEST_F(ClipboardHistoryControllerTest, ShowMenu) {
+  base::HistogramTester histogram_tester;
+
+  // Copy something to enable the clipboard history menu.
+  WriteTextToClipboardAndConfirm(u"test");
+
+  ShowMenu();
+
+  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+  // No user journey time should be recorded as the menu is still showing.
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 1);
+
+  // Hide the menu.
+  PressAndReleaseKey(ui::VKEY_ESCAPE);
+
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 1);
+
+  // Reshow the menu.
+  ShowMenu();
+
+  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+
+  // No new UserJourneyTime histogram should be recorded as the menu is
+  // still showing.
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 2);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 2);
+
+  // Hide the menu.
+  PressAndReleaseKey(ui::VKEY_ESCAPE);
+
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 2);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 2);
+  histogram_tester.ExpectTotalCount(
+      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 2);
+}
+
+// Verifies that the clipboard history is disabled in some user modes, which
+// means that the clipboard history should not be recorded and meanwhile the
+// menu view should not show (https://crbug.com/1100739).
+TEST_F(ClipboardHistoryControllerTest, VerifyAvailabilityInUserModes) {
+  // Write one item into the clipboard history.
+  WriteTextToClipboardAndConfirm(u"text");
+
+  constexpr struct {
+    user_manager::UserType user_type;
+    bool is_enabled;
+  } kTestCases[] = {{user_manager::USER_TYPE_REGULAR, true},
+                    {user_manager::USER_TYPE_GUEST, true},
+                    {user_manager::USER_TYPE_PUBLIC_ACCOUNT, false},
+                    {user_manager::USER_TYPE_KIOSK_APP, false},
+                    {user_manager::USER_TYPE_CHILD, true},
+                    {user_manager::USER_TYPE_ARC_KIOSK_APP, false},
+                    {user_manager::USER_TYPE_WEB_KIOSK_APP, false}};
+
+  UserSession session;
+  session.session_id = 1u;
+  session.user_info.account_id = AccountId::FromUserEmail("user1@test.com");
+  session.user_info.display_name = "User 1";
+  session.user_info.display_email = "user1@test.com";
+
+  for (const auto& test_case : kTestCases) {
+    // Switch to the target user mode.
+    session.user_info.type = test_case.user_type;
+    Shell::Get()->session_controller()->UpdateUserSession(session);
+
+    // Write a new item into the clipboard buffer.
+    {
+      ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
+      scw.WriteText(u"test");
+    }
+
+    if (test_case.is_enabled) {
+      WaitForOperationConfirmed();
+    } else {
+      FlushMessageLoop();
+      // Note: This check might not catch a scenario where a mode expected to be
+      // disabled actually allows writes to go through, because the operation
+      // might not have finished yet in that case. The history verification
+      // below mitigates the chance that such a bug would not be caught.
+      EXPECT_TRUE(operation_confirmed_future_.IsEmpty());
+    }
+
+    const std::list<ClipboardHistoryItem>& items =
+        Shell::Get()->clipboard_history_controller()->history()->GetItems();
+    if (test_case.is_enabled) {
+      // Verify that the new item should be included in the clipboard history
+      // and the menu should be able to show.
+      EXPECT_EQ(items.size(), 2u);
+
+      ShowMenu();
+
+      EXPECT_TRUE(
+          Shell::Get()->clipboard_history_controller()->IsMenuShowing());
+
+      PressAndReleaseKey(ui::VKEY_ESCAPE);
+
+      EXPECT_FALSE(
+          Shell::Get()->clipboard_history_controller()->IsMenuShowing());
+
+      // Restore the clipboard history by removing the new item.
+      ClipboardHistory* clipboard_history = const_cast<ClipboardHistory*>(
+          Shell::Get()->clipboard_history_controller()->history());
+      clipboard_history->RemoveItemForId(items.begin()->id());
+    } else {
+      // Verify that the new item should not be written into the clipboard
+      // history. The menu cannot show although the clipboard history is
+      // non-empty.
+      EXPECT_EQ(items.size(), 1u);
+
+      ShowMenu();
+
+      EXPECT_FALSE(
+          Shell::Get()->clipboard_history_controller()->IsMenuShowing());
+    }
+  }
+}
+
+// Verifies that the clipboard history menu is disabled when the screen for
+// user adding shows.
+TEST_F(ClipboardHistoryControllerTest, DisableInUserAddingScreen) {
+  WriteTextToClipboardAndConfirm(u"text");
+
+  // Emulate that the user adding screen displays.
+  Shell::Get()->session_controller()->ShowMultiProfileLogin();
+
+  // Try to show the clipboard history menu; verify that the menu does not show.
+  ShowMenu();
+  EXPECT_FALSE(Shell::Get()->clipboard_history_controller()->IsMenuShowing());
+}
+
+// Tests that pressing Search while holding the V key does not show the
+// Launcher.
+TEST_F(ClipboardHistoryControllerTest, VThenSearchDoesNotShowLauncher) {
+  GetEventGenerator()->PressKey(ui::VKEY_V, ui::EF_NONE);
+  GetEventGenerator()->PressKey(ui::VKEY_COMMAND, ui::EF_NONE);
+
+  // Release V, which could trigger a key released accelerator.
+  GetEventGenerator()->ReleaseKey(ui::VKEY_V, ui::EF_COMMAND_DOWN);
+
+  EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible(
+      /*display_id=*/absl::nullopt));
+
+  // Release Search, which could trigger the Launcher.
+  GetEventGenerator()->ReleaseKey(ui::VKEY_COMMAND, ui::EF_NONE);
+
+  EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible(
+      /*display_id=*/absl::nullopt));
+}
+
+// Tests that clearing a single item from the clipboard clears clipboard
+// history.
+TEST_F(ClipboardHistoryControllerTest, ClearClipboardClearsHistory) {
+  // Write a single item to the clipboard.
+  WriteTextToClipboardAndConfirm(u"test");
+
+  // Clear the clipboard.
+  ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
+  FlushMessageLoop();
+
+  // History should also be cleared.
+  const std::list<ClipboardHistoryItem>& items =
+      Shell::Get()->clipboard_history_controller()->history()->GetItems();
+  EXPECT_TRUE(items.empty());
+
+  ShowMenu();
+
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+}
+
+// Tests that clearing the clipboard closes the clipboard history menu.
+TEST_F(ClipboardHistoryControllerTest,
+       ClearingClipboardClosesClipboardHistory) {
+  // Write a single item to the clipboard.
+  WriteTextToClipboardAndConfirm(u"test");
+
+  ASSERT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
+
+  ShowMenu();
+  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
+
+  // The cursor is visible after showing the clipboard history menu through
+  // the accelerator.
+  EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
+
+  ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
+  FlushMessageLoop();
+
+  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
+}
+
 TEST_F(ClipboardHistoryControllerTest, EncodeImage) {
-  // Write a bitmap to ClipboardHistory.
+  // Write a bitmap to the clipboard.
   SkBitmap test_bitmap = gfx::test::CreateBitmap(3, 2);
   WriteImageToClipboardAndConfirm(test_bitmap);
 
@@ -259,7 +483,7 @@
 }
 
 TEST_F(ClipboardHistoryControllerTest, EncodeMultipleImages) {
-  // Write a bunch of bitmaps to ClipboardHistory.
+  // Write a bunch of bitmaps to the clipboard.
   std::vector<const SkBitmap> test_bitmaps;
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(2, 1));
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(3, 2));
@@ -282,7 +506,7 @@
 }
 
 TEST_F(ClipboardHistoryControllerTest, WriteBitmapWhileEncodingImage) {
-  // Write a bitmap to ClipboardHistory.
+  // Write a bitmap to the clipboard.
   std::vector<const SkBitmap> test_bitmaps;
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(3, 2));
   test_bitmaps.emplace_back(gfx::test::CreateBitmap(4, 3));
@@ -315,7 +539,7 @@
 }
 
 TEST_F(ClipboardHistoryControllerTest, LockedScreenText) {
-  // Write text to ClipboardHistory and verify that it can be retrieved.
+  // Write text to the clipboard and verify that it can be retrieved.
   WriteTextToClipboardAndConfirm(u"test");
   auto history_list = GetHistoryValues();
   ASSERT_EQ(history_list.size(), 1u);
@@ -325,7 +549,7 @@
 }
 
 TEST_F(ClipboardHistoryControllerTest, LockedScreenImage) {
-  // Write a bitmap to ClipboardHistory and verify that it can be returned.
+  // Write a bitmap to the clipboard and verify that it can be returned.
   SkBitmap test_bitmap = gfx::test::CreateBitmap(3, 2);
   WriteImageToClipboardAndConfirm(test_bitmap);
   auto result = GetHistoryValues();
@@ -509,316 +733,16 @@
   }
 }
 
-// Base class for tests of Clipboard History parameterized by whether the
-// `kClipboardHistoryRefresh` feature flag is enabled.
-class ClipboardHistoryControllerRefreshTest
+class ClipboardHistoryControllerShowSourceTest
     : public ClipboardHistoryControllerTest,
-      public testing::WithParamInterface</*refresh_enabled=*/bool> {
+      public testing::WithParamInterface<ClipboardHistoryControllerShowSource> {
  public:
-  ClipboardHistoryControllerRefreshTest() {
-    std::vector<base::test::FeatureRef> refresh_features = {
-        chromeos::features::kClipboardHistoryRefresh,
-        chromeos::features::kJelly};
-    std::vector<base::test::FeatureRef> enabled_features;
-    std::vector<base::test::FeatureRef> disabled_features;
-    (IsClipboardHistoryRefreshEnabled() ? enabled_features : disabled_features)
-        .swap(refresh_features);
-    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
-  }
-
-  bool IsClipboardHistoryRefreshEnabled() const { return GetParam(); }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  ClipboardHistoryControllerShowSource GetSource() const { return GetParam(); }
 };
 
 INSTANTIATE_TEST_SUITE_P(All,
-                         ClipboardHistoryControllerRefreshTest,
-                         /*refresh_enabled=*/testing::Bool());
-
-// Tests that search + v with no history fails to show a menu.
-TEST_P(ClipboardHistoryControllerRefreshTest, NoHistoryNoMenu) {
-  base::HistogramTester histogram_tester;
-  ShowMenu();
-
-  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 0);
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 0);
-}
-
-// Tests that search + v shows a menu when there is something to show.
-TEST_P(ClipboardHistoryControllerRefreshTest, ShowMenu) {
-  base::HistogramTester histogram_tester;
-
-  // Copy something to enable the clipboard history menu.
-  WriteTextToClipboardAndConfirm(u"test");
-
-  // TODO(b/267694484): Do not fork these values once the menu can show items
-  // with the refresh feature enabled.
-  const int num_items_shown = IsClipboardHistoryRefreshEnabled() ? 0 : 1;
-  int num_items_shown_samples = 0;
-  ShowMenu();
-
-  if (!IsClipboardHistoryRefreshEnabled()) {
-    ++num_items_shown_samples;
-  }
-
-  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
-  // No user journey time should be recorded as the menu is still showing.
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
-  histogram_tester.ExpectBucketCount(
-      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", num_items_shown,
-      num_items_shown_samples);
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown",
-      num_items_shown_samples);
-
-  // Hide the menu.
-  PressAndReleaseKey(ui::VKEY_ESCAPE);
-
-  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
-  histogram_tester.ExpectBucketCount(
-      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", num_items_shown,
-      num_items_shown_samples);
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown",
-      num_items_shown_samples);
-
-  // Reshow the menu.
-  ShowMenu();
-
-  if (!IsClipboardHistoryRefreshEnabled()) {
-    ++num_items_shown_samples;
-  }
-
-  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
-
-  // No new UserJourneyTime histogram should be recorded as the menu is
-  // still showing.
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
-  histogram_tester.ExpectBucketCount(
-      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", num_items_shown,
-      num_items_shown_samples);
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown",
-      num_items_shown_samples);
-
-  // Hide the menu.
-  PressAndReleaseKey(ui::VKEY_ESCAPE);
-
-  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 2);
-  histogram_tester.ExpectBucketCount(
-      "Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", num_items_shown,
-      num_items_shown_samples);
-  histogram_tester.ExpectTotalCount(
-      "Ash.ClipboardHistory.ContextMenu.DisplayFormatShown",
-      num_items_shown_samples);
-}
-
-// Verifies that the clipboard history is disabled in some user modes, which
-// means that the clipboard history should not be recorded and meanwhile the
-// menu view should not show (https://crbug.com/1100739).
-TEST_P(ClipboardHistoryControllerRefreshTest, VerifyAvailabilityInUserModes) {
-  // Write one item into the clipboard history.
-  WriteTextToClipboardAndConfirm(u"text");
-
-  constexpr struct {
-    user_manager::UserType user_type;
-    bool is_enabled;
-  } kTestCases[] = {{user_manager::USER_TYPE_REGULAR, true},
-                    {user_manager::USER_TYPE_GUEST, true},
-                    {user_manager::USER_TYPE_PUBLIC_ACCOUNT, false},
-                    {user_manager::USER_TYPE_KIOSK_APP, false},
-                    {user_manager::USER_TYPE_CHILD, true},
-                    {user_manager::USER_TYPE_ARC_KIOSK_APP, false},
-                    {user_manager::USER_TYPE_WEB_KIOSK_APP, false}};
-
-  UserSession session;
-  session.session_id = 1u;
-  session.user_info.account_id = AccountId::FromUserEmail("user1@test.com");
-  session.user_info.display_name = "User 1";
-  session.user_info.display_email = "user1@test.com";
-
-  for (const auto& test_case : kTestCases) {
-    // Switch to the target user mode.
-    session.user_info.type = test_case.user_type;
-    Shell::Get()->session_controller()->UpdateUserSession(session);
-
-    // Write a new item into the clipboard buffer.
-    {
-      ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
-      scw.WriteText(u"test");
-    }
-
-    if (test_case.is_enabled) {
-      WaitForOperationConfirmed();
-    } else {
-      FlushMessageLoop();
-      // Note: This check might not catch a scenario where a mode expected to be
-      // disabled actually allows writes to go through, because the operation
-      // might not have finished yet in that case. The history verification
-      // below mitigates the chance that such a bug would not be caught.
-      EXPECT_TRUE(operation_confirmed_future_.IsEmpty());
-    }
-
-    const std::list<ClipboardHistoryItem>& items =
-        Shell::Get()->clipboard_history_controller()->history()->GetItems();
-    if (test_case.is_enabled) {
-      // Verify that the new item should be included in the clipboard history
-      // and the menu should be able to show.
-      EXPECT_EQ(items.size(), 2u);
-
-      ShowMenu();
-
-      EXPECT_TRUE(
-          Shell::Get()->clipboard_history_controller()->IsMenuShowing());
-
-      PressAndReleaseKey(ui::VKEY_ESCAPE);
-
-      EXPECT_FALSE(
-          Shell::Get()->clipboard_history_controller()->IsMenuShowing());
-
-      if (IsClipboardHistoryRefreshEnabled()) {
-        // Wait for the clipboard manager to be fully destroyed so that the
-        // presence of an open modal window does not block the next test case's
-        // accelerator action from being performed.
-        FlushMessageLoop();
-      }
-
-      // Restore the clipboard history by removing the new item.
-      ClipboardHistory* clipboard_history = const_cast<ClipboardHistory*>(
-          Shell::Get()->clipboard_history_controller()->history());
-      clipboard_history->RemoveItemForId(items.begin()->id());
-    } else {
-      // Verify that the new item should not be written into the clipboard
-      // history. The menu cannot show although the clipboard history is
-      // non-empty.
-      EXPECT_EQ(items.size(), 1u);
-
-      ShowMenu();
-
-      EXPECT_FALSE(
-          Shell::Get()->clipboard_history_controller()->IsMenuShowing());
-    }
-  }
-}
-
-// Verifies that the clipboard history menu is disabled when the screen for
-// user adding shows.
-TEST_P(ClipboardHistoryControllerRefreshTest, DisableInUserAddingScreen) {
-  WriteTextToClipboardAndConfirm(u"text");
-
-  // Emulate that the user adding screen displays.
-  Shell::Get()->session_controller()->ShowMultiProfileLogin();
-
-  // Try to show the clipboard history menu; verify that the menu does not show.
-  ShowMenu();
-  EXPECT_FALSE(Shell::Get()->clipboard_history_controller()->IsMenuShowing());
-}
-
-// Tests that pressing and holding VKEY_V, then the search key (EF_COMMAND_DOWN)
-// does not show the AppList.
-TEST_P(ClipboardHistoryControllerRefreshTest, VThenSearchDoesNotShowLauncher) {
-  GetEventGenerator()->PressKey(ui::VKEY_V, ui::EF_NONE);
-  GetEventGenerator()->PressKey(ui::VKEY_LWIN, ui::EF_NONE);
-
-  // Release VKEY_V, which could trigger a key released accelerator.
-  GetEventGenerator()->ReleaseKey(ui::VKEY_V, ui::EF_NONE);
-
-  EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible(
-      /*display_id=*/absl::nullopt));
-
-  // Release VKEY_LWIN(search/launcher), which could trigger the app list.
-  GetEventGenerator()->ReleaseKey(ui::VKEY_LWIN, ui::EF_NONE);
-
-  EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible(
-      /*display_id=*/absl::nullopt));
-}
-
-// Tests that clearing the clipboard clears ClipboardHistory
-TEST_P(ClipboardHistoryControllerRefreshTest, ClearClipboardClearsHistory) {
-  // Write a single item to ClipboardHistory.
-  WriteTextToClipboardAndConfirm(u"test");
-
-  // Clear the clipboard.
-  ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
-  FlushMessageLoop();
-
-  // History should also be cleared.
-  const std::list<ClipboardHistoryItem>& items =
-      Shell::Get()->clipboard_history_controller()->history()->GetItems();
-  EXPECT_TRUE(items.empty());
-
-  ShowMenu();
-
-  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
-}
-
-// Tests that clearing the clipboard closes the ClipboardHistory menu.
-TEST_P(ClipboardHistoryControllerRefreshTest,
-       ClearingClipboardClosesClipboardHistory) {
-  // Write a single item to ClipboardHistory.
-  WriteTextToClipboardAndConfirm(u"test");
-
-  ASSERT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
-
-  ShowMenu();
-  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
-
-  // The cursor is visible after showing the clipboard history menu through
-  // the accelerator.
-  EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
-
-  ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
-  FlushMessageLoop();
-
-  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
-}
-
-class ClipboardHistoryControllerShowSourceTest
-    : public ClipboardHistoryControllerTest,
-      public testing::WithParamInterface<
-          std::tuple<ClipboardHistoryControllerShowSource,
-                     /*refresh_enabled=*/bool>> {
- public:
-  ClipboardHistoryControllerShowSourceTest() {
-    std::vector<base::test::FeatureRef> refresh_features = {
-        chromeos::features::kClipboardHistoryRefresh,
-        chromeos::features::kJelly};
-    std::vector<base::test::FeatureRef> enabled_features;
-    std::vector<base::test::FeatureRef> disabled_features;
-    (IsClipboardHistoryRefreshEnabled() ? enabled_features : disabled_features)
-        .swap(refresh_features);
-    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
-  }
-
-  ClipboardHistoryControllerShowSource GetSource() const {
-    return std::get<0>(GetParam());
-  }
-
-  bool IsClipboardHistoryRefreshEnabled() const {
-    return std::get<1>(GetParam());
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    ClipboardHistoryControllerShowSourceTest,
-    testing::Combine(testing::ValuesIn(GetClipboardHistoryShowSources()),
-                     /*refresh_enabled=*/testing::Bool()));
+                         ClipboardHistoryControllerShowSourceTest,
+                         testing::ValuesIn(GetClipboardHistoryShowSources()));
 
 // Tests that `ShowMenu()` returns whether the menu was shown successfully.
 TEST_P(ClipboardHistoryControllerShowSourceTest, ShowMenuReturnsSuccess) {
@@ -872,12 +796,6 @@
 // Tests that the client-provided `OnMenuClosingCallback` runs before the menu
 // closes.
 TEST_P(ClipboardHistoryControllerShowSourceTest, OnMenuClosingCallback) {
-  if (IsClipboardHistoryRefreshEnabled()) {
-    // TODO(b/267694484): Do not skip this test case once the menu can paste
-    // with the refresh feature enabled.
-    GTEST_SKIP();
-  }
-
   base::test::RepeatingTestFuture<bool> on_menu_closing_future;
   base::HistogramTester histogram_tester;
 
@@ -921,8 +839,8 @@
                                       /*expected_bucket_count=*/1);
 }
 
-// TODO(b/278109818): Move clipboard history refresh tests into a separate test
-// file.
+// TODO(http://b/278109818): Move clipboard history refresh tests into a
+// separate test file.
 
 // A parameterized test base to verify the clipboard history refresh feature on
 // every display format.
diff --git a/ash/clipboard/clipboard_manager_bubble_view.cc b/ash/clipboard/clipboard_manager_bubble_view.cc
deleted file mode 100644
index abe41ed9..0000000
--- a/ash/clipboard/clipboard_manager_bubble_view.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/clipboard/clipboard_manager_bubble_view.h"
-
-#include <memory>
-
-#include "ash/bubble/bubble_constants.h"
-#include "ash/style/system_shadow.h"
-#include "base/memory/ptr_util.h"
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/accessibility/ax_node_data.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
-#include "ui/gfx/geometry/insets.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/views/bubble/bubble_border.h"
-#include "ui/views/bubble/bubble_frame_view.h"
-#include "ui/views/highlight_border.h"
-
-using views::BubbleBorder;
-using views::BubbleFrameView;
-using views::HighlightBorder;
-using views::NonClientFrameView;
-using views::Widget;
-
-namespace ash {
-
-namespace {
-
-constexpr int kClipboardManagerCornerRadius = 12;
-constexpr int kClipboardManagerHeight = 100;
-constexpr int kClipboardManagerWidth = 320;
-
-}  // namespace
-
-// static
-ClipboardManagerBubbleView* ClipboardManagerBubbleView::Create(
-    const gfx::Rect& anchor_rect) {
-  auto* bubble_view = new ClipboardManagerBubbleView(anchor_rect);
-  // Future calls to `GetWidget()` will return the `Widget` initialized in
-  // `CreateBubble()`.
-  CreateBubble(base::WrapUnique(bubble_view));
-  return bubble_view;
-}
-
-ClipboardManagerBubbleView::~ClipboardManagerBubbleView() = default;
-
-gfx::Size ClipboardManagerBubbleView::CalculatePreferredSize() const {
-  // TODO(b/267694484): Calculate height based on clipboard contents.
-  return gfx::Size(kClipboardManagerWidth, kClipboardManagerHeight);
-}
-
-std::unique_ptr<NonClientFrameView>
-ClipboardManagerBubbleView::CreateNonClientFrameView(Widget* widget) {
-  auto frame = BubbleDialogDelegateView::CreateNonClientFrameView(widget);
-
-  auto* bubble_border =
-      static_cast<BubbleFrameView*>(frame.get())->bubble_border();
-  bubble_border->set_avoid_shadow_overlap(true);
-  bubble_border->set_md_shadow_elevation(
-      SystemShadow::GetElevationFromType(SystemShadow::Type::kElevation12));
-  return frame;
-}
-
-void ClipboardManagerBubbleView::GetAccessibleNodeData(
-    ui::AXNodeData* node_data) {
-  node_data->role = ax::mojom::Role::kMenu;
-  node_data->SetNameChecked(GetAccessibleWindowTitle());
-}
-
-std::u16string ClipboardManagerBubbleView::GetAccessibleWindowTitle() const {
-  // TODO(b/267694484): Finalize a11y label.
-  return u"[i18n] Clipboard menu";
-}
-
-void ClipboardManagerBubbleView::OnThemeChanged() {
-  views::BubbleDialogDelegateView::OnThemeChanged();
-
-  set_color(
-      GetColorProvider()->GetColor(cros_tokens::kCrosSysSystemBaseElevated));
-}
-
-ClipboardManagerBubbleView::ClipboardManagerBubbleView(
-    const gfx::Rect& anchor_rect) {
-  SetAnchorRect(anchor_rect);
-  SetButtons(ui::DIALOG_BUTTON_NONE);
-  set_force_create_contents_background(true);
-  set_margins(gfx::Insets());
-  set_corner_radius(kClipboardManagerCornerRadius);
-  SetBorder(std::make_unique<views::HighlightBorder>(
-      kClipboardManagerCornerRadius,
-      views::HighlightBorder::Type::kHighlightBorderOnShadow));
-
-  // TODO(b/267694484): Create a clipboard manager container that serves as a
-  // parent so that the launcher will know not to close when the manager shows.
-  set_has_parent(false);
-
-  // Like other menus, the clipboard manager should close in response to the Esc
-  // key or the loss of focus.
-  set_close_on_deactivate(true);
-
-  // TODO(b/267694484): Enable arrow key traversal once the menu has items.
-  // TODO(b/267694484): Set initially focused view once the menu has items.
-
-  SetAccessibleWindowRole(ax::mojom::Role::kMenu);
-}
-
-BEGIN_METADATA(ClipboardManagerBubbleView, views::BubbleDialogDelegateView)
-END_METADATA
-
-}  // namespace ash
diff --git a/ash/clipboard/clipboard_manager_bubble_view.h b/ash/clipboard/clipboard_manager_bubble_view.h
deleted file mode 100644
index edda7bf..0000000
--- a/ash/clipboard/clipboard_manager_bubble_view.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_CLIPBOARD_CLIPBOARD_MANAGER_BUBBLE_VIEW_H_
-#define ASH_CLIPBOARD_CLIPBOARD_MANAGER_BUBBLE_VIEW_H_
-
-#include "ash/ash_export.h"
-#include "ui/accessibility/ax_enums.mojom-forward.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/views/bubble/bubble_dialog_delegate_view.h"
-
-namespace gfx {
-class Rect;
-}  // namespace gfx
-
-namespace ash {
-
-// Contents view class for the clipboard manager widget.
-class ASH_EXPORT ClipboardManagerBubbleView
-    : public views::BubbleDialogDelegateView {
- public:
-  METADATA_HEADER(ClipboardManagerBubbleView);
-
-  // Returns a pointer to the `ClipboardManagerBubbleView` whose bubble widget
-  // has been initialized. Owned by said widget.
-  static ClipboardManagerBubbleView* Create(const gfx::Rect& anchor_rect);
-
-  ClipboardManagerBubbleView(const ClipboardManagerBubbleView&) = delete;
-  ClipboardManagerBubbleView& operator=(const ClipboardManagerBubbleView&) =
-      delete;
-  ~ClipboardManagerBubbleView() override;
-
- private:
-  // views::BubbleDialogDelegateView:
-  gfx::Size CalculatePreferredSize() const override;
-  std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
-      views::Widget* widget) override;
-  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  std::u16string GetAccessibleWindowTitle() const override;
-  void OnThemeChanged() override;
-
-  explicit ClipboardManagerBubbleView(const gfx::Rect& anchor_rect);
-};
-
-}  // namespace ash
-
-#endif  // ASH_CLIPBOARD_CLIPBOARD_MANAGER_BUBBLE_VIEW_H_
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
index 0dab2d8..21b8e9d 100644
--- a/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view.cc
@@ -53,6 +53,8 @@
 constexpr int kVersionButtonLargeCornerRadius = 16;
 constexpr int kVersionButtonSmallCornerRadius = 4;
 
+constexpr float kVersionButtonStrokeWidth = 1.0f;
+
 // Corners for the `VersionButton` contents. If it's shown alongside its
 // "partner" (the `SubmitFeedbackButton`) then only one side is rounded,
 // otherwise both sides are rounded. Calling
@@ -248,18 +250,21 @@
   // views::LabelButton:
   void PaintButtonContents(gfx::Canvas* canvas) override {
     cc::PaintFlags flags;
+    gfx::RectF bounds(GetLocalBounds());
     if (features::IsQsRevampEnabled()) {
       flags.setColor(
           GetColorProvider()->GetColor(cros_tokens::kCrosSysSeparator));
       flags.setStyle(cc::PaintFlags::kStroke_Style);
+      const float half_stroke_width = kVersionButtonStrokeWidth / 2.0f;
+      bounds.Inset(half_stroke_width);
     } else {
       flags.setColor(channel_indicator_utils::GetBgColor(channel_));
       flags.setStyle(cc::PaintFlags::kFill_Style);
     }
     flags.setAntiAlias(true);
+    flags.setStrokeWidth(kVersionButtonStrokeWidth);
     canvas->DrawPath(
-        SkPath().addRoundRect(gfx::RectToSkRect(GetLocalBounds()),
-                              content_corners_, SkPathDirection::kCW),
+        SkPath().addRoundRect(gfx::RectFToSkRect(bounds), content_corners_),
         flags);
   }
 
@@ -345,18 +350,21 @@
   // views::LabelButton:
   void PaintButtonContents(gfx::Canvas* canvas) override {
     cc::PaintFlags flags;
+    gfx::RectF bounds(GetLocalBounds());
     if (features::IsQsRevampEnabled()) {
       flags.setColor(
           GetColorProvider()->GetColor(cros_tokens::kCrosSysSeparator));
       flags.setStyle(cc::PaintFlags::kStroke_Style);
+      const float half_stroke_width = kVersionButtonStrokeWidth / 2.0f;
+      bounds.Inset(half_stroke_width);
     } else {
       flags.setColor(channel_indicator_utils::GetBgColor(channel_));
       flags.setStyle(cc::PaintFlags::kFill_Style);
     }
     flags.setAntiAlias(true);
+    flags.setStrokeWidth(kVersionButtonStrokeWidth);
     canvas->DrawPath(
-        SkPath().addRoundRect(gfx::RectToSkRect(GetLocalBounds()),
-                              content_corners_, SkPathDirection::kCW),
+        SkPath().addRoundRect(gfx::RectFToSkRect(bounds), content_corners_),
         flags);
     IconButton::PaintButtonContents(canvas);
   }
diff --git a/ash/system/channel_indicator/channel_indicator_quick_settings_view_pixeltest.cc b/ash/system/channel_indicator/channel_indicator_quick_settings_view_pixeltest.cc
index 6d6a6cc..9249df0 100644
--- a/ash/system/channel_indicator/channel_indicator_quick_settings_view_pixeltest.cc
+++ b/ash/system/channel_indicator/channel_indicator_quick_settings_view_pixeltest.cc
@@ -113,7 +113,7 @@
   // `ChannelIndicatorQuickSettingsView`.
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "feedback_button_visible",
-      /*revision_number=*/2, view()));
+      /*revision_number=*/3, view()));
 }
 
 }  // namespace ash
diff --git a/ash/system/time/calendar_event_list_item_view_jelly.cc b/ash/system/time/calendar_event_list_item_view_jelly.cc
index 5aec9d0..6b2268eb 100644
--- a/ash/system/time/calendar_event_list_item_view_jelly.cc
+++ b/ash/system/time/calendar_event_list_item_view_jelly.cc
@@ -16,7 +16,6 @@
 #include "ash/style/style_util.h"
 #include "ash/style/typography.h"
 #include "ash/system/model/system_tray_model.h"
-#include "ash/system/time/calendar_metrics.h"
 #include "ash/system/time/calendar_utils.h"
 #include "ash/system/time/calendar_view_controller.h"
 #include "ash/system/time/event_date_formatter_util.h"
diff --git a/ash/system/time/calendar_event_list_item_view_jelly.h b/ash/system/time/calendar_event_list_item_view_jelly.h
index cba71ebf..4726780a 100644
--- a/ash/system/time/calendar_event_list_item_view_jelly.h
+++ b/ash/system/time/calendar_event_list_item_view_jelly.h
@@ -22,11 +22,13 @@
 
 namespace ash {
 
-// Label ID's.
+// View ID's.
 constexpr int kSummaryLabelID = 100;
 constexpr int kTimeLabelID = 101;
 constexpr int kEventListItemDotID = 102;
 constexpr int kJoinButtonID = 103;
+constexpr int kEventListMultiDayEventsContainer = 104;
+constexpr int kEventListSameDayEventsContainer = 105;
 
 class CalendarViewController;
 
diff --git a/ash/system/time/calendar_event_list_view.cc b/ash/system/time/calendar_event_list_view.cc
index a6774bb3..4502f46a 100644
--- a/ash/system/time/calendar_event_list_view.cc
+++ b/ash/system/time/calendar_event_list_view.cc
@@ -226,11 +226,13 @@
 }
 
 std::unique_ptr<views::View> CalendarEventListView::CreateChildEventListView(
-    std::list<google_apis::calendar::CalendarEvent> events) {
+    std::list<google_apis::calendar::CalendarEvent> events,
+    int parent_view_id) {
   auto container = std::make_unique<views::View>();
   container->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, gfx::Insets(),
       kChildEventListBetweenChildSpacing));
+  container->SetID(parent_view_id);
 
   const int events_size = events.size();
   for (SingleDayEventList::iterator it = events.begin(); it != events.end();
@@ -269,10 +271,12 @@
     // and early return (the following methods in `UpdateListItems` handle empty
     // state etc).
     if (!multi_day_events.empty()) {
-      content_view_->AddChildView(CreateChildEventListView(multi_day_events));
+      content_view_->AddChildView(CreateChildEventListView(
+          multi_day_events, kEventListMultiDayEventsContainer));
     }
     if (!all_other_events.empty()) {
-      content_view_->AddChildView(CreateChildEventListView(all_other_events));
+      content_view_->AddChildView(CreateChildEventListView(
+          all_other_events, kEventListSameDayEventsContainer));
     }
 
     content_view_->InvalidateLayout();
diff --git a/ash/system/time/calendar_event_list_view.h b/ash/system/time/calendar_event_list_view.h
index 0ccbb28..6dfe145 100644
--- a/ash/system/time/calendar_event_list_view.h
+++ b/ash/system/time/calendar_event_list_view.h
@@ -47,11 +47,13 @@
   // Updates the event list entries.
   void UpdateListItems();
 
-  // Takes a list of `CalendarEvent`'s and generates a parent container
-  // containing each `CalendarEvent` as a `CalendarEventListItemViewJelly` view.
+  // Takes a list of `CalendarEvent`'s and a parent view id and generates a
+  // parent container containing each `CalendarEvent` as a
+  // `CalendarEventListItemViewJelly` view.
   // Returns the parent container.
   std::unique_ptr<views::View> CreateChildEventListView(
-      std::list<google_apis::calendar::CalendarEvent> events);
+      std::list<google_apis::calendar::CalendarEvent> events,
+      int parent_view_id);
 
   // Owned by `CalendarView`.
   raw_ptr<CalendarViewController, ExperimentalAsh> calendar_view_controller_;
diff --git a/ash/system/time/calendar_event_list_view_unittest.cc b/ash/system/time/calendar_event_list_view_unittest.cc
index d315ef7..20db9cd 100644
--- a/ash/system/time/calendar_event_list_view_unittest.cc
+++ b/ash/system/time/calendar_event_list_view_unittest.cc
@@ -4,10 +4,12 @@
 
 #include "ash/system/time/calendar_event_list_view.h"
 
+#include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/time/calendar_event_list_item_view.h"
+#include "ash/system/time/calendar_event_list_item_view_jelly.h"
 #include "ash/system/time/calendar_unittest_utils.h"
 #include "ash/system/time/calendar_utils.h"
 #include "ash/system/time/calendar_view_controller.h"
@@ -50,7 +52,8 @@
 
 }  // namespace
 
-class CalendarViewEventListViewTest : public AshTestBase {
+class CalendarViewEventListViewTest : public AshTestBase,
+                                      public testing::WithParamInterface<bool> {
  public:
   CalendarViewEventListViewTest() = default;
   CalendarViewEventListViewTest(const CalendarViewEventListViewTest&) = delete;
@@ -59,6 +62,8 @@
   ~CalendarViewEventListViewTest() override = default;
 
   void SetUp() override {
+    scoped_feature_list_.InitWithFeatureState(features::kCalendarJelly,
+                                              IsCalendarJellyEnabled());
     AshTestBase::SetUp();
     controller_ = std::make_unique<CalendarViewController>();
   }
@@ -92,22 +97,34 @@
                                    /*row_index=*/0);
   }
 
+  // The way we send metrics is slightly different for Jelly so we need to
+  // ensure this value is set to true in the controller.
+  void SetEventListIsShowingForMetrics() {
+    controller_->is_event_list_showing_ = true;
+  }
+
   CalendarEventListView* event_list_view() { return event_list_view_.get(); }
   views::View* content_view() { return event_list_view_->content_view_; }
   CalendarViewController* controller() { return controller_.get(); }
 
-  views::Label* GetSummary(int child_index) {
-    return static_cast<views::Label*>(
-        static_cast<CalendarEventListItemView*>(
-            content_view()->children()[child_index])
-            ->summary_);
+  views::View* GetSameDayEventsContainer() {
+    views::View* container =
+        content_view()->GetViewByID(kEventListSameDayEventsContainer);
+    CHECK(container);
+
+    return container;
   }
 
-  views::Label* GetTimeRange(int child_index) {
-    return static_cast<views::Label*>(
-        static_cast<CalendarEventListItemView*>(
-            content_view()->children()[child_index])
-            ->time_range_);
+  views::Label* GetSummary(int child_index) {
+    return features::IsCalendarJellyEnabled()
+               ? static_cast<views::Label*>(
+                     static_cast<CalendarEventListItemViewJelly*>(
+                         GetSameDayEventsContainer()->children()[child_index])
+                         ->GetViewByID(kSummaryLabelID))
+               : static_cast<views::Label*>(
+                     static_cast<CalendarEventListItemView*>(
+                         content_view()->children()[child_index])
+                         ->summary_);
   }
 
   std::u16string GetEmptyLabel() {
@@ -116,27 +133,47 @@
         ->GetText();
   }
 
+  ActionableView* GetActionableView(int child_index) {
+    return features::IsCalendarJellyEnabled()
+               ? static_cast<ActionableView*>(
+                     GetSameDayEventsContainer()->children()[child_index])
+               : static_cast<ActionableView*>(
+                     content_view()->children()[child_index]);
+  }
+
+  size_t GetContentViewSize() {
+    return features::IsCalendarJellyEnabled()
+               ? GetSameDayEventsContainer()->children().size()
+               : content_view()->children().size();
+  }
+
+  size_t GetEmptyContentViewSize() { return content_view()->children().size(); }
+
+  bool IsCalendarJellyEnabled() { return GetParam(); }
+
  private:
   std::unique_ptr<CalendarEventListView> event_list_view_;
   std::unique_ptr<CalendarViewController> controller_;
-  base::test::ScopedFeatureList features_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-TEST_F(CalendarViewEventListViewTest, ShowEvents) {
+INSTANTIATE_TEST_SUITE_P(All, CalendarViewEventListViewTest, testing::Bool());
+
+TEST_P(CalendarViewEventListViewTest, ShowEvents) {
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
 
   CreateEventListView(date - base::Days(1));
 
   // No events on 17 Nov 2021, so we see the empty list default.
-  EXPECT_EQ(1u, content_view()->children().size());
+  EXPECT_EQ(1u, GetEmptyContentViewSize());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_EVENTS),
             GetEmptyLabel());
 
   SetSelectedDate(date);
 
   // 3 events on 18 Nov 2021. And they should be sorted by the start time.
-  EXPECT_EQ(3u, content_view()->children().size());
+  EXPECT_EQ(3u, GetContentViewSize());
   EXPECT_EQ(u"summary_1", GetSummary(0)->GetText());
   EXPECT_EQ(u"summary_0", GetSummary(1)->GetText());
   EXPECT_EQ(u"summary_2", GetSummary(2)->GetText());
@@ -145,33 +182,33 @@
 
   // 1 event on 19 Nov 2021. For no title mettings, shows "No title" as the
   // meeting summary.
-  EXPECT_EQ(1u, content_view()->children().size());
+  EXPECT_EQ(1u, GetContentViewSize());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_TITLE),
             GetSummary(0)->GetText());
 
   SetSelectedDate(date + base::Days(2));
 
   // 0 event on 20 Nov 2021.
-  EXPECT_EQ(1u, content_view()->children().size());
+  EXPECT_EQ(1u, GetEmptyContentViewSize());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_EVENTS),
             GetEmptyLabel());
 
   SetSelectedDate(date + base::Days(3));
 
   // 2 events on 21 Nov 2021.
-  EXPECT_EQ(2u, content_view()->children().size());
+  EXPECT_EQ(2u, GetContentViewSize());
   EXPECT_EQ(u"summary_4", GetSummary(0)->GetText());
   EXPECT_EQ(u"summary_5", GetSummary(1)->GetText());
 }
 
-TEST_F(CalendarViewEventListViewTest, LaunchEmptyList) {
+TEST_P(CalendarViewEventListViewTest, LaunchEmptyList) {
   base::HistogramTester histogram_tester;
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
   CreateEventListView(date - base::Days(1));
 
   // No events, so we see the empty list by default.
-  EXPECT_EQ(1u, content_view()->children().size());
+  EXPECT_EQ(1u, GetEmptyContentViewSize());
   views::Button* empty_list_button =
       static_cast<views::Button*>(content_view()->children()[0]->children()[0]);
   empty_list_button->AcceleratorPressed(
@@ -184,16 +221,18 @@
             0);
 }
 
-TEST_F(CalendarViewEventListViewTest, LaunchItem) {
+TEST_P(CalendarViewEventListViewTest, LaunchItem) {
   base::HistogramTester histogram_tester;
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
   CreateEventListView(date);
-  EXPECT_EQ(3u, content_view()->children().size());
+  if (features::IsCalendarJellyEnabled()) {
+    SetEventListIsShowingForMetrics();
+  }
+  EXPECT_EQ(3u, GetContentViewSize());
 
   // Launch the first item.
-  views::Button* first_item =
-      static_cast<views::Button*>(content_view()->children()[0]);
+  ActionableView* first_item = GetActionableView(0);
   first_item->AcceleratorPressed(
       ui::Accelerator(ui::KeyboardCode::VKEY_SPACE, /*modifiers=*/0));
 
@@ -201,51 +240,13 @@
       "Ash.Calendar.UserJourneyTime.EventLaunched", 1);
   histogram_tester.ExpectTotalCount("Ash.Calendar.EventListItem.Activated", 1);
   EXPECT_EQ(histogram_tester.GetTotalSum(
-                "Ash.Calendar.EventListView.EventDisplayedCount"),
+                features::IsCalendarJellyEnabled()
+                    ? "Ash.Calendar.EventListViewJelly.EventDisplayedCount"
+                    : "Ash.Calendar.EventListView.EventDisplayedCount"),
             3);
 }
 
-TEST_F(CalendarViewEventListViewTest, CheckTimeFormat) {
-  ash::system::ScopedTimezoneSettings timezone_settings(u"GMT");
-
-  // Date of first day which holds a normal event and a multi-day event.
-  base::Time date;
-  ASSERT_TRUE(base::Time::FromString("22 Nov 2021 10:00 GMT", &date));
-
-  // Date of the second day which holds the second day of the multi-day event.
-  base::Time date_2;
-  ASSERT_TRUE(base::Time::FromString("23 Nov 2021 10:00 GMT", &date_2));
-
-  // Set the time in AM/PM format.
-  Shell::Get()->system_tray_model()->SetUse24HourClock(false);
-
-  CreateEventListView(date);
-
-  SetSelectedDate(date);
-  EXPECT_EQ(u"8:30\u2009–\u20099:30\u202fPM", GetTimeRange(0)->GetText());
-  EXPECT_EQ(u"11:30\u2009–\u200911:59\u202fPM", GetTimeRange(1)->GetText());
-
-  // Select the second day of the multi-day event.
-  SetSelectedDate(date_2);
-  EXPECT_EQ(u"12:00\u2009–\u200912:30\u202fAM", GetTimeRange(0)->GetText());
-
-  // Set the time in 24 hour format.
-  Shell::Get()->system_tray_model()->SetUse24HourClock(true);
-
-  // Regenerate the event list to refresh events time range.
-  CreateEventListView(date);
-
-  SetSelectedDate(date);
-  EXPECT_EQ(u"20:30\u2009–\u200921:30", GetTimeRange(0)->GetText());
-  EXPECT_EQ(u"23:30\u2009–\u200923:59", GetTimeRange(1)->GetText());
-
-  SetSelectedDate(date_2);
-  EXPECT_EQ(u"00:00\u2009–\u200900:30", GetTimeRange(0)->GetText());
-}
-
-TEST_F(CalendarViewEventListViewTest, RefreshEvents) {
-  // Sets the timezone to "America/Los_Angeles".
-  ash::system::ScopedTimezoneSettings timezone_settings(u"America/Los_Angeles");
+TEST_P(CalendarViewEventListViewTest, RefreshEvents) {
   base::Time date;
   ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
   CreateEventListView(date);
@@ -253,7 +254,7 @@
   SetSelectedDate(date);
 
   // With the initial event list there should be 3 events on the 18th.
-  EXPECT_EQ(3u, content_view()->children().size());
+  EXPECT_EQ(3u, GetContentViewSize());
 
   base::Time start_of_month = calendar_utils::GetStartOfMonthUTC(
       controller()->selected_date_midnight());
@@ -267,7 +268,7 @@
   RefetchEvents(start_of_month, event_list.get());
 
   // Shows 0 events and shows open in google calendar button after the refresh.
-  EXPECT_EQ(1u, content_view()->children().size());
+  EXPECT_EQ(1u, GetEmptyContentViewSize());
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_EVENTS),
             GetEmptyLabel());
 
@@ -276,7 +277,7 @@
   RefetchEvents(start_of_month, event_list.get());
 
   // Shows 1 event after the refresh.
-  EXPECT_EQ(1u, content_view()->children().size());
+  EXPECT_EQ(1u, GetContentViewSize());
   EXPECT_EQ(u"summary_0", GetSummary(0)->GetText());
 }
 
diff --git a/ash/system/time/calendar_up_next_pixeltest.cc b/ash/system/time/calendar_up_next_pixeltest.cc
index 0318549..ed905a2 100644
--- a/ash/system/time/calendar_up_next_pixeltest.cc
+++ b/ash/system/time/calendar_up_next_pixeltest.cc
@@ -24,7 +24,9 @@
 std::unique_ptr<google_apis::calendar::CalendarEvent> CreateEvent(
     const base::Time start_time,
     const base::Time end_time,
-    const char* summary = "Event with long name that should ellipsis",
+    const char* summary =
+        "Event with a very very very very very very very long name that should "
+        "ellipsis",
     bool all_day_event = false,
     const GURL video_conference_url = GURL()) {
   return calendar_test_utils::CreateEvent(
@@ -133,7 +135,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "calendar_up_next_single_upcoming_event",
-      /*revision_number=*/2, Widget()));
+      /*revision_number=*/3, Widget()));
 }
 
 TEST_F(CalendarUpNextViewPixelTest,
@@ -157,7 +159,7 @@
 
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "calendar_up_next_multiple_upcoming_events",
-      /*revision_number=*/2, Widget()));
+      /*revision_number=*/3, Widget()));
 }
 
 TEST_F(
diff --git a/ash/system/time/calendar_up_next_view.cc b/ash/system/time/calendar_up_next_view.cc
index c7b94d6c4..601aba55 100644
--- a/ash/system/time/calendar_up_next_view.cc
+++ b/ash/system/time/calendar_up_next_view.cc
@@ -272,7 +272,8 @@
   // scrollable, we need to set it's preferred size here so it's bigger
   // than the `scroll_view_` and therefore scrolls. See
   // https://crbug.com/1384131.
-  if (content_view_) {
+  // For single events we want it to be constrained to the scroll view width.
+  if (content_view_ && displayed_events_.size() > 1) {
     content_view_->SizeToPreferredSize();
   }
 
diff --git a/ash/system/time/calendar_up_next_view_unittest.cc b/ash/system/time/calendar_up_next_view_unittest.cc
index ad18f2a..bd7b2209 100644
--- a/ash/system/time/calendar_up_next_view_unittest.cc
+++ b/ash/system/time/calendar_up_next_view_unittest.cc
@@ -27,9 +27,10 @@
     const base::Time start_time,
     const base::Time end_time,
     bool all_day_event = false,
-    const GURL video_conference_url = GURL()) {
+    const GURL video_conference_url = GURL(),
+    const char* summary = "summary") {
   return calendar_test_utils::CreateEvent(
-      "id_0", "summary_0", start_time, end_time,
+      "id_0", summary, start_time, end_time,
       google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
       google_apis::calendar::CalendarEvent::ResponseStatus::kAccepted,
       all_day_event, video_conference_url);
@@ -38,7 +39,8 @@
 std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>>
 CreateUpcomingEvents(int event_count = 1,
                      bool all_day_event = false,
-                     const GURL video_conference_url = GURL()) {
+                     const GURL video_conference_url = GURL(),
+                     const char* summary = "summary") {
   std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events;
   auto event_in_ten_mins_start_time =
       base::subtle::TimeNowIgnoringOverride().LocalMidnight() +
@@ -48,7 +50,7 @@
   for (int i = 0; i < event_count; ++i) {
     events.push_back(CreateEvent(event_in_ten_mins_start_time,
                                  event_in_ten_mins_end_time, all_day_event,
-                                 video_conference_url));
+                                 video_conference_url, summary));
   }
 
   return events;
@@ -201,7 +203,7 @@
 }
 
 TEST_F(CalendarUpNextViewTest,
-       ShouldShowSingleEventTakingUpFullWidthOfParentView) {
+       ShouldShowSingleEventWithShortTitleTakingUpFullWidthOfParentView) {
   // Set time override.
   base::subtle::ScopedTimeClockOverrides time_override(
       []() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
@@ -216,6 +218,24 @@
 }
 
 TEST_F(CalendarUpNextViewTest,
+       ShouldShowSingleEventWithLongTitleTakingUpFullWidthOfParentView) {
+  // Set time override.
+  base::subtle::ScopedTimeClockOverrides time_override(
+      []() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
+      nullptr, nullptr);
+
+  // Create UpNextView with a single upcoming event.
+  CreateUpNextView(
+      CreateUpcomingEvents(1, false, GURL(),
+                           "Meeting title with really long long long long long "
+                           "long long name that should ellipsis"));
+
+  EXPECT_EQ(GetContentsView()->children().size(), size_t(1));
+  EXPECT_EQ(GetContentsView()->children()[0]->width(),
+            GetScrollView()->width());
+}
+
+TEST_F(CalendarUpNextViewTest,
        ShouldScrollLeftAndRightWhenScrollButtonsArePressed) {
   // Set time override.
   base::subtle::ScopedTimeClockOverrides time_override(
diff --git a/ash/system/time/calendar_view_unittest.cc b/ash/system/time/calendar_view_unittest.cc
index b0af4de..31a00e7 100644
--- a/ash/system/time/calendar_view_unittest.cc
+++ b/ash/system/time/calendar_view_unittest.cc
@@ -2126,7 +2126,11 @@
 // bubble.
 class CalendarViewWithMessageCenterTest : public AshTestBase {
  public:
-  CalendarViewWithMessageCenterTest() = default;
+  CalendarViewWithMessageCenterTest() {
+    // This test case is only valid without `kQsRevamp` enabled. Once the
+    // `kQsRevamp` feature flag is deleted, this test can be deleted.
+    scoped_feature_list_.InitAndDisableFeature(features::kQsRevamp);
+  }
   CalendarViewWithMessageCenterTest(const CalendarViewWithMessageCenterTest&) =
       delete;
   CalendarViewWithMessageCenterTest& operator=(
@@ -2192,6 +2196,9 @@
     ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
     generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_SHIFT_DOWN);
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Tests `Tab` / `Shift+Tab` navigation within two bubbles.
diff --git a/ash/system/unified/buttons.cc b/ash/system/unified/buttons.cc
index c26479143..230ee67 100644
--- a/ash/system/unified/buttons.cc
+++ b/ash/system/unified/buttons.cc
@@ -43,9 +43,8 @@
 namespace {
 
 // Constants used with QsRevamp.
-constexpr int kManagedStateHighlightRadius = 16;
-constexpr SkScalar kManagedStateCornerRadii[] = {16, 16, 16, 16,
-                                                 16, 16, 16, 16};
+constexpr int kManagedStateCornerRadius = 16;
+constexpr float kManagedStateStrokeWidth = 1.0f;
 constexpr auto kManagedStateBorderInsets = gfx::Insets::TLBR(0, 12, 0, 12);
 constexpr gfx::Size kManagedStateImageSize(20, 20);
 
@@ -111,7 +110,7 @@
   if (features::IsQsRevampEnabled()) {
     views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
     views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
-                                                  kManagedStateHighlightRadius);
+                                                  kManagedStateCornerRadius);
   } else {
     views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF);
   }
@@ -150,11 +149,12 @@
   cc::PaintFlags flags;
   flags.setColor(GetColorProvider()->GetColor(cros_tokens::kCrosSysSeparator));
   flags.setStyle(cc::PaintFlags::kStroke_Style);
+  flags.setStrokeWidth(kManagedStateStrokeWidth);
   flags.setAntiAlias(true);
-  canvas->DrawPath(
-      SkPath().addRoundRect(gfx::RectToSkRect(GetLocalBounds()),
-                            kManagedStateCornerRadii, SkPathDirection::kCW),
-      flags);
+  const float half_stroke_width = kManagedStateStrokeWidth / 2.0f;
+  gfx::RectF bounds(GetLocalBounds());
+  bounds.Inset(half_stroke_width);
+  canvas->DrawRoundRect(bounds, kManagedStateCornerRadius, flags);
 }
 
 BEGIN_METADATA(ManagedStateView, views::Button)
diff --git a/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.h b/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.h
index 8187af6..0a3da04 100644
--- a/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.h
+++ b/ash/webui/shortcut_customization_ui/backend/accelerator_layout_table.h
@@ -1128,6 +1128,8 @@
      mojom::AcceleratorSource::kAsh},
 
     // Accessibility
+    // TODO(jimmyxgong): Allow this to be modifiable but only after revising the
+    // notification that hardcodes ctrl + alt + z into the notification message.
     {AcceleratorAction::kToggleSpokenFeedback,
      IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_SPOKEN_FEEDBACK,
      mojom::AcceleratorCategory::kAccessibility,
@@ -1138,31 +1140,31 @@
      IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_HIGH_CONTRAST,
      mojom::AcceleratorCategory::kAccessibility,
      mojom::AcceleratorSubcategory::kVisibility,
-     /*locked=*/true, mojom::AcceleratorLayoutStyle::kDefault,
+     /*locked=*/false, mojom::AcceleratorLayoutStyle::kDefault,
      mojom::AcceleratorSource::kAsh},
     {AcceleratorAction::kToggleDockedMagnifier,
      IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_DOCKED_MAGNIFIER,
      mojom::AcceleratorCategory::kAccessibility,
      mojom::AcceleratorSubcategory::kVisibility,
-     /*locked=*/true, mojom::AcceleratorLayoutStyle::kDefault,
+     /*locked=*/false, mojom::AcceleratorLayoutStyle::kDefault,
      mojom::AcceleratorSource::kAsh},
     {AcceleratorAction::kToggleFullscreenMagnifier,
      IDS_ASH_ACCELERATOR_DESCRIPTION_TOGGLE_FULLSCREEN_MAGNIFIER,
      mojom::AcceleratorCategory::kAccessibility,
      mojom::AcceleratorSubcategory::kVisibility,
-     /*locked=*/true, mojom::AcceleratorLayoutStyle::kDefault,
+     /*locked=*/false, mojom::AcceleratorLayoutStyle::kDefault,
      mojom::AcceleratorSource::kAsh},
     {AcceleratorAction::kMagnifierZoomIn,
      IDS_ASH_ACCELERATOR_DESCRIPTION_MAGNIFIER_ZOOM_IN,
      mojom::AcceleratorCategory::kAccessibility,
      mojom::AcceleratorSubcategory::kVisibility,
-     /*locked=*/true, mojom::AcceleratorLayoutStyle::kDefault,
+     /*locked=*/false, mojom::AcceleratorLayoutStyle::kDefault,
      mojom::AcceleratorSource::kAsh},
     {AcceleratorAction::kMagnifierZoomOut,
      IDS_ASH_ACCELERATOR_DESCRIPTION_MAGNIFIER_ZOOM_OUT,
      mojom::AcceleratorCategory::kAccessibility,
      mojom::AcceleratorSubcategory::kVisibility,
-     /*locked=*/true, mojom::AcceleratorLayoutStyle::kDefault,
+     /*locked=*/false, mojom::AcceleratorLayoutStyle::kDefault,
      mojom::AcceleratorSource::kAsh},
     {NonConfigurableActions::kAmbientSwitchFocus,
      IDS_AMBIENT_ACCELERATOR_DESCRIPTION_SWITCH_FOCUS,
@@ -1180,7 +1182,7 @@
      IDS_ASH_ACCELERATOR_DESCRIPTION_FOCUS_SHELF,
      mojom::AcceleratorCategory::kAccessibility,
      mojom::AcceleratorSubcategory::kAccessibilityNavigation,
-     /*locked=*/true, mojom::AcceleratorLayoutStyle::kDefault,
+     /*locked=*/false, mojom::AcceleratorLayoutStyle::kDefault,
      mojom::AcceleratorSource::kAsh},
     {NonConfigurableActions::kAmbientHighlightNextItemOnShelf,
      IDS_AMBIENT_ACCELERATOR_DESCRIPTION_HIGHLIGHT_NEXT_ITEM_ON_SHELF,
diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc
index 8e45ac7..773368c 100644
--- a/base/allocator/partition_alloc_features.cc
+++ b/base/allocator/partition_alloc_features.cc
@@ -166,6 +166,30 @@
     &kPartitionAllocBackupRefPtr, "brp-mode", BackupRefPtrMode::kEnabled,
     &kBackupRefPtrModeOptions};
 
+BASE_FEATURE(kPartitionAllocMemoryTagging,
+             "PartitionAllocMemoryTagging",
+             FEATURE_DISABLED_BY_DEFAULT);
+
+constexpr FeatureParam<MemtagMode>::Option kMemtagModeOptions[] = {
+    {MemtagMode::kSync, "sync"},
+    {MemtagMode::kAsync, "async"}};
+
+const base::FeatureParam<MemtagMode> kMemtagModeParam{
+    &kPartitionAllocMemoryTagging, "memtag-mode", MemtagMode::kAsync,
+    &kMemtagModeOptions};
+
+constexpr FeatureParam<MemoryTaggingEnabledProcesses>::Option
+    kMemoryTaggingEnabledProcessesOptions[] = {
+        {MemoryTaggingEnabledProcesses::kBrowserOnly, "browser-only"},
+        {MemoryTaggingEnabledProcesses::kNonRenderer, "non-renderer"},
+        {MemoryTaggingEnabledProcesses::kAllProcesses, "all-processes"}};
+
+const base::FeatureParam<MemoryTaggingEnabledProcesses>
+    kMemoryTaggingEnabledProcessesParam{
+        &kPartitionAllocMemoryTagging, "enabled-processes",
+        MemoryTaggingEnabledProcesses::kBrowserOnly,
+        &kMemoryTaggingEnabledProcessesOptions};
+
 const base::FeatureParam<bool> kBackupRefPtrAsanEnableDereferenceCheckParam{
     &kPartitionAllocBackupRefPtr, "asan-enable-dereference-check", true};
 const base::FeatureParam<bool> kBackupRefPtrAsanEnableExtractionCheckParam{
diff --git a/base/allocator/partition_alloc_features.h b/base/allocator/partition_alloc_features.h
index 597879f..7aa4d97 100644
--- a/base/allocator/partition_alloc_features.h
+++ b/base/allocator/partition_alloc_features.h
@@ -119,6 +119,22 @@
   k16B,
 };
 
+enum class MemtagMode {
+  // memtagMode will be SYNC.
+  kSync,
+  // memtagMode will be ASYNC.
+  kAsync,
+};
+
+enum class MemoryTaggingEnabledProcesses {
+  // Memory tagging enabled only in the browser process.
+  kBrowserOnly,
+  // Memory tagging enabled in all processes, except renderer.
+  kNonRenderer,
+  // Memory tagging enabled in all processes.
+  kAllProcesses,
+};
+
 enum class AlternateBucketDistributionMode : uint8_t {
   kDefault,
   kDenser,
@@ -131,6 +147,10 @@
     kBackupRefPtrModeParam;
 extern const BASE_EXPORT base::FeatureParam<BackupRefPtrRefCountSize>
     kBackupRefPtrRefCountSizeParam;
+BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocMemoryTagging);
+extern const BASE_EXPORT base::FeatureParam<MemtagMode> kMemtagModeParam;
+extern const BASE_EXPORT base::FeatureParam<MemoryTaggingEnabledProcesses>
+    kMemoryTaggingEnabledProcessesParam;
 extern const BASE_EXPORT base::FeatureParam<bool>
     kBackupRefPtrAsanEnableDereferenceCheckParam;
 extern const BASE_EXPORT base::FeatureParam<bool>
diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc
index 74e1213..503e132 100644
--- a/base/allocator/partition_alloc_support.cc
+++ b/base/allocator/partition_alloc_support.cc
@@ -26,6 +26,7 @@
 #include "base/allocator/partition_allocator/thread_cache.h"
 #include "base/at_exit.h"
 #include "base/check.h"
+#include "base/cpu.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/debug/stack_trace.h"
 #include "base/debug/task_trace.h"
@@ -888,6 +889,33 @@
 }
 
 // static
+bool PartitionAllocSupport::ShouldEnableMemoryTagging(
+    const std::string& process_type) {
+  if (!base::CPU::GetInstanceNoAllocation().has_mte()) {
+    return false;
+  }
+
+  DCHECK(base::FeatureList::GetInstance());
+  if (!base::FeatureList::IsEnabled(
+          base::features::kPartitionAllocMemoryTagging)) {
+    return false;
+  }
+  switch (base::features::kMemoryTaggingEnabledProcessesParam.Get()) {
+    case base::features::MemoryTaggingEnabledProcesses::kBrowserOnly:
+      return process_type.empty();
+    case base::features::MemoryTaggingEnabledProcesses::kNonRenderer:
+      return process_type != switches::kRendererProcess;
+    case base::features::MemoryTaggingEnabledProcesses::kAllProcesses:
+      return true;
+  }
+}
+
+// static
+bool PartitionAllocSupport::ShouldEnableMemoryTaggingInRendererProcess() {
+  return ShouldEnableMemoryTagging(switches::kRendererProcess);
+}
+
+// static
 PartitionAllocSupport::BrpConfiguration
 PartitionAllocSupport::GetBrpConfiguration(const std::string& process_type) {
   // TODO(bartekn): Switch to DCHECK once confirmed there are no issues.
@@ -1147,15 +1175,54 @@
                 .Get()
           : base::features::AlternateBucketDistributionMode::kDefault;
 
+#if PA_CONFIG(HAS_MEMORY_TAGGING)
+  bool enable_memory_tagging = ShouldEnableMemoryTagging(process_type);
+#if BUILDFLAG(IS_ANDROID)
+  if (enable_memory_tagging) {
+    partition_alloc::TagViolationReportingMode reporting_mode;
+    switch (base::features::kMemtagModeParam.Get()) {
+      case base::features::MemtagMode::kSync:
+        reporting_mode =
+            partition_alloc::TagViolationReportingMode::kSynchronous;
+        break;
+      case base::features::MemtagMode::kAsync:
+        reporting_mode =
+            partition_alloc::TagViolationReportingMode::kAsynchronous;
+        break;
+    }
+    partition_alloc::internal::ChangeMemoryTaggingModeForAllThreadsPerProcess(
+        reporting_mode);
+    CHECK_EQ(partition_alloc::internal::GetMemoryTaggingModeForCurrentThread(),
+             reporting_mode);
+  } else if (base::CPU::GetInstanceNoAllocation().has_mte()) {
+    partition_alloc::internal::ChangeMemoryTaggingModeForAllThreadsPerProcess(
+        partition_alloc::TagViolationReportingMode::kDisabled);
+    CHECK_EQ(partition_alloc::internal::GetMemoryTaggingModeForCurrentThread(),
+             partition_alloc::TagViolationReportingMode::kDisabled);
+  }
+#endif  // BUILDFLAG(IS_ANDROID)
+#else   // PA_CONFIG(HAS_MEMORY_TAGGING)
+  bool enable_memory_tagging = false;
+#endif  // PA_CONFIG(HAS_MEMORY_TAGGING)
+
   allocator_shim::ConfigurePartitions(
       allocator_shim::EnableBrp(brp_config.enable_brp),
       allocator_shim::EnableBrpPartitionMemoryReclaimer(
           brp_config.enable_brp_partition_memory_reclaimer),
-      allocator_shim::SplitMainPartition(brp_config.split_main_partition),
+      allocator_shim::EnableMemoryTagging(enable_memory_tagging),
+      allocator_shim::SplitMainPartition(brp_config.split_main_partition ||
+                                         enable_memory_tagging),
       allocator_shim::UseDedicatedAlignedPartition(
           brp_config.use_dedicated_aligned_partition),
       brp_config.ref_count_size,
       allocator_shim::AlternateBucketDistribution(bucket_distribution));
+
+  const uint32_t extras_size = allocator_shim::GetMainPartitionRootExtrasSize();
+  // As per description, extras are optional and are expected not to
+  // exceed (cookie + max(BRP ref-count)) == 16 + 16 == 32 bytes.
+  // 100 is a reasonable cap for this value.
+  UmaHistogramCounts100("Memory.PartitionAlloc.PartitionRoot.ExtrasSize",
+                        int(extras_size));
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
   // If BRP is not enabled, check if any of PCScan flags is enabled.
diff --git a/base/allocator/partition_alloc_support.h b/base/allocator/partition_alloc_support.h
index 774515b..2d4ebf3 100644
--- a/base/allocator/partition_alloc_support.h
+++ b/base/allocator/partition_alloc_support.h
@@ -97,6 +97,13 @@
 
   static BrpConfiguration GetBrpConfiguration(const std::string& process_type);
 
+  // Returns true if memory tagging should be enabled if available for the given
+  // process type. May be called multiple times per process.
+  static bool ShouldEnableMemoryTagging(const std::string& process_type);
+
+  // For calling from within third_party/blink/.
+  static bool ShouldEnableMemoryTaggingInRendererProcess();
+
  private:
   PartitionAllocSupport();
 
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index a15f634..05e46a8 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -396,6 +396,13 @@
             .backup_ref_ptr = PartitionOptions::BackupRefPtr::kEnabled,
 #endif
             .ref_count_size = GetParam().ref_count_size,
+#if PA_CONFIG(HAS_MEMORY_TAGGING)
+            .memory_tagging =
+                partition_alloc::internal::base::CPU::GetInstanceNoAllocation()
+                        .has_mte()
+                    ? PartitionOptions::MemoryTagging::kEnabled
+                    : PartitionOptions::MemoryTagging::kDisabled,
+#endif
           },
           PartitionTestOptions{.use_memory_reclaimer = true,
                                .uncap_empty_slot_span_memory = true,
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index d72c498a5..8a97bb0d 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -974,18 +974,21 @@
         slot_size <= kMaxMemoryTaggingSize);
   }
 
+#if PA_CONFIG(HAS_MEMORY_TAGGING)
   const bool use_tagging =
       root->IsMemoryTaggingEnabled() && slot_size <= kMaxMemoryTaggingSize;
   if (PA_LIKELY(use_tagging)) {
     // Ensure the MTE-tag of the memory pointed by |return_slot| is unguessable.
     TagMemoryRangeRandomly(return_slot, slot_size);
   }
+#endif  // PA_CONFIG(HAS_MEMORY_TAGGING)
   // Add all slots that fit within so far committed pages to the free list.
   PartitionFreelistEntry* prev_entry = nullptr;
   uintptr_t next_slot_end = next_slot + slot_size;
   size_t free_list_entries_added = 0;
   while (next_slot_end <= commit_end) {
     void* next_slot_ptr;
+#if PA_CONFIG(HAS_MEMORY_TAGGING)
     if (PA_LIKELY(use_tagging)) {
       // Ensure the MTE-tag of the memory pointed by other provisioned slot is
       // unguessable. They will be returned to the app as is, and the MTE-tag
@@ -995,6 +998,9 @@
       // No MTE-tagging for larger slots, just cast.
       next_slot_ptr = reinterpret_cast<void*>(next_slot);
     }
+#else  // PA_CONFIG(HAS_MEMORY_TAGGING)
+    next_slot_ptr = reinterpret_cast<void*>(next_slot);
+#endif
     auto* entry = PartitionFreelistEntry::EmplaceAndInitNull(next_slot_ptr);
     if (!slot_span->get_freelist_head()) {
       PA_DCHECK(!prev_entry);
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 38fb9e97..c12a33b 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -921,6 +921,16 @@
          PartitionOptions::UseConfigurablePool::kIfAvailable) &&
         IsConfigurablePoolAvailable();
     PA_DCHECK(!flags.use_configurable_pool || IsConfigurablePoolAvailable());
+#if PA_CONFIG(HAS_MEMORY_TAGGING)
+    flags.memory_tagging_enabled_ =
+        opts.memory_tagging == PartitionOptions::MemoryTagging::kEnabled;
+    // Memory tagging is not supported in the configurable pool because MTE
+    // stores tagging information in the high bits of the pointer, it causes
+    // issues with components like V8's ArrayBuffers which use custom pointer
+    // representations. All custom representations encountered so far rely on an
+    // "is in configurable pool?" check, so we use that as a proxy.
+    PA_CHECK(!flags.memory_tagging_enabled_ || !flags.use_configurable_pool);
+#endif
 
     // brp_enabled() is not supported in the configurable pool because
     // BRP requires objects to be in a different Pool.
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index eefca65..a0e23e1 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -180,6 +180,11 @@
     kIfAvailable,
   };
 
+  enum class MemoryTagging : uint8_t {
+    kEnabled,
+    kDisabled,
+  };
+
   AlignedAlloc aligned_alloc = AlignedAlloc::kDisallowed;
   ThreadCache thread_cache = ThreadCache::kDisabled;
   Quarantine quarantine = Quarantine::kDisallowed;
@@ -187,6 +192,7 @@
   BackupRefPtr backup_ref_ptr = BackupRefPtr::kDisabled;
   UseConfigurablePool use_configurable_pool = UseConfigurablePool::kNo;
   size_t ref_count_size;
+  MemoryTagging memory_tagging = MemoryTagging::kDisabled;
 #if BUILDFLAG(ENABLE_THREAD_ISOLATION)
   ThreadIsolationOption thread_isolation;
 #endif
@@ -250,7 +256,9 @@
 #endif  // PA_CONFIG(ENABLE_MAC11_MALLOC_SIZE_HACK)
 #endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
     bool use_configurable_pool;
-
+#if PA_CONFIG(HAS_MEMORY_TAGGING)
+    bool memory_tagging_enabled_ = false;
+#endif
 #if BUILDFLAG(ENABLE_THREAD_ISOLATION)
     ThreadIsolationOption thread_isolation;
 #endif
@@ -1150,16 +1158,11 @@
   FreeNoHooks(object);
 }
 
-// Returns whether MTE is supported for this partition root. Because MTE stores
-// tagging information in the high bits of the pointer, it causes issues with
-// components like V8's ArrayBuffers which use custom pointer representations.
-// All custom representations encountered so far rely on an "is in configurable
-// pool?" check, so we use that as a proxy.
 template <bool thread_safe>
 PA_ALWAYS_INLINE bool PartitionRoot<thread_safe>::IsMemoryTaggingEnabled()
     const {
 #if PA_CONFIG(HAS_MEMORY_TAGGING)
-  return !flags.use_configurable_pool;
+  return flags.memory_tagging_enabled_;
 #else
   return false;
 #endif
diff --git a/base/allocator/partition_allocator/shim/allocator_shim.h b/base/allocator/partition_allocator/shim/allocator_shim.h
index 2463e52a..9af7726 100644
--- a/base/allocator/partition_allocator/shim/allocator_shim.h
+++ b/base/allocator/partition_allocator/shim/allocator_shim.h
@@ -182,6 +182,8 @@
 using EnableBrp = base::StrongAlias<class EnableBrpTag, bool>;
 using EnableBrpPartitionMemoryReclaimer =
     base::StrongAlias<class EnableBrpPartitionMemoryReclaimerTag, bool>;
+using EnableMemoryTagging =
+    base::StrongAlias<class EnableMemoryTaggingTag, bool>;
 using SplitMainPartition = base::StrongAlias<class SplitMainPartitionTag, bool>;
 using UseDedicatedAlignedPartition =
     base::StrongAlias<class UseDedicatedAlignedPartitionTag, bool>;
@@ -194,11 +196,14 @@
 BASE_EXPORT void ConfigurePartitions(
     EnableBrp enable_brp,
     EnableBrpPartitionMemoryReclaimer enable_brp_memory_reclaimer,
+    EnableMemoryTagging enable_memory_tagging,
     SplitMainPartition split_main_partition,
     UseDedicatedAlignedPartition use_dedicated_aligned_partition,
     size_t ref_count_size,
     AlternateBucketDistribution use_alternate_bucket_distribution);
 
+BASE_EXPORT uint32_t GetMainPartitionRootExtrasSize();
+
 #if BUILDFLAG(USE_STARSCAN)
 BASE_EXPORT void EnablePCScan(partition_alloc::internal::PCScan::InitConfig);
 #endif
diff --git a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
index a8c4afc..62e0d41e 100644
--- a/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -549,6 +549,7 @@
 void ConfigurePartitions(
     EnableBrp enable_brp,
     EnableBrpPartitionMemoryReclaimer enable_brp_memory_reclaimer,
+    EnableMemoryTagging enable_memory_tagging,
     SplitMainPartition split_main_partition,
     UseDedicatedAlignedPartition use_dedicated_aligned_partition,
     size_t ref_count_size,
@@ -612,7 +613,11 @@
                   ? partition_alloc::PartitionOptions::BackupRefPtr::kEnabled
                   : partition_alloc::PartitionOptions::BackupRefPtr::kDisabled,
           .ref_count_size = ref_count_size,
-      });
+          .memory_tagging =
+              enable_memory_tagging
+                  ? partition_alloc::PartitionOptions::MemoryTagging::kEnabled
+                  : partition_alloc::PartitionOptions::MemoryTagging::
+                        kDisabled});
   partition_alloc::ThreadSafePartitionRoot* new_root = new_main_partition.get();
 
   partition_alloc::ThreadSafePartitionRoot* new_aligned_root;
@@ -676,6 +681,16 @@
   }
 }
 
+// No synchronization provided: `PartitionRoot.flags` is only written
+// to in `PartitionRoot::Init()`.
+uint32_t GetMainPartitionRootExtrasSize() {
+#if PA_CONFIG(EXTRAS_REQUIRED)
+  return g_root.Get()->flags.extras_size;
+#else
+  return 0;
+#endif  // PA_CONFIG(EXTRAS_REQUIRED)
+}
+
 #if BUILDFLAG(USE_STARSCAN)
 void EnablePCScan(partition_alloc::internal::PCScan::InitConfig config) {
   partition_alloc::internal::base::PlatformThread::SetThreadNameHook(
diff --git a/base/android/jni_generator/common.py b/base/android/jni_generator/common.py
new file mode 100644
index 0000000..f9eefff
--- /dev/null
+++ b/base/android/jni_generator/common.py
@@ -0,0 +1,10 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Common logic needed by other modules."""
+
+
+def EscapeClassName(fully_qualified_class):
+  """Returns an escaped string concatenating the Java package and class."""
+  escaped = fully_qualified_class.replace('_', '_1')
+  return escaped.replace('/', '_').replace('$', '_00024')
diff --git a/base/android/jni_generator/golden/SampleForAnnotationProcessor_manual.h.golden b/base/android/jni_generator/golden/SampleForAnnotationProcessor_manual.h.golden
index 73d6eeb..18805d47 100644
--- a/base/android/jni_generator/golden/SampleForAnnotationProcessor_manual.h.golden
+++ b/base/android/jni_generator/golden/SampleForAnnotationProcessor_manual.h.golden
@@ -228,7 +228,7 @@
 
 // Step 4: Registration function.
 
-namespace None {
+namespace  {
 
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
@@ -240,6 +240,6 @@
   return true;
 }
 
-}  // namespace None
+}  // namespace 
 
 #endif  // TEMP_DIR_HEADER_
diff --git a/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_manual.h.golden b/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_manual.h.golden
index 2140fd1..a1341e7 100644
--- a/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_manual.h.golden
+++ b/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_manual.h.golden
@@ -248,7 +248,7 @@
 
 // Step 4: Registration function.
 
-namespace None {
+namespace  {
 
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
@@ -260,6 +260,6 @@
   return true;
 }
 
-}  // namespace None
+}  // namespace 
 
 #endif  // TEMP_DIR_HEADER_
diff --git a/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_proxy_manual.h.golden b/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_proxy_manual.h.golden
index 1fce3940..17d1645 100644
--- a/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_proxy_manual.h.golden
+++ b/base/android/jni_generator/golden/SampleForAnnotationProcessor_package_prefix_proxy_manual.h.golden
@@ -175,7 +175,7 @@
 
 // Step 4: Registration function.
 
-namespace None {
+namespace  {
 
 bool RegisterNatives(JNIEnv* env) {
   // Register natives in a proxy.
@@ -187,6 +187,6 @@
   return true;
 }
 
-}  // namespace None
+}  // namespace 
 
 #endif  // TEMP_DIR_HEADER_
diff --git a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleProxyEdgeCases.java b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleProxyEdgeCases.java
index f87573a..a809edd0 100644
--- a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleProxyEdgeCases.java
+++ b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleProxyEdgeCases.java
@@ -6,6 +6,7 @@
 
 import org.chromium.example.jni_generator.Boolean;
 
+@SomeAnnotation("that contains class Foo ")
 class SampleProxyEdgeCases {
     enum Integer {}
 
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 49eb9f6..1cb0b13 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -8,6 +8,7 @@
 import argparse
 import base64
 import collections
+import dataclasses
 import hashlib
 import os
 import re
@@ -19,6 +20,13 @@
 import textwrap
 import zipfile
 
+# TODO(crbug.com/1410871): Move nested classes into separate files and remove
+#     this hack (or create a separate main.py).
+# https://stackoverflow.com/questions/15159854/python-namespace-main-class-not-isinstance-of-package-class
+if __name__ == '__main__':
+  import jni_generator
+  sys.exit(jni_generator.main())
+
 _FILE_DIR = os.path.dirname(__file__)
 _CHROMIUM_SRC = os.path.join(_FILE_DIR, os.pardir, os.pardir, os.pardir)
 _BUILD_ANDROID_GYP = os.path.join(_CHROMIUM_SRC, 'build', 'android', 'gyp')
@@ -30,38 +38,19 @@
 from util import build_utils
 import action_helpers  # build_utils adds //build to sys.path.
 
+import common
+import models
+import parse
+import proxy
 import type_resolver
 
-# Match single line comments, multiline comments, character literals, and
-# double-quoted strings.
-_COMMENT_REMOVER_REGEX = re.compile(
-    r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
-    re.DOTALL | re.MULTILINE)
 
 _EXTRACT_NATIVES_REGEX = re.compile(
     r'(@NativeClassQualifiedName'
     r'\(\"(?P<native_class_name>\S*?)\"\)\s+)?'
-    r'(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native '
-    r'(?P<return_type>\S*) '
-    r'(?P<name>native\w+)\((?P<params>.*?)\);')
-
-_MAIN_DEX_REGEX = re.compile(r'^\s*(?:@(?:\w+\.)*\w+\s+)*@MainDex\b',
-                             re.MULTILINE)
-
-# Matches on method declarations unlike _EXTRACT_NATIVES_REGEX
-# doesn't require name to be prefixed with native, and does not
-# require a native qualifier.
-_EXTRACT_METHODS_REGEX = re.compile(
-    r'(@NativeClassQualifiedName\(\"(?P<native_class_name>\S*?)\"\)\s*)?'
-    r'(?P<qualifiers>'
-    r'((public|private|static|final|abstract|protected|native)\s*)*)\s+'
+    r'(?P<qualifiers>\w+\s\w+|\w+|\s+)\s*native\s+'
     r'(?P<return_type>\S*)\s+'
-    r'(?P<name>\w+)\((?P<params>.*?)\);',
-    flags=re.DOTALL)
-
-_NATIVE_PROXY_EXTRACTION_REGEX = re.compile(
-    r'@NativeMethods(?:\(\s*"(?P<module_name>\w+)"\s*\))?[\S\s]+?interface\s*'
-    r'(?P<interface_name>\w*)\s*(?P<interface_body>{(\s*.*)+?\s*})')
+    r'(?P<name>native\w+)\((?P<params>.*?)\);', re.DOTALL)
 
 # Use 100 columns rather than 80 because it makes many lines more readable.
 _WRAP_LINE_LENGTH = 100
@@ -97,11 +86,11 @@
 }
 
 
-class ParseError(Exception):
+class ParseError(SyntaxError):
   """Exception thrown when we can't parse the input file."""
 
   def __init__(self, description, *context_lines):
-    Exception.__init__(self)
+    super().__init__()
     self.description = description
     self.context_lines = context_lines
 
@@ -110,38 +99,42 @@
     return '***\nERROR: %s\n\n%s\n***' % (self.description, context)
 
 
-class Param(object):
-  """Describes a param for a method, either java or native."""
-
-  def __init__(self, **kwargs):
-    self.annotations = kwargs.get('annotations', [])
-    self.datatype = kwargs['datatype']
-    self.name = kwargs['name']
-
-
 class NativeMethod(object):
   """Describes a C/C++ method that is called by Java code"""
 
   def __init__(self, **kwargs):
-    self.static = kwargs['static']
     self.return_type = kwargs['return_type']
     self.params = kwargs['params']
     self.is_proxy = kwargs.get('is_proxy', False)
+    self.static = kwargs.get('static', self.is_proxy)
 
     self.name = kwargs['name']
-    if self.is_proxy:
-      # Proxy methods don't have a native prefix so the first letter is
-      # lowercase. But we still want the CPP declaration to use upper camel
-      # case for the method name.
-      self.name = self.name[0].upper() + self.name[1:]
-
-    self.proxy_name = kwargs.get('proxy_name', self.name)
-    self.hashed_proxy_name = kwargs.get('hashed_proxy_name', None)
-    self.switch_num = None
+    # Proxy methods don't have a native prefix so the first letter is
+    # lowercase. But we still want the CPP declaration to use upper camel
+    # case for the method name.
+    self.cpp_name = self.name[0].upper() + self.name[1:]
+    self.is_test_only = _NameIsTestOnly(self.name)
 
     if self.params:
       assert type(self.params) is list
-      assert type(self.params[0]) is Param
+      assert type(self.params[0]) is models.Param
+
+    if self.is_proxy:
+      self.proxy_params = [
+          dataclasses.replace(p, datatype=JavaTypeToProxyCast(p.datatype))
+          for p in self.params
+      ]
+      self.proxy_return_type = JavaTypeToProxyCast(self.return_type)
+      self.proxy_return_and_signature = (self.proxy_return_type,
+                                         tuple(p.datatype
+                                               for p in self.proxy_params))
+
+      self.proxy_name, self.hashed_proxy_name = proxy.create_method_names(
+          kwargs['java_class'], self.name, self.is_test_only)
+      self.switch_num = None
+    else:
+      self.proxy_params = self.params
+      self.proxy_return_type = self.return_type
 
     ptr_type = kwargs.get('ptr_type', 'int')
     if (self.params and self.params[0].datatype == ptr_type
@@ -156,8 +149,6 @@
     else:
       self.type = 'function'
     self.method_id_var_name = kwargs.get('method_id_var_name', None)
-    self.return_and_signature = (self.return_type,
-                                 tuple(p.datatype for p in self.params))
 
 
 class CalledByNative(object):
@@ -188,7 +179,7 @@
 
 def JavaDataTypeToC(java_type):
   """Returns a C datatype for the given java type."""
-  java_type = _StripGenerics(java_type)
+  java_type = parse.strip_generics(java_type)
   if java_type in JAVA_POD_TYPE_MAP:
     return JAVA_POD_TYPE_MAP[java_type]
   elif java_type in JAVA_TYPE_MAP:
@@ -262,13 +253,6 @@
   return [c_type + ' jcaller']
 
 
-def GetFullyQualifiedClassWithPackagePrefix(fully_qualified_class,
-                                            package_prefix):
-  if package_prefix:
-    return '%s/%s' % (package_prefix.replace(".", "/"), fully_qualified_class)
-  return fully_qualified_class
-
-
 def _GetParamsInDeclaration(native):
   """Returns the params for the forward declaration.
 
@@ -303,134 +287,27 @@
   return ',\n    '.join(params)
 
 
-def _StripGenerics(value):
-  """Strips Java generics from a string."""
-  nest_level = 0  # How deeply we are nested inside the generics.
-  start_index = 0  # Starting index of the last non-generic region.
-  out = []
-
-  for i, c in enumerate(value):
-    if c == '<':
-      if nest_level == 0:
-        out.append(value[start_index:i])
-      nest_level += 1
-    elif c == '>':
-      start_index = i + 1
-      nest_level -= 1
-  out.append(value[start_index:])
-
-  return ''.join(out)
-
-
 def _NameIsTestOnly(name):
   return name.endswith('ForTest') or name.endswith('ForTesting')
 
 
-# TODO(crbug.com/1406605): Move these to top-level functions.
-class JniParams(object):
-  @staticmethod
-  def ParseJavaPSignature(signature_line):
-    prefix = 'Signature: '
-    index = signature_line.find(prefix)
-    if index == -1:
-      prefix = 'descriptor: '
-      index = signature_line.index(prefix)
-    return '"%s"' % signature_line[index + len(prefix):]
-
-  @staticmethod
-  def Parse(params, use_proxy_types=False, from_javap=False):
-    """Parses the params into a list of Param objects."""
-    if not params:
-      return []
-    ret = []
-    params = _StripGenerics(params)
-    for p in params.split(','):
-      items = p.split()
-
-      if 'final' in items:
-        items.remove('final')
-
-      # Remove @Annotations from parameters.
-      annotations = []
-      while items[0].startswith('@'):
-        annotations.append(items[0])
-        del items[0]
-
-      param = Param(
-          annotations=annotations,
-          datatype=items[0],
-          name=(items[1] if len(items) > 1 else 'p%s' % len(ret)),
-      )
-      # Handle varargs.
-      if param.datatype.endswith('...'):
-        param.datatype = param.datatype[:-3] + '[]'
-
-      if from_javap:
-        param.datatype = param.datatype.replace('.', '/')
-
-      if use_proxy_types:
-        param.datatype = JavaTypeToProxyCast(param.datatype)
-
-      ret += [param]
-    return ret
-
-
-def ExtractJNINamespace(contents):
-  re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)')
-  m = re.findall(re_jni_namespace, contents)
-  if not m:
-    return ''
-  return m[0]
-
-
-def ExtractFullyQualifiedJavaClassName(file_name, contents):
-  assert not file_name.endswith('.kt'), (
-      f'Found {file_name}, but Kotlin is not supported by JNI generator.')
-  re_package = re.compile('.*?package (.*?);')
-  matches = re.findall(re_package, contents)
-  if not matches:
-    raise SyntaxError('Unable to find "package" line in %s' % file_name)
-  class_path = matches[0].replace('.', '/')
-  class_name = os.path.splitext(os.path.basename(file_name))[0]
-  return class_path + '/' + class_name
-
-
 def ExtractNatives(contents, ptr_type):
   """Returns a list of dict containing information about a native method."""
-  contents = contents.replace('\n', '')
   natives = []
   for match in _EXTRACT_NATIVES_REGEX.finditer(contents):
-    native = NativeMethod(
-        static='static' in match.group('qualifiers'),
-        native_class_name=match.group('native_class_name'),
-        return_type=match.group('return_type'),
-        name=match.group('name').replace('native', ''),
-        params=JniParams.Parse(match.group('params')),
-        ptr_type=ptr_type)
+    native = NativeMethod(static='static' in match.group('qualifiers'),
+                          native_class_name=match.group('native_class_name'),
+                          return_type=match.group('return_type'),
+                          name=match.group('name').replace('native', ''),
+                          params=parse.parse_param_list(match.group('params')),
+                          ptr_type=ptr_type)
     natives += [native]
   return natives
 
 
-def IsMainDexJavaClass(contents):
-  """Returns True if the class or any of its methods are annotated as @MainDex.
-
-  JNI registration doesn't always need to be completed for non-browser processes
-  since most Java code is only used by the browser process. Classes that are
-  needed by non-browser processes must explicitly be annotated with @MainDex
-  to force JNI registration.
-  """
-  return bool(_MAIN_DEX_REGEX.search(contents))
-
-
-def EscapeClassName(fully_qualified_class):
-  """Returns an escaped string concatenating the Java package and class."""
-  escaped = fully_qualified_class.replace('_', '_1')
-  return escaped.replace('/', '_').replace('$', '_00024')
-
-
 def GetRegistrationFunctionName(fully_qualified_class):
   """Returns the register name with a given class."""
-  return 'RegisterNative_' + EscapeClassName(fully_qualified_class)
+  return 'RegisterNative_' + common.EscapeClassName(fully_qualified_class)
 
 
 def GetStaticCastForReturnType(return_type):
@@ -450,7 +327,7 @@
       'float[]': 'jfloatArray',
       'double[]': 'jdoubleArray'
   }
-  return_type = _StripGenerics(return_type)
+  return_type = parse.strip_generics(return_type)
   ret = type_map.get(return_type, None)
   if ret:
     return ret
@@ -495,30 +372,24 @@
   return ret
 
 
-def GetMangledMethodName(jni_params, name, params, return_type):
+def GetMangledMethodName(resolver, name, params, return_type):
   """Returns a mangled method name for the given signature.
 
      The returned name can be used as a C identifier and will be unique for all
      valid overloads of the same method.
 
-  Args:
-     jni_params: JniParams object.
-     name: string.
-     params: list of Param.
-     return_type: string.
-
   Returns:
       A mangled name.
   """
   mangled_items = []
   for datatype in [return_type] + [x.datatype for x in params]:
-    mangled_items += [GetMangledParam(jni_params.java_to_jni(datatype))]
+    mangled_items += [GetMangledParam(resolver.java_to_jni(datatype))]
   mangled_name = name + '_'.join(mangled_items)
   assert re.match(r'[0-9a-zA-Z_]+', mangled_name)
   return mangled_name
 
 
-def MangleCalledByNatives(jni_params, called_by_natives):
+def MangleCalledByNatives(resolver, called_by_natives):
   """Mangles all the overloads from the call_by_natives list."""
   method_counts = collections.defaultdict(
       lambda: collections.defaultdict(lambda: 0))
@@ -531,7 +402,7 @@
     method_name = called_by_native.name
     method_id_var_name = method_name
     if method_counts[java_class_name][method_name] > 1:
-      method_id_var_name = GetMangledMethodName(jni_params, method_name,
+      method_id_var_name = GetMangledMethodName(resolver, method_name,
                                                 called_by_native.params,
                                                 called_by_native.return_type)
     called_by_native.method_id_var_name = method_id_var_name
@@ -560,13 +431,9 @@
   return re.sub('^(?: {2})+$\n', '', string, flags=re.MULTILINE)
 
 
-def ExtractCalledByNatives(jni_params, contents):
+def ExtractCalledByNatives(resolver, contents):
   """Parses all methods annotated with @CalledByNative.
 
-  Args:
-    jni_params: JniParams object.
-    contents: the contents of the java file.
-
   Returns:
     A list of dict with information about the annotated methods.
     TODO(bulach): return a CalledByNative object.
@@ -593,7 +460,7 @@
                        return_type=return_type,
                        name=name,
                        is_constructor=is_constructor,
-                       params=JniParams.Parse(match.group('params')))
+                       params=parse.parse_param_list(match.group('params')))
     ]
   # Check for any @CalledByNative occurrences that weren't matched.
   unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
@@ -601,48 +468,24 @@
     if '@CalledByNative' in line1:
       raise ParseError('could not parse @CalledByNative method signature',
                        line1, line2)
-  return MangleCalledByNatives(jni_params, called_by_natives)
-
-
-def RemoveComments(contents):
-  # We need to support both inline and block comments, and we need to handle
-  # strings that contain '//' or '/*'.
-  # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java
-  # parser. Maybe we could ditch JNIFromJavaSource and just always use
-  # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT.
-  # http://code.google.com/p/chromium/issues/detail?id=138941
-  def replacer(match):
-    # Replace matches that are comments with nothing; return literals/strings
-    # unchanged.
-    s = match.group(0)
-    if s.startswith('/'):
-      return ''
-    else:
-      return s
-
-  return _COMMENT_REMOVER_REGEX.sub(replacer, contents)
+  return MangleCalledByNatives(resolver, called_by_natives)
 
 
 class JNIFromJavaP(object):
   """Uses 'javap' to parse a .class file and generate the JNI header file."""
 
   def __init__(self, contents, options):
-    self.contents = contents
-    self.namespace = options.namespace
+    self.options = options
     for line in contents:
-      class_name = re.match(
-          '.*?(public).*?(class|interface) (?P<class_name>\S+?)( |\Z)', line)
-      if class_name:
-        self.fully_qualified_class = class_name.group('class_name')
+      m = re.match('.*?(public).*?(?:class|interface) (\S+?)( |\Z)', line)
+      if m:
+        fqn = m.group(2).split('<', 1)[0].replace('.', '/')
+        self.java_class = models.JavaClass(fqn, visibility=m.group(1))
         break
-    self.fully_qualified_class = self.fully_qualified_class.replace('.', '/')
-    # Java 7's javap includes type parameters in output, like HashSet<T>. Strip
-    # away the <...> and use the raw class name that Java 6 would've given us.
-    self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0]
-    self.jni_params = type_resolver.TypeResolver(self.fully_qualified_class)
-    self.java_class_name = self.fully_qualified_class.split('/')[-1]
-    if not self.namespace:
-      self.namespace = 'JNI_' + self.java_class_name
+    else:
+      raise SyntaxError('Could not find java class in javap output')
+    self.jni_namespace = options.namespace or 'JNI_' + self.java_class.name
+    self.type_resolver = type_resolver.TypeResolver(self.java_class)
     re_method = re.compile('(?P<prefix>.*?)(?P<return_type>\S+?) (?P<name>\w+?)'
                            '\((?P<params>.*?)\)')
     self.called_by_natives = []
@@ -658,11 +501,12 @@
               java_class_name='',
               return_type=match.group('return_type').replace('.', '/'),
               name=match.group('name'),
-              params=JniParams.Parse(match.group('params'), from_javap=True),
-              signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))
+              params=parse.parse_param_list(match.group('params'),
+                                            from_javap=True),
+              signature=parse.parse_javap_signature(contents[lineno + 1]))
       ]
     re_constructor = re.compile('(.*?)public ' +
-                                self.fully_qualified_class.replace('/', '.') +
+                                self.java_class.full_name_with_dots +
                                 '\((?P<params>.*?)\)')
     for lineno, content in enumerate(contents[2:], 2):
       match = re.match(re_constructor, content)
@@ -673,15 +517,15 @@
                          unchecked=options.unchecked_exceptions,
                          static=False,
                          java_class_name='',
-                         return_type=self.fully_qualified_class,
+                         return_type=self.java_class.full_name_with_slashes,
                          name='Constructor',
-                         params=JniParams.Parse(match.group('params'),
-                                                from_javap=True),
-                         signature=JniParams.ParseJavaPSignature(
-                             contents[lineno + 1]),
+                         params=parse.parse_param_list(match.group('params'),
+                                                       from_javap=True),
+                         signature=parse.parse_javap_signature(contents[lineno +
+                                                                        1]),
                          is_constructor=True)
       ]
-    self.called_by_natives = MangleCalledByNatives(self.jni_params,
+    self.called_by_natives = MangleCalledByNatives(self.type_resolver,
                                                    self.called_by_natives)
     self.constant_fields = []
     re_constant_field = re.compile('.*?public static final int (?P<name>.*?);')
@@ -698,15 +542,15 @@
         self.constant_fields.append(
             ConstantField(name=match.group('name'), value=value.group('value')))
 
+  def GetContent(self):
     # We pass in an empty string for the module (which will make the JNI use the
     # base module's files) for all javap-derived JNI. There may be a way to get
     # the module from a jar file, but it's not needed right now.
-    self.inl_header_file_generator = InlHeaderFileGenerator(
-        '', self.namespace, self.fully_qualified_class, [],
-        self.called_by_natives, self.constant_fields, self.jni_params, options)
-
-  def GetContent(self):
-    return self.inl_header_file_generator.GetContent()
+    generator = InlHeaderFileGenerator('', self.jni_namespace, self.java_class,
+                                       [], self.called_by_natives,
+                                       self.constant_fields, self.type_resolver,
+                                       self.options)
+    return generator.GetContent()
 
   @staticmethod
   def CreateFromClass(class_file, options):
@@ -722,158 +566,54 @@
     jni_from_javap = JNIFromJavaP(stdout.split('\n'), options)
     return jni_from_javap
 
-
-# 'Proxy' native methods are declared in an @NativeMethods interface without
-# a native qualifier and indicate that the JNI annotation processor should
-# generate code to link between the equivalent native method as if it were
-# declared statically.
-# Under the hood the annotation processor generates the actual native method
-# declaration in another class (org.chromium.base.natives.GEN_JNI)
-# but generates wrapper code so it can be called through the declaring class.
-class ProxyHelpers(object):
-  MAX_CHARS_FOR_HASHED_NATIVE_METHODS = 8
-
-  @staticmethod
-  def GetClass(short_name, name_prefix=None):
-    if not name_prefix:
-      name_prefix = ''
-    else:
-      name_prefix += '_'
-    return name_prefix + ('N' if short_name else 'GEN_JNI')
-
-  @staticmethod
-  def GetPackage(short_name, package_prefix=None):
-    package = 'J' if short_name else 'org/chromium/base/natives'
-    return GetFullyQualifiedClassWithPackagePrefix(package, package_prefix)
-
-  @staticmethod
-  def GetQualifiedClass(short_name, name_prefix=None, package_prefix=None):
-    return '%s/%s' % (ProxyHelpers.GetPackage(short_name, package_prefix),
-                      ProxyHelpers.GetClass(short_name, name_prefix))
-
-  @staticmethod
-  def CreateHashedMethodName(fully_qualified_class_name, method_name):
-    descriptor = EscapeClassName(fully_qualified_class_name + '/' + method_name)
-
-    if not isinstance(descriptor, bytes):
-      descriptor = descriptor.encode()
-    hash = hashlib.md5(descriptor).digest()
-    hash_b64 = base64.b64encode(hash, altchars=b'$_')
-    if not isinstance(hash_b64, str):
-      hash_b64 = hash_b64.decode()
-
-    long_hash = ('M' + hash_b64).rstrip('=')
-    hashed_name = long_hash[:ProxyHelpers.MAX_CHARS_FOR_HASHED_NATIVE_METHODS]
-
-    # If the method is a test-only method, we don't care about saving size on
-    # the method name, since it shouldn't show up in the binary. Additionally,
-    # if we just hash the name, our checkers which enforce that we have no
-    # "ForTesting" methods by checking for the suffix "ForTesting" will miss
-    # these. We could preserve the name entirely and not hash anything, but
-    # that risks collisions. So, instead, we just append "ForTesting" to any
-    # test-only hashes, to ensure we catch any test-only methods that
-    # shouldn't be in our final binary.
-    if _NameIsTestOnly(method_name):
-      return hashed_name + '_ForTesting'
-    return hashed_name
-
-  @staticmethod
-  def CreateProxyMethodName(fully_qualified_class, old_name, use_hash=False):
-    """Returns the literal method name for the corresponding proxy method"""
-    if use_hash:
-      return ProxyHelpers.CreateHashedMethodName(fully_qualified_class,
-                                                 old_name)
-
-    # The annotation processor currently uses a method name
-    # org_chromium_example_foo_method_1name escaping _ to _1
-    # and then using the appending the method name to the qualified
-    # class. Since we need to escape underscores for jni to work
-    # we need to double escape _1 to _11
-    # This is the literal name of the GEN_JNI it still needs to be escaped once.
-    return EscapeClassName(fully_qualified_class + '/' + old_name)
-
-  @staticmethod
-  def ExtractStaticProxyNatives(fully_qualified_class,
-                                contents,
-                                ptr_type,
-                                include_test_only=True):
-    methods = []
-    first_match = True
-    module_name = None
-    for match in _NATIVE_PROXY_EXTRACTION_REGEX.finditer(contents):
-      interface_body = match.group('interface_body')
-      if first_match:
-        module_name = match.group('module_name')
-        first_match = False
-      else:
-        assert module_name == match.group(
-            'module_name'
-        ), 'JNI cannot belong to two modules in one file {} and {}'.format(
-            module_name, match.group('module_name'))
-      for method in _EXTRACT_METHODS_REGEX.finditer(interface_body):
-        name = method.group('name')
-        if not include_test_only and _NameIsTestOnly(name):
-          continue
-
-        params = JniParams.Parse(method.group('params'), use_proxy_types=True)
-        return_type = JavaTypeToProxyCast(method.group('return_type'))
-        proxy_name = ProxyHelpers.CreateProxyMethodName(fully_qualified_class,
-                                                        name,
-                                                        use_hash=False)
-        hashed_proxy_name = ProxyHelpers.CreateProxyMethodName(
-            fully_qualified_class, name, use_hash=True)
-        native = NativeMethod(
-            static=True,
-            return_type=return_type,
-            name=name,
-            native_class_name=method.group('native_class_name'),
-            params=params,
-            is_proxy=True,
-            proxy_name=proxy_name,
-            hashed_proxy_name=hashed_proxy_name,
-            ptr_type=ptr_type)
-        methods.append(native)
-
-    if not module_name:
-      module_name = ''
-    return methods, module_name
-
-
 class JNIFromJavaSource(object):
   """Uses the given java source file to generate the JNI header file."""
 
-  def __init__(self, contents, fully_qualified_class, options):
-    if options.package_prefix:
-      fully_qualified_class = GetFullyQualifiedClassWithPackagePrefix(
-          fully_qualified_class, options.package_prefix)
-    contents = RemoveComments(contents)
-    self.jni_params = type_resolver.TypeResolver(fully_qualified_class)
-    self.jni_params.parse_imports_and_nested_types(contents)
-    jni_namespace = ExtractJNINamespace(contents) or options.namespace
-    called_by_natives = ExtractCalledByNatives(self.jni_params, contents)
+  def __init__(self, parsed_file, options):
+    self.options = options
+    self.filename = parsed_file.filename
+    self.java_class = parsed_file.java_class
+    self.type_resolver = parsed_file.type_resolver
+    self.jni_namespace = parsed_file.jni_namespace or options.namespace
+    self.module_name = parsed_file.module_name
 
-    natives, module_name = ProxyHelpers.ExtractStaticProxyNatives(
-        fully_qualified_class, contents, options.ptr_type)
-    natives += ExtractNatives(contents, options.ptr_type)
+    proxy_natives = []
+    for parsed_method in parsed_file.proxy_methods:
+      proxy_natives.append(
+          NativeMethod(is_proxy=True,
+                       java_class=self.java_class,
+                       name=parsed_method.name,
+                       return_type=parsed_method.return_type,
+                       params=parsed_method.params,
+                       native_class_name=parsed_method.native_class_name,
+                       ptr_type='long'))
 
-    if len(natives) == 0 and len(called_by_natives) == 0:
-      raise SyntaxError(
-          'Unable to find any JNI methods for %s.' % fully_qualified_class)
-    inl_header_file_generator = InlHeaderFileGenerator(
-        module_name, jni_namespace, fully_qualified_class, natives,
-        called_by_natives, [], self.jni_params, options)
-    self.content = inl_header_file_generator.GetContent()
+    self.natives = proxy_natives + parsed_file.non_proxy_natives
+    self.called_by_natives = parsed_file.called_by_natives
+
+  @property
+  def proxy_natives(self):
+    return [n for n in self.natives if n.is_proxy]
+
+  @property
+  def non_proxy_natives(self):
+    return [n for n in self.natives if not n.is_proxy]
+
+  def RemoveTestOnlyNatives(self):
+    self.natives = [n for n in self.natives if not n.is_test_only]
 
   def GetContent(self):
-    return self.content
+    generator = InlHeaderFileGenerator(self.module_name, self.jni_namespace,
+                                       self.java_class, self.natives,
+                                       self.called_by_natives, [],
+                                       self.type_resolver, self.options)
+    return generator.GetContent()
 
   @staticmethod
-  def CreateFromFile(java_file_name, options):
-    with open(java_file_name) as f:
-      contents = f.read()
-    fully_qualified_class = ExtractFullyQualifiedJavaClassName(
-        java_file_name, contents)
-    return JNIFromJavaSource(contents, fully_qualified_class, options)
+  def CreateFromFile(filename, options):
+    parsed_file = parse.parse_java_file(filename,
+                                        package_prefix=options.package_prefix)
+    return JNIFromJavaSource(parsed_file, options)
 
 
 class HeaderFileGeneratorHelper(object):
@@ -894,6 +634,10 @@
     self.package_prefix = package_prefix
     self.split_name = split_name
     self.enable_jni_multiplexing = enable_jni_multiplexing
+    self.gen_jni_class = proxy.get_gen_jni_class(short=use_proxy_hash
+                                                 or enable_jni_multiplexing,
+                                                 name_prefix=module_name,
+                                                 package_prefix=package_prefix)
 
   def GetStubName(self, native):
     """Return the name of the stub function for this native method.
@@ -906,19 +650,20 @@
     """
     if native.is_proxy:
       if self.use_proxy_hash:
-        method_name = EscapeClassName(native.hashed_proxy_name)
+        method_name = common.EscapeClassName(native.hashed_proxy_name)
       else:
-        method_name = EscapeClassName(native.proxy_name)
-      return 'Java_%s_%s' % (EscapeClassName(
-          ProxyHelpers.GetQualifiedClass(
-              self.use_proxy_hash or self.enable_jni_multiplexing,
-              self.module_name, self.package_prefix)), method_name)
+        method_name = common.EscapeClassName(native.proxy_name)
+      return 'Java_%s_%s' % (common.EscapeClassName(
+          self.gen_jni_class.full_name_with_slashes), method_name)
 
     template = Template('Java_${JAVA_NAME}_native${NAME}')
 
     java_name = self.fully_qualified_class
 
-    values = {'NAME': native.name, 'JAVA_NAME': EscapeClassName(java_name)}
+    values = {
+        'NAME': native.cpp_name,
+        'JAVA_NAME': common.EscapeClassName(java_name)
+    }
     return template.substitute(values)
 
   def GetUniqueClasses(self, origin):
@@ -926,8 +671,7 @@
     for entry in origin:
       if isinstance(entry, NativeMethod) and entry.is_proxy:
         short_name = self.use_proxy_hash or self.enable_jni_multiplexing
-        ret[ProxyHelpers.GetClass(short_name, self.module_name)] \
-          = ProxyHelpers.GetQualifiedClass(short_name, self.module_name, self.package_prefix)
+        ret[self.gen_jni_class.name] = self.gen_jni_class.full_name_with_slashes
         continue
       ret[self.class_name] = self.fully_qualified_class
 
@@ -955,14 +699,12 @@
 
     for full_clazz in classes.values():
       values = {
-          'JAVA_CLASS': EscapeClassName(full_clazz),
+          'JAVA_CLASS': common.EscapeClassName(full_clazz),
           'JNI_CLASS_PATH': full_clazz,
       }
       # Since all proxy methods use the same class, defining this in every
       # header file would result in duplicated extern initializations.
-      if full_clazz != ProxyHelpers.GetQualifiedClass(
-          self.use_proxy_hash or self.enable_jni_multiplexing, self.module_name,
-          self.package_prefix):
+      if full_clazz != self.gen_jni_class.full_name_with_slashes:
         ret += [template.substitute(values)]
 
     class_getter = """\
@@ -987,15 +729,13 @@
     for full_clazz in classes.values():
       values = {
           'JAVA_CLASS':
-          EscapeClassName(full_clazz),
+          common.EscapeClassName(full_clazz),
           'MAYBE_SPLIT_NAME_ARG':
           (('"%s", ' % self.split_name) if self.split_name else '')
       }
       # Since all proxy methods use the same class, defining this in every
       # header file would result in duplicated extern initializations.
-      if full_clazz != ProxyHelpers.GetQualifiedClass(
-          self.use_proxy_hash or self.enable_jni_multiplexing, self.module_name,
-          self.package_prefix):
+      if full_clazz != self.gen_jni_class.full_name_with_slashes:
         ret += [template.substitute(values)]
 
     return ''.join(ret)
@@ -1004,21 +744,22 @@
 class InlHeaderFileGenerator(object):
   """Generates an inline header file for JNI integration."""
 
-  def __init__(self, module_name, namespace, fully_qualified_class, natives,
-               called_by_natives, constant_fields, jni_params, options):
+  def __init__(self, module_name, namespace, java_class, natives,
+               called_by_natives, constant_fields, resolver, options):
     self.namespace = namespace
-    self.fully_qualified_class = fully_qualified_class
-    self.class_name = self.fully_qualified_class.split('/')[-1]
+    self.java_class = java_class
+    self.class_name = java_class.name
     self.natives = natives
     self.called_by_natives = called_by_natives
-    self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
+    self.header_guard = java_class.full_name_with_slashes.replace('/',
+                                                                  '_') + '_JNI'
     self.constant_fields = constant_fields
-    self.jni_params = jni_params
+    self.type_resolver = resolver
     self.options = options
     self.helper = HeaderFileGeneratorHelper(
-        self.class_name,
+        java_class.name,
         module_name,
-        fully_qualified_class,
+        self.java_class.full_name_with_slashes,
         self.options.use_proxy_hash,
         self.options.package_prefix,
         split_name=self.options.split_name,
@@ -1058,7 +799,7 @@
 """)
     values = {
         'SCRIPT_NAME': GetScriptName(),
-        'FULLY_QUALIFIED_CLASS': self.fully_qualified_class,
+        'FULLY_QUALIFIED_CLASS': self.java_class.full_name_with_slashes,
         'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(),
         'CONSTANT_FIELDS': self.GetConstantFieldsString(),
         'METHOD_STUBS': self.GetMethodStubsString(),
@@ -1086,7 +827,7 @@
   def GetConstantFieldsString(self):
     if not self.constant_fields:
       return ''
-    ret = ['enum Java_%s_constant_fields {' % self.class_name]
+    ret = ['enum Java_%s_constant_fields {' % self.java_class.name]
     for c in self.constant_fields:
       ret += ['  %s = %s,' % (c.name, c.value)]
     ret += ['};', '']
@@ -1145,7 +886,7 @@
         })
 
   def GetImplementationMethodName(self, native):
-    return 'JNI_%s_%s' % (self.class_name, native.name)
+    return 'JNI_%s_%s' % (self.java_class.name, native.cpp_name)
 
   def GetNativeStub(self, native):
     is_method = native.type == 'method'
@@ -1183,7 +924,7 @@
     values = {
         'RETURN': return_type,
         'RETURN_DECLARATION': return_declaration,
-        'NAME': native.name,
+        'NAME': native.cpp_name,
         'IMPL_METHOD_NAME': self.GetImplementationMethodName(native),
         'PARAMS': ',\n    '.join(params_in_declaration),
         'PARAMS_IN_STUB': GetParamsInStub(native),
@@ -1243,8 +984,8 @@
 
   def GetCalledByNativeValues(self, called_by_native):
     """Fills in necessary values for the CalledByNative methods."""
-    java_class_only = called_by_native.java_class_name or self.class_name
-    java_class = self.fully_qualified_class
+    java_class_only = called_by_native.java_class_name or self.java_class.name
+    java_class = self.java_class.full_name_with_slashes
     if called_by_native.java_class_name:
       java_class += '$' + called_by_native.java_class_name
 
@@ -1297,12 +1038,12 @@
     if called_by_native.signature:
       jni_signature = called_by_native.signature
     else:
-      jni_signature = self.jni_params.create_signature(called_by_native.params,
-                                                       jni_return_type)
+      jni_signature = self.type_resolver.create_signature(
+          called_by_native.params, jni_return_type)
     java_name_full = java_class.replace('/', '.') + '.' + jni_name
     return {
         'JAVA_CLASS_ONLY': java_class_only,
-        'JAVA_CLASS': EscapeClassName(java_class),
+        'JAVA_CLASS': common.EscapeClassName(java_class),
         'RETURN_TYPE': return_type,
         'OPTIONAL_ERROR_RETURN': optional_error_return,
         'RETURN_DECLARATION': return_declaration,
@@ -1387,29 +1128,6 @@
   return '\n'.join(ret)
 
 
-def GenerateJNIHeader(input_file, output_file, options):
-  try:
-    if os.path.splitext(input_file)[1] == '.class':
-      # The current package-prefix implementation does not support adding
-      # prefix to java compiled classes. The current support is only for
-      # java source files.
-      assert not options.package_prefix
-      jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options)
-      content = jni_from_javap.GetContent()
-    else:
-      jni_from_java_source = JNIFromJavaSource.CreateFromFile(
-          input_file, options)
-      content = jni_from_java_source.GetContent()
-  except ParseError as e:
-    print(e)
-    sys.exit(1)
-  if output_file:
-    with action_helpers.atomic_output(output_file, mode='w') as f:
-      f.write(content)
-  else:
-    print(content)
-
-
 def GetScriptName():
   script_components = os.path.abspath(__file__).split(os.path.sep)
   base_index = 0
@@ -1435,6 +1153,67 @@
           os.remove(file_path)
 
 
+def _CheckSameModule(jni_objs):
+  files_by_module = collections.defaultdict(list)
+  for jni_obj in jni_objs:
+    if jni_obj.proxy_natives:
+      files_by_module[jni_obj.module_name].append(jni_obj.filename)
+  if len(files_by_module) > 1:
+    sys.stderr.write(
+        'Multiple values for @NativeMethods(moduleName) is not supported.\n')
+    for module_name, filenames in files_by_module.items():
+      sys.stderr.write(f'module_name={module_name}\n')
+      for filename in filenames:
+        sys.stderr.write(f'  {filename}\n')
+    sys.exit(1)
+
+
+def _CheckNotEmpty(jni_objs):
+  has_empty = False
+  for jni_obj in jni_objs:
+    if not (jni_obj.natives or jni_obj.called_by_natives):
+      has_empty = True
+      sys.stderr.write(f'No native methods found in {jni_obj.filename}.\n')
+  if has_empty:
+    sys.exit(1)
+
+
+def _ParseClassFiles(jar_file, class_files, options):
+  # Parse javap output.
+  ret = []
+  with tempfile.TemporaryDirectory() as temp_dir:
+    with zipfile.ZipFile(jar_file) as z:
+      z.extractall(temp_dir, class_files)
+    for class_file in class_files:
+      class_file = os.path.join(temp_dir, class_file)
+      ret.append(JNIFromJavaP.CreateFromClass(class_file, options))
+  return ret
+
+
+def DoGeneration(options):
+  try:
+    if options.jar_file:
+      jni_objs = _ParseClassFiles(options.jar_file, options.input_files,
+                                  options)
+    else:
+      jni_objs = [
+          JNIFromJavaSource.CreateFromFile(f, options)
+          for f in options.input_files
+      ]
+      _CheckNotEmpty(jni_objs)
+      _CheckSameModule(jni_objs)
+  except (ParseError, SyntaxError) as e:
+    sys.stderr.write(f'{e}\n')
+    sys.exit(1)
+
+  # Write .h files
+  for jni_obj, header_name in zip(jni_objs, options.output_names):
+    output_file = os.path.join(options.output_dir, header_name)
+    content = jni_obj.GetContent()
+    with action_helpers.atomic_output(output_file, 'w') as f:
+      f.write(content)
+
+
 def main():
   description = """
 This script will parse the given java source code extracting the native
@@ -1509,18 +1288,19 @@
       help='Split name that the Java classes should be loaded from.')
   parser.add_argument(
       '--package_prefix',
-      help=
-      'Adds a prefix to the classes fully qualified-name. Effectively changing a class name from'
-      'foo.bar -> prefix.foo.bar')
+      help='Adds a prefix to the classes fully qualified-name. Effectively '
+      'changing a class name fromfoo.bar -> prefix.foo.bar')
   # TODO(agrieve): --stamp used only to make incremental builds work.
   #     Remove --stamp at some point after 2022.
   parser.add_argument('--stamp',
                       help='Process --prev_output_dir and touch this file.')
   args = parser.parse_args()
+  if args.jar_file and args.package_prefix:
+    parser.error('--package_prefix not implemented for --jar_file')
+
   # Kotlin files are not supported by jni_generator.py, but they do end up in
   # the list of source files passed to jni_generator.py.
   input_files = [f for f in args.input_files if not f.endswith('.kt')]
-  output_names = args.output_names
 
   if args.prev_output_dir:
     _RemoveStaleHeaders(args.prev_output_dir, [])
@@ -1529,28 +1309,12 @@
     build_utils.Touch(args.stamp)
     sys.exit(0)
 
-  if output_names:
-    # Remove existing headers so that moving .java source files but not updating
-    # the corresponding C++ include will be a compile failure (otherwise
-    # incremental builds will usually not catch this).
-    _RemoveStaleHeaders(args.output_dir, output_names)
-  else:
-    output_names = [None] * len(input_files)
-  temp_dir = tempfile.mkdtemp()
-  try:
-    if args.jar_file:
-      with zipfile.ZipFile(args.jar_file) as z:
-        z.extractall(temp_dir, input_files)
-      input_files = [os.path.join(temp_dir, f) for f in input_files]
+  # Remove existing headers so that moving .java source files but not updating
+  # the corresponding C++ include will be a compile failure (otherwise
+  # incremental builds will usually not catch this).
+  _RemoveStaleHeaders(args.output_dir, args.output_names)
 
-    for java_path, header_name in zip(input_files, output_names):
-      if header_name:
-        header_path = os.path.join(args.output_dir, header_name)
-      else:
-        header_path = None
-      GenerateJNIHeader(java_path, header_path, args)
-  finally:
-    shutil.rmtree(temp_dir)
+  DoGeneration(args)
 
 
 if __name__ == '__main__':
diff --git a/base/android/jni_generator/jni_generator.pydeps b/base/android/jni_generator/jni_generator.pydeps
index abdd0da..377f664 100644
--- a/base/android/jni_generator/jni_generator.pydeps
+++ b/base/android/jni_generator/jni_generator.pydeps
@@ -4,6 +4,10 @@
 ../../../build/android/gyp/util/__init__.py
 ../../../build/android/gyp/util/build_utils.py
 ../../../build/gn_helpers.py
+common.py
 java_lang_classes.py
 jni_generator.py
+models.py
+parse.py
+proxy.py
 type_resolver.py
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index a5d14e5..aea26fe 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -15,15 +15,17 @@
 import copy
 import difflib
 import inspect
+import logging
 import os
+import pathlib
+import shlex
+import subprocess
 import sys
 import tempfile
 import unittest
-import jni_generator
-import jni_registration_generator
 import zipfile
-from util import build_utils
 
+_SCRIPT_DIR = os.path.normpath(os.path.dirname(__file__))
 _INCLUDES = ('base/android/jni_generator/jni_generator_helper.h')
 _JAVA_SRC_DIR = os.path.join('java', 'src', 'org', 'chromium', 'example',
                              'jni_generator')
@@ -33,50 +35,105 @@
 _REBASELINE_ENV = 'REBASELINE'
 
 
-class JniGeneratorOptions(object):
-  """The mock options object which is passed to the jni_generator.py script."""
-
+class CommonOptions:
   def __init__(self):
-    self.namespace = None
+    self.enable_jni_multiplexing = False
+    self.package_prefix = None
+    self.use_proxy_hash = False
+
+  def to_args(self):
+    ret = []
+    if self.package_prefix:
+      ret += ['--package_prefix', self.package_prefix]
+    return ret
+
+
+class JniGeneratorOptions(CommonOptions):
+  def __init__(self):
+    super().__init__()
     self.includes = _INCLUDES
-    self.ptr_type = 'long'
-    self.cpp = 'cpp'
-    self.javap = build_utils.JAVAP_PATH
-    self.enable_profiling = False
-    self.use_proxy_hash = False
-    self.enable_jni_multiplexing = False
-    self.unchecked_exceptions = False
-    self.split_name = None
-    self.package_prefix = None
+    self.input_file = None
+    self.jar_file = None
+    self.output_dir = None
+    self.output_name = 'output.h'
+
+  def to_args(self):
+    ret = super().to_args()
+    ret += ['--output_dir', self.output_dir]
+    ret += ['--input_file', self.input_file]
+    ret += ['--output_name', self.output_name]
+    if self.jar_file:
+      ret += ['--jar_file', self.jar_file]
+    if self.enable_jni_multiplexing:
+      ret.append('--enable_jni_multiplexing')
+    if self.includes:
+      ret += ['--includes', self.includes, '--ptr_type', 'long']
+    if self.use_proxy_hash:
+      ret.append('--use_proxy_hash')
+    return ret
 
 
-class JniRegistrationGeneratorOptions(object):
+class JniRegistrationGeneratorOptions(CommonOptions):
   """The mock options object which is passed to the jni_generator.py script."""
 
   def __init__(self):
-    self.sources_exclusions = []
-    self.namespace = None
-    self.enable_proxy_mocks = False
-    self.require_mocks = False
-    self.use_proxy_hash = False
-    self.enable_jni_multiplexing = False
-    self.manual_jni_registration = False
-    self.include_test_only = False
-    self.header_path = None
-    self.module_name = ''
-    self.package_prefix = None
-    self.remove_uncalled_methods = False
+    super().__init__()
     self.add_stubs_for_missing_native = False
+    self.enable_proxy_mocks = False
+    self.header_path = None
+    self.include_test_only = False
+    self.manual_jni_registration = False
+    self.module_name = ''
+    self.remove_uncalled_methods = False
+    self.require_mocks = False
+
+  def to_args(self):
+    ret = super().to_args()
+    if self.add_stubs_for_missing_native:
+      ret.append('--add-stubs-for-missing-native')
+    if self.enable_jni_multiplexing:
+      ret.append('--enable-jni-multiplexing')
+    if self.enable_proxy_mocks:
+      ret.append('--enable-proxy-mocks')
+    if self.header_path:
+      ret += ['--header-path', self.header_path]
+    if self.include_test_only:
+      ret.append('--include-test-only')
+    if self.manual_jni_registration:
+      ret.append('--manual-jni-registration')
+    if self.module_name:
+      ret += ['--module-name', self.module_name]
+    if self.package_prefix:
+      ret += ['--package_prefix', self.package_prefix]
+    if self.remove_uncalled_methods:
+      ret.append('--remove-uncalled-methods')
+    if self.require_mocks:
+      ret.append('--require-mocks')
+    if self.use_proxy_hash:
+      ret.append('--use-proxy-hash')
+    return ret
 
 
 class BaseTest(unittest.TestCase):
 
-  def _TestEndToEndGeneration(self, input_java, options, golden):
-    input_java_path = self._JoinScriptDir(
-        os.path.join(_JAVA_SRC_DIR, input_java))
+  def _TestEndToEndGeneration(self, input_file, options, golden):
     with tempfile.TemporaryDirectory() as tdir:
-      output_path = os.path.join(tdir, 'output.h')
-      jni_generator.GenerateJNIHeader(input_java_path, output_path, options)
+      relative_input_file = self._JoinScriptDir(
+          os.path.join(_JAVA_SRC_DIR, input_file))
+      if input_file.endswith('.class'):
+        jar_path = os.path.join(tdir, 'input.jar')
+        with zipfile.ZipFile(jar_path, 'w') as z:
+          z.write(relative_input_file, input_file)
+        options.jar_file = jar_path
+        options.input_file = input_file
+      else:
+        options.input_file = relative_input_file
+
+      options.output_dir = tdir
+      cmd = [self._JoinScriptDir('jni_generator.py')] + options.to_args()
+      logging.info('Running: %s', shlex.join(cmd))
+      subprocess.check_call(cmd)
+      output_path = os.path.join(tdir, options.output_name)
       with open(output_path, 'r') as f:
         contents = f.read()
       self.AssertGoldenTextEquals(contents, golden)
@@ -88,26 +145,38 @@
                                 src_files_for_asserts_and_stubs=None,
                                 header_golden=None):
     with tempfile.TemporaryDirectory() as tdir:
-      options.srcjar_path = os.path.join(tdir, 'srcjar.jar')
-      if header_golden:
-        options.header_path = os.path.join(tdir, 'header.h')
-
-      input_java_paths = {
+      native_sources = [
           self._JoinScriptDir(os.path.join(_JAVA_SRC_DIR, f))
           for f in input_src_files
-      }
+      ]
 
       if src_files_for_asserts_and_stubs:
-        asserts_and_stubs_java_paths = {
+        java_sources = [
             self._JoinScriptDir(os.path.join(_JAVA_SRC_DIR, f))
             for f in src_files_for_asserts_and_stubs
-        }
+        ]
       else:
-        asserts_and_stubs_java_paths = input_java_paths
+        java_sources = native_sources
 
-      jni_registration_generator._Generate(options, input_java_paths,
-                                           asserts_and_stubs_java_paths)
-      with zipfile.ZipFile(options.srcjar_path, 'r') as srcjar:
+      java_sources_file = pathlib.Path(tdir) / 'java_sources.txt'
+      java_sources_file.write_text('\n'.join(java_sources))
+      native_sources_file = pathlib.Path(tdir) / 'native_sources.txt'
+      native_sources_file.write_text('\n'.join(native_sources))
+
+      cmd = [self._JoinScriptDir('jni_registration_generator.py')]
+      cmd += ['--java-sources-files', str(java_sources_file)]
+      cmd += ['--native-sources-file', str(native_sources_file)]
+      srcjar_path = os.path.join(tdir, 'srcjar.jar')
+      cmd += ['--srcjar-path', srcjar_path]
+      if header_golden:
+        header_path = os.path.join(tdir, 'header.h')
+        cmd += ['--header-path', header_path]
+
+      cmd += options.to_args()
+      logging.info('Running: %s', shlex.join(cmd))
+      subprocess.check_call(cmd)
+
+      with zipfile.ZipFile(srcjar_path, 'r') as srcjar:
         for name in srcjar.namelist():
           self.assertTrue(
               name in name_to_goldens,
@@ -115,7 +184,7 @@
           contents = srcjar.read(name).decode('utf-8')
           self.AssertGoldenTextEquals(contents, name_to_goldens[name])
       if header_golden:
-        with open(options.header_path, 'r') as f:
+        with open(header_path, 'r') as f:
           # Temp directory will cause some diffs each time we run if we don't
           # normalize.
           contents = f.read().replace(
@@ -123,8 +192,7 @@
           self.AssertGoldenTextEquals(contents, header_golden)
 
   def _JoinScriptDir(self, path):
-    script_dir = os.path.dirname(sys.argv[0])
-    return os.path.join(script_dir, path)
+    return os.path.join(_SCRIPT_DIR, path)
 
   def _JoinGoldenPath(self, golden_file_name):
     return self._JoinScriptDir(os.path.join('golden', golden_file_name))
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
index adccd89..4e4b786 100755
--- a/base/android/jni_generator/jni_registration_generator.py
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -18,8 +18,10 @@
 import sys
 import zipfile
 
+import common
 import jni_generator
-import type_resolver
+import models
+import proxy
 
 from util import build_utils
 import action_helpers  # build_utils adds //build to sys.path.
@@ -63,15 +65,14 @@
     # will be used later to generate stubs and make assertions.
     for result in pool.imap_unordered(functools.partial(_DictForPath, options),
                                       native_sources):
-      d, path, natives = result
+      d, path, jni_obj = result
       if d:
         results.append(d)
-      cached_results_for_stubs[path] = natives
+      cached_results_for_stubs[path] = jni_obj
 
   used_sources = {d['FILE_PATH'] for d in results}
-  stubs_file_paths = _GenerateStubsAndAssert(options, used_sources,
-                                             java_sources,
-                                             cached_results_for_stubs)
+  stub_dicts = _GenerateStubsAndAssert(options, used_sources, java_sources,
+                                       cached_results_for_stubs)
   # Sort to make output deterministic.
   results.sort(key=lambda d: d['FULL_CLASS_NAME'])
 
@@ -79,6 +80,14 @@
   for key in MERGEABLE_KEYS:
     combined_dict[key] = ''.join(d.get(key, '') for d in results)
 
+  short_gen_jni_class = proxy.get_gen_jni_class(
+      short=True,
+      name_prefix=options.module_name,
+      package_prefix=options.package_prefix)
+  full_gen_jni_class = proxy.get_gen_jni_class(
+      short=False,
+      name_prefix=options.module_name,
+      package_prefix=options.package_prefix)
   # PROXY_NATIVE_SIGNATURES and PROXY_NATIVE_METHOD_ARRAY will have
   # duplicates for JNI multiplexing since all native methods with similar
   # signatures map to the same proxy. Similarly, there may be multiple switch
@@ -93,13 +102,12 @@
         set(combined_dict['PROXY_NATIVE_METHOD_ARRAY'].split('},\n')))
     combined_dict['PROXY_NATIVE_METHOD_ARRAY'] = '},\n'.join(
         p for p in proxy_native_array_list if p != '') + '}'
-
     signature_to_cases = collections.defaultdict(list)
     for d in results:
       for signature, cases in d['SIGNATURE_TO_CASES'].items():
         signature_to_cases[signature].extend(cases)
     combined_dict['FORWARDING_CALLS'] = _AddForwardingCalls(
-        signature_to_cases, options.module_name, options.package_prefix)
+        signature_to_cases, short_gen_jni_class)
 
   if options.header_path:
     header_guard = os.path.splitext(options.header_path)[0].upper() + '_'
@@ -110,33 +118,27 @@
     with action_helpers.atomic_output(options.header_path, mode='w') as f:
       f.write(header_content)
 
-  stub_methods = []
-  if stubs_file_paths:
-    for f in stubs_file_paths:
-      stub_dict = _DictForPath(
-          options,
-          f,
-          stub_only=True,
-          cached_results_for_stubs=cached_results_for_stubs)[0]
-      if stub_dict:
-        stub_methods.append(stub_dict['STUBS'])
-  stub_methods_string = ''.join(stub_methods)
+  stub_methods_string = ''.join(d['STUBS'] for d in stub_dicts)
 
   with action_helpers.atomic_output(options.srcjar_path) as f:
     with zipfile.ZipFile(f, 'w') as srcjar:
       if options.use_proxy_hash or options.enable_jni_multiplexing:
+        gen_jni_class = short_gen_jni_class
+      else:
+        gen_jni_class = full_gen_jni_class
+
+      if options.use_proxy_hash or options.enable_jni_multiplexing:
         # J/N.java
         zip_helpers.add_to_zip_hermetic(
             srcjar,
-            '%s.java' % jni_generator.ProxyHelpers.GetQualifiedClass(
-                True, options.module_name, options.package_prefix),
-            data=CreateProxyJavaFromDict(options, combined_dict))
+            f'{short_gen_jni_class.full_name_with_slashes}.java',
+            data=CreateProxyJavaFromDict(options, gen_jni_class, combined_dict))
         # org/chromium/base/natives/GEN_JNI.java
         zip_helpers.add_to_zip_hermetic(
             srcjar,
-            '%s.java' % jni_generator.ProxyHelpers.GetQualifiedClass(
-                False, options.module_name, options.package_prefix),
+            f'{full_gen_jni_class.full_name_with_slashes}.java',
             data=CreateProxyJavaFromDict(options,
+                                         full_gen_jni_class,
                                          combined_dict,
                                          stub_methods=stub_methods_string,
                                          forwarding=True))
@@ -144,9 +146,9 @@
         # org/chromium/base/natives/GEN_JNI.java
         zip_helpers.add_to_zip_hermetic(
             srcjar,
-            '%s.java' % jni_generator.ProxyHelpers.GetQualifiedClass(
-                False, options.module_name, options.package_prefix),
+            f'{full_gen_jni_class.full_name_with_slashes}.java',
             data=CreateProxyJavaFromDict(options,
+                                         gen_jni_class,
                                          combined_dict,
                                          stub_methods=stub_methods_string))
 
@@ -157,17 +159,16 @@
   java_only = java_sources - native_sources
   # Using stub_only because we just need this to do a boolean check to see if
   # the files have JNI - we don't need any actual output.
-  java_only = [
-      f for f in java_only
-      if _DictForPath(options,
+  dict_by_path = {
+      f: _DictForPath(options,
                       f,
                       stub_only=True,
                       cached_results_for_stubs=cached_results_for_stubs)[0]
-  ]
-  # Sorting is required for determinism: crbug.com/1446011.
-  java_only = sorted(java_only)
+      for f in java_only
+  }
+  dict_by_path = {k: v for k, v in sorted(dict_by_path.items()) if v}
   failed = False
-  if not options.add_stubs_for_missing_native and java_only:
+  if not options.add_stubs_for_missing_native and dict_by_path:
     failed = True
     warning_message = '''Failed JNI assertion!
 We reference Java files which use JNI, but our native library does not depend on
@@ -175,7 +176,7 @@
 To bypass this check, you can add stubs to Java with add_stubs_for_missing_jni.
 Excess Java files below:
 '''
-    warning_message += str(java_only)
+    warning_message += ', '.join(dict_by_path)
     sys.stderr.write(warning_message)
   if not options.remove_uncalled_methods and native_only:
     failed = True
@@ -189,52 +190,38 @@
     sys.stderr.write(warning_message)
   if failed:
     sys.exit(1)
-  return java_only
+  return list(dict_by_path.values())
 
 
 def _DictForPath(options, path, stub_only=False, cached_results_for_stubs=None):
   # The cached results are generated by the real runs, which happen first, so
   # the cache is only for the stub checks after.
   assert (cached_results_for_stubs is not None) == stub_only
-  natives = stub_only and cached_results_for_stubs.get(path)
-  if not natives:
+  jni_obj = stub_only and cached_results_for_stubs.get(path)
+  if not jni_obj:
     with open(path) as f:
-      contents = jni_generator.RemoveComments(f.read())
-      if '@JniIgnoreNatives' in contents:
+      # TODO(crbug.com/1410871): Remove annotation once using GN metadata to
+      #     only parse specific files.
+      if '@JniIgnoreNatives' in f.read():
         return None, path, None
 
-    fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName(
-        path, contents)
+    jni_obj = jni_generator.JNIFromJavaSource.CreateFromFile(path, options)
+    if not options.include_test_only:
+      jni_obj.RemoveTestOnlyNatives()
 
-    if options.package_prefix:
-      fully_qualified_class = jni_generator.GetFullyQualifiedClassWithPackagePrefix(
-          fully_qualified_class, options.package_prefix)
+  if jni_obj.module_name != options.module_name:
+    # Ignoring any code from modules we aren't looking at.
+    return None, path, jni_obj
 
-    natives, found_module_name = jni_generator.ProxyHelpers.ExtractStaticProxyNatives(
-        fully_qualified_class=fully_qualified_class,
-        contents=contents,
-        ptr_type='long',
-        include_test_only=options.include_test_only)
-
-    if found_module_name != options.module_name:
-      # Ignoring any code from modules we aren't looking at.
-      return None, path, None
-
-  if not natives:
-    return None, path, None
+  if not jni_obj.natives:
+    return None, path, jni_obj
   if stub_only:
-    return {'STUBS': _GenerateStubs(natives)}, path, natives
+    return {'STUBS': _GenerateStubs(jni_obj.proxy_natives)}, path, jni_obj
 
-  natives += jni_generator.ExtractNatives(contents, 'long')
   # The namespace for the content is separate from the namespace for the
   # generated header file.
-  content_namespace = jni_generator.ExtractJNINamespace(contents)
-  jni_params = type_resolver.TypeResolver(fully_qualified_class)
-  jni_params.parse_imports_and_nested_types(contents)
-  dict_generator = DictionaryGenerator(options, path, content_namespace,
-                                       fully_qualified_class, natives,
-                                       jni_params)
-  return dict_generator.Generate(), path, natives
+  dict_generator = DictionaryGenerator(jni_obj, options)
+  return dict_generator.Generate(), path, jni_obj
 
 
 def _GenerateStubs(natives):
@@ -247,16 +234,16 @@
     }""")
 
     params_with_types = ', '.join('%s %s' % (p.datatype, p.name)
-                                  for p in native.params)
+                                  for p in native.proxy_params)
     final_string += template.substitute({
-        'RETURN_TYPE': native.return_type,
+        'RETURN_TYPE': native.proxy_return_type,
         'METHOD_NAME': native.proxy_name,
         'PARAMS_WITH_TYPES': params_with_types,
     })
   return final_string
 
 
-def _AddForwardingCalls(signature_to_cases, module_name, package_prefix):
+def _AddForwardingCalls(signature_to_cases, short_gen_jni_class):
   template = string.Template("""
 JNI_GENERATOR_EXPORT ${RETURN} Java_${CLASS_NAME}_${PROXY_SIGNATURE}(
     JNIEnv* env,
@@ -281,11 +268,9 @@
             'RETURN':
             jni_generator.JavaDataTypeToC(return_type),
             'CLASS_NAME':
-            jni_generator.EscapeClassName(
-                jni_generator.ProxyHelpers.GetQualifiedClass(
-                    True, module_name, package_prefix)),
+            common.EscapeClassName(short_gen_jni_class.full_name_with_slashes),
             'PROXY_SIGNATURE':
-            jni_generator.EscapeClassName(
+            common.EscapeClassName(
                 _GetMultiplexProxyName(return_type, params_list)),
             'PARAMS_IN_STUB':
             params_in_stub,
@@ -298,7 +283,7 @@
   return ''.join(s for s in switch_statements)
 
 
-def _SetProxyRegistrationFields(options, registration_dict):
+def _SetProxyRegistrationFields(options, gen_jni_class, registration_dict):
   registration_template = string.Template("""\
 
 static const JNINativeMethod kMethods_${ESCAPED_PROXY_CLASS}[] = {
@@ -358,21 +343,14 @@
   short_name = options.use_proxy_hash or options.enable_jni_multiplexing
   sub_dict = {
       'ESCAPED_PROXY_CLASS':
-      jni_generator.EscapeClassName(
-          jni_generator.ProxyHelpers.GetQualifiedClass(short_name,
-                                                       options.module_name,
-                                                       options.package_prefix)),
+      common.EscapeClassName(gen_jni_class.full_name_with_slashes),
       'PROXY_CLASS':
-      jni_generator.ProxyHelpers.GetQualifiedClass(short_name,
-                                                   options.module_name,
-                                                   options.package_prefix),
+      gen_jni_class.full_name_with_slashes,
       'KMETHODS':
       registration_dict['PROXY_NATIVE_METHOD_ARRAY'],
       'REGISTRATION_NAME':
       jni_generator.GetRegistrationFunctionName(
-          jni_generator.ProxyHelpers.GetQualifiedClass(short_name,
-                                                       options.module_name,
-                                                       options.package_prefix)),
+          gen_jni_class.full_name_with_slashes),
   }
 
   if registration_dict['PROXY_NATIVE_METHOD_ARRAY']:
@@ -393,6 +371,7 @@
 
 
 def CreateProxyJavaFromDict(options,
+                            gen_jni_class,
                             registration_dict,
                             stub_methods='',
                             forwarding=False):
@@ -413,13 +392,6 @@
 }
 """)
 
-  is_natives_class = not forwarding and (options.use_proxy_hash
-                                         or options.enable_jni_multiplexing)
-  class_name = jni_generator.ProxyHelpers.GetClass(is_natives_class,
-                                                   options.module_name)
-  package = jni_generator.ProxyHelpers.GetPackage(is_natives_class,
-                                                  options.package_prefix)
-
   if forwarding or not (options.use_proxy_hash
                         or options.enable_jni_multiplexing):
     fields = string.Template("""\
@@ -439,9 +411,9 @@
   methods += stub_methods
 
   return template.substitute({
-      'CLASS_NAME': class_name,
+      'CLASS_NAME': gen_jni_class.name,
       'FIELDS': fields,
-      'PACKAGE': package.replace('/', '.'),
+      'PACKAGE': gen_jni_class.package_with_dots,
       'METHODS': methods
   })
 
@@ -480,7 +452,11 @@
 ${MANUAL_REGISTRATION}
 #endif  // ${HEADER_GUARD}
 """)
-  _SetProxyRegistrationFields(options, registration_dict)
+  gen_jni_class = proxy.get_gen_jni_class(short=options.use_proxy_hash
+                                          or options.enable_jni_multiplexing,
+                                          name_prefix=options.module_name,
+                                          package_prefix=options.package_prefix)
+  _SetProxyRegistrationFields(options, gen_jni_class, registration_dict)
   if not options.enable_jni_multiplexing:
     registration_dict['FORWARDING_CALLS'] = ''
   if len(registration_dict['FORWARD_DECLARATIONS']) == 0:
@@ -510,25 +486,28 @@
 class DictionaryGenerator(object):
   """Generates an inline header file for JNI registration."""
 
-  def __init__(self, options, file_path, content_namespace,
-               fully_qualified_class, natives, jni_params):
+  def __init__(self, jni_obj, options):
     self.options = options
-    self.file_path = file_path
-    self.content_namespace = content_namespace
-    self.natives = natives
-    self.proxy_natives = [n for n in natives if n.is_proxy]
-    self.non_proxy_natives = [n for n in natives if not n.is_proxy]
-    self.fully_qualified_class = fully_qualified_class
-    self.jni_params = jni_params
-    self.class_name = self.fully_qualified_class.split('/')[-1]
+    self.file_path = jni_obj.filename
+    self.content_namespace = jni_obj.jni_namespace
+    self.natives = jni_obj.natives
+    self.proxy_natives = jni_obj.proxy_natives
+    self.non_proxy_natives = jni_obj.non_proxy_natives
+    self.fully_qualified_class = jni_obj.java_class.full_name_with_slashes
+    self.type_resolver = jni_obj.type_resolver
+    self.class_name = jni_obj.java_class.name
     self.helper = jni_generator.HeaderFileGeneratorHelper(
         self.class_name,
-        options.module_name,
-        fully_qualified_class,
+        jni_obj.module_name,
+        self.fully_qualified_class,
         options.use_proxy_hash,
         options.package_prefix,
         enable_jni_multiplexing=options.enable_jni_multiplexing)
     self.registration_dict = None
+    self.gen_jni_class = proxy.get_gen_jni_class(
+        short=options.use_proxy_hash or options.enable_jni_multiplexing,
+        name_prefix=options.module_name,
+        package_prefix=options.package_prefix)
 
   def Generate(self):
     self.registration_dict = {
@@ -552,7 +531,7 @@
 
     if self.options.use_proxy_hash or self.options.enable_jni_multiplexing:
       self.registration_dict['FORWARDING_PROXY_METHODS'] = ('\n'.join(
-          _MakeForwardingProxy(self.options, native)
+          _MakeForwardingProxy(self.options, self.gen_jni_class, native)
           for native in self.proxy_natives))
 
     return self.registration_dict
@@ -576,7 +555,7 @@
     forward_declaration = ''
     for native in self.natives:
       value = {
-          'RETURN': jni_generator.JavaDataTypeToC(native.return_type),
+          'RETURN': jni_generator.JavaDataTypeToC(native.proxy_return_type),
           'STUB_NAME': self.helper.GetStubName(native),
           'PARAMS_IN_STUB': jni_generator.GetParamsInStub(native),
       }
@@ -634,26 +613,25 @@
     template = string.Template('    { "${NAME}", ${JNI_SIGNATURE}, ' +
                                'reinterpret_cast<void*>(${STUB_NAME}) },')
 
-    name = 'native' + native.name
-    jni_signature = self.jni_params.create_signature(native.params,
-                                                     native.return_type)
+    name = 'native' + native.cpp_name
+    jni_signature = self.type_resolver.create_signature(
+        native.proxy_params, native.proxy_return_type)
     stub_name = self.helper.GetStubName(native)
 
     if native.is_proxy:
       # Literal name of the native method in the class that contains the actual
       # native declaration.
       if self.options.enable_jni_multiplexing:
-        return_type, params_list = native.return_and_signature
-        class_name = jni_generator.EscapeClassName(
-            jni_generator.ProxyHelpers.GetQualifiedClass(
-                True, self.options.module_name, self.options.package_prefix))
-        proxy_signature = jni_generator.EscapeClassName(
+        return_type, params_list = native.proxy_return_and_signature
+        class_name = common.EscapeClassName(
+            self.gen_jni_class.full_name_with_slashes)
+        proxy_signature = common.EscapeClassName(
             _GetMultiplexProxyName(return_type, params_list))
 
         name = _GetMultiplexProxyName(return_type, params_list)
-        jni_signature = self.jni_params.create_signature(
-            [jni_generator.Param(datatype='long', name='switch_num')] +
-            native.params, native.return_type)
+        jni_signature = self.type_resolver.create_signature(
+            [models.Param(annotations=[], datatype='long', name='switch_num')] +
+            native.proxy_params, native.return_type)
         stub_name = 'Java_' + class_name + '_' + proxy_signature
       elif self.options.use_proxy_hash:
         name = native.hashed_proxy_name
@@ -682,9 +660,7 @@
     all_classes[self.class_name] = self.fully_qualified_class
 
     for clazz, full_clazz in all_classes.items():
-      if clazz == jni_generator.ProxyHelpers.GetClass(
-          self.options.use_proxy_hash or self.options.enable_jni_multiplexing,
-          self.options.module_name):
+      if clazz == self.gen_jni_class.name:
         continue
 
       kmethods = self._GetKMethodsString(clazz)
@@ -694,7 +670,7 @@
       if kmethods:
         values = {
             'NAMESPACE': namespace_str,
-            'JAVA_CLASS': jni_generator.EscapeClassName(full_clazz),
+            'JAVA_CLASS': common.EscapeClassName(full_clazz),
             'KMETHODS': kmethods
         }
         ret += [template.substitute(values)]
@@ -775,7 +751,7 @@
 
     signature_to_cases = collections.defaultdict(list)
     for native in self.proxy_natives:
-      signature = native.return_and_signature
+      signature = native.proxy_return_and_signature
       params = _GetParamsListForMultiplex(signature[1], with_types=False)
       values = {
           'SWITCH_NUM': native.switch_num,
@@ -836,39 +812,37 @@
   return 'resolve_for_' + return_type.replace('[]', '_array').lower() + params
 
 
-def _MakeForwardingProxy(options, proxy_native):
+def _MakeForwardingProxy(options, gen_jni_class, proxy_native):
   template = string.Template("""
     public static ${RETURN_TYPE} ${METHOD_NAME}(${PARAMS_WITH_TYPES}) {
         ${MAYBE_RETURN}${PROXY_CLASS}.${PROXY_METHOD_NAME}(${PARAM_NAMES});
     }""")
 
-  params_with_types = ', '.join(
-      '%s %s' % (p.datatype, p.name) for p in proxy_native.params)
-  param_names = ', '.join(p.name for p in proxy_native.params)
-  proxy_class = jni_generator.ProxyHelpers.GetQualifiedClass(
-      True, options.module_name, options.package_prefix)
+  params_with_types = ', '.join('%s %s' % (p.datatype, p.name)
+                                for p in proxy_native.proxy_params)
+  param_names = ', '.join(p.name for p in proxy_native.proxy_params)
 
   if options.enable_jni_multiplexing:
     if not param_names:
       param_names = proxy_native.switch_num + 'L'
     else:
       param_names = proxy_native.switch_num + 'L, ' + param_names
-    return_type, params_list = proxy_native.return_and_signature
+    return_type, params_list = proxy_native.proxy_return_and_signature
     proxy_method_name = _GetMultiplexProxyName(return_type, params_list)
   else:
     proxy_method_name = proxy_native.hashed_proxy_name
 
   return template.substitute({
       'RETURN_TYPE':
-      proxy_native.return_type,
+      proxy_native.proxy_return_type,
       'METHOD_NAME':
       proxy_native.proxy_name,
       'PARAMS_WITH_TYPES':
       params_with_types,
       'MAYBE_RETURN':
-      '' if proxy_native.return_type == 'void' else 'return ',
+      '' if proxy_native.proxy_return_type == 'void' else 'return ',
       'PROXY_CLASS':
-      proxy_class.replace('/', '.'),
+      gen_jni_class.full_name_with_dots,
       'PROXY_METHOD_NAME':
       proxy_method_name,
       'PARAM_NAMES':
@@ -878,7 +852,7 @@
 
 def _MakeProxySignature(options, proxy_native):
   params_with_types = ', '.join('%s %s' % (p.datatype, p.name)
-                                for p in proxy_native.params)
+                                for p in proxy_native.proxy_params)
   native_method_line = """
       public static native ${RETURN} ${PROXY_NAME}(${PARAMS_WITH_TYPES});"""
 
@@ -889,7 +863,7 @@
     signature_template = string.Template(native_method_line)
 
     alt_name = None
-    return_type, params_list = proxy_native.return_and_signature
+    return_type, params_list = proxy_native.proxy_return_and_signature
     proxy_name = _GetMultiplexProxyName(return_type, params_list)
     params_with_types = 'long switch_num' + _GetParamsListForMultiplex(
         params_list, with_types=True)
@@ -910,13 +884,18 @@
 
   return signature_template.substitute({
       'ALT_NAME': alt_name,
-      'RETURN': proxy_native.return_type,
+      'RETURN': proxy_native.proxy_return_type,
       'PROXY_NAME': proxy_name,
       'PARAMS_WITH_TYPES': params_with_types,
   })
 
 
 def _GetFilesSetFromSources(sources_files, file_exclusions):
+  def should_include(p):
+    return ((p.startswith('..') or os.path.isabs(p))
+            and p not in file_exclusions and not p.endswith('.kt')
+            and not p.endswith('package-info.java'))
+
   java_file_paths = set()
   for f in sources_files:
     # Skip generated files (ones starting with ..), since the GN targets do not
@@ -924,8 +903,7 @@
     # running. Thus, we don't support JNI from generated files. Also skip Kotlin
     # files as they are not supported by JNI generation.
     java_file_paths.update(p for p in build_utils.ReadSourcesList(f)
-                           if p.startswith('..') and p not in file_exclusions
-                           and not p.endswith('.kt'))
+                           if should_include(p))
   return java_file_paths
 
 
@@ -989,7 +967,6 @@
       'an @JniNatives interface')
   arg_parser.add_argument(
       '--module-name',
-      default='',
       help='Only look at natives annotated with a specific module name.')
   arg_parser.add_argument(
       '--enable-jni-multiplexing',
diff --git a/base/android/jni_generator/jni_registration_generator.pydeps b/base/android/jni_generator/jni_registration_generator.pydeps
index 1ff70f0..dd984a4e 100644
--- a/base/android/jni_generator/jni_registration_generator.pydeps
+++ b/base/android/jni_generator/jni_registration_generator.pydeps
@@ -5,7 +5,11 @@
 ../../../build/android/gyp/util/build_utils.py
 ../../../build/gn_helpers.py
 ../../../build/zip_helpers.py
+common.py
 java_lang_classes.py
 jni_generator.py
 jni_registration_generator.py
+models.py
+parse.py
+proxy.py
 type_resolver.py
diff --git a/base/android/jni_generator/models.py b/base/android/jni_generator/models.py
new file mode 100644
index 0000000..a4be753
--- /dev/null
+++ b/base/android/jni_generator/models.py
@@ -0,0 +1,65 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Classes that are the "M" of MVC."""
+
+import dataclasses
+from typing import List
+from typing import Optional
+
+
+@dataclasses.dataclass(frozen=True)
+class Param:
+  """Describes a param for a method, either java or native."""
+  annotations: List[str]
+  datatype: str
+  name: str
+
+
+@dataclasses.dataclass(frozen=True)
+class JavaClass:
+  _fqn: str
+  visibility: Optional[str] = None
+
+  def __post_init__(self):
+    assert '.' not in self._fqn, f'{self._fqn} should have / and $, but not .'
+
+  def __str__(self):
+    return self.full_name_with_dots
+
+  @property
+  def nested_name(self):
+    return self.name.rsplit('$', 1)[-1]
+
+  @property
+  def name(self):
+    return self._fqn.rsplit('/', 1)[-1]
+
+  @property
+  def package_with_slashes(self):
+    return self._fqn.rsplit('/', 1)[0]
+
+  @property
+  def package_with_dots(self):
+    return self.package_with_slashes.replace('/', '.')
+
+  @property
+  def full_name_with_slashes(self):
+    return self._fqn
+
+  @property
+  def full_name_with_dots(self):
+    return self._fqn.replace('/', '.')
+
+  @property
+  def is_public(self):
+    return self.visibility == 'public'
+
+  def make_prefixed(self, prefix=None):
+    if not prefix:
+      return self
+    prefix = prefix.replace('.', '/')
+    return JavaClass(f'{prefix}/{self._fqn}', visibility=self.visibility)
+
+  def make_nested(self, name, visibility=None):
+    return JavaClass(f'{self._fqn}${name}', visibility=visibility)
diff --git a/base/android/jni_generator/parse.py b/base/android/jni_generator/parse.py
new file mode 100644
index 0000000..b3a4801
--- /dev/null
+++ b/base/android/jni_generator/parse.py
@@ -0,0 +1,290 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+import dataclasses
+import os
+import re
+from typing import List
+from typing import Optional
+
+import models
+import type_resolver
+
+
+@dataclasses.dataclass
+class ParsedMethod:
+  name: str
+  return_type: str
+  params: List[models.Param]
+  native_class_name: str
+
+
+@dataclasses.dataclass
+class ParsedFile:
+  filename: str
+  java_class: models.JavaClass
+  type_resolver: type_resolver.TypeResolver
+  proxy_methods: List[ParsedMethod]
+  non_proxy_natives: list  # [jni_generator.NativeMethod]
+  called_by_natives: list  # [jni_generator.CalledByNative]
+  proxy_interface: Optional[models.JavaClass] = None
+  module_name: Optional[str] = None  # E.g. @NativeMethods("module_name")
+  jni_namespace: Optional[str] = None  # E.g. @JNINamespace("content")
+
+
+@dataclasses.dataclass
+class _ParsedProxyNatives:
+  interface_name: str
+  visibility: str
+  module_name: str
+  methods: List[ParsedMethod]
+
+
+# Match single line comments, multiline comments, character literals, and
+# double-quoted strings.
+_COMMENT_REMOVER_REGEX = re.compile(
+    r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
+    re.DOTALL | re.MULTILINE)
+
+
+def _remove_comments(contents):
+  # We need to support both inline and block comments, and we need to handle
+  # strings that contain '//' or '/*'.
+  def replacer(match):
+    # Replace matches that are comments with nothing; return literals/strings
+    # unchanged.
+    s = match.group(0)
+    if s.startswith('/'):
+      return ''
+    else:
+      return s
+
+  return _COMMENT_REMOVER_REGEX.sub(replacer, contents)
+
+
+_PACKAGE_REGEX = re.compile('^package\s+(\S+?);', flags=re.MULTILINE)
+
+
+def _parse_package(contents):
+  match = _PACKAGE_REGEX.search(contents)
+  if not match:
+    raise SyntaxError('Unable to find "package" line')
+  return match.group(1)
+
+
+_CLASSES_REGEX = re.compile(
+    r'^(.*?)(?:\b(public|protected|private)?\b)\s*'
+    r'(?:\b(?:static|abstract|final|sealed)\s+)*'
+    r'\b(?:class|interface|enum)\s+(\w+?)\b[^"]*?$',
+    flags=re.MULTILINE)
+
+
+# Does not handle doubly-nested classes.
+def _parse_java_classes(contents):
+  package = _parse_package(contents).replace('.', '/')
+  outer_class = None
+  nested_classes = []
+  for m in _CLASSES_REGEX.finditer(contents):
+    preamble, visibility, class_name = m.groups()
+    # Ignore annoations like @Foo("contains the words class Bar")
+    if preamble.count('"') % 2 != 0:
+      continue
+    if outer_class is None:
+      outer_class = models.JavaClass(f'{package}/{class_name}', visibility)
+    else:
+      nested_classes.append(outer_class.make_nested(class_name, visibility))
+
+  if outer_class is None:
+    raise SyntaxError('No classes found.')
+
+  return outer_class, nested_classes
+
+
+def parse_javap_signature(signature_line):
+  prefix = 'Signature: '
+  index = signature_line.find(prefix)
+  if index == -1:
+    prefix = 'descriptor: '
+    index = signature_line.index(prefix)
+  return '"%s"' % signature_line[index + len(prefix):]
+
+
+def strip_generics(value):
+  """Strips Java generics from a string."""
+  nest_level = 0  # How deeply we are nested inside the generics.
+  start_index = 0  # Starting index of the last non-generic region.
+  out = []
+
+  for i, c in enumerate(value):
+    if c == '<':
+      if nest_level == 0:
+        out.append(value[start_index:i])
+      nest_level += 1
+    elif c == '>':
+      start_index = i + 1
+      nest_level -= 1
+  out.append(value[start_index:])
+  return ''.join(out)
+
+
+def parse_param_list(line, from_javap=False):
+  """Parses the params into a list of Param objects."""
+  if not line:
+    return []
+  ret = []
+  line = strip_generics(line)
+  for p in line.split(','):
+    items = p.split()
+
+    if 'final' in items:
+      items.remove('final')
+
+    # Remove @Annotations from parameters.
+    annotations = []
+    while items[0].startswith('@'):
+      annotations.append(items[0])
+      del items[0]
+
+    datatype = items[0]
+    # Handle varargs.
+    if datatype.endswith('...'):
+      datatype = datatype[:-3] + '[]'
+
+    if from_javap:
+      datatype = datatype.replace('.', '/')
+
+    name = items[1] if len(items) > 1 else 'p%s' % len(ret)
+
+    ret.append(
+        models.Param(annotations=annotations, datatype=datatype, name=name))
+
+  return ret
+
+
+_NATIVE_PROXY_EXTRACTION_REGEX = re.compile(
+    r'@NativeMethods(?:\(\s*"(?P<module_name>\w+)"\s*\))?[\S\s]+?'
+    r'(?P<visibility>public)?\binterface\s*'
+    r'(?P<interface_name>\w*)\s*(?P<interface_body>{(\s*.*)+?\s*})')
+
+# Matches on method declarations unlike _EXTRACT_NATIVES_REGEX
+# doesn't require name to be prefixed with native, and does not
+# require a native qualifier.
+_EXTRACT_METHODS_REGEX = re.compile(
+    r'(@NativeClassQualifiedName\(\"(?P<native_class_name>\S*?)\"\)\s*)?'
+    r'(?P<qualifiers>'
+    r'((public|private|static|final|abstract|protected|native)\s*)*)\s+'
+    r'(?P<return_type>\S*)\s+'
+    r'(?P<name>\w+)\((?P<params>.*?)\);',
+    flags=re.DOTALL)
+
+
+def _parse_proxy_natives(contents):
+  matches = list(_NATIVE_PROXY_EXTRACTION_REGEX.finditer(contents))
+  if not matches:
+    return None
+  if len(matches) > 1:
+    raise SyntaxError(
+        'Multiple @NativeMethod interfaces in one class is not supported.')
+
+  match = matches[0]
+  ret = _ParsedProxyNatives(interface_name=match.group('interface_name'),
+                            visibility=match.group('visibility') == 'public',
+                            module_name=match.group('module_name'),
+                            methods=[])
+  interface_body = match.group('interface_body')
+
+  for match in _EXTRACT_METHODS_REGEX.finditer(interface_body):
+    params = parse_param_list(match.group('params'))
+    ret.methods.append(
+        ParsedMethod(name=match.group('name'),
+                     return_type=match.group('return_type'),
+                     params=params,
+                     native_class_name=match.group('native_class_name')))
+  if not ret.methods:
+    raise SyntaxError('Found no methods within @NativeMethod interface.')
+  return ret
+
+
+_IMPORT_REGEX = re.compile(r'^import\s+([^\s*]+);', flags=re.MULTILINE)
+_IMPORT_CLASS_NAME_REGEX = re.compile(r'^(.*?)\.([A-Z].*)')
+
+
+def _parse_imports(contents):
+  # Regex skips static imports as well as wildcard imports.
+  names = _IMPORT_REGEX.findall(contents)
+  for name in names:
+    m = _IMPORT_CLASS_NAME_REGEX.match(name)
+    if m:
+      package, class_name = m.groups()
+      yield models.JavaClass(
+          package.replace('.', '/') + '/' + class_name.replace('.', '$'))
+
+
+_JNI_NAMESPACE_REGEX = re.compile('@JNINamespace\("(.*?)"\)')
+
+
+def _parse_jni_namespace(contents):
+  m = _JNI_NAMESPACE_REGEX.findall(contents)
+  if not m:
+    return ''
+  if len(m) > 1:
+    raise SyntaxError('Found multiple @JNINamespace attributes.')
+  return m[0]
+
+
+def _do_parse(filename, *, package_prefix):
+  assert not filename.endswith('.kt'), (
+      f'Found {filename}, but Kotlin is not supported by JNI generator.')
+  with open(filename) as f:
+    contents = f.read()
+  contents = _remove_comments(contents)
+
+  outer_class, nested_classes = _parse_java_classes(contents)
+
+  expected_name = os.path.splitext(os.path.basename(filename))[0]
+  if outer_class.name != expected_name:
+    raise SyntaxError(
+        f'Found class "{outer_class.name}" but expected "{expected_name}".')
+
+  if package_prefix:
+    outer_class = outer_class.make_prefixed(package_prefix)
+    nested_classes = [c.make_prefixed(package_prefix) for c in nested_classes]
+
+  resolver = type_resolver.TypeResolver(outer_class)
+  for java_class in _parse_imports(contents):
+    resolver.add_import(java_class)
+  for java_class in nested_classes:
+    resolver.add_nested_class(java_class)
+
+  parsed_proxy_natives = _parse_proxy_natives(contents)
+  jni_namespace = _parse_jni_namespace(contents)
+
+  # TODO(crbug.com/1406605): Remove circular dep.
+  import jni_generator
+  non_proxy_natives = jni_generator.ExtractNatives(contents, 'long')
+  called_by_natives = jni_generator.ExtractCalledByNatives(resolver, contents)
+
+  ret = ParsedFile(filename=filename,
+                   jni_namespace=jni_namespace,
+                   java_class=outer_class,
+                   type_resolver=resolver,
+                   proxy_methods=[],
+                   non_proxy_natives=non_proxy_natives,
+                   called_by_natives=called_by_natives)
+
+  if parsed_proxy_natives:
+    ret.module_name = parsed_proxy_natives.module_name
+    ret.proxy_interface = outer_class.make_nested(
+        parsed_proxy_natives.interface_name,
+        visibility=parsed_proxy_natives.visibility)
+    ret.proxy_methods = parsed_proxy_natives.methods
+
+  return ret
+
+
+def parse_java_file(filename, *, package_prefix=None):
+  try:
+    return _do_parse(filename, package_prefix=package_prefix)
+  except SyntaxError as e:
+    e.msg += f' (when parsing {filename})'
+    raise
diff --git a/base/android/jni_generator/proxy.py b/base/android/jni_generator/proxy.py
new file mode 100644
index 0000000..0c1d319
--- /dev/null
+++ b/base/android/jni_generator/proxy.py
@@ -0,0 +1,54 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Logic related to proxying calls through GEN_JNI.java."""
+
+import base64
+import hashlib
+
+import common
+import models
+
+# 'Proxy' native methods are declared in an @NativeMethods interface without
+# a native qualifier and indicate that the JNI annotation processor should
+# generate code to link between the equivalent native method as if it were
+# declared statically.
+
+_MAX_CHARS_FOR_HASHED_NATIVE_METHODS = 8
+
+
+def get_gen_jni_class(*, short=False, name_prefix=None, package_prefix=None):
+  """Returns the JavaClass for GEN_JNI."""
+  package = 'J' if short else 'org/chromium/base/natives'
+  name_prefix = name_prefix + '_' if name_prefix else ''
+  name = name_prefix + ('N' if short else 'GEN_JNI')
+
+  return models.JavaClass(f'{package}/{name}').make_prefixed(package_prefix)
+
+
+def _create_hashed_method_name(non_hashed_name, is_test_only):
+  md5 = hashlib.md5(non_hashed_name.encode('utf8')).digest()
+  hash_b64 = base64.b64encode(md5, altchars=b'$_').decode('utf-8')
+
+  long_hash = ('M' + hash_b64).rstrip('=')
+  hashed_name = long_hash[:_MAX_CHARS_FOR_HASHED_NATIVE_METHODS]
+
+  # If the method is a test-only method, we don't care about saving size on
+  # the method name, since it shouldn't show up in the binary. Additionally,
+  # if we just hash the name, our checkers which enforce that we have no
+  # "ForTesting" methods by checking for the suffix "ForTesting" will miss
+  # these. We could preserve the name entirely and not hash anything, but
+  # that risks collisions. So, instead, we just append "ForTesting" to any
+  # test-only hashes, to ensure we catch any test-only methods that
+  # shouldn't be in our final binary.
+  if is_test_only:
+    return hashed_name + '_ForTesting'
+  return hashed_name
+
+
+def create_method_names(java_class, method_name, is_test_only):
+  """Returns the method name used in GEN_JNI (both hashed an non-hashed)."""
+  proxy_name = common.EscapeClassName(
+      f'{java_class.full_name_with_slashes}/{method_name}')
+  hashed_proxy_name = _create_hashed_method_name(proxy_name, is_test_only)
+  return proxy_name, hashed_proxy_name
diff --git a/base/android/jni_generator/type_resolver.py b/base/android/jni_generator/type_resolver.py
index d5594f7..06afd98 100644
--- a/base/android/jni_generator/type_resolver.py
+++ b/base/android/jni_generator/type_resolver.py
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import re
-
 import java_lang_classes
 
 _PRIMITIVE_MAP = {
@@ -18,26 +16,19 @@
     'void': 'V',
 }
 
-_IMPORT_REGEX = re.compile(r'^import\s+(\S+?);', flags=re.MULTILINE)
-_INNER_TYPES_REGEX = re.compile(r'(?:class|interface|enum)\s+?(\w+?)\W')
-
 
 class TypeResolver:
   """Converts type names to fully qualified names."""
-  def __init__(self, fully_qualified_class):
-    self._fully_qualified_class = fully_qualified_class
-    self._package = '/'.join(fully_qualified_class.split('/')[:-1])
-    self._imports = []
-    self._inner_classes = []
+  def __init__(self, java_class):
+    self._java_class = java_class
+    self.imports = []
+    self._nested_classes = []
 
-  def parse_imports_and_nested_types(self, contents):
-    names = _IMPORT_REGEX.findall(contents)
-    self._imports.extend(
-        n.replace('.', '/') for n in names if not n.endswith('*'))
+  def add_import(self, java_class):
+    self.imports.append(java_class)
 
-    for name in _INNER_TYPES_REGEX.findall(contents):
-      if not self._fully_qualified_class.endswith(name):
-        self._inner_classes.append(f'{self._fully_qualified_class}${name}')
+  def add_nested_class(self, java_class):
+    self._nested_classes.append(java_class)
 
   def java_to_jni(self, param):
     """Converts a java param into a JNI signature type."""
@@ -61,25 +52,19 @@
       # Coming from javap, use the fully qualified param directly.
       return param
 
-    for qualified_name in ([self._fully_qualified_class] + self._inner_classes):
-      if (qualified_name.endswith('/' + param)
-          or qualified_name.endswith('$' + param.replace('.', '$'))
-          or qualified_name == param):
-        return qualified_name
+    if self._java_class.name == param:
+      return self._java_class.full_name_with_slashes
+
+    for clazz in self._nested_classes:
+      if param in (clazz.name, clazz.nested_name):
+        return clazz.full_name_with_slashes
 
     # Is it from an import? (e.g. referecing Class from import pkg.Class;
     # note that referencing an inner class Inner from import pkg.Class.Inner
     # is not supported).
-    for qualified_name in self._imports:
-      if qualified_name.endswith('/' + param):
-        # Ensure it's not an inner class.
-        components = qualified_name.split('/')
-        if len(components) > 2 and components[-2][0].isupper():
-          raise SyntaxError('Inner class (%s) can not be imported '
-                            'and used by JNI (%s). Please import the outer '
-                            'class and use Outer.Inner instead.' %
-                            (qualified_name, param))
-        return qualified_name
+    for clazz in self.imports:
+      if param in (clazz.name, clazz.nested_name):
+        return clazz.full_name_with_slashes
 
     # Is it an inner class from an outer class import? (e.g. referencing
     # Class.Inner from import pkg.Class).
@@ -87,9 +72,9 @@
       components = param.split('.')
       outer = '/'.join(components[:-1])
       inner = components[-1]
-      for qualified_name in self._imports:
-        if qualified_name.endswith('/' + outer):
-          return f'{qualified_name}${inner}'
+      for clazz in self.imports:
+        if clazz.name == outer:
+          return f'{clazz.full_name_with_slashes}${inner}'
       param = param.replace('.', '$')
 
     # java.lang classes always take priority over types from the same package.
@@ -99,7 +84,7 @@
       return f'java/lang/{param}'
 
     # Type not found, falling back to same package as this class.
-    return f'{self._package}/{param}'
+    return f'{self._java_class.package_with_slashes}/{param}'
 
   def create_signature(self, params, returns):
     """Returns the JNI signature for the given datatypes."""
diff --git a/base/trace_event/builtin_categories.h b/base/trace_event/builtin_categories.h
index dd59416..caf0822 100644
--- a/base/trace_event/builtin_categories.h
+++ b/base/trace_event/builtin_categories.h
@@ -258,6 +258,7 @@
   X(TRACE_DISABLED_BY_DEFAULT("skia.gpu"))                               \
   X(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache"))                         \
   X(TRACE_DISABLED_BY_DEFAULT("skia.shaders"))                           \
+  X(TRACE_DISABLED_BY_DEFAULT("skottie"))                                \
   X(TRACE_DISABLED_BY_DEFAULT("SyncFileSystem"))                         \
   X(TRACE_DISABLED_BY_DEFAULT("system_power"))                           \
   X(TRACE_DISABLED_BY_DEFAULT("system_stats"))                           \
diff --git a/base/values.cc b/base/values.cc
index 9121574..6024d315 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -909,6 +909,30 @@
                         base::to_address(storage_.cend()));
 }
 
+// TODO(crbug.com/1446739): Implement reverse iterators in
+// CheckedContiguousIterator and use them here.
+std::vector<Value>::reverse_iterator Value::List::rend() {
+  return storage_.rend();
+}
+
+// TODO(crbug.com/1446739): Implement reverse iterators in
+// CheckedContiguousIterator and use them here.
+std::vector<Value>::const_reverse_iterator Value::List::rend() const {
+  return storage_.rend();
+}
+
+// TODO(crbug.com/1446739): Implement reverse iterators in
+// CheckedContiguousIterator and use them here.
+std::vector<Value>::reverse_iterator Value::List::rbegin() {
+  return storage_.rbegin();
+}
+
+// TODO(crbug.com/1446739): Implement reverse iterators in
+// CheckedContiguousIterator and use them here.
+std::vector<Value>::const_reverse_iterator Value::List::rbegin() const {
+  return storage_.rbegin();
+}
+
 const Value& Value::List::front() const {
   CHECK(!storage_.empty());
   return storage_.front();
diff --git a/base/values.h b/base/values.h
index 930ff25..5d62e8c 100644
--- a/base/values.h
+++ b/base/values.h
@@ -140,6 +140,8 @@
 //       `front()`, `back()`, `reserve()`, `operator[]`, `clear()`, `erase()`:
 //       Identical to the STL container equivalents, with additional safety
 //       checks, e.g. `operator[]` will `CHECK()` if the index is out of range.
+// - `rbegin()` and `rend()` are also supported, but there are no safety checks
+// (see crbug.com/1446739).
 // - `Clone()`: Create a deep copy.
 // - `Append()`: Append a value to the end of the list. Accepts `Value` or any
 //       of the subtypes that `Value` can hold.
@@ -628,6 +630,15 @@
     const_iterator end() const;
     const_iterator cend() const;
 
+    // Returns a reverse iterator preceding the first value in this list. May
+    // not be dereferenced.
+    std::vector<Value>::reverse_iterator rend();
+    std::vector<Value>::const_reverse_iterator rend() const;
+
+    // Returns a reverse iterator to the last value in this list.
+    std::vector<Value>::reverse_iterator rbegin();
+    std::vector<Value>::const_reverse_iterator rbegin() const;
+
     // Returns a reference to the first value in the container. Fails with
     // `CHECK()` if the list is empty.
     const Value& front() const;
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index 0b99a937..6d1cf6f 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -534,6 +534,29 @@
   EXPECT_EQ(*iter, "Hello world!");
 }
 
+TEST(ValuesTest, ReverseIter) {
+  Value::List list;
+  const Value::List& const_list = list;
+
+  list.Append(Value(true));
+  list.Append(Value(123));
+  list.Append(Value("Hello world!"));
+
+  auto iter = list.rbegin();
+  EXPECT_TRUE(const_list.rbegin() == iter);
+  EXPECT_EQ(*iter, "Hello world!");
+
+  ++iter;
+  EXPECT_EQ(*iter, 123);
+
+  ++iter;
+  EXPECT_EQ(*iter, true);
+
+  ++iter;
+  EXPECT_TRUE(list.rend() == iter);
+  EXPECT_TRUE(const_list.rend() == iter);
+}
+
 // Test all three behaviors of EnsureDict() (Create a new dict where no
 // matchining values exist, return an existing dict, create a dict overwriting
 // a value of another type).
diff --git a/build/android/gyp/javac_output_processor.py b/build/android/gyp/javac_output_processor.py
index 6f1fbf6..6faf5de5 100755
--- a/build/android/gyp/javac_output_processor.py
+++ b/build/android/gyp/javac_output_processor.py
@@ -90,9 +90,6 @@
         re.compile(fileline_prefix + r'( error: symbol not found [\w.]+)$'),
     ]
 
-    self._filter_out_re = re.compile(r'.*warning.*Cannot use file \S+ because'
-                                     r' it is locked by another process')
-
     # Example: import org.chromium.url.GURL;
     self._import_re = re.compile(r'\s*import (?P<imported_class>[\w\.]+);$')
 
@@ -111,14 +108,12 @@
   def Process(self, lines):
     """ Processes javac output.
 
-      - Removes unnecessary output.
       - Applies colors to output.
       - Suggests GN dep to add for 'unresolved symbol in Java import' errors.
       """
     lines = self._ElaborateLinesForUnknownSymbol(iter(lines))
     for line in lines:
-      if not self._filter_out_re.match(line):
-        yield self._ApplyColors(line)
+      yield self._ApplyColors(line)
     if self._suggested_deps:
 
       def yellow(text):
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index 4617d0bf..d5dbb3a 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -189,6 +189,18 @@
       'All illegal access operations)')
 
 
+# This filter applies globally to all CheckOutput calls. We use this to prevent
+# messages from failing the build, without actually removing them.
+def _FailureFilter(output):
+  # This is a message that comes from the JDK which can't be disabled, which as
+  # far as we can tell, doesn't cause any real issues. It only happens
+  # occasionally on the bots. See crbug.com/1441023 for details.
+  jdk_filter = (r'.*warning.*Cannot use file \S+ because'
+                r' it is locked by another process')
+  output = FilterLines(output, jdk_filter)
+  return output
+
+
 # This can be used in most cases like subprocess.check_output(). The output,
 # particularly when the command fails, better highlights the command's failure.
 # If the command fails, raises a build_utils.CalledProcessError.
@@ -238,7 +250,7 @@
     else:
       stream_name = 'stderr'
 
-    if fail_on_output:
+    if fail_on_output and _FailureFilter(stdout + stderr):
       MSG = """
 Command failed because it wrote to {}.
 You can often set treat_warnings_as_errors=false to not treat output as \
diff --git a/build/android/java/test/DefaultLocaleLintTest.java b/build/android/java/test/DefaultLocaleLintTest.java
index 76f9ea5..d1f6e285 100644
--- a/build/android/java/test/DefaultLocaleLintTest.java
+++ b/build/android/java/test/DefaultLocaleLintTest.java
@@ -9,7 +9,7 @@
 /**
  * Class which fails 'DefaultLocale' lint check.
  */
-public class LintTest extends Application {
+public class DefaultLocaleLintTest extends Application {
     public String testTriggerDefaultLocaleCheck(int any) {
         // String format with an integer requires a Locale since it may be formatted differently.
         return String.format("Test %d", any);
diff --git a/build/android/java/test/NewApiLintTest.java b/build/android/java/test/NewApiLintTest.java
index 66d576a..35a1f622 100644
--- a/build/android/java/test/NewApiLintTest.java
+++ b/build/android/java/test/NewApiLintTest.java
@@ -9,7 +9,7 @@
 /**
  * Class which fails 'NewAPI' lint check.
  */
-public class NewApiTest extends Application {
+public class NewApiLintTest extends Application {
     public String testTriggerNewApiCheck() {
         // This was added in API level 30.
         return getApplicationContext().getAttributionTag();
diff --git a/build/rust/cargo_crate.gni b/build/rust/cargo_crate.gni
index 6f85f6b..9368a9ca 100644
--- a/build/rust/cargo_crate.gni
+++ b/build/rust/cargo_crate.gni
@@ -103,7 +103,7 @@
   if (defined(invoker.rustc_metadata)) {
     _rustc_metadata = invoker.rustc_metadata
   } else if (defined(invoker.epoch)) {
-    _rustc_metadata = invoker.epoch
+    _rustc_metadata = "${_crate_name}-${invoker.epoch}"
   }
 
   # Executables need to have unique names. Work out a prefix.
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc
index 4f75391..d047e73 100644
--- a/cc/metrics/compositor_frame_reporter.cc
+++ b/cc/metrics/compositor_frame_reporter.cc
@@ -1396,13 +1396,6 @@
         NOTREACHED();
     }
   }
-  // To handle web test failures which causes an event to be coalesced with an
-  // event having null(0) timestamp.
-  // TODO(b/276722271) : Investigate if this needs to be fixed on test side or
-  // if this is a valid scenario.
-  if (last_coalesced_ts.is_null()) {
-    last_coalesced_ts = input_generation_ts;
-  }
 
   TRACE_EVENT("input,input.scrolling", "PresentedFrameInformation",
               [events_metrics = std::cref(events_metrics_), fling_input_count,
diff --git a/cc/metrics/scroll_jank_dropped_frame_tracker.cc b/cc/metrics/scroll_jank_dropped_frame_tracker.cc
index 0ea6a79..82af453c 100644
--- a/cc/metrics/scroll_jank_dropped_frame_tracker.cc
+++ b/cc/metrics/scroll_jank_dropped_frame_tracker.cc
@@ -37,8 +37,14 @@
     base::TimeTicks last_input_generation_ts,
     base::TimeTicks presentation_ts,
     base::TimeDelta vsync_interval) {
-  DCHECK_GE(last_input_generation_ts, first_input_generation_ts);
-  DCHECK_GT(presentation_ts, last_input_generation_ts);
+  if ((last_input_generation_ts < first_input_generation_ts) ||
+      (presentation_ts <= last_input_generation_ts)) {
+    // TODO(crbug/1447358): Investigate when these edge cases can be triggered
+    // in field and web tests. We have already seen this triggered in field, and
+    // some web tests where an event with null(0) timestamp gets coalesced with
+    // a "normal" input.
+    return;
+  }
   // TODO(b/276722271) : Analyze and reduce these cases of out of order
   // frame termination.
   if (presentation_ts <= prev_presentation_ts_) {
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index ac63480..739901a28 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -1671,7 +1671,7 @@
       error_pixels_percentage =
           std::max(is_record_filter ? 12.f : 0.2f, error_pixels_percentage);
       max_abs_error = std::max(is_record_filter ? 246 : 2, max_abs_error);
-      avg_error = std::max(is_record_filter ? 59.1f : 2.f, avg_error);
+      avg_error = std::max(is_record_filter ? 59.6f : 2.f, avg_error);
     } else if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) {
       switch (GetTextBlobStrategy(GetParam())) {
         case TextBlobStrategy::kRecordFilter:
diff --git a/chrome/VERSION b/chrome/VERSION
index d6fcafd..c0f5bc0e 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=115
 MINOR=0
-BUILD=5789
+BUILD=5790
 PATCH=0
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 761e1e4..1bbdd5f 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -473,6 +473,7 @@
   "java/res/layout/autofill_server_data_label.xml",
   "java/res/layout/autofill_server_data_text_label.xml",
   "java/res/layout/autofill_update_address_profile_prompt.xml",
+  "java/res/layout/bookmark_add_new_folder_input_layout.xml",
   "java/res/layout/bookmark_edit.xml",
   "java/res/layout/bookmark_folder_select_activity.xml",
   "java/res/layout/bookmark_main.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 55211c7..d1c57840 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -166,6 +166,7 @@
   "java/src/org/chromium/chrome/browser/background_task_scheduler/ProxyNativeTask.java",
   "java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BasicBookmarkQueryHandler.java",
+  "java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinator.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkDelegate.java",
   "java/src/org/chromium/chrome/browser/bookmarks/BookmarkFeatures.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 52e35e4..290bbbfa 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -45,11 +45,13 @@
   "junit/src/org/chromium/chrome/browser/base/SplitPreloaderTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonControllerUnitTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BasicBookmarkQueryHandlerTest.java",
+  "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkImageFetcherTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinatorTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerViewBinderTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkSaveFlowMediatorTest.java",
+  "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/BookmarkUiPrefsTest.java",
   "junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java",
diff --git a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
index fe26672..d06bf35 100644
--- a/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/monochrome_public_bundle__base.AndroidManifest.expected
@@ -87,6 +87,7 @@
       android:label="@string/app_name"
       android:largeHeap="false"
       android:manageSpaceActivity="@string/manage_space_activity"
+      android:memtagMode="async"
       android:multiArch="true"
       android:networkSecurityConfig="@xml/network_security_config"
       android:roundIcon="@drawable/ic_launcher_round"
diff --git a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
index 5e65e21f..0d46f9b 100644
--- a/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
+++ b/chrome/android/expectations/trichrome_chrome_bundle__base.AndroidManifest.expected
@@ -87,6 +87,7 @@
       android:label="@string/app_name"
       android:largeHeap="false"
       android:manageSpaceActivity="@string/manage_space_activity"
+      android:memtagMode="async"
       android:multiArch="true"
       android:networkSecurityConfig="@xml/network_security_config"
       android:roundIcon="@drawable/ic_launcher_round"
diff --git a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
index ae4fd99..698a3bb 100644
--- a/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
+++ b/chrome/android/features/cablev2_authenticator/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
@@ -32,7 +32,6 @@
 import org.chromium.blink.mojom.ResidentKeyRequirement;
 import org.chromium.components.webauthn.Fido2Api;
 import org.chromium.components.webauthn.Fido2ApiCall;
-import org.chromium.content_public.browser.WebAuthenticationDelegate;
 
 import java.nio.ByteBuffer;
 import java.security.NoSuchAlgorithmException;
@@ -134,9 +133,9 @@
         mAttestationAcceptable =
                 params.authenticatorSelection.residentKey == ResidentKeyRequirement.DISCOURAGED;
 
-        Fido2ApiCall call = new Fido2ApiCall(mContext, WebAuthenticationDelegate.Support.BROWSER);
+        Fido2ApiCall call = new Fido2ApiCall(mContext);
         Parcel args = call.start();
-        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult(call);
+        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult();
         args.writeStrongBinder(result);
         args.writeInt(1); // This indicates that the following options are present.
 
@@ -158,9 +157,9 @@
         PublicKeyCredentialRequestOptions params =
                 PublicKeyCredentialRequestOptions.deserialize(ByteBuffer.wrap(serializedParams));
 
-        Fido2ApiCall call = new Fido2ApiCall(mContext, WebAuthenticationDelegate.Support.BROWSER);
+        Fido2ApiCall call = new Fido2ApiCall(mContext);
         Parcel args = call.start();
-        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult(call);
+        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult();
         args.writeStrongBinder(result);
         args.writeInt(1); // This indicates that the following options are present.
         Fido2Api.appendBrowserGetAssertionOptionsToParcel(params,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java
index aa329c5..fa74db8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerProperties.java
@@ -46,7 +46,21 @@
     public static final PropertyModel.WritableIntPropertyKey MODE =
             new PropertyModel.WritableIntPropertyKey();
 
+    /**
+     * A property which is set to focus on the passed tab index for accessibility.
+     */
+    public static final PropertyModel.WritableIntPropertyKey FOCUS_TAB_INDEX_FOR_ACCESSIBILITY =
+            new PropertyModel.WritableIntPropertyKey();
+
+    /**
+     * A property which removes the accessibility focus on the passed tab index due to content
+     * change reasons.
+     */
+    public static final PropertyModel.WritableIntPropertyKey UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY =
+            new PropertyModel.WritableIntPropertyKey();
+
     public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {IS_VISIBLE, IS_INCOGNITO,
             VISIBILITY_LISTENER, INITIAL_SCROLL_INDEX, ANIMATE_VISIBILITY_CHANGES, TOP_MARGIN,
-            BOTTOM_CONTROLS_HEIGHT, SHADOW_TOP_OFFSET, BOTTOM_PADDING, MODE};
+            BOTTOM_CONTROLS_HEIGHT, SHADOW_TOP_OFFSET, BOTTOM_PADDING, MODE,
+            FOCUS_TAB_INDEX_FOR_ACCESSIBILITY, UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY};
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
index e064bf7b..c33d801 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinder.java
@@ -7,20 +7,26 @@
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.ANIMATE_VISIBILITY_CHANGES;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.BOTTOM_CONTROLS_HEIGHT;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.BOTTOM_PADDING;
+import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.INITIAL_SCROLL_INDEX;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.IS_INCOGNITO;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.IS_VISIBLE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.MODE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.SHADOW_TOP_OFFSET;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.TOP_MARGIN;
+import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY;
 import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.VISIBILITY_LISTENER;
 
 import android.app.Activity;
 import android.graphics.Rect;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
 
 import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
@@ -71,6 +77,12 @@
             view.setShadowTopOffset(model.get(SHADOW_TOP_OFFSET));
         } else if (BOTTOM_PADDING == propertyKey) {
             view.setBottomPadding(model.get(BOTTOM_PADDING));
+        } else if (FOCUS_TAB_INDEX_FOR_ACCESSIBILITY == propertyKey) {
+            updateAccessibilityFocus(model.get(FOCUS_TAB_INDEX_FOR_ACCESSIBILITY), view,
+                    AccessibilityEvent.TYPE_VIEW_FOCUSED, /*shouldFocus=*/true);
+        } else if (UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY == propertyKey) {
+            updateAccessibilityFocus(model.get(UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY), view,
+                    AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE, /*shouldFocus=*/false);
         }
     }
 
@@ -146,4 +158,17 @@
         assert false : "Unexpected MODE when setting INITIAL_SCROLL_INDEX.";
         return 0;
     }
+
+    private static void updateAccessibilityFocus(int index, @NonNull TabListRecyclerView view,
+            int accessibilityEventType, boolean shouldFocus) {
+        RecyclerView.ViewHolder selectedViewHolder = view.findViewHolderForAdapterPosition(index);
+        if (selectedViewHolder == null) return;
+        View focusView = selectedViewHolder.itemView;
+        if (shouldFocus) {
+            focusView.requestFocus();
+        } else {
+            focusView.clearFocus();
+        }
+        focusView.sendAccessibilityEvent(accessibilityEventType);
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
index 03fe277..7e35857 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherCoordinator.java
@@ -11,7 +11,6 @@
 import android.os.SystemClock;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -258,37 +257,6 @@
             RecordHistogram.recordTimesHistogram("Android.TabSwitcher.SetupRecyclerView.Time",
                     SystemClock.uptimeMillis() - startTimeMs);
 
-            if (TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(activity)) {
-                mMediator.addTabSwitcherViewObserver(new TabSwitcherViewObserver() {
-                    @Override
-                    public void startedShowing() {}
-
-                    @Override
-                    public void finishedShowing() {
-                        if (!mTabModelSelector.isTabStateInitialized()) return;
-
-                        int selectedIndex = mTabModelSelector.getTabModelFilterProvider()
-                                                    .getCurrentTabModelFilter()
-                                                    .index();
-                        ViewHolder selectedViewHolder =
-                                mTabListCoordinator.getContainerView()
-                                        .findViewHolderForAdapterPosition(selectedIndex);
-
-                        if (selectedViewHolder == null) return;
-
-                        View focusView = selectedViewHolder.itemView;
-                        focusView.requestFocus();
-                        focusView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                    }
-
-                    @Override
-                    public void startedHiding() {}
-
-                    @Override
-                    public void finishedHiding() {}
-                });
-            }
-
             mMessageCardProviderCoordinator = new MessageCardProviderCoordinator(
                     activity, tabModelSelector::isIncognitoSelected, (identifier) -> {
                         if (identifier == MessageService.MessageType.PRICE_MESSAGE
@@ -565,19 +533,10 @@
 
     @Override
     public void requestFocusOnCurrentTab() {
-        if (!mTabModelSelector.isTabStateInitialized()) return;
-
-        int selectedIndex =
-                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index();
-        ViewHolder selectedViewHolder =
-                mTabListCoordinator.getContainerView().findViewHolderForAdapterPosition(
-                        selectedIndex);
-
-        if (selectedViewHolder == null) return;
-
-        View focusView = selectedViewHolder.itemView;
-        focusView.requestFocus();
-        focusView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+        // TODO(crbug.com/1447564): Ideally, this shouldn't be called directly and instead mediator
+        // should listen for |requestFocusOnCurrentTab| signal implicitly and apply changes. This
+        // would require refactoring TabSwitcher.TabListDelegate and its implementation.
+        mMediator.requestAccessibilityFocusOnCurrentTab();
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
index 0b99450..40797c0 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediator.java
@@ -134,6 +134,7 @@
                                                            .getCurrentTabModelFilter(),
                             false, mShowTabsInMruOrder);
                     setInitialScrollIndexOffset();
+                    requestAccessibilityFocusOnCurrentTab();
                 }
 
                 @Override
@@ -184,6 +185,7 @@
     private Context mContext;
     private SnackbarManager mSnackbarManager;
     private boolean mIsTransitionInProgress;
+    private boolean mIsTabSwitcherShowing;
 
     /**
      * Interface to delegate resetting the tab grid.
@@ -330,6 +332,7 @@
                 if (clearIncognitoTabListForReauth()) return;
                 mResetHandler.resetWithTabList(currentTabModelFilter, false, mShowTabsInMruOrder);
                 setInitialScrollIndexOffset();
+                requestAccessibilityFocusOnCurrentTab();
             }
         };
         mTabModelSelector.addObserver(mTabModelSelectorObserver);
@@ -824,6 +827,12 @@
 
     @Override
     public void finishedShowing() {
+        mIsTabSwitcherShowing = true;
+
+        if (TabUiFeatureUtilities.isTabGroupsAndroidContinuationEnabled(mContext)) {
+            requestAccessibilityFocusOnCurrentTab();
+        }
+
         for (TabSwitcherViewObserver observer : mObservers) {
             observer.finishedShowing();
         }
@@ -838,6 +847,7 @@
 
     @Override
     public void finishedHiding() {
+        mIsTabSwitcherShowing = false;
         for (TabSwitcherViewObserver observer : mObservers) {
             observer.finishedHiding();
         }
@@ -1071,6 +1081,34 @@
         mPriceMessageService = priceMessageService;
     }
 
+    void requestAccessibilityFocusOnCurrentTab() {
+        if (!mIsTabSwitcherShowing || !mTabModelSelector.isTabStateInitialized()) {
+            return;
+        }
+
+        if (mTabModelSelector.isIncognitoSelected() && mIncognitoReauthController != null
+                && mIncognitoReauthController.isReauthPageShowing()) {
+            return;
+        }
+
+        mContainerViewModel.set(TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY,
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index());
+    }
+
+    void removeAccessibilityFocusFromCurrentTab() {
+        if (!mIsTabSwitcherShowing || !mTabModelSelector.isTabStateInitialized()) {
+            return;
+        }
+
+        if (!mTabModelSelector.isIncognitoSelected() || mIncognitoReauthController == null
+                || !mIncognitoReauthController.isReauthPageShowing()) {
+            return;
+        }
+
+        mContainerViewModel.set(TabListContainerProperties.UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY,
+                mTabModelSelector.getTabModelFilterProvider().getCurrentTabModelFilter().index());
+    }
+
     // GridCardOnClickListenerProvider implementation.
     @Override
     @Nullable
@@ -1169,6 +1207,7 @@
         if (mIncognitoReauthController != null
                 && mIncognitoReauthController.isIncognitoReauthPending()) {
             mResetHandler.resetWithTabList(null, false, mShowTabsInMruOrder);
+            removeAccessibilityFocusFromCurrentTab();
             return true;
         }
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderUnitTest.java
new file mode 100644
index 0000000..4bd558f
--- /dev/null
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderUnitTest.java
@@ -0,0 +1,93 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.LooperMode;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Robolectric tests for {@link TabListContainerViewBinder}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+@LooperMode(LooperMode.Mode.PAUSED)
+public class TabListContainerViewBinderUnitTest {
+    private static final int TAB_MODEL_FILTER_INDEX = 2;
+
+    private class MockViewHolder extends RecyclerView.ViewHolder {
+        public MockViewHolder(@NonNull View itemView) {
+            super(itemView);
+        }
+    }
+
+    @Mock
+    private PropertyModel mPropertyModelMock;
+    @Mock
+    private TabListRecyclerView mTabListRecyclerViewMock;
+    @Mock
+    private View mViewMock;
+
+    private MockViewHolder mMockViewHolder;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    @SmallTest
+    public void testFocusTabIndexForAccessibilityProperty() {
+        mMockViewHolder = spy(new MockViewHolder(mViewMock));
+        doReturn(mMockViewHolder)
+                .when(mTabListRecyclerViewMock)
+                .findViewHolderForAdapterPosition(eq(TAB_MODEL_FILTER_INDEX));
+
+        doReturn(TAB_MODEL_FILTER_INDEX)
+                .when(mPropertyModelMock)
+                .get(TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY);
+        TabListContainerViewBinder.bind(mPropertyModelMock, mTabListRecyclerViewMock,
+                TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY);
+        verify(mViewMock).requestFocus();
+        verify(mViewMock).sendAccessibilityEvent(eq(AccessibilityEvent.TYPE_VIEW_FOCUSED));
+    }
+
+    @Test
+    @SmallTest
+    public void testUnfocusTabIndexForAccessibilityProperty() {
+        mMockViewHolder = spy(new MockViewHolder(mViewMock));
+        doReturn(mMockViewHolder)
+                .when(mTabListRecyclerViewMock)
+                .findViewHolderForAdapterPosition(eq(TAB_MODEL_FILTER_INDEX));
+
+        doReturn(TAB_MODEL_FILTER_INDEX)
+                .when(mPropertyModelMock)
+                .get(TabListContainerProperties.UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY);
+        TabListContainerViewBinder.bind(mPropertyModelMock, mTabListRecyclerViewMock,
+                TabListContainerProperties.UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY);
+        verify(mViewMock).clearFocus();
+        verify(mViewMock).sendAccessibilityEvent(
+                eq(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE));
+    }
+}
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
index d4fc341..7fc33b5 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMediatorUnitTest.java
@@ -29,6 +29,8 @@
 import android.content.res.Resources;
 import android.view.View;
 
+import androidx.test.filters.SmallTest;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -104,6 +106,7 @@
     private static final int TAB2_ID = 789;
     private static final int TAB3_ID = 123;
     private static final int TAB4_ID = 357;
+    private static final int TAB_MODEL_FILTER_INDEX = 2;
 
     private final OneshotSupplierImpl<IncognitoReauthController>
             mIncognitoReauthControllerSupplier = new OneshotSupplierImpl<>();
@@ -203,7 +206,7 @@
         doReturn(mTab2).when(mTabModelFilter).getTabAt(1);
         doReturn(mTab3).when(mTabModelFilter).getTabAt(2);
         doReturn(false).when(mTabModelFilter).isIncognito();
-        doReturn(2).when(mTabModelFilter).index();
+        doReturn(TAB_MODEL_FILTER_INDEX).when(mTabModelFilter).index();
         doReturn(3).when(mTabModelFilter).getCount();
 
         doReturn(2).when(mTabModel).index();
@@ -1092,6 +1095,82 @@
         verifyNoMoreInteractions(mResetHandler);
     }
 
+    @Test
+    @SmallTest
+    public void testFocusTabForAccessibility_IsInvoked_OnIncognitoReauthSuccess() {
+        initAndAssertAllProperties();
+        mModel.set(TabListContainerProperties.IS_VISIBLE, true);
+        doReturn(true).when(mTabModelFilter).isIncognito();
+        doReturn(true).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(true).when(mTabModelSelector).isTabStateInitialized();
+        doReturn(false).when(mIncognitoReauthController).isReauthPageShowing();
+        doReturn(true).when(mTabModel).isIncognito();
+
+        // Mock showing overview mode.
+        doNothing().when(mTabSwitcherViewObserver).finishedShowing();
+        mMediator.finishedShowing();
+
+        // Mock that the re-auth was successful.
+        mIncognitoReauthCallbackArgumentCaptor.getValue().onIncognitoReauthSuccess();
+
+        assertThat(mModel.get(TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY),
+                equalTo(TAB_MODEL_FILTER_INDEX));
+    }
+
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID)
+    public void testFocusTabForAccessibility_IsInvoked_OnOverviewModeFinishedShowing() {
+        initAndAssertAllProperties();
+
+        doReturn(true).when(mTabModelSelector).isTabStateInitialized();
+
+        // Mock showing overview mode.
+        doNothing().when(mTabSwitcherViewObserver).finishedShowing();
+        mMediator.finishedShowing();
+
+        assertThat(mModel.get(TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY),
+                equalTo(TAB_MODEL_FILTER_INDEX));
+    }
+
+    @Test
+    @SmallTest
+    public void testFocusTabForAccessibility_IsInvoked_OnTabModelSelected() {
+        initAndAssertAllProperties();
+
+        mModel.set(TabListContainerProperties.IS_VISIBLE, true);
+        doReturn(true).when(mTabModelSelector).isTabStateInitialized();
+
+        // Mock showing overview mode.
+        doNothing().when(mTabSwitcherViewObserver).finishedShowing();
+        mMediator.finishedShowing();
+
+        mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mTabModel, null);
+        assertThat(mModel.get(TabListContainerProperties.FOCUS_TAB_INDEX_FOR_ACCESSIBILITY),
+                equalTo(TAB_MODEL_FILTER_INDEX));
+    }
+
+    @Test
+    @SmallTest
+    public void testUnfocusTabForAccessibility_IsInvoked_WhenResetTabListForIncognitoReauth() {
+        initAndAssertAllProperties();
+
+        mModel.set(TabListContainerProperties.IS_VISIBLE, true);
+        doReturn(true).when(mTabModelSelector).isTabStateInitialized();
+
+        // Mock showing overview mode.
+        doNothing().when(mTabSwitcherViewObserver).finishedShowing();
+        mMediator.finishedShowing();
+
+        doReturn(true).when(mTabModelSelector).isIncognitoSelected();
+        doReturn(true).when(mIncognitoReauthController).isIncognitoReauthPending();
+        doReturn(true).when(mIncognitoReauthController).isReauthPageShowing();
+        mTabModelSelectorObserverCaptor.getValue().onTabModelSelected(mTabModel, null);
+
+        assertThat(mModel.get(TabListContainerProperties.UNFOCUS_TAB_INDEX_FOR_ACCESSIBILITY),
+                equalTo(TAB_MODEL_FILTER_INDEX));
+    }
+
     private void initAndAssertAllProperties() {
         assertThat(mModel.get(TabListContainerProperties.VISIBILITY_LISTENER),
                 instanceOf(TabSwitcherMediator.class));
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index e0581c9..e5ed6c34 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -152,6 +152,7 @@
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridViewBinderUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupTitleEditorUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiMediatorUnitTest.java",
+  "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListFaviconProviderTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java",
   "//chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabSelectionEditorActionUnitTestHelper.java",
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index ab63b00..6f3722c 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -184,6 +184,7 @@
         android:icon="@drawable/ic_launcher"
         android:roundIcon="@drawable/ic_launcher_round"
         android:label="{% block application_label %}@string/app_name{% endblock %}"
+        android:memtagMode="async"
         android:largeHeap="false"
         android:manageSpaceActivity="@string/manage_space_activity"
         android:supportsRtl="true"
@@ -457,13 +458,13 @@
         </receiver>
 
         <!-- Phishing Protection related -->
-        <receiver android:name="org.chromium.chrome.browser.safe_browsing.PasswordProtectionBroadcastReceiver" 
+        <receiver android:name="org.chromium.chrome.browser.safe_browsing.PasswordProtectionBroadcastReceiver"
         android:exported="true"
         android:permission="com.google.android.gms.permission.INTERNAL_BROADCAST">
             <intent-filter>
                 <action android:name="com.android.chrome.safe_browsing.LOGIN" />
             </intent-filter>
-        </receiver> 
+        </receiver>
 
         <!-- Upgrade related -->
         <receiver android:name="org.chromium.chrome.browser.upgrade.PackageReplacedBroadcastReceiver"
diff --git a/chrome/android/java/res/layout/bookmark_add_new_folder_input_layout.xml b/chrome/android/java/res/layout/bookmark_add_new_folder_input_layout.xml
new file mode 100644
index 0000000..fd90b3a
--- /dev/null
+++ b/chrome/android/java/res/layout/bookmark_add_new_folder_input_layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <org.chromium.chrome.browser.bookmarks.BookmarkTextInputLayout
+                android:id="@+id/folder_title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="22dp"
+                android:layout_marginVertical="8dp"
+                android:layout_marginTop="24dp"
+                android:hint="@string/title"
+                app:emptyErrorMessage="@string/bookmark_missing_title">
+
+                <com.google.android.material.textfield.TextInputEditText
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:imeOptions="flagNoExtractUi"
+                    android:inputType="textCapSentences|textAutoCorrect" />
+            </org.chromium.chrome.browser.bookmarks.BookmarkTextInputLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/values-sw600dp/dimens.xml b/chrome/android/java/res/values-sw600dp/dimens.xml
index 9fd4714..df8dbba4 100644
--- a/chrome/android/java/res/values-sw600dp/dimens.xml
+++ b/chrome/android/java/res/values-sw600dp/dimens.xml
@@ -28,13 +28,10 @@
 
     <dimen name="omnibox_suggestion_icon_area_size">40dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_start">0dp</dimen>
-    <dimen name="omnibox_suggestion_36dp_icon_margin_start_smaller">0dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_start_smallest">0dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_end">4dp</dimen>
-    <dimen name="omnibox_suggestion_36dp_icon_margin_end_smaller">4dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_end_smallest">4dp</dimen>
     <dimen name="omnibox_suggestion_24dp_icon_margin_start">8dp</dimen>
-    <dimen name="omnibox_suggestion_24dp_icon_margin_end">8dp</dimen>
     <dimen name="omnibox_suggestion_start_padding">4dp</dimen>
     <dimen name="omnibox_suggestion_end_padding">8dp</dimen>
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 558284c..f2296d0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1783,7 +1783,8 @@
                 getTabReparentingControllerSupplier(),
                 // TODO(sinansahin): This currently only checks for incognito extras in the intent.
                 // We should make it more robust by using more signals.
-                IntentHandler.hasAnyIncognitoExtra(getIntent().getExtras()), mBackPressManager);
+                IntentHandler.hasAnyIncognitoExtra(getIntent().getExtras()), mBackPressManager,
+                this::getSavedInstanceState);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index ed45df8e..9fc0b41 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -530,7 +530,7 @@
                 /* statusBarColorProvider= */ this, getIntentRequestTracker(),
                 mTabReparentingControllerSupplier,
                 /*ephemeralTabCoordinatorSupplier=*/new ObservableSupplierImpl<>(),
-                false, mBackPressManager);
+                false, mBackPressManager, ()->null);
         // clang-format on
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java
index 6c359a1..2a30b84 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/bookmarks/BookmarkAddEditFolderActivity.java
@@ -212,8 +212,7 @@
         } else if (item == mSaveButton) {
             assert mIsAddMode;
 
-            if (mFolderTitle.isEmpty()) {
-                mFolderTitle.validate();
+            if (!mFolderTitle.validate()) {
                 mFolderTitle.requestFocus();
                 return true;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AddressEditor.java
index f501fc50..fb83a046 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AddressEditor.java
@@ -144,8 +144,8 @@
     public AddressEditor(EditorDialog editorDialog, Delegate delegate, boolean saveToDisk,
             boolean isUpdate, boolean isMigrationToAccount) {
         this(editorDialog, delegate,
-                new AutofillAddress(editorDialog.getContext(), new AutofillProfile()), saveToDisk,
-                isUpdate, isMigrationToAccount, true);
+                new AutofillAddress(editorDialog.getContext(), AutofillProfile.builder().build()),
+                saveToDisk, isUpdate, isMigrationToAccount, true);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java
index bda5409a..f05e20d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillCreditCardEditor.java
@@ -51,7 +51,7 @@
                 getActivity(), android.R.layout.simple_spinner_item);
         profilesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 
-        AutofillProfile noSelection = new AutofillProfile();
+        AutofillProfile noSelection = AutofillProfile.builder().build();
         noSelection.setLabel(getActivity().getString(R.string.select));
         profilesAdapter.add(noSelection);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java
index 20185abd..583b083 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java
@@ -11,7 +11,7 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchGroupProto.AuxiliarySearchGroup;
+import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchGroupProto.AuxiliarySearchBookmarkGroup;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 
@@ -38,10 +38,10 @@
     /**
      * @return AuxiliarySearchGroup for bookmarks, which is necessary for the auxiliary search.
      */
-    public @Nullable AuxiliarySearchGroup getBookmarksSearchableData() {
+    public @Nullable AuxiliarySearchBookmarkGroup getBookmarksSearchableData() {
         if (mNativeBridge != 0) {
             try {
-                return AuxiliarySearchGroup.parseFrom(
+                return AuxiliarySearchBookmarkGroup.parseFrom(
                         AuxiliarySearchBridgeJni.get().getBookmarksSearchableData(mNativeBridge));
 
             } catch (InvalidProtocolBufferException e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeTest.java b/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeTest.java
index 5c52d39b..24245f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridgeTest.java
@@ -22,8 +22,7 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchGroupProto.AuxiliarySearchGroup;
-import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchGroupProto.GroupType;
+import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchGroupProto.AuxiliarySearchBookmarkGroup;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.util.browser.Features;
@@ -35,9 +34,7 @@
 @Config(manifest = Config.NONE)
 @Features.EnableFeatures({ChromeFeatureList.ANDROID_APP_INTEGRATION})
 public final class AuxiliarySearchBridgeTest {
-    private static final String TAB_TITLE = "tab";
     private static final String BOOKMARK_TITLE = "bookmark";
-    private static final String TAB_URL = "https://tab.google.com";
     private static final String BOOKMARK_URL = "https://bookmark.google.com";
     private static final long FAKE_NATIVE_PROVIDER = 1;
 
@@ -74,14 +71,11 @@
     public void getSearchableDataTest() {
         doReturn(false).when(mProfile).isOffTheRecord();
 
-        var bookmark = AuxiliarySearchGroup.Entry.newBuilder()
+        var bookmark = AuxiliarySearchBookmarkGroup.Bookmark.newBuilder()
                                .setTitle(BOOKMARK_TITLE)
                                .setUrl(BOOKMARK_URL)
                                .build();
-        var proto = AuxiliarySearchGroup.newBuilder()
-                            .addEntry(bookmark)
-                            .setGroupType(GroupType.BOOKMARK_GROUP)
-                            .build();
+        var proto = AuxiliarySearchBookmarkGroup.newBuilder().addBookmark(bookmark).build();
 
         doReturn(FAKE_NATIVE_PROVIDER).when(mMockAuxiliarySearchBridgeJni).getForProfile(mProfile);
         doReturn(proto.toByteArray())
@@ -91,11 +85,10 @@
         AuxiliarySearchBridge bridge = new AuxiliarySearchBridge(mProfile);
         verify(mMockAuxiliarySearchBridgeJni).getForProfile(mProfile);
 
-        AuxiliarySearchGroup group = bridge.getBookmarksSearchableData();
+        AuxiliarySearchBookmarkGroup group = bridge.getBookmarksSearchableData();
 
-        assertEquals(group.getEntryCount(), 1);
-        assertEquals(group.getEntry(0).getTitle(), BOOKMARK_TITLE);
-        assertEquals(group.getEntry(0).getUrl(), BOOKMARK_URL);
-        assertEquals(group.getGroupType(), GroupType.BOOKMARK_GROUP);
+        assertEquals(group.getBookmarkCount(), 1);
+        assertEquals(group.getBookmark(0).getTitle(), BOOKMARK_TITLE);
+        assertEquals(group.getBookmark(0).getUrl(), BOOKMARK_URL);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinator.java
new file mode 100644
index 0000000..6dfd2592
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinator.java
@@ -0,0 +1,96 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.bookmarks;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import org.chromium.chrome.R;
+import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.ui.modaldialog.DialogDismissalCause;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Class for showing a dialog to add a new folder. */
+public class BookmarkAddNewFolderCoordinator {
+    private final Context mContext;
+    private final ModalDialogManager mModalDialogManager;
+    private final BookmarkModel mBookmarkModel;
+
+    private PropertyModel mModel;
+
+    /**
+     * @param context The android context from which this is being called.
+     * @param modalDialogManager The manager for modal dialogs.
+     * @param bookmarkModel The underlying bookmark model.
+     */
+    public BookmarkAddNewFolderCoordinator(
+            Context context, ModalDialogManager modalDialogManager, BookmarkModel bookmarkModel) {
+        mContext = context;
+        mModalDialogManager = modalDialogManager;
+        mBookmarkModel = bookmarkModel;
+    }
+
+    /**
+     * Show the dialog for the given parent. If the parent is the root, then the folder will be
+     * created under "other bookmarks".
+     */
+    public void show(BookmarkId parent) {
+        View customView = LayoutInflater.from(mContext).inflate(
+                R.layout.bookmark_add_new_folder_input_layout, null);
+        BookmarkTextInputLayout folderTitle = customView.findViewById(R.id.folder_title);
+
+        ModalDialogProperties.Controller dialogController = new ModalDialogProperties.Controller() {
+            @Override
+            public void onClick(PropertyModel model, int buttonType) {
+                if (buttonType == ModalDialogProperties.ButtonType.POSITIVE
+                        && !folderTitle.validate()) {
+                    folderTitle.requestFocus();
+                    return;
+                }
+
+                final @DialogDismissalCause int cause;
+                if (buttonType == ModalDialogProperties.ButtonType.POSITIVE) {
+                    addFolder(parent, folderTitle.getTrimmedText());
+                    cause = DialogDismissalCause.POSITIVE_BUTTON_CLICKED;
+                } else {
+                    cause = DialogDismissalCause.NEGATIVE_BUTTON_CLICKED;
+                }
+
+                mModalDialogManager.dismissDialog(mModel, cause);
+            }
+
+            @Override
+            public void onDismiss(PropertyModel model, int dismissalCause) {}
+        };
+
+        Resources res = mContext.getResources();
+        mModel = new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
+                         .with(ModalDialogProperties.CONTROLLER, dialogController)
+                         .with(ModalDialogProperties.TITLE,
+                                 res.getString(R.string.create_new_folder))
+                         .with(ModalDialogProperties.POSITIVE_BUTTON_TEXT,
+                                 res.getString(R.string.app_banner_add))
+                         .with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT,
+                                 res.getString(R.string.cancel))
+                         .with(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE, true)
+                         .with(ModalDialogProperties.BUTTON_STYLES,
+                                 ModalDialogProperties.ButtonStyles.PRIMARY_FILLED_NEGATIVE_OUTLINE)
+                         .with(ModalDialogProperties.CUSTOM_VIEW, customView)
+                         .build();
+        mModalDialogManager.showDialog(mModel, ModalDialogType.APP);
+    }
+
+    private void addFolder(BookmarkId parent, String title) {
+        mBookmarkModel.addFolder(parent.equals(mBookmarkModel.getRootFolderId())
+                        ? mBookmarkModel.getOtherFolderId()
+                        : parent,
+                0, title);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
index 2d15a1a0..ea77b70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.native_page.BasicNativePage;
 import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
 import org.chromium.components.browser_ui.util.ConversionUtils;
 import org.chromium.components.browser_ui.util.GlobalDiscardableReferencePool;
 import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableRecyclerViewAdapter;
@@ -46,9 +47,12 @@
 import org.chromium.components.image_fetcher.ImageFetcherConfig;
 import org.chromium.components.image_fetcher.ImageFetcherFactory;
 import org.chromium.ui.KeyboardVisibilityDelegate;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 
 /** Responsible for setting up sub-components and routing incoming/outgoing signals */
+// TODO(crbug.com/1446506): Add a new coordinator so this class doesn't own everything.
 public class BookmarkManagerCoordinator
         implements SearchDelegate, BackPressHandler, OnAttachStateChangeListener {
     private static final int FAVICON_MAX_CACHE_SIZE_BYTES =
@@ -79,6 +83,7 @@
     private final BookmarkModel mBookmarkModel;
     private final Profile mProfile;
     private final BookmarkUiPrefs mBookmarkUiPrefs;
+    private final ModalDialogManager mModalDialogManager;
 
     /**
      * Creates an instance of {@link BookmarkManagerCoordinator}. It also initializes resources,
@@ -135,13 +140,16 @@
         itemAnimator.setAddDuration(0);
         itemAnimator.setRemoveDuration(0);
 
+        mModalDialogManager =
+                new ModalDialogManager(new AppModalPresenter(context), ModalDialogType.APP);
+
         // Using OneshotSupplier as an alternative to a 2-step initialization process.
         OneshotSupplierImpl<BookmarkDelegate> bookmarkDelegateSupplier =
                 new OneshotSupplierImpl<>();
         mBookmarkToolbarCoordinator = new BookmarkToolbarCoordinator(context, mSelectableListLayout,
                 mSelectionDelegate, /*searchDelegate=*/this, dragReorderableRecyclerViewAdapter,
                 isDialogUi, bookmarkDelegateSupplier, mBookmarkModel, mBookmarkOpener,
-                mBookmarkUiPrefs);
+                mBookmarkUiPrefs, mModalDialogManager);
         mSelectableListLayout.configureWideDisplayStyle();
 
         LargeIconBridge largeIconBridge = new LargeIconBridge(mProfile);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
index 68e1c98..d44a314 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
@@ -121,15 +121,9 @@
                         openFolder(parent.getId());
                     }
                 } else {
-                    if (node.isFolder()) {
-                        refresh();
-                    } else {
-                        int deletedPosition = getPositionForBookmark(node.getId());
-                        if (deletedPosition >= 0) {
-                            mModelList.removeAt(deletedPosition);
-                            syncAdapterAndSelectionDelegate();
-                        }
-                    }
+                    // Needs to remove the current node, and update any transitive parents that may
+                    // be showing child counts. Just refresh() for now.
+                    refresh();
                 }
             } else if (getCurrentUiMode() == BookmarkUiMode.SEARCHING) {
                 // We cannot rely on removing the specific list item that corresponds to the
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java
index 96ac406..798c766 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayout.java
@@ -65,10 +65,16 @@
      * Check the text and show or hide error message if needed.
      * If there is a need for extra validation, this method should be overridden
      * and extra validation statements should be added after calling super.validate()
+     *
+     * @return whether the input is valid.
      */
-    public void validate() {
+    public boolean validate() {
+        boolean isValid = !isEmpty();
         if (mEmptyErrorMessage != null) {
-            setError(isEmpty() ? mEmptyErrorMessage : null);
+            setError(isValid ? null : mEmptyErrorMessage);
+            setErrorEnabled(!isValid);
         }
+
+        return isValid;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java
index 108d116..2089016 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbar.java
@@ -48,6 +48,7 @@
     private boolean mIsSelectionUiShowing;
     private boolean mSearchButtonVisible;
     private boolean mEditButtonVisible;
+    private boolean mNewFolderButtonVisible;
 
     private Runnable mOpenSearchUiRunnable;
     private Callback<BookmarkId> mOpenFolderCallback;
@@ -126,6 +127,11 @@
         getMenu().findItem(R.id.edit_menu_id).setVisible(visible);
     }
 
+    void setNewFolderButtonVisible(boolean visible) {
+        mNewFolderButtonVisible = visible;
+        getMenu().findItem(R.id.create_new_folder_menu_id).setVisible(visible);
+    }
+
     void setNavigationButtonState(@NavigationButton int navigationButtonState) {
         setNavigationButton(navigationButtonState);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
index f179c6e..0d534b0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarCoordinator.java
@@ -14,6 +14,7 @@
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableListLayout;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectableListToolbar.SearchDelegate;
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
+import org.chromium.ui.modaldialog.ModalDialogManager;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -29,7 +30,7 @@
             DragReorderableRecyclerViewAdapter dragReorderableRecyclerViewAdapter,
             boolean isDialogUi, OneshotSupplier<BookmarkDelegate> bookmarkDelegateSupplier,
             BookmarkModel bookmarkModel, BookmarkOpener bookmarkOpener,
-            BookmarkUiPrefs bookmarkUiPrefs) {
+            BookmarkUiPrefs bookmarkUiPrefs, ModalDialogManager modalDialogManager) {
         mToolbar = (BookmarkToolbar) selectableListLayout.initializeToolbar(
                 R.layout.bookmark_toolbar, selectionDelegate, 0, R.id.normal_menu_group,
                 R.id.selection_mode_menu_group, null, isDialogUi);
@@ -45,7 +46,8 @@
         mModel.set(BookmarkToolbarProperties.DRAG_ENABLED, false);
         mMediator = new BookmarkToolbarMediator(context, mModel, dragReorderableRecyclerViewAdapter,
                 bookmarkDelegateSupplier, selectionDelegate, bookmarkModel, bookmarkOpener,
-                bookmarkUiPrefs);
+                bookmarkUiPrefs,
+                new BookmarkAddNewFolderCoordinator(context, modalDialogManager, bookmarkModel));
 
         PropertyModelChangeProcessor.create(mModel, mToolbar, BookmarkToolbarViewBinder::bind);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java
index e56e398..dcdc218b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediator.java
@@ -31,6 +31,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 
 import java.util.List;
+import java.util.Objects;
 
 /** Responsible for the business logic for the BookmarkManagerToolbar. */
 class BookmarkToolbarMediator implements BookmarkUiObserver, DragListener,
@@ -42,6 +43,7 @@
     private final BookmarkModel mBookmarkModel;
     private final BookmarkOpener mBookmarkOpener;
     private final BookmarkUiPrefs mBookmarkUiPrefs;
+    private final BookmarkAddNewFolderCoordinator mBookmarkAddNewFolderCoordinator;
 
     // TODO(crbug.com/1413463): Remove reference to BookmarkDelegate if possible.
     private @Nullable BookmarkDelegate mBookmarkDelegate;
@@ -52,7 +54,8 @@
             DragReorderableRecyclerViewAdapter dragReorderableRecyclerViewAdapter,
             OneshotSupplier<BookmarkDelegate> bookmarkDelegateSupplier,
             SelectionDelegate selectionDelegate, BookmarkModel bookmarkModel,
-            BookmarkOpener bookmarkOpener, BookmarkUiPrefs bookmarkUiPrefs) {
+            BookmarkOpener bookmarkOpener, BookmarkUiPrefs bookmarkUiPrefs,
+            BookmarkAddNewFolderCoordinator bookmarkAddNewFolderCoordinator) {
         mContext = context;
         mModel = model;
 
@@ -64,6 +67,7 @@
         mBookmarkModel = bookmarkModel;
         mBookmarkOpener = bookmarkOpener;
         mBookmarkUiPrefs = bookmarkUiPrefs;
+        mBookmarkAddNewFolderCoordinator = bookmarkAddNewFolderCoordinator;
 
         if (BookmarkFeatures.isAndroidImprovedBookmarksEnabled()) {
             mModel.set(BookmarkToolbarProperties.CHECKED_SORT_MENU_ID,
@@ -89,6 +93,7 @@
         // Sorting/viewing submenu needs to be caught, but haven't been implemented yet.
         // TODO(crbug.com/1413463): Handle the new toolbar options.
         if (id == R.id.create_new_folder_menu_id) {
+            mBookmarkAddNewFolderCoordinator.show(mCurrentFolder);
             return true;
         } else if (id == R.id.normal_options_submenu) {
             return true;
@@ -229,6 +234,7 @@
                 folderItem != null && folderItem.isEditable());
         if (folderItem == null) return;
 
+        // Title, navigation buttons.
         String title;
         @NavigationButton
         int navigationButton;
@@ -259,6 +265,12 @@
         // Should typically be the last thing done, because lots of other properties will trigger
         // an incorrect button state, and we need to override that.
         mModel.set(BookmarkToolbarProperties.NAVIGATION_BUTTON_STATE, navigationButton);
+
+        // New folder button.
+        if (BookmarkFeatures.isAndroidImprovedBookmarksEnabled()) {
+            mModel.set(BookmarkToolbarProperties.NEW_FOLDER_BUTTON_VISIBLE,
+                    isAddNewFolderButtonVisible());
+        }
     }
 
     @Override
@@ -296,4 +308,9 @@
         }
         return ResourcesCompat.ID_NULL;
     }
+
+    private boolean isAddNewFolderButtonVisible() {
+        return !Objects.equals(mCurrentFolder, mBookmarkModel.getReadingListFolder())
+                && !Objects.equals(mCurrentFolder, mBookmarkModel.getPartnerFolderId());
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java
index b509c5c..a3f8c44c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarProperties.java
@@ -40,6 +40,8 @@
     static final WritableBooleanPropertyKey SEARCH_BUTTON_VISIBLE =
             new WritableBooleanPropertyKey();
     static final WritableBooleanPropertyKey EDIT_BUTTON_VISIBLE = new WritableBooleanPropertyKey();
+    static final WritableBooleanPropertyKey NEW_FOLDER_BUTTON_VISIBLE =
+            new WritableBooleanPropertyKey();
     // Can change within SelectableListToolbar which makes the model value to become stale.
     static final WritableObjectPropertyKey<Integer> NAVIGATION_BUTTON_STATE =
             new WritableObjectPropertyKey<>(/*skipEquality=*/true);
@@ -63,7 +65,8 @@
 
     static final PropertyKey[] ALL_KEYS = {BOOKMARK_MODEL, BOOKMARK_OPENER, SELECTION_DELEGATE,
             TITLE, BOOKMARK_UI_MODE, SOFT_KEYBOARD_VISIBLE, IS_DIALOG_UI, DRAG_ENABLED,
-            SEARCH_BUTTON_VISIBLE, EDIT_BUTTON_VISIBLE, NAVIGATION_BUTTON_STATE, CURRENT_FOLDER,
-            CHECKED_SORT_MENU_ID, CHECKED_VIEW_MENU_ID, OPEN_SEARCH_UI_RUNNABLE,
-            MENU_ID_CLICKED_FUNCTION, OPEN_FOLDER_CALLBACK, FAKE_SELECTION_STATE_CHANGE};
+            SEARCH_BUTTON_VISIBLE, EDIT_BUTTON_VISIBLE, NEW_FOLDER_BUTTON_VISIBLE,
+            NAVIGATION_BUTTON_STATE, CURRENT_FOLDER, CHECKED_SORT_MENU_ID, CHECKED_VIEW_MENU_ID,
+            OPEN_SEARCH_UI_RUNNABLE, MENU_ID_CLICKED_FUNCTION, OPEN_FOLDER_CALLBACK,
+            FAKE_SELECTION_STATE_CHANGE};
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java
index 5ce84ed..ae1b949 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarViewBinder.java
@@ -39,6 +39,9 @@
         } else if (key == BookmarkToolbarProperties.EDIT_BUTTON_VISIBLE) {
             bookmarkToolbar.setEditButtonVisible(
                     model.get(BookmarkToolbarProperties.EDIT_BUTTON_VISIBLE));
+        } else if (key == BookmarkToolbarProperties.NEW_FOLDER_BUTTON_VISIBLE) {
+            bookmarkToolbar.setNewFolderButtonVisible(
+                    model.get(BookmarkToolbarProperties.NEW_FOLDER_BUTTON_VISIBLE));
         } else if (key == BookmarkToolbarProperties.NAVIGATION_BUTTON_STATE) {
             bookmarkToolbar.setNavigationButtonState(
                     model.get(BookmarkToolbarProperties.NAVIGATION_BUTTON_STATE));
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
index b21490d..77c2c2f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -177,7 +177,7 @@
                 snackbarManagerSupplier, activityType,
                 isInOverviewModeSupplier, isWarmOnResumeSupplier, appMenuDelegate,
                 statusBarColorProvider, intentRequestTracker, new OneshotSupplierImpl<>(),
-                ephemeralTabCoordinatorSupplier, false, backPressManager);
+                ephemeralTabCoordinatorSupplier, false, backPressManager, ()->null);
         // clang-format on
         mToolbarCoordinator = customTabToolbarCoordinator;
         mNavigationController = customTabNavigationController;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
index b3a7916..93a0d7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java
@@ -158,7 +158,7 @@
         final String editTitle;
         final AutofillAddress address;
         if (toEdit == null) {
-            address = new AutofillAddress(mContext, new AutofillProfile());
+            address = new AutofillAddress(mContext, AutofillProfile.builder().build());
             editTitle = mContext.getString(R.string.autofill_create_profile);
         } else {
             address = toEdit;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
index baf775a0..cf1fcc21 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ContactEditor.java
@@ -174,9 +174,9 @@
         super.edit(toEdit, doneCallback, cancelCallback);
 
         final AutofillContact contact = toEdit == null
-                ? new AutofillContact(mContext, new AutofillProfile(), null, null, null,
-                          INVALID_NAME | INVALID_PHONE_NUMBER | INVALID_EMAIL, mRequestPayerName,
-                          mRequestPayerPhone, mRequestPayerEmail)
+                ? new AutofillContact(mContext, AutofillProfile.builder().build(), null, null, null,
+                        INVALID_NAME | INVALID_PHONE_NUMBER | INVALID_EMAIL, mRequestPayerName,
+                        mRequestPayerPhone, mRequestPayerEmail)
                 : toEdit;
 
         final EditorFieldModel nameField = mRequestPayerName
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 ec5cfc5..6f7f986f9 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
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tabbed_mode;
 
+import android.os.Bundle;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
@@ -252,6 +253,7 @@
      * @param tabReparentingControllerSupplier Supplier for the {@link TabReparentingController}.
      * @param initializeUiWithIncognitoColors Whether to initialize the UI with incognito colors.
      * @param backPressManager The {@link BackPressManager} handling back press.
+     * @param savedInstanceStateSupplier Supplies the saved instance state.
      */
     public TabbedRootUiCoordinator(@NonNull AppCompatActivity activity,
             @Nullable Callback<Boolean> onOmniboxFocusChangedListener,
@@ -292,7 +294,8 @@
             @NonNull Supplier<InsetObserverView> insetObserverViewSupplier,
             @NonNull Function<Tab, Boolean> backButtonShouldCloseTabFn,
             OneshotSupplier<TabReparentingController> tabReparentingControllerSupplier,
-            boolean initializeUiWithIncognitoColors, @NonNull BackPressManager backPressManager) {
+            boolean initializeUiWithIncognitoColors, @NonNull BackPressManager backPressManager,
+            @NonNull Supplier<Bundle> savedInstanceStateSupplier) {
         super(activity, onOmniboxFocusChangedListener, shareDelegateSupplier, tabProvider,
                 profileSupplier, bookmarkModelSupplier, tabBookmarkerSupplier,
                 contextualSearchManagerSupplier, tabModelSelectorSupplier, startSurfaceSupplier,
@@ -305,7 +308,8 @@
                 compositorViewHolderSupplier, tabContentManagerSupplier, snackbarManagerSupplier,
                 activityType, isInOverviewModeSupplier, isWarmOnResumeSupplier, appMenuDelegate,
                 statusBarColorProvider, intentRequestTracker, tabReparentingControllerSupplier,
-                ephemeralTabCoordinatorSupplier, initializeUiWithIncognitoColors, backPressManager);
+                ephemeralTabCoordinatorSupplier, initializeUiWithIncognitoColors, backPressManager,
+                savedInstanceStateSupplier);
         mControlContainerHeightResource = controlContainerHeightResource;
         mInsetObserverViewSupplier = insetObserverViewSupplier;
         mBackButtonShouldCloseTabFn = backButtonShouldCloseTabFn;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 5ee5d0a..7e57ff6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -308,6 +308,7 @@
     private final Supplier<EphemeralTabCoordinator> mEphemeralTabCoordinatorSupplier;
     @Nullable
     private final BackPressManager mBackPressManager;
+    private final @NonNull Supplier<Bundle> mSavedInstanceStateSupplier;
     @Nullable
     private PageZoomCoordinator mPageZoomCoordinator;
     private AppMenuObserver mAppMenuObserver;
@@ -360,6 +361,7 @@
      * @param ephemeralTabCoordinatorSupplier Supplies the {@link EphemeralTabCoordinator}.
      * @param initializeUiWithIncognitoColors Whether to initialize the UI with incognito colors.
      * @param backPressManager The {@link BackPressManager} handling back press.
+     * @param savedInstanceStateSupplier Supplies the saved instance state.
      */
     public RootUiCoordinator(@NonNull AppCompatActivity activity,
             @Nullable Callback<Boolean> onOmniboxFocusChangedListener,
@@ -397,7 +399,8 @@
             @NonNull IntentRequestTracker intentRequestTracker,
             @NonNull OneshotSupplier<TabReparentingController> tabReparentingControllerSupplier,
             @NonNull Supplier<EphemeralTabCoordinator> ephemeralTabCoordinatorSupplier,
-            boolean initializeUiWithIncognitoColors, @Nullable BackPressManager backPressManager) {
+            boolean initializeUiWithIncognitoColors, @Nullable BackPressManager backPressManager,
+            @NonNull Supplier<Bundle> savedInstanceStateSupplier) {
         mCallbackController = new CallbackController();
         mActivity = activity;
         mWindowAndroid = windowAndroid;
@@ -424,6 +427,7 @@
         mTabReparentingControllerSupplier = tabReparentingControllerSupplier;
         mInitializeUiWithIncognitoColors = initializeUiWithIncognitoColors;
         mBackPressManager = backPressManager;
+        mSavedInstanceStateSupplier = savedInstanceStateSupplier;
 
         mMenuOrKeyboardActionController = menuOrKeyboardActionController;
         mMenuOrKeyboardActionController.registerMenuOrKeyboardActionHandler(this);
@@ -802,10 +806,14 @@
                 getIncognitoReauthCoordinatorFactory();
         assert incognitoReauthCoordinatorFactory
                 != null : "Sub-classes need to provide a valid factory instance.";
+        boolean isIncognitoReauthPendingOnRestore = mSavedInstanceStateSupplier.hasValue()
+                && mSavedInstanceStateSupplier.get().getBoolean(
+                        IncognitoReauthControllerImpl.KEY_IS_INCOGNITO_REAUTH_PENDING, false);
         mIncognitoReauthController =
                 new IncognitoReauthControllerImpl(mTabModelSelectorSupplier.get(),
                         mActivityLifecycleDispatcher, mLayoutStateProviderOneShotSupplier,
-                        mProfileSupplier, incognitoReauthCoordinatorFactory, mActivity.getTaskId());
+                        mProfileSupplier, incognitoReauthCoordinatorFactory,
+                        () -> isIncognitoReauthPendingOnRestore, mActivity.getTaskId());
         mIncognitoReauthControllerOneshotSupplier.set(mIncognitoReauthController);
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AddressEditorRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AddressEditorRenderTest.java
index 0f26617b..174e48aa0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AddressEditorRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AddressEditorRenderTest.java
@@ -77,12 +77,32 @@
             new AddressUiComponent(AddressField.ADMIN_AREA, "State", true, false),
             new AddressUiComponent(AddressField.POSTAL_CODE, "ZIP", true, false));
 
-    private static final AutofillProfile sLocalProfile = new AutofillProfile("", true,
-            Source.LOCAL_OR_SYNCABLE, /*honorific prefix=*/"", "Seb Doe", "Google", "111 First St",
-            "CA", "Los Angeles", "", "90291", "", "US", "650-253-0000", "first@gmail.com", "en-US");
-    private static final AutofillProfile sAccountProfile = new AutofillProfile("", true,
-            Source.ACCOUNT, /*honorific prefix=*/"", "Seb Doe", "Google", "111 First St", "CA",
-            "Los Angeles", "", "90291", "", "US", "650-253-0000", "first@gmail.com", "en-US");
+    private static final AutofillProfile sLocalProfile = AutofillProfile.builder()
+                                                                 .setFullName("Seb Doe")
+                                                                 .setCompanyName("Google")
+                                                                 .setStreetAddress("111 First St")
+                                                                 .setRegion("CA")
+                                                                 .setLocality("Los Angeles")
+                                                                 .setPostalCode("90291")
+                                                                 .setCountryCode("US")
+                                                                 .setPhoneNumber("650-253-0000")
+                                                                 .setEmailAddress("first@gmail.com")
+                                                                 .setLanguageCode("en-US")
+                                                                 .build();
+    private static final AutofillProfile sAccountProfile =
+            AutofillProfile.builder()
+                    .setSource(Source.ACCOUNT)
+                    .setFullName("Seb Doe")
+                    .setCompanyName("Google")
+                    .setStreetAddress("111 First St")
+                    .setRegion("CA")
+                    .setLocality("Los Angeles")
+                    .setPostalCode("90291")
+                    .setCountryCode("US")
+                    .setPhoneNumber("650-253-0000")
+                    .setEmailAddress("first@gmail.com")
+                    .setLanguageCode("en-US")
+                    .build();
 
     @ParameterAnnotations.ClassParameter
     private static List<ParameterSet> sClassParams =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java
index 48bfd776..23feb19 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptRenderTest.java
@@ -120,7 +120,8 @@
         View dialogView = runOnUiThreadBlocking(() -> {
             mPrompt = new SaveUpdateAddressProfilePrompt(mPromptController,
                     getActivity().getModalDialogManager(), getActivity(), mProfile,
-                    new AutofillProfile(), /*isUpdate=*/false, /*isMigrationToAccount=*/false);
+                    AutofillProfile.builder().build(), /*isUpdate=*/false,
+                    /*isMigrationToAccount=*/false);
             mPrompt.setDialogDetails(/*title=*/"Dialog title", /*positiveButtonText=*/"Accept",
                     /*negativeButtonText=*/"Cancel");
             mPrompt.setSaveOrMigrateDetails(
@@ -139,7 +140,8 @@
         View dialogView = runOnUiThreadBlocking(() -> {
             mPrompt = new SaveUpdateAddressProfilePrompt(mPromptController,
                     getActivity().getModalDialogManager(), getActivity(), mProfile,
-                    new AutofillProfile(), /*isUpdate=*/false, /*isMigrationToAccount=*/false);
+                    AutofillProfile.builder().build(), /*isUpdate=*/false,
+                    /*isMigrationToAccount=*/false);
             mPrompt.setDialogDetails(/*title=*/"Dialog title", /*positiveButtonText=*/"Accept",
                     /*negativeButtonText=*/"Cancel");
             mPrompt.setSaveOrMigrateDetails(
@@ -163,7 +165,8 @@
         View dialogView = runOnUiThreadBlocking(() -> {
             mPrompt = new SaveUpdateAddressProfilePrompt(mPromptController,
                     getActivity().getModalDialogManager(), getActivity(), mProfile,
-                    new AutofillProfile(), /*isUpdate=*/false, /*isMigrationToAccount=*/true);
+                    AutofillProfile.builder().build(), /*isUpdate=*/false,
+                    /*isMigrationToAccount=*/true);
             mPrompt.setDialogDetails(/*title=*/"Dialog title", /*positiveButtonText=*/"Accept",
                     /*negativeButtonText=*/"Cancel");
             mPrompt.setSaveOrMigrateDetails(
@@ -187,7 +190,8 @@
         View dialogView = runOnUiThreadBlocking(() -> {
             mPrompt = new SaveUpdateAddressProfilePrompt(mPromptController,
                     getActivity().getModalDialogManager(), getActivity(), mProfile,
-                    new AutofillProfile(), /*isUpdate=*/true, /*isMigrationToAccount=*/false);
+                    AutofillProfile.builder().build(), /*isUpdate=*/true,
+                    /*isMigrationToAccount=*/false);
             mPrompt.setDialogDetails(/*title=*/"Dialog title", /*positiveButtonText=*/"Accept",
                     /*negativeButtonText=*/"Cancel");
             mPrompt.setUpdateDetails(
@@ -206,7 +210,8 @@
         View dialogView = runOnUiThreadBlocking(() -> {
             mPrompt = new SaveUpdateAddressProfilePrompt(mPromptController,
                     getActivity().getModalDialogManager(), getActivity(), mProfile,
-                    new AutofillProfile(), /*isUpdate=*/true, /*isMigrationToAccount=*/false);
+                    AutofillProfile.builder().build(), /*isUpdate=*/true,
+                    /*isMigrationToAccount=*/false);
             mPrompt.setDialogDetails(/*title=*/"Dialog title", /*positiveButtonText=*/"Accept",
                     /*negativeButtonText=*/"Cancel");
             mPrompt.setUpdateDetails(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
index 8a43705..c9cc910 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragmentTest.java
@@ -80,9 +80,20 @@
                     .setEmailAddress("first@gmail.com")
                     .setLanguageCode("en-US")
                     .build();
-    private static final AutofillProfile sAccountProfile = new AutofillProfile("", true, Source.ACCOUNT,
-            "" /* honorific prefix */, "Artik Doe", "Google", "999 Fourth St", "California",
-            "Los Angeles", "", "90291", "", "US", "650-253-0000", "artik@gmail.com", "en-US");
+    private static final AutofillProfile sAccountProfile =
+            AutofillProfile.builder()
+                    .setSource(Source.ACCOUNT)
+                    .setFullName("Artik Doe")
+                    .setCompanyName("Google")
+                    .setStreetAddress("999 Fourth St")
+                    .setRegion("California")
+                    .setLocality("Los Angeles")
+                    .setPostalCode("90291")
+                    .setCountryCode("US")
+                    .setPhoneNumber("650-253-0000")
+                    .setEmailAddress("artik@gmail.com")
+                    .setLanguageCode("en-US")
+                    .build();
 
     @Rule
     public final AutofillTestRule rule = new AutofillTestRule();
@@ -393,9 +404,19 @@
         String email = "test@account";
         setUpMockPrimaryAccount(email);
 
-        mHelper.setProfile(new AutofillProfile("", true, Source.ACCOUNT, "" /* honorific prefix */,
-                "Account Updated #0", "Google", "111 Fourth St", "California", "Los Angeles", "",
-                "90291", "", "US", "650-253-0000", "fourth@gmail.com", "en-US"));
+        mHelper.setProfile(AutofillProfile.builder()
+                                   .setSource(Source.ACCOUNT)
+                                   .setFullName("Account Updated #0")
+                                   .setCompanyName("Google")
+                                   .setStreetAddress("111 Fourth St")
+                                   .setRegion("California")
+                                   .setLocality("Los Angeles")
+                                   .setPostalCode("90291")
+                                   .setCountryCode("US")
+                                   .setPhoneNumber("650-253-0000")
+                                   .setEmailAddress("fourth@gmail.com")
+                                   .setLanguageCode("en-US")
+                                   .build());
 
         AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
         Context context = autofillProfileFragment.getContext();
@@ -445,10 +466,21 @@
     @Feature({"Preferences"})
     @Features.EnableFeatures({ChromeFeatureList.AUTOFILL_ENABLE_SUPPORT_FOR_HONORIFIC_PREFIXES})
     public void testEditInvalidAccountProfile() throws Exception {
-        mHelper.setProfile(new AutofillProfile("", true, Source.ACCOUNT, "" /* honorific prefix */,
-                "Account Updated #0", "Google",
-                "" /** Street address is required in US but already missing. */, "California",
-                "Los Angeles", "", "90291", "", "US", "650-253-0000", "fourth@gmail.com", "en-US"));
+        mHelper.setProfile(
+                AutofillProfile.builder()
+                        .setSource(Source.ACCOUNT)
+                        .setFullName("Account Updated #0")
+                        .setCompanyName("Google")
+                        .setStreetAddress(
+                                "") /** Street address is required in US but already missing. */
+                        .setRegion("California")
+                        .setLocality("Los Angeles")
+                        .setPostalCode("90291")
+                        .setCountryCode("US")
+                        .setPhoneNumber("650-253-0000")
+                        .setEmailAddress("fourth@gmail.com")
+                        .setLanguageCode("en-US")
+                        .build());
 
         AutofillProfilesFragment autofillProfileFragment = sSettingsActivityTestRule.getFragment();
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
index de1dd22..2c68662 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
@@ -238,7 +238,7 @@
         private OnSuccessListener<List<WebAuthnCredentialDetails>> mSuccessCallback;
 
         @Override
-        public void invokeFido2GetCredentials(String relyingPartyId, int supportLevel,
+        public void invokeFido2GetCredentials(String relyingPartyId,
                 OnSuccessListener<List<WebAuthnCredentialDetails>> successCallback,
                 OnFailureListener failureCallback) {
             if (mInvokeCallbackImmediately) {
@@ -419,8 +419,7 @@
 
         mCreationOptions = Fido2ApiTestHelper.createDefaultMakeCredentialOptions();
         mRequestOptions = Fido2ApiTestHelper.createDefaultGetAssertionOptions();
-        mRequest = new Fido2CredentialRequest(
-                mIntentSender, WebAuthenticationDelegate.Support.BROWSER);
+        mRequest = new Fido2CredentialRequest(mIntentSender);
         AuthenticatorImpl.overrideFido2CredentialRequestForTesting(mRequest);
 
         mFido2ApiCallHelper = new MockFido2ApiCallHelper();
@@ -646,8 +645,7 @@
     @Test
     @SmallTest
     public void testAuthenticatorImplMakeCredential_success() {
-        AuthenticatorImpl authenticator = new AuthenticatorImpl(
-                mIntentSender, mFrameHost, WebAuthenticationDelegate.Support.BROWSER);
+        AuthenticatorImpl authenticator = new AuthenticatorImpl(mIntentSender, mFrameHost);
         mIntentSender.setNextResultIntent(
                 Fido2ApiTestHelper.createSuccessfulMakeCredentialIntent());
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -665,8 +663,7 @@
     @Test
     @SmallTest
     public void testAuthenticatorImplMakeCredential_resultCanceled() {
-        AuthenticatorImpl authenticator = new AuthenticatorImpl(
-                mIntentSender, mFrameHost, WebAuthenticationDelegate.Support.BROWSER);
+        AuthenticatorImpl authenticator = new AuthenticatorImpl(mIntentSender, mFrameHost);
         mIntentSender.setNextResult(Activity.RESULT_CANCELED, null);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             authenticator.makeCredential(mCreationOptions,
@@ -1019,8 +1016,7 @@
     @Test
     @SmallTest
     public void testAuthenticatorImplGetAssertionWithUvmRequestedWithUvmResponded_success() {
-        AuthenticatorImpl authenticator = new AuthenticatorImpl(
-                mIntentSender, mFrameHost, WebAuthenticationDelegate.Support.BROWSER);
+        AuthenticatorImpl authenticator = new AuthenticatorImpl(mIntentSender, mFrameHost);
         mIntentSender.setNextResultIntent(
                 Fido2ApiTestHelper.createSuccessfulGetAssertionIntentWithUvm());
         mRequestOptions.userVerificationMethods = true;
@@ -1039,8 +1035,7 @@
     @Test
     @SmallTest
     public void testAuthenticatorImplGetAssertion_resultCanceled() {
-        AuthenticatorImpl authenticator = new AuthenticatorImpl(
-                mIntentSender, mFrameHost, WebAuthenticationDelegate.Support.BROWSER);
+        AuthenticatorImpl authenticator = new AuthenticatorImpl(mIntentSender, mFrameHost);
         mIntentSender.setNextResult(Activity.RESULT_CANCELED, null);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             authenticator.getAssertion(mRequestOptions,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
index 7926de0..9cce1ef 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptTest.java
@@ -105,7 +105,7 @@
     }
 
     private void createAndShowPrompt(boolean isUpdate, boolean isMigrationToAccount) {
-        AutofillProfile dummyProfile = new AutofillProfile();
+        AutofillProfile dummyProfile = AutofillProfile.builder().build();
         mModalDialogManager = new FakeModalDialogManager(ModalDialogType.APP);
         mPrompt = new SaveUpdateAddressProfilePrompt(mPromptController, mModalDialogManager,
                 mActivity, mProfile, dummyProfile, isUpdate, isMigrationToAccount);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AddressEditorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AddressEditorTest.java
index e26573b..3962710 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AddressEditorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AddressEditorTest.java
@@ -89,14 +89,32 @@
             new AddressUiComponent(
                     AddressField.STREET_ADDRESS, "street address label", true, true));
 
-    private static final AutofillProfile sLocalProfile = new AutofillProfile("",
-            true, Source.LOCAL_OR_SYNCABLE, "" /* honorific prefix */,
-            "Seb Doe", "Google", "111 First St", "CA", "Los Angeles", "", "90291", "", "US",
-            "650-253-0000", "first@gmail.com", "en-US");
-    private static final AutofillProfile sAccountProfile = new AutofillProfile("",
-            true, Source.ACCOUNT, "" /* honorific prefix */, "Seb Doe",
-            "Google", "111 First St", "CA", "Los Angeles", "", "90291", "", "US", "650-253-0000",
-            "first@gmail.com", "en-US");
+    private static final AutofillProfile sLocalProfile = AutofillProfile.builder()
+                                                                 .setFullName("Seb Doe")
+                                                                 .setCompanyName("Google")
+                                                                 .setStreetAddress("111 First St")
+                                                                 .setRegion("CA")
+                                                                 .setLocality("Los Angeles")
+                                                                 .setPostalCode("90291")
+                                                                 .setCountryCode("US")
+                                                                 .setPhoneNumber("650-253-0000")
+                                                                 .setEmailAddress("first@gmail.com")
+                                                                 .setLanguageCode("en-US")
+                                                                 .build();
+    private static final AutofillProfile sAccountProfile =
+            AutofillProfile.builder()
+                    .setSource(Source.ACCOUNT)
+                    .setFullName("Seb Doe")
+                    .setCompanyName("Google")
+                    .setStreetAddress("111 First St")
+                    .setRegion("CA")
+                    .setLocality("Los Angeles")
+                    .setPostalCode("90291")
+                    .setCountryCode("US")
+                    .setPhoneNumber("650-253-0000")
+                    .setEmailAddress("first@gmail.com")
+                    .setLanguageCode("en-US")
+                    .build();
 
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -633,7 +651,7 @@
                 /*isMigrationToAccount=*/false);
 
         mAddressEditor.showEditorDialog();
-        validateShownFields(mEditorModelCapture.getValue(), new AutofillProfile(),
+        validateShownFields(mEditorModelCapture.getValue(), AutofillProfile.builder().build(),
                 /*shouldMarkFieldsRequired=*/false);
     }
 
@@ -647,7 +665,7 @@
                 /*isMigrationToAccount=*/false);
 
         mAddressEditor.showEditorDialog();
-        validateShownFields(mEditorModelCapture.getValue(), new AutofillProfile(),
+        validateShownFields(mEditorModelCapture.getValue(), AutofillProfile.builder().build(),
                 /*shouldMarkFieldsRequired=*/true,
                 /*shouldMarkFieldsRequiredWhenAddressFieldEmpty=*/true);
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java
new file mode 100644
index 0000000..4770232
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkAddNewFolderCoordinatorTest.java
@@ -0,0 +1,120 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.bookmarks;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.LooperMode;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.chrome.R;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.ui.base.TestActivity;
+import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/** Unit tests for {@link BookmarkAddNewFolderCoordinator}. */
+@Batch(Batch.UNIT_TESTS)
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+@LooperMode(LooperMode.Mode.LEGACY)
+public class BookmarkAddNewFolderCoordinatorTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+    @Rule
+    public ActivityScenarioRule<TestActivity> mActivityScenarios =
+            new ActivityScenarioRule<>(TestActivity.class);
+
+    @Mock
+    private BookmarkModel mBookmarkModel;
+    @Mock
+    private ModalDialogManager mModalDialogManager;
+    @Mock
+    private BookmarkId mUserBokmarkId;
+    @Mock
+    private BookmarkId mRootFolderId;
+    @Mock
+    private BookmarkId mOtherFolderId;
+    @Captor
+    private ArgumentCaptor<PropertyModel> mModelCaptor;
+
+    private BookmarkAddNewFolderCoordinator mAddNewFolderCoordinator;
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = new ContextThemeWrapper(
+                ApplicationProvider.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
+
+        // Setup the bookmark model ids/items.
+        doReturn(mRootFolderId).when(mBookmarkModel).getRootFolderId();
+        doReturn(mOtherFolderId).when(mBookmarkModel).getOtherFolderId();
+
+        mAddNewFolderCoordinator =
+                new BookmarkAddNewFolderCoordinator(mContext, mModalDialogManager, mBookmarkModel);
+    }
+
+    @Test
+    public void testAdd() {
+        mAddNewFolderCoordinator.show(mUserBokmarkId);
+        verify(mModalDialogManager).showDialog(mModelCaptor.capture(), eq(ModalDialogType.APP));
+
+        PropertyModel model = mModelCaptor.getValue();
+        assertEquals("Create new folder", model.get(ModalDialogProperties.TITLE));
+        assertEquals("Add", model.get(ModalDialogProperties.POSITIVE_BUTTON_TEXT));
+        assertEquals("Cancel", model.get(ModalDialogProperties.NEGATIVE_BUTTON_TEXT));
+
+        BookmarkTextInputLayout folderTitle =
+                model.get(ModalDialogProperties.CUSTOM_VIEW).findViewById(R.id.folder_title);
+        folderTitle.getEditText().setText("user folder");
+
+        ModalDialogProperties.Controller dialogController =
+                model.get(ModalDialogProperties.CONTROLLER);
+        dialogController.onClick(model, ModalDialogProperties.ButtonType.POSITIVE);
+        verify(mBookmarkModel).addFolder(mUserBokmarkId, 0, "user folder");
+    }
+
+    @Test
+    public void testAdd_rootFolder() {
+        mAddNewFolderCoordinator.show(mRootFolderId);
+        verify(mModalDialogManager).showDialog(mModelCaptor.capture(), eq(ModalDialogType.APP));
+
+        PropertyModel model = mModelCaptor.getValue();
+        BookmarkTextInputLayout folderTitle =
+                model.get(ModalDialogProperties.CUSTOM_VIEW).findViewById(R.id.folder_title);
+        folderTitle.getEditText().setText("user folder");
+
+        ModalDialogProperties.Controller dialogController =
+                model.get(ModalDialogProperties.CONTROLLER);
+        dialogController.onClick(model, ModalDialogProperties.ButtonType.POSITIVE);
+        verify(mBookmarkModel).addFolder(mOtherFolderId, 0, "user folder");
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
index 2e732f4..69d923a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
@@ -187,7 +187,7 @@
     private final BookmarkItem mFolderItem3 =
             new BookmarkItem(mFolderId3, "Folder3", null, true, mFolderId1, true, false, 0, false);
     private final BookmarkItem mBookmarkItem21 = new BookmarkItem(mBookmarkId21, "Bookmark21",
-            JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), false, mFolderId1, true, false, 0,
+            JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL), false, mFolderId2, true, false, 0,
             false);
     private final BookmarkItem mReadingListFolderItem = new BookmarkItem(mReadingListFolderId,
             "Reading List", null, true, mRootFolderId, false, false, 0, false);
@@ -773,7 +773,7 @@
     }
 
     @Test
-    public void testcreateListMenuModelList() {
+    public void testCreateListMenuModelList() {
         finishLoading();
         mMediator.openFolder(mFolderId2);
 
@@ -844,4 +844,23 @@
         menu.onItemClick(null, null, 3, 0);
         verify(mBookmarkModel).deleteBookmarks(mBookmarkId21);
     }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.ANDROID_IMPROVED_BOOKMARKS)
+    public void testParentFolderUpdatedWhenChildDeleted() {
+        finishLoading();
+        mMediator.openFolder(mFolderId1);
+        mBookmarkUiPrefs.setBookmarkRowDisplayPref(BookmarkRowDisplayPref.VISUAL);
+        assertEquals(3, mModelList.size());
+        assertEquals(
+                1, mModelList.get(1).model.get(ImprovedBookmarkRowProperties.FOLDER_CHILD_COUNT));
+
+        doReturn(0).when(mBookmarkModel).getTotalBookmarkCount(mFolderId2);
+        verify(mBookmarkModel).addObserver(mBookmarkModelObserverArgumentCaptor.capture());
+        mBookmarkModelObserverArgumentCaptor.getValue().bookmarkNodeRemoved(
+                mFolderItem2, 0, mBookmarkItem21, false);
+
+        assertEquals(
+                0, mModelList.get(1).model.get(ImprovedBookmarkRowProperties.FOLDER_CHILD_COUNT));
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java
new file mode 100644
index 0000000..c8da64b
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkTextInputLayoutTest.java
@@ -0,0 +1,98 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.bookmarks;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.chrome.R;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.bookmarks.BookmarkItem;
+import org.chromium.components.browser_ui.widget.dragreorder.DragReorderableRecyclerViewAdapter;
+import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate;
+import org.chromium.ui.base.TestActivity;
+
+/** Unit tests for {@link BookmarkTextInputLayout}. */
+@Batch(Batch.UNIT_TESTS)
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class BookmarkTextInputLayoutTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+    @Rule
+    public ActivityScenarioRule<TestActivity> mActivityScenarios =
+            new ActivityScenarioRule<>(TestActivity.class);
+
+    @Mock
+    private BookmarkDelegate mBookmarkDelegate;
+    @Mock
+    private DragReorderableRecyclerViewAdapter mDragReorderableRecyclerViewAdapter;
+    @Mock
+    private BookmarkModel mBookmarkModel;
+    @Mock
+    private BookmarkOpener mBookmarkOpener;
+    @Mock
+    private SelectionDelegate mSelectionDelegate;
+    @Mock
+    private Runnable mOpenSearchUiRunnable;
+    @Mock
+    private Callback mOpenFolderCallback;
+    @Mock
+    private BookmarkId mBookmarkId;
+    @Mock
+    private BookmarkItem mBookmarkItem;
+    @Mock
+    private BookmarkUiPrefs mBookmarkUiPrefs;
+    @Mock
+    private BookmarkAddNewFolderCoordinator mBookmarkAddNewFolderCoordinator;
+
+    private Context mContext;
+    private BookmarkTextInputLayout mBookmarkTextInputLayout;
+
+    @Before
+    public void setUp() {
+        mContext = new ContextThemeWrapper(
+                ApplicationProvider.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
+
+        View customView = LayoutInflater.from(mContext).inflate(
+                R.layout.bookmark_add_new_folder_input_layout, null);
+        mBookmarkTextInputLayout = customView.findViewById(R.id.folder_title);
+    }
+
+    @Test
+    public void testValidate() {
+        mBookmarkTextInputLayout.getEditText().setText("test");
+        assertTrue(mBookmarkTextInputLayout.validate());
+    }
+
+    @Test
+    public void testValidate_empty() {
+        assertFalse(mBookmarkTextInputLayout.validate());
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
index 8917ab0..513a7ba9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkToolbarMediatorTest.java
@@ -76,27 +76,29 @@
     public ActivityScenarioRule<TestActivity> mActivityScenarios =
             new ActivityScenarioRule<>(TestActivity.class);
     @Mock
-    BookmarkDelegate mBookmarkDelegate;
+    private BookmarkDelegate mBookmarkDelegate;
     @Mock
-    DragReorderableRecyclerViewAdapter mDragReorderableRecyclerViewAdapter;
+    private DragReorderableRecyclerViewAdapter mDragReorderableRecyclerViewAdapter;
     @Mock
-    BookmarkModel mBookmarkModel;
+    private BookmarkModel mBookmarkModel;
     @Mock
-    BookmarkOpener mBookmarkOpener;
+    private BookmarkOpener mBookmarkOpener;
     @Mock
-    SelectionDelegate mSelectionDelegate;
+    private SelectionDelegate mSelectionDelegate;
     @Mock
-    Runnable mOpenSearchUiRunnable;
+    private Runnable mOpenSearchUiRunnable;
     @Mock
-    Callback mOpenFolderCallback;
+    private Callback mOpenFolderCallback;
     @Mock
-    BookmarkId mBookmarkId;
+    private BookmarkId mBookmarkId;
     @Mock
-    BookmarkItem mBookmarkItem;
+    private BookmarkItem mBookmarkItem;
     @Mock
-    BookmarkUiPrefs mBookmarkUiPrefs;
+    private BookmarkUiPrefs mBookmarkUiPrefs;
+    @Mock
+    private BookmarkAddNewFolderCoordinator mBookmarkAddNewFolderCoordinator;
     @Spy
-    Context mContext;
+    private Context mContext;
 
     BookmarkToolbarMediator mMediator;
     PropertyModel mModel;
@@ -129,9 +131,10 @@
                          .with(BookmarkToolbarProperties.OPEN_FOLDER_CALLBACK, mOpenFolderCallback)
                          .build();
         mBookmarkDelegateSupplier = new OneshotSupplierImpl<>();
-        mMediator = new BookmarkToolbarMediator(mContext, mModel,
-                mDragReorderableRecyclerViewAdapter, mBookmarkDelegateSupplier, mSelectionDelegate,
-                mBookmarkModel, mBookmarkOpener, mBookmarkUiPrefs);
+        mMediator =
+                new BookmarkToolbarMediator(mContext, mModel, mDragReorderableRecyclerViewAdapter,
+                        mBookmarkDelegateSupplier, mSelectionDelegate, mBookmarkModel,
+                        mBookmarkOpener, mBookmarkUiPrefs, mBookmarkAddNewFolderCoordinator);
         mBookmarkDelegateSupplier.set(mBookmarkDelegate);
     }
 
@@ -151,9 +154,10 @@
         verify(mContext).startActivity(intentCaptor.capture());
         assertEquals(clazz.getName(), intentCaptor.getValue().getComponent().getClassName());
 
-        mMediator = new BookmarkToolbarMediator(mContext, mModel,
-                mDragReorderableRecyclerViewAdapter, mBookmarkDelegateSupplier, mSelectionDelegate,
-                mBookmarkModel, mBookmarkOpener, mBookmarkUiPrefs);
+        mMediator =
+                new BookmarkToolbarMediator(mContext, mModel, mDragReorderableRecyclerViewAdapter,
+                        mBookmarkDelegateSupplier, mSelectionDelegate, mBookmarkModel,
+                        mBookmarkOpener, mBookmarkUiPrefs, mBookmarkAddNewFolderCoordinator);
     }
 
     @Test
@@ -319,6 +323,29 @@
     }
 
     @Test
+    public void testOnMenuItemClick_addNewFolder() {
+        assertTrue(mMediator.onMenuIdClick(R.id.create_new_folder_menu_id));
+        verify(mBookmarkAddNewFolderCoordinator).show(any());
+    }
+
+    @Test
+    public void testAddNewFolder() {
+        mMediator.onFolderStateSet(mBookmarkId);
+        assertTrue(mModel.get(BookmarkToolbarProperties.NEW_FOLDER_BUTTON_VISIBLE));
+        assertTrue(mMediator.onMenuIdClick(R.id.create_new_folder_menu_id));
+        verify(mBookmarkAddNewFolderCoordinator).show(any());
+
+        doReturn(mBookmarkId).when(mBookmarkModel).getReadingListFolder();
+        mMediator.onFolderStateSet(mBookmarkId);
+        assertFalse(mModel.get(BookmarkToolbarProperties.NEW_FOLDER_BUTTON_VISIBLE));
+
+        doReturn(null).when(mBookmarkModel).getReadingListFolder();
+        doReturn(mBookmarkId).when(mBookmarkModel).getPartnerFolderId();
+        mMediator.onFolderStateSet(mBookmarkId);
+        assertFalse(mModel.get(BookmarkToolbarProperties.NEW_FOLDER_BUTTON_VISIBLE));
+    }
+
+    @Test
     public void testOnMenuItemClick_sortOptions() {
         assertTrue(mMediator.onMenuIdClick(R.id.sort_by_newest));
         assertEquals(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/payments/AddressEditorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/payments/AddressEditorTest.java
index 416cb97..a05b641f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/payments/AddressEditorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/payments/AddressEditorTest.java
@@ -44,7 +44,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
-import org.chromium.chrome.browser.autofill.Source;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorModel;
 import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge;
@@ -85,13 +84,18 @@
             new AddressUiComponent(AddressField.STREET_ADDRESS, /*label=*/"street address label",
                     /*isRequired=*/true, /*isFullLine=*/true));
 
-    private static final AutofillProfile sProfile = new AutofillProfile(/*guid=*/"",
-            /*isLocal=*/true, Source.LOCAL_OR_SYNCABLE, /*honorific prefix=*/"",
-            /*fullName=*/"Seb Doe", /*companyName=*/"Google", /*streetAddress=*/"111 First St",
-            /*region=*/"CA", /*locality=*/"Los Angeles", /*dependentLocality=*/"",
-            /*postalCode=*/"90291", /*sortingCode=*/"", /*countryCode*/ "US",
-            /*phoneNumber=*/"650-253-0000", /*emailAddress=*/"first@gmail.com",
-            /*languageCode=*/"en-US");
+    private static final AutofillProfile sProfile = AutofillProfile.builder()
+                                                            .setFullName("Seb Doe")
+                                                            .setCompanyName("Google")
+                                                            .setStreetAddress("111 First St")
+                                                            .setRegion("CA")
+                                                            .setLocality("Los Angeles")
+                                                            .setPostalCode("90291")
+                                                            .setCountryCode("US")
+                                                            .setPhoneNumber("650-253-0000")
+                                                            .setEmailAddress("first@gmail.com")
+                                                            .setLanguageCode("en-US")
+                                                            .build();
 
     @Rule
     public TestRule mProcessor = new Features.JUnitProcessor();
@@ -367,7 +371,7 @@
         mAddressEditor.setEditorDialog(mEditorDialog);
         mAddressEditor.edit(null, unused -> {});
 
-        validateShownFields(mEditorModelCapture.getValue(), new AutofillProfile());
+        validateShownFields(mEditorModelCapture.getValue(), AutofillProfile.builder().build());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
index 8c58b37..44cee08d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webapps/WebApkUpdateManagerUnitTest.java
@@ -41,6 +41,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.UmaRecorderHolder;
 import org.chromium.base.task.PostTask;
+import org.chromium.base.task.test.BackgroundShadowAsyncTask;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.JniMocker;
@@ -77,12 +78,12 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
 /**
  * Unit tests for WebApkUpdateManager.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {ShadowUrlUtilities.class})
+@Config(manifest = Config.NONE,
+        shadows = {ShadowUrlUtilities.class, BackgroundShadowAsyncTask.class})
 @LooperMode(LooperMode.Mode.LEGACY)
 public class WebApkUpdateManagerUnitTest {
     @Rule
@@ -280,6 +281,16 @@
         }
 
         @Override
+        protected void encodeIconsInBackground(String updateRequestPath, WebappInfo info,
+                String primaryIconUrl, String splashIconUrl, boolean isManifestStale,
+                boolean isAppIdentityUpdateSupported, List<Integer> updateReasons,
+                Callback<Boolean> callback) {
+            storeWebApkUpdateRequestToFile(updateRequestPath, info, primaryIconUrl, "",
+                    splashIconUrl, "", isManifestStale, isAppIdentityUpdateSupported, updateReasons,
+                    callback);
+        }
+
+        @Override
         protected WebApkUpdateDataFetcher buildFetcher() {
             mFetcher = new TestWebApkUpdateDataFetcher();
             return mFetcher;
@@ -348,26 +359,21 @@
         }
     }
 
-    private void registerStorageForWebApkPackage(String webApkPackageName) {
+    private void registerStorageForWebApkPackage(String webApkPackageName) throws Exception {
         try {
-            // AtomicBoolean because Java requires |registered| to be final.
-            final AtomicBoolean registered = new AtomicBoolean();
             CallbackHelper helper = new CallbackHelper();
             WebappRegistry.getInstance().register(
                     WebappIntentUtils.getIdForWebApkPackage(webApkPackageName),
                     new WebappRegistry.FetchWebappDataStorageCallback() {
                         @Override
                         public void onWebappDataStorageRetrieved(WebappDataStorage storage) {
-                            registered.set(true);
                             helper.notifyCalled();
                         }
                     });
-            boolean registeredOnTime = registered.get();
+            BackgroundShadowAsyncTask.runBackgroundTasks();
+            ShadowLooper.runUiThreadTasks();
+
             helper.waitForFirst();
-            // Assert here instead of in WebappRegistry callback because asserting in the callback
-            // does not fail the test. Wait till the callback is called to fail the test in order
-            // to get the stacktrace.
-            assertTrue("WebappRegistry should be synchronous", registeredOnTime);
         } catch (TimeoutException e) {
             fail();
         }
@@ -622,7 +628,7 @@
     }
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         UmaRecorderHolder.resetForTesting();
 
         PathUtils.setPrivateDataDirectorySuffix("chrome");
@@ -1350,7 +1356,7 @@
      * Test that WebappDataStorage#setShouldForceUpdate() is a no-op for unbound WebAPKs.
      */
     @Test
-    public void testForceUpdateUnboundWebApk() {
+    public void testForceUpdateUnboundWebApk() throws Exception {
         registerWebApk(UNBOUND_WEBAPK_PACKAGE_NAME, defaultManifestData(),
                 REQUEST_UPDATE_FOR_SHELL_APK_VERSION);
         registerStorageForWebApkPackage(UNBOUND_WEBAPK_PACKAGE_NAME);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestRobolectricTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestRobolectricTest.java
index f747651..2f09c5c 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestRobolectricTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestRobolectricTest.java
@@ -33,7 +33,6 @@
 import org.chromium.content.browser.ClientDataJsonImpl;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.RenderFrameHost.WebAuthSecurityChecksResults;
-import org.chromium.content_public.browser.WebAuthenticationDelegate;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.device.DeviceFeatureList;
 import org.chromium.net.GURLUtils;
@@ -87,7 +86,7 @@
         mRequestOptions = Fido2ApiTestHelper.createDefaultGetAssertionOptions();
 
         mRequest = new Fido2CredentialRequest(
-                /*intentSender=*/null, WebAuthenticationDelegate.Support.BROWSER);
+                /*intentSender=*/null);
         AuthenticatorImpl.overrideFido2CredentialRequestForTesting(mRequest);
 
         Fido2ApiTestHelper.mockFido2CredentialRequestJni(mMocker);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/webauth/OWNERS b/chrome/android/junit/src/org/chromium/chrome/browser/webauth/OWNERS
new file mode 100644
index 0000000..dd5ad77
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/webauth/OWNERS
@@ -0,0 +1 @@
+file://device/fido/OWNERS
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index a3986b3..5740d1c 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1186,12 +1186,6 @@
           <message name="IDS_FIND_AND_EDIT_MENU" desc="The text label of the Find and edit sub menu item">
             &amp;Find and edit
           </message>
-          <message name="IDS_PARENT_BLOCKED_SITE_BANNER_TITLE" desc="The banner title for the first time a user is blocked from a website because of parental controls">
-            New: Family Link choices for Chrome apply here
-          </message>
-          <message name="IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE" desc="The banner message for the first time a user is blocked from a website because of parental controls">
-            The settings your parent chose are now keeping you safer online
-          </message>
           <message name="IDS_PASSWORD_MANAGER_SUBMENU_OPTION" desc="The text label of the password manager for the passwords and autofill submenu">
             &amp;Google Password Manager
           </message>
@@ -9908,16 +9902,19 @@
       </message>
       <if expr="use_titlecase">
         <then>
-          <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF" desc="In Title Case: The label of item for share this tab to other devices in different context menus.">
+          <message name="IDS_MENU_SEND_TAB_TO_SELF" desc="In Title Case: The label of a menu entry to share the current tab to other devices">
             Send to Your Devices
           </message>
         </then>
         <else>
-          <message name="IDS_CONTEXT_MENU_SEND_TAB_TO_SELF" desc="The label of item for share this tab to other devices in different context menus.">
+          <message name="IDS_MENU_SEND_TAB_TO_SELF" desc="The label of a menu entry to share the current tab to other devices">
             Send to your devices
           </message>
         </else>
       </if>
+      <message name="IDS_SEND_TAB_TO_SELF" desc="The title of an action or bubble letting the user share the current tab to other devices">
+        Send to your devices
+      </message>
       <if expr="is_android">
       <message name="IDS_SEND_TAB_TO_SELF_MESSAGE" desc="Text for a message indicating that the tab was shared from a syncing device.">
         Page shared from <ph name="device_name">$1<ex>Tanya's Pixel 2</ex></ph>
@@ -12334,7 +12331,7 @@
         Sign in easily
       </message>
       <message name="IDS_AUTO_SIGNIN_FIRST_RUN_TEXT" desc="The text of the dialog during the autosign-in first run experience for the Chrome signed out users.">
-        <ph name="PASSWORD_MANAGER_BRAND">$1<ex>Google Chrome</ex></ph> automatically signs you in to eligible sites with passwords you saved.
+        <ph name="PASSWORD_MANAGER_BRAND">$1<ex>Google Password Manager</ex></ph> remembers how you signed in and automatically signs you in when possible. When off, you'll be asked for confirmation every time.
       </message>
       <if expr="use_titlecase">
         <message name="IDS_AUTO_SIGNIN_FIRST_RUN_OK" desc="The text of the OK button in the dialog during the autosign-in first run experience.">
@@ -14868,6 +14865,21 @@
         </message>
       </if>
     </if>
+    <!-- Performance hovercard strings -->
+    <if expr="not is_android">
+      <message name="IDS_HOVERCARD_TAB_MEMORY_USAGE" desc="Text at the bottom of a hovercard that shows how much memory a specific tab is using.">
+        This tab is using <ph name="MEMORY_USAGE">$1<ex>300 MB</ex></ph>
+      </message>
+      <message name="IDS_HOVERCARD_TAB_HIGH_MEMORY_USAGE" desc="Text at the bottom of a hovercard that indicates a tab is experiencing high memory usage.">
+        High memory usage: <ph name="MEMORY_USAGE">$1<ex>300 MB</ex></ph>
+      </message>
+      <message name="IDS_HOVERCARD_INACTIVE_TAB_MEMORY_SAVINGS" desc="Text at the bottom of a hovercard indicating how much memory has been saved when a tab was made inactive.">
+        Inactive tab savings: <ph name="MEMORY_SAVINGS">$1<ex>300 MB</ex></ph>
+      </message>
+      <message name="IDS_HOVERCARD_INACTIVE_TAB" desc="Text at the bottom of a hovercard indicating that a tab is inactive.">
+        Inactive tab
+      </message>
+    </if>
     <message name="IDS_ACCNAME_SIDEBAR_WEBVIEW_LOCATION_BAR" desc="The accessible name of the sidebar webview's location bar.">
       Location
     </message>
diff --git a/chrome/app/generated_resources_grd/IDS_AUTO_SIGNIN_FIRST_RUN_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_AUTO_SIGNIN_FIRST_RUN_TEXT.png.sha1
new file mode 100644
index 0000000..f3c5ec99
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_AUTO_SIGNIN_FIRST_RUN_TEXT.png.sha1
@@ -0,0 +1 @@
+534c8c490311008092a11113892960138e0df963
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_CONTEXT_MENU_SEND_TAB_TO_SELF.png.sha1 b/chrome/app/generated_resources_grd/IDS_CONTEXT_MENU_SEND_TAB_TO_SELF.png.sha1
deleted file mode 100644
index 969bbc4..0000000
--- a/chrome/app/generated_resources_grd/IDS_CONTEXT_MENU_SEND_TAB_TO_SELF.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-486d7fc8373a3f2b699dd21d753ec5372bb0066a
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_HOVERCARD_INACTIVE_TAB.png.sha1 b/chrome/app/generated_resources_grd/IDS_HOVERCARD_INACTIVE_TAB.png.sha1
new file mode 100644
index 0000000..7f08b5b
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HOVERCARD_INACTIVE_TAB.png.sha1
@@ -0,0 +1 @@
+d0e34c27b1226a6e08897dfda2f1df7f206b1282
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_HOVERCARD_INACTIVE_TAB_MEMORY_SAVINGS.png.sha1 b/chrome/app/generated_resources_grd/IDS_HOVERCARD_INACTIVE_TAB_MEMORY_SAVINGS.png.sha1
new file mode 100644
index 0000000..3c4f4ed
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HOVERCARD_INACTIVE_TAB_MEMORY_SAVINGS.png.sha1
@@ -0,0 +1 @@
+3552865f496bd6d2b8b36d4f45a7a50f84f24ad4
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_HOVERCARD_TAB_HIGH_MEMORY_USAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_HOVERCARD_TAB_HIGH_MEMORY_USAGE.png.sha1
new file mode 100644
index 0000000..1a22642
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HOVERCARD_TAB_HIGH_MEMORY_USAGE.png.sha1
@@ -0,0 +1 @@
+e48e2cea6587990a61c2e18dbf2248e7bfe4ab7d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_HOVERCARD_TAB_MEMORY_USAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_HOVERCARD_TAB_MEMORY_USAGE.png.sha1
new file mode 100644
index 0000000..ae2e5cc
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_HOVERCARD_TAB_MEMORY_USAGE.png.sha1
@@ -0,0 +1 @@
+345778131e342c9d908e736bb2e119ee67322b41
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_MENU_SEND_TAB_TO_SELF.png.sha1 b/chrome/app/generated_resources_grd/IDS_MENU_SEND_TAB_TO_SELF.png.sha1
new file mode 100644
index 0000000..dff18a17
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_MENU_SEND_TAB_TO_SELF.png.sha1
@@ -0,0 +1 @@
+c975b451ac27079008716cf1fad73539c42f118d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE.png.sha1
deleted file mode 100644
index 999d50c..0000000
--- a/chrome/app/generated_resources_grd/IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d85c09f6a737ae05b69046553163c237b41d49dd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PARENT_BLOCKED_SITE_BANNER_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_PARENT_BLOCKED_SITE_BANNER_TITLE.png.sha1
deleted file mode 100644
index 999d50c..0000000
--- a/chrome/app/generated_resources_grd/IDS_PARENT_BLOCKED_SITE_BANNER_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-d85c09f6a737ae05b69046553163c237b41d49dd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_SEND_TAB_TO_SELF.png.sha1 b/chrome/app/generated_resources_grd/IDS_SEND_TAB_TO_SELF.png.sha1
new file mode 100644
index 0000000..27abf81
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SEND_TAB_TO_SELF.png.sha1
@@ -0,0 +1 @@
+adb05737449ce67c3bc962cf374d19dd04f7c35c
\ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp
index b5fb877..d5eff7d 100644
--- a/chrome/app/password_manager_ui_strings.grdp
+++ b/chrome/app/password_manager_ui_strings.grdp
@@ -22,7 +22,7 @@
     Sign in automatically
   </message>
   <message name="IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC" desc="Text that describes the 'Sign in automatically' functionality to users.">
-    Easily sign in to sites and apps with your saved passwords. When turned off, you’ll be asked before signing in.
+    <ph name="BRAND">$1<ex>Google Password Manager</ex></ph> remembers how you signed in and automatically signs you in when possible. When off, you'll be asked for confirmation every time.
   </message>
   <message name="IDS_PASSWORD_MANAGER_UI_PASSWORDS_DESCRIPTION" desc="Description for the password section entry used for managing passwords.">
     Create, save, and manage your passwords so you can easily sign in to sites and apps. <ph name="BEGIN_LINK">‎&lt;a target='_blank' href='$1'&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;</ph>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC.png.sha1
index 4968407..9346f84 100644
--- a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC.png.sha1
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC.png.sha1
@@ -1 +1 @@
-73689595772847d3aa136f6b9c77736dfd5bf275
\ No newline at end of file
+f54f4fc714a73920eb239c8e9ab534d30579568a
\ No newline at end of file
diff --git a/chrome/app/theme/default_100_percent/common/autofill_mandatory_reauth_confirmation.png b/chrome/app/theme/default_100_percent/common/autofill_mandatory_reauth_confirmation.png
new file mode 100644
index 0000000..fd4e663
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/autofill_mandatory_reauth_confirmation.png
Binary files differ
diff --git a/chrome/app/theme/default_200_percent/common/autofill_mandatory_reauth_confirmation.png b/chrome/app/theme/default_200_percent/common/autofill_mandatory_reauth_confirmation.png
new file mode 100644
index 0000000..2a8e973c
--- /dev/null
+++ b/chrome/app/theme/default_200_percent/common/autofill_mandatory_reauth_confirmation.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index a54255a..cbc877b 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -276,6 +276,7 @@
         <structure type="chrome_scaled_image" name="IDR_PRIVACY_SANDBOX_CONFIRMATION_BANNER" file="common/privacy_sandbox_confirmation_banner.png" />
         <structure type="chrome_scaled_image" name="IDR_PRIVACY_SANDBOX_CONFIRMATION_BANNER_DARK" file="common/privacy_sandbox_confirmation_banner_dark.png" />
         <structure type="chrome_scaled_image" name="IDR_AUTOFILL_MANDATORY_REAUTH_OPT_IN" file="common/autofill_mandatory_reauth_opt_in.png" />
+        <structure type="chrome_scaled_image" name="IDR_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION" file="common/autofill_mandatory_reauth_confirmation.png" />
       </if>
       <structure type="chrome_scaled_image" name="IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP" file="screen_capture_notification_grip.png" />
       <if expr="not is_android">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index f736a92..77d5dfbe 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -497,6 +497,8 @@
     "enterprise/reporting/legacy_tech/legacy_tech_report_generator.h",
     "enterprise/reporting/legacy_tech/legacy_tech_report_policy_handler.cc",
     "enterprise/reporting/legacy_tech/legacy_tech_report_policy_handler.h",
+    "enterprise/reporting/legacy_tech/legacy_tech_service.cc",
+    "enterprise/reporting/legacy_tech/legacy_tech_service.h",
     "enterprise/reporting/legacy_tech/legacy_tech_url_matcher.cc",
     "enterprise/reporting/legacy_tech/legacy_tech_url_matcher.h",
     "enterprise/reporting/prefs.cc",
@@ -6212,10 +6214,8 @@
       "enterprise/idle/dialog_manager.h",
       "enterprise/profile_management/profile_management_features.cc",
       "enterprise/profile_management/profile_management_features.h",
-      "enterprise/profile_token_management/profile_token_navigation_throttle.cc",
-      "enterprise/profile_token_management/profile_token_navigation_throttle.h",
-      "enterprise/profile_token_management/token_management_features.cc",
-      "enterprise/profile_token_management/token_management_features.h",
+      "enterprise/profile_management/profile_management_navigation_throttle.cc",
+      "enterprise/profile_management/profile_management_navigation_throttle.h",
       "enterprise/remote_commands/rotate_attestation_credential_job.cc",
       "enterprise/remote_commands/rotate_attestation_credential_job.h",
       "enterprise/signals/signals_aggregator_factory.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2cfb36800..473261c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6736,6 +6736,10 @@
      flag_descriptions::kEnableWindowsGamingInputDataFetcherName,
      flag_descriptions::kEnableWindowsGamingInputDataFetcherDescription, kOsWin,
      FEATURE_VALUE_TYPE(features::kEnableWindowsGamingInputDataFetcher)},
+
+    {"windows11-mica-titlebar", flag_descriptions::kWindows11MicaTitlebarName,
+     flag_descriptions::kWindows11MicaTitlebarDescription, kOsWin,
+     FEATURE_VALUE_TYPE(kWindows11MicaTitlebar)},
 #endif
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/android/auxiliary_search/auxiliary_search_provider.cc b/chrome/browser/android/auxiliary_search/auxiliary_search_provider.cc
index ac9a831..62451f3f 100644
--- a/chrome/browser/android/auxiliary_search/auxiliary_search_provider.cc
+++ b/chrome/browser/android/auxiliary_search/auxiliary_search_provider.cc
@@ -65,7 +65,7 @@
 
 base::android::ScopedJavaLocalRef<jbyteArray>
 AuxiliarySearchProvider::GetBookmarksSearchableData(JNIEnv* env) const {
-  auxiliary_search::AuxiliarySearchGroup group;
+  auxiliary_search::AuxiliarySearchBookmarkGroup group;
   std::string serialized_group;
 
   GetBookmarks(BookmarkModelFactory::GetForBrowserContext(profile_.get()),
@@ -80,15 +80,15 @@
 
 void AuxiliarySearchProvider::GetBookmarks(
     bookmarks::BookmarkModel* model,
-    auxiliary_search::AuxiliarySearchGroup* group) const {
+    auxiliary_search::AuxiliarySearchBookmarkGroup* group) const {
   std::vector<const BookmarkNode*> nodes;
   bookmarks::GetMostRecentlyUsedEntries(model, kMaxBookmarksCount, &nodes);
   for (const BookmarkNode* node : nodes) {
-    auxiliary_search::AuxiliarySearchGroup_Entry* entry = group->add_entry();
-    entry->set_title(base::UTF16ToUTF8(node->GetTitle()));
-    entry->set_url(node->url().spec());
+    auxiliary_search::AuxiliarySearchBookmarkGroup_Bookmark* bookmark =
+        group->add_bookmark();
+    bookmark->set_title(base::UTF16ToUTF8(node->GetTitle()));
+    bookmark->set_url(node->url().spec());
   }
-  group->set_group_type(auxiliary_search::GroupType::BOOKMARK_GROUP);
 }
 
 // static
diff --git a/chrome/browser/android/auxiliary_search/auxiliary_search_provider.h b/chrome/browser/android/auxiliary_search/auxiliary_search_provider.h
index 0905e640..60bb84e 100644
--- a/chrome/browser/android/auxiliary_search/auxiliary_search_provider.h
+++ b/chrome/browser/android/auxiliary_search/auxiliary_search_provider.h
@@ -12,7 +12,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 
 namespace auxiliary_search {
-class AuxiliarySearchGroup;
+class AuxiliarySearchBookmarkGroup;
 }
 namespace bookmarks {
 class BookmarkModel;
@@ -32,8 +32,9 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(AuxiliarySearchProviderTest, QueryBookmarks);
 
-  void GetBookmarks(bookmarks::BookmarkModel* model,
-                    auxiliary_search::AuxiliarySearchGroup* group) const;
+  void GetBookmarks(
+      bookmarks::BookmarkModel* model,
+      auxiliary_search::AuxiliarySearchBookmarkGroup* group) const;
 
   raw_ptr<Profile> profile_;
 };
diff --git a/chrome/browser/android/auxiliary_search/auxiliary_search_provider_unittest.cc b/chrome/browser/android/auxiliary_search/auxiliary_search_provider_unittest.cc
index 9b673c1..0344cadc 100644
--- a/chrome/browser/android/auxiliary_search/auxiliary_search_provider_unittest.cc
+++ b/chrome/browser/android/auxiliary_search/auxiliary_search_provider_unittest.cc
@@ -53,7 +53,7 @@
 }
 
 TEST_F(AuxiliarySearchProviderTest, QueryBookmarks) {
-  auxiliary_search::AuxiliarySearchGroup group;
+  auxiliary_search::AuxiliarySearchBookmarkGroup group;
   for (int i = 0; i < 200; i++) {
     std::string number = base::NumberToString(i);
     BookmarkNode* node = AsMutable(model_->AddURL(
@@ -63,13 +63,14 @@
   }
   provider->GetBookmarks(model_.get(), &group);
 
-  EXPECT_EQ(100, group.entry_size());
+  EXPECT_EQ(100, group.bookmark_size());
 
   std::unordered_set<int> bookmark_titles_int;
   for (int i = 0; i < 100; i++) {
-    auxiliary_search::AuxiliarySearchGroup_Entry entry = group.entry(i);
+    auxiliary_search::AuxiliarySearchBookmarkGroup_Bookmark bookmark =
+        group.bookmark(i);
     int title_int;
-    EXPECT_TRUE(base::StringToInt(entry.title(), &title_int));
+    EXPECT_TRUE(base::StringToInt(bookmark.title(), &title_int));
 
     EXPECT_TRUE(title_int >= 100 && title_int <= 199);
     bookmark_titles_int.insert(title_int);
diff --git a/chrome/browser/android/auxiliary_search/proto/auxiliary_search_group.proto b/chrome/browser/android/auxiliary_search/proto/auxiliary_search_group.proto
index 2cd6944..db7213db 100644
--- a/chrome/browser/android/auxiliary_search/proto/auxiliary_search_group.proto
+++ b/chrome/browser/android/auxiliary_search/proto/auxiliary_search_group.proto
@@ -10,18 +10,11 @@
 
 package auxiliary_search;
 
-enum GroupType {
-  UNKNOWN_GROUP_TYPE = 0;
-  BOOKMARK_GROUP = 1;
-  TAB_GROUP = 2;
-}
-
 // Contains information about the tabs and bookmarks.
-message AuxiliarySearchGroup {
-  message Entry {
+message AuxiliarySearchBookmarkGroup {
+  message Bookmark {
     optional string title = 1;
     optional string url = 2;
   }
-  repeated Entry entry = 1;
-  optional GroupType group_type = 2;
+  repeated Bookmark bookmark = 1;
 }
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 3e0c7f1..3bb3000 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -5436,6 +5436,7 @@
     "policy/dlp/dlp_content_manager_ash_unittest.cc",
     "policy/dlp/dlp_files_controller_ash_unittest.cc",
     "policy/dlp/dlp_files_event_storage_unittest.cc",
+    "policy/dlp/files_policy_notification_manager_unittest.cc",
     "policy/enrollment/account_status_check_fetcher_unittest.cc",
     "policy/enrollment/auto_enrollment_client_impl_unittest.cc",
     "policy/enrollment/auto_enrollment_controller_unittest.cc",
diff --git a/chrome/browser/ash/app_restore/full_restore_app_launch_handler.cc b/chrome/browser/ash/app_restore/full_restore_app_launch_handler.cc
index 7761977a..95e48547 100644
--- a/chrome/browser/ash/app_restore/full_restore_app_launch_handler.cc
+++ b/chrome/browser/ash/app_restore/full_restore_app_launch_handler.cc
@@ -103,11 +103,13 @@
 
     // OS Setting should be launched after browser to have OS setting window in
     // front.
-    UserSessionManager::GetInstance()->MaybeLaunchSettings(profile());
+    UserSessionManager::GetInstance()->PerformPostBrowserLaunchOOBEActions(
+        profile());
     return;
   }
 
-  UserSessionManager::GetInstance()->MaybeLaunchSettings(profile());
+  UserSessionManager::GetInstance()->PerformPostBrowserLaunchOOBEActions(
+      profile());
 
   // If the restore data hasn't been loaded, or the user hasn't chosen to
   // restore, set `should_launch_browser_` as true, and wait the restore data
@@ -186,7 +188,8 @@
 void FullRestoreAppLaunchHandler::ForceLaunchBrowserForTesting() {
   ::full_restore::AddChromeBrowserLaunchInfoForTesting(profile()->GetPath());
   UserSessionManager::GetInstance()->LaunchBrowser(profile());
-  UserSessionManager::GetInstance()->MaybeLaunchSettings(profile());
+  UserSessionManager::GetInstance()->PerformPostBrowserLaunchOOBEActions(
+      profile());
 }
 
 void FullRestoreAppLaunchHandler::OnExtensionLaunching(
@@ -337,7 +340,8 @@
                                    SessionRestore::RESTORE_APPS, startup_tabs);
   }
 
-  UserSessionManager::GetInstance()->MaybeLaunchSettings(profile());
+  UserSessionManager::GetInstance()->PerformPostBrowserLaunchOOBEActions(
+      profile());
 }
 
 void FullRestoreAppLaunchHandler::MaybeRestoreLacros() {
diff --git a/chrome/browser/ash/input_method/suggestions_service_client.cc b/chrome/browser/ash/input_method/suggestions_service_client.cc
index 650a5c3..73fffd9 100644
--- a/chrome/browser/ash/input_method/suggestions_service_client.cc
+++ b/chrome/browser/ash/input_method/suggestions_service_client.cc
@@ -9,7 +9,6 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/ash/input_method/suggestion_enums.h"
-#include "chrome/browser/ash/input_method/text_utils.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -70,6 +69,13 @@
                              .text = candidate->get_multi_word()->text};
 }
 
+std::string TrimText(const std::string& text) {
+  size_t text_length = text.length();
+  return text_length > kMaxNumberCharsSent
+             ? text.substr(text_length - kMaxNumberCharsSent)
+             : text;
+}
+
 MultiWordSuggestionType ToSuggestionType(
     const ime::AssistiveSuggestionMode& suggestion_mode) {
   switch (suggestion_mode) {
@@ -142,7 +148,7 @@
   RecordRequestCandidates(suggestion_mode);
 
   auto query = TextSuggesterQuery::New();
-  query->text = GetLastNCodepoints(preceding_text, kMaxNumberCharsSent);
+  query->text = TrimText(preceding_text);
   query->suggestion_mode = ToTextSuggestionModeMojom(suggestion_mode);
 
   for (const auto& candidate : completion_candidates) {
diff --git a/chrome/browser/ash/input_method/text_utils.cc b/chrome/browser/ash/input_method/text_utils.cc
index 1ed8be1..0a99a6f2 100644
--- a/chrome/browser/ash/input_method/text_utils.cc
+++ b/chrome/browser/ash/input_method/text_utils.cc
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/input_method/text_utils.h"
-#include "base/i18n/char_iterator.h"
-#include "base/strings/utf_string_conversions.h"
 
 // TODO(crbug/1223597) The rules to detect sentence end is not perfect, and we
 // may want to use regex to improve readability.
@@ -182,17 +180,5 @@
                   text.substr(start, end - start + 1));
 }
 
-std::string GetLastNCodepoints(const std::string& text, size_t N) {
-  std::u16string u16text = base::UTF8ToUTF16(text);
-  for (auto iter = base::i18n::UTF16CharIterator::LowerBound(
-           u16text, u16text.length() - 1);
-       !iter.start(); iter.Rewind()) {
-    if ((size_t)iter.char_offset() == -N + 1) {
-      return base::UTF16ToUTF8(u16text.substr(iter.array_pos()));
-    }
-  }
-  return text;
-}
-
 }  // namespace input_method
 }  // namespace ash
diff --git a/chrome/browser/ash/input_method/text_utils.h b/chrome/browser/ash/input_method/text_utils.h
index e90c496..646e919 100644
--- a/chrome/browser/ash/input_method/text_utils.h
+++ b/chrome/browser/ash/input_method/text_utils.h
@@ -44,7 +44,6 @@
 // Find the sentence containing the cursor position |pos|.
 Sentence FindCurrentSentence(const std::u16string& text, uint32_t pos);
 
-std::string GetLastNCodepoints(const std::string& text, size_t N);
 }  // namespace input_method
 }  // namespace ash
 
diff --git a/chrome/browser/ash/input_method/text_utils_unittest.cc b/chrome/browser/ash/input_method/text_utils_unittest.cc
index 1d53f6b..d41efdc 100644
--- a/chrome/browser/ash/input_method/text_utils_unittest.cc
+++ b/chrome/browser/ash/input_method/text_utils_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/input_method/text_utils.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
@@ -105,17 +106,6 @@
             Sentence(gfx::Range(0, 13), u"This is a dog"));
 }
 
-TEST(TextUtilsTest, GetCodepointsWithEmoji) {
-  EXPECT_EQ(GetLastNCodepoints("😀😀😀", 2), "😀😀");
-  EXPECT_EQ(GetLastNCodepoints("😁😁Hey", 3), "Hey");
-  EXPECT_EQ(GetLastNCodepoints("😁😁Hey", 4), "😁Hey");
-  EXPECT_EQ(GetLastNCodepoints("Nice job! 😀😀😀", 8), "job! 😀😀😀");
-}
-
-TEST(TextUtilsTest, GetCodepointsWithChineseCharacters) {
-  EXPECT_EQ(GetLastNCodepoints("你好 Rob!", 6), "好 Rob!");
-}
-
 }  // namespace
 }  // namespace input_method
 }  // namespace ash
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn b/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
index 04353a9..eb88892 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/BUILD.gn
@@ -8,7 +8,6 @@
 
 source_set("connectivity") {
   deps = [
-    "proto",
     "//base",
     "//chrome/browser:browser_process",
     "//chrome/browser/ash/login/oobe_quick_start:oobe_quick_start_pref_names",
@@ -32,8 +31,6 @@
     "fast_pair_advertiser.h",
     "fido_assertion_info.cc",
     "fido_assertion_info.h",
-    "handshake_helpers.cc",
-    "handshake_helpers.h",
     "random_session_id.cc",
     "random_session_id.h",
     "target_device_connection_broker.cc",
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
index c1f93ba..c52dda7 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.cc
@@ -12,7 +12,6 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/target_device_connection_broker.h"
 #include "chrome/browser/ash/login/oobe_quick_start/logging/logging.h"
@@ -23,6 +22,7 @@
 #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom-forward.h"
 #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom-shared.h"
 #include "chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom.h"
+#include "crypto/random.h"
 
 namespace ash::quick_start {
 
@@ -41,21 +41,31 @@
     mojo::SharedRemote<mojom::QuickStartDecoder> quick_start_decoder,
     ConnectionClosedCallback on_connection_closed,
     ConnectionAuthenticatedCallback on_connection_authenticated) {
+  auto nonce_generator = std::make_unique<NonceGenerator>();
   return std::make_unique<Connection>(
       nearby_connection, session_context, std::move(quick_start_decoder),
-      std::move(on_connection_closed), std::move(on_connection_authenticated));
+      std::move(nonce_generator), std::move(on_connection_closed),
+      std::move(on_connection_authenticated));
+}
+
+Connection::Nonce Connection::NonceGenerator::Generate() {
+  Nonce nonce;
+  crypto::RandBytes(nonce);
+  return nonce;
 }
 
 Connection::Connection(
     NearbyConnection* nearby_connection,
     Connection::SessionContext session_context,
     mojo::SharedRemote<mojom::QuickStartDecoder> quick_start_decoder,
+    std::unique_ptr<NonceGenerator> nonce_generator,
     ConnectionClosedCallback on_connection_closed,
     ConnectionAuthenticatedCallback on_connection_authenticated)
     : nearby_connection_(nearby_connection),
       random_session_id_(session_context.session_id),
       shared_secret_(session_context.shared_secret),
       secondary_shared_secret_(session_context.secondary_shared_secret),
+      nonce_generator_(std::move(nonce_generator)),
       on_connection_closed_(std::move(on_connection_closed)),
       on_connection_authenticated_(std::move(on_connection_authenticated)),
       decoder_(std::move(quick_start_decoder)) {
@@ -260,8 +270,9 @@
 
 void Connection::InitiateHandshake(const std::string& authentication_token,
                                    HandshakeSuccessCallback callback) {
-  nearby_connection_->Write(
-      handshake::BuildHandshakeMessage(authentication_token, shared_secret_));
+  Connection::Nonce nonce = nonce_generator_->Generate();
+  nearby_connection_->Write(requests::BuildTargetDeviceHandshakeMessage(
+      authentication_token, shared_secret_, nonce));
 
   // TODO(b/234655072): Read response from phone and run callback.
 }
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
index 95f6a635..a2f2a09 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection.h
@@ -35,6 +35,7 @@
     : public TargetDeviceConnectionBroker::AuthenticatedConnection {
  public:
   using SharedSecret = TargetDeviceConnectionBroker::SharedSecret;
+  using Nonce = std::array<uint8_t, 12>;
   using HandshakeSuccessCallback = base::OnceCallback<void(bool)>;
   using ConnectionAuthenticatedCallback = base::OnceCallback<void(
       base::WeakPtr<TargetDeviceConnectionBroker::AuthenticatedConnection>)>;
@@ -70,9 +71,20 @@
         ConnectionAuthenticatedCallback on_connection_authenticated);
   };
 
+  class NonceGenerator {
+   public:
+    NonceGenerator() = default;
+    NonceGenerator(const NonceGenerator&) = delete;
+    NonceGenerator& operator=(const NonceGenerator&) = delete;
+    virtual ~NonceGenerator() = default;
+
+    virtual Nonce Generate();
+  };
+
   Connection(NearbyConnection* nearby_connection,
              SessionContext session_context,
              mojo::SharedRemote<mojom::QuickStartDecoder> quick_start_decoder,
+             std::unique_ptr<NonceGenerator> nonce_generator,
              ConnectionClosedCallback on_connection_closed,
              ConnectionAuthenticatedCallback on_connection_authenticated);
 
@@ -185,6 +197,7 @@
   SharedSecret shared_secret_;
   SharedSecret secondary_shared_secret_;
   State connection_state_ = State::kOpen;
+  std::unique_ptr<NonceGenerator> nonce_generator_;
   ConnectionClosedCallback on_connection_closed_;
   bool authenticated_ = false;
   ConnectionAuthenticatedCallback on_connection_authenticated_;
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
index 0be5fd8..3369600 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/connection_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/timer/mock_timer.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/random_session_id.h"
 #include "chrome/browser/ash/login/oobe_quick_start/connectivity/target_device_connection_broker.h"
 #include "chrome/browser/nearby_sharing/fake_nearby_connection.h"
@@ -71,10 +70,27 @@
     0xab, 0xa0, 0xe3, 0xfc, 0xd3, 0x5a, 0x04, 0x01, 0x63, 0xf6, 0xf5,
     0xeb, 0x40, 0x7f, 0x4b, 0xac, 0xe4, 0xd1, 0xbf, 0x20, 0x19};
 
+// 12 random bytes to use as the nonce.
+constexpr std::array<uint8_t, 12> kNonce = {0x60, 0x3e, 0x87, 0x69, 0xa3, 0x55,
+                                            0xd3, 0x49, 0xbd, 0x0a, 0x63, 0xed};
+
+// A serialized auth message produced with |kAuthToken|, |kSharedSecret|, and
+// |kNonce|. Uses the "Target" role.
+constexpr std::array<uint8_t, 50> kTargetDeviceAuthMessage = {
+    0x08, 0x01, 0x12, 0x2e, 0x0a, 0x0c, 0x60, 0x3e, 0x87, 0x69,
+    0xa3, 0x55, 0xd3, 0x49, 0xbd, 0x0a, 0x63, 0xed, 0x12, 0x1e,
+    0x44, 0x28, 0x93, 0x04, 0xd3, 0xc0, 0x03, 0x50, 0xc9, 0x9d,
+    0x4f, 0x8d, 0x01, 0xaa, 0xcf, 0xc6, 0x43, 0x41, 0xa2, 0xcf,
+    0x4a, 0x91, 0x6e, 0x49, 0x14, 0x9d, 0x2e, 0xea, 0x9a, 0xf6};
+
 // 6 random bytes to use as the RandomSessionId.
 constexpr std::array<uint8_t, 6> kRandomSessionId = {0x6b, 0xb3, 0x85,
                                                      0x27, 0xbb, 0x28};
 
+class ConstantNonceGenerator : public Connection::NonceGenerator {
+  Connection::Nonce Generate() override { return kNonce; }
+};
+
 constexpr base::TimeDelta kNotifySourceOfUpdateResponseTimeout =
     base::Seconds(3);
 
@@ -99,6 +115,7 @@
         nearby_connection, session_context_,
         mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>(
             fake_quick_start_decoder_->GetRemote()),
+        std::make_unique<ConstantNonceGenerator>(),
         /*on_connection_closed=*/base::DoNothing(),
         /*on_connection_authenticated=*/
         base::BindLambdaForTesting(
@@ -412,6 +429,7 @@
           fake_nearby_connection_.get(), session_context_,
           mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>(
               fake_quick_start_decoder_->GetRemote()),
+          std::make_unique<ConstantNonceGenerator>(),
           /*on_connection_closed=*/future.GetCallback(),
           /*on_connection_authenticated=*/base::DoNothing());
 
@@ -435,6 +453,7 @@
           fake_nearby_connection_.get(), session_context_,
           mojo::SharedRemote<ash::quick_start::mojom::QuickStartDecoder>(
               fake_quick_start_decoder_->GetRemote()),
+          std::make_unique<ConstantNonceGenerator>(),
           /*on_connection_closed=*/future.GetCallback(),
           /*on_connection_authenticated=*/base::DoNothing());
 
@@ -452,9 +471,12 @@
 
 TEST_F(ConnectionTest, InitiateHandshake) {
   connection_->InitiateHandshake(kAuthToken, base::DoNothing());
-
-  // TODO(b/234655072): Test that the correct info is written to the
-  // NearbyConnection once handshake parsing is implemented.
+  std::vector<uint8_t> written_payload =
+      fake_nearby_connection_->GetWrittenData();
+  ASSERT_EQ(kTargetDeviceAuthMessage.size(), written_payload.size());
+  for (size_t i = 0; i < kTargetDeviceAuthMessage.size(); i++) {
+    EXPECT_EQ(kTargetDeviceAuthMessage[i], written_payload[i]);
+  }
 }
 
 TEST_F(ConnectionTest, TestUserVerificationRequested_ReturnsResult) {
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_connection.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_connection.cc
index 4413e06a..c303829b 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_connection.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/connectivity/fake_connection.cc
@@ -32,6 +32,7 @@
     : Connection(nearby_connection,
                  session_context,
                  std::move(quick_start_decoder),
+                 std::make_unique<Connection::NonceGenerator>(),
                  std::move(on_connection_closed),
                  std::move(on_connection_authenticated)) {}
 
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.cc b/chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.cc
deleted file mode 100644
index f5b13ae..0000000
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.h"
-
-#include "base/containers/span.h"
-#include "chrome/browser/ash/login/oobe_quick_start/connectivity/proto/aes_gcm_authentication_message.pb.h"
-#include "crypto/aead.h"
-#include "crypto/random.h"
-
-namespace ash::quick_start::handshake {
-
-namespace {
-
-std::vector<uint8_t> EncryptPayload(
-    const proto::V1Message::AuthenticationPayload& payload,
-    base::span<const uint8_t> secret,
-    base::span<const uint8_t> nonce) {
-  std::string payload_bytes;
-  payload.SerializeToString(&payload_bytes);
-
-  crypto::Aead aead(crypto::Aead::AES_256_GCM);
-  aead.Init(secret);
-  return aead.Seal(
-      std::vector<uint8_t>(payload_bytes.begin(), payload_bytes.end()), nonce,
-      /*additional_data=*/base::span<uint8_t>());
-}
-
-}  // namespace
-
-std::vector<uint8_t> BuildHandshakeMessage(
-    const std::string& auth_token,
-    std::array<uint8_t, 32> secret,
-    absl::optional<std::array<uint8_t, 12>> nonce,
-    DeviceRole role) {
-  std::array<uint8_t, 12> nonce_bytes;
-  if (nonce) {
-    nonce_bytes = *nonce;
-  } else {
-    crypto::RandBytes(nonce_bytes);
-  }
-
-  proto::V1Message::AuthenticationPayload auth_payload;
-  auth_payload.set_role(static_cast<int32_t>(role));
-  auth_payload.set_auth_string(auth_token);
-
-  std::vector<uint8_t> encrypted_payload =
-      EncryptPayload(auth_payload, secret, nonce_bytes);
-
-  proto::AesGcmAuthenticationMessage auth_message;
-  auth_message.set_version(proto::AesGcmAuthenticationMessage::V1);
-  proto::V1Message* v1_message = auth_message.mutable_v1();
-  v1_message->set_nonce(std::string(nonce_bytes.begin(), nonce_bytes.end()));
-  v1_message->set_payload(
-      std::string(encrypted_payload.begin(), encrypted_payload.end()));
-
-  std::string auth_message_serialized;
-  auth_message.SerializeToString(&auth_message_serialized);
-
-  return std::vector<uint8_t>(auth_message_serialized.begin(),
-                              auth_message_serialized.end());
-}
-
-}  // namespace ash::quick_start::handshake
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.h b/chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.h
deleted file mode 100644
index 0cb5e59..0000000
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/handshake_helpers.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_HANDSHAKE_HELPERS_H_
-#define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_HANDSHAKE_HELPERS_H_
-
-#include <array>
-
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace ash::quick_start::handshake {
-
-// The role for the device producing a handshake message. Values come from
-// this enum:
-// http://google3/java/com/google/android/gms/smartdevice/d2d/proto/aes_gcm_authentication_message.proto;l=26;rcl=489093041
-enum class DeviceRole {
-  kTarget = 1,
-  kSource = 2,
-};
-
-// Build and serialize an AesGcmAuthenticationMessage proto. If |nonce| and
-// |role| are not provided, then a new random nonce and the target device role
-// will be used.
-std::vector<uint8_t> BuildHandshakeMessage(
-    const std::string& auth_token,
-    std::array<uint8_t, 32> secret,
-    absl::optional<std::array<uint8_t, 12>> nonce = absl::nullopt,
-    DeviceRole role = DeviceRole::kTarget);
-
-}  // namespace ash::quick_start::handshake
-
-#endif  // CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_CONNECTIVITY_HANDSHAKE_HELPERS_H_
diff --git a/chrome/browser/ash/login/screens/display_size_screen.cc b/chrome/browser/ash/login/screens/display_size_screen.cc
index 06d51f3..a1929aa 100644
--- a/chrome/browser/ash/login/screens/display_size_screen.cc
+++ b/chrome/browser/ash/login/screens/display_size_screen.cc
@@ -50,6 +50,37 @@
   }
 }
 
+void DisplaySizeScreen::MaybeUpdateZoomFactor(Profile* profile) {
+  auto* prefs = profile->GetPrefs();
+  if (!prefs->HasPrefPath(prefs::kOobeDisplaySizeFactorDeferred)) {
+    return;
+  }
+
+  auto factors = GetZoomFactors();
+  // Verify thae existence of available factors.
+  if (factors.empty()) {
+    return;
+  }
+
+  double stored_zoom_factor =
+      prefs->GetDouble(prefs::kOobeDisplaySizeFactorDeferred);
+  prefs->ClearPref(prefs::kOobeDisplaySizeFactorDeferred);
+
+  // Find the nearest available zoom factor to the stored zoom factor. This is
+  // done to account for any changes in the available zoom factors since the
+  // preference was stored.
+  float selected_zoom_factor = factors[0];
+  for (float factor : factors) {
+    if (abs(stored_zoom_factor - factor) < abs(selected_zoom_factor - factor)) {
+      selected_zoom_factor = factor;
+    }
+  }
+
+  auto display_id_ = display::Screen::GetScreen()->GetPrimaryDisplay().id();
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  display_manager->UpdateZoomFactor(display_id_, selected_zoom_factor);
+}
+
 DisplaySizeScreen::DisplaySizeScreen(base::WeakPtr<DisplaySizeScreenView> view,
                                      const ScreenExitCallback& exit_callback)
     : BaseScreen(DisplaySizeScreenView::kScreenId, OobeScreenPriority::DEFAULT),
diff --git a/chrome/browser/ash/login/screens/display_size_screen.h b/chrome/browser/ash/login/screens/display_size_screen.h
index 4bd1e32..300a5986 100644
--- a/chrome/browser/ash/login/screens/display_size_screen.h
+++ b/chrome/browser/ash/login/screens/display_size_screen.h
@@ -12,6 +12,8 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ash/login/screens/base_screen.h"
 
+class Profile;
+
 namespace ash {
 class DisplaySizeScreenView;
 
@@ -24,6 +26,11 @@
 
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
 
+  static std::string GetResultString(Result result);
+
+  // Updates zoom factor if `kOobeDisplaySizeFactorDeferred` pref is set.
+  static void MaybeUpdateZoomFactor(Profile* profile);
+
   DisplaySizeScreen(base::WeakPtr<DisplaySizeScreenView> view,
                     const ScreenExitCallback& exit_callback);
 
@@ -32,8 +39,6 @@
 
   ~DisplaySizeScreen() override;
 
-  static std::string GetResultString(Result result);
-
  private:
   // BaseScreen:
   bool ShouldBeSkipped(const WizardContext& context) const override;
diff --git a/chrome/browser/ash/login/session/chrome_session_manager.cc b/chrome/browser/ash/login/session/chrome_session_manager.cc
index 40e9faa1..994ccf26 100644
--- a/chrome/browser/ash/login/session/chrome_session_manager.cc
+++ b/chrome/browser/ash/login/session/chrome_session_manager.cc
@@ -218,7 +218,8 @@
     UserSessionManager::GetInstance()->CheckEolInfo(user_profile);
 
   UserSessionManager::GetInstance()->ShowNotificationsIfNeeded(user_profile);
-  UserSessionManager::GetInstance()->MaybeLaunchSettings(user_profile);
+  UserSessionManager::GetInstance()->PerformPostBrowserLaunchOOBEActions(
+      user_profile);
 }
 
 void LaunchShimlessRma() {
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index cdbc709..db45519f 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -68,6 +68,7 @@
 #include "chrome/browser/ash/login/quick_unlock/pin_backend.h"
 #include "chrome/browser/ash/login/saml/password_sync_token_verifier.h"
 #include "chrome/browser/ash/login/saml/password_sync_token_verifier_factory.h"
+#include "chrome/browser/ash/login/screens/display_size_screen.h"
 #include "chrome/browser/ash/login/screens/sync_consent_screen.h"
 #include "chrome/browser/ash/login/security_token_session_controller_factory.h"
 #include "chrome/browser/ash/login/session/user_session_initializer.h"
@@ -1372,12 +1373,12 @@
       user_manager::KnownUser(g_browser_process->local_state())
           .GetOnboardingCompletedVersion(user->GetAccountId());
   if (!onboarding_completed_version.has_value()) {
-    // Kiosks do not have onboarding.
+    // Device local accounts do not have onboarding.
     if (LoginDisplayHost::default_host() &&
         LoginDisplayHost::default_host()->GetSigninUI() &&
         user_manager->GetPrimaryUser() == user &&
         !user_manager->IsUserNonCryptohomeDataEphemeral(user->GetAccountId()) &&
-        !user->IsKioskType()) {
+        !user->IsDeviceLocalAccount()) {
       LoginDisplayHost::default_host()
           ->GetSigninUI()
           ->SetAuthSessionForOnboarding(user_context);
@@ -2042,8 +2043,11 @@
       ->ShowAdbSideloadingPolicyChangeNotificationIfNeeded();
 }
 
-void UserSessionManager::MaybeLaunchSettings(Profile* profile) {
+void UserSessionManager::PerformPostBrowserLaunchOOBEActions(Profile* profile) {
   SyncConsentScreen::MaybeLaunchSyncConsentSettings(profile);
+  if (features::IsOobeDisplaySizeEnabled()) {
+    DisplaySizeScreen::MaybeUpdateZoomFactor(profile);
+  }
 }
 
 void UserSessionManager::OnRestoreActiveSessions(
@@ -2259,7 +2263,7 @@
       }
     } else if (!IsFullRestoreEnabled(profile)) {
       LaunchBrowser(profile);
-      MaybeLaunchSettings(profile);
+      PerformPostBrowserLaunchOOBEActions(profile);
     } else {
       full_restore::FullRestoreService::GetForProfile(profile)
           ->LaunchBrowserWhenReady();
diff --git a/chrome/browser/ash/login/session/user_session_manager.h b/chrome/browser/ash/login/session/user_session_manager.h
index f3a0b04..bad3b2c 100644
--- a/chrome/browser/ash/login/session/user_session_manager.h
+++ b/chrome/browser/ash/login/session/user_session_manager.h
@@ -225,8 +225,9 @@
   // Show various notifications if applicable.
   void ShowNotificationsIfNeeded(Profile* profile);
 
-  // Launch various setting pages (or dialogs) if applicable.
-  void MaybeLaunchSettings(Profile* profile);
+  // Perform actions that were deferred from OOBE or onboarding flow once the
+  // browser is launched if applicable.
+  void PerformPostBrowserLaunchOOBEActions(Profile* profile);
 
   // Invoked when the user is logging in for the first time, or is logging in to
   // an ephemeral session type, such as guest or a public session.
diff --git a/chrome/browser/ash/login/signin/oauth2_login_manager.cc b/chrome/browser/ash/login/signin/oauth2_login_manager.cc
index 0a43a84..1722e6b 100644
--- a/chrome/browser/ash/login/signin/oauth2_login_manager.cc
+++ b/chrome/browser/ash/login/signin/oauth2_login_manager.cc
@@ -85,7 +85,12 @@
 
   CheckIfTokensHaveBeenLoaded();
 
-  account_reconcilor_observation_.Observe(GetAccountReconcilor());
+  // ContinueSessionRestore could be called multiple times when network
+  // connection changes. Only add observation once.
+  if (!account_reconcilor_observation_.IsObserving()) {
+    account_reconcilor_observation_.Observe(GetAccountReconcilor());
+  }
+
   const signin_metrics::AccountReconcilorState state =
       GetAccountReconcilor()->GetState();
   if (IsTerminalState(state)) {
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index fba3a3f..443b5e6f 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -94,6 +94,7 @@
 #include "components/account_id/account_id.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/policy/core/common/cloud/affiliation.h"
+#include "components/policy/core/common/device_local_account_type.h"
 #include "components/policy/core/common/policy_details.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -623,16 +624,38 @@
 
 bool ChromeUserManagerImpl::IsEphemeralAccountId(
     const AccountId& account_id) const {
-  // Owner account should never be ephemeral.
+  // Data belonging to the device owner is never ephemeral.
   if (account_id == GetOwnerAccountId()) {
     return false;
   }
 
+  // Data belonging to the stub users is never ephemeral.
+  if (IsStubAccountId(account_id)) {
+    return false;
+  }
+
+  // Data belonging to the guest user is always ephemeral.
+  if (IsGuestAccountId(account_id)) {
+    return true;
+  }
+
+  // Data belonging to the public accounts (e.g. managed guest sessions) is
+  // always ephemeral.
+  if (const auto type =
+          policy::GetDeviceLocalAccountType(account_id.GetUserEmail());
+      type.has_value() &&
+      (type.value() == policy::DeviceLocalAccountType::kPublicSession ||
+       type.value() == policy::DeviceLocalAccountType::kSamlPublicSession)) {
+    return true;
+  }
+
   policy::BrowserPolicyConnectorAsh* connector =
       g_browser_process->platform_part()->browser_policy_connector_ash();
-  return GetEphemeralModeConfig().IsAccountIdIncluded(account_id) &&
-         (connector->IsDeviceEnterpriseManaged() ||
-          GetOwnerAccountId().is_valid());
+  const bool device_is_owned =
+      connector->IsDeviceEnterpriseManaged() || GetOwnerAccountId().is_valid();
+
+  return device_is_owned &&
+         GetEphemeralModeConfig().IsAccountIdIncluded(account_id);
 }
 
 const std::string& ChromeUserManagerImpl::GetApplicationLocale() const {
diff --git a/chrome/browser/ash/login/users/user_manager_unittest.cc b/chrome/browser/ash/login/users/user_manager_unittest.cc
index 21a3d70d..3dd0f50 100644
--- a/chrome/browser/ash/login/users/user_manager_unittest.cc
+++ b/chrome/browser/ash/login/users/user_manager_unittest.cc
@@ -36,11 +36,13 @@
 #include "chromeos/ash/components/settings/cros_settings_names.h"
 #include "chromeos/ash/components/system/fake_statistics_provider.h"
 #include "components/account_id/account_id.h"
+#include "components/policy/core/common/device_local_account_type.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
+#include "components/user_manager/user_names.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/common/features/feature_session_type.h"
@@ -61,9 +63,10 @@
 
 constexpr char kDeviceLocalAccountId[] = "device_local_account";
 
-AccountId CreateDeviceLocalKioskAppAccountId(const std::string& account_id) {
-  return AccountId::FromUserEmail(policy::GenerateDeviceLocalAccountUserId(
-      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_KIOSK_APP));
+AccountId CreateDeviceLocalAccountId(const std::string& account_id,
+                                     policy::DeviceLocalAccount::Type type) {
+  return AccountId::FromUserEmail(
+      policy::GenerateDeviceLocalAccountUserId(account_id, type));
 }
 
 }  // namespace
@@ -231,6 +234,23 @@
                      kiosk_app_id))));
   }
 
+  // Should be used to setup device local accounts of `TYPE_PUBLIC_SESSION` and
+  // `TYPE_SAML_PUBLIC_SESSION` types.
+  void SetDeviceLocalPublicAccount(
+      const std::string& account_id,
+      policy::DeviceLocalAccount::Type type,
+      policy::DeviceLocalAccount::EphemeralMode ephemeral_mode) {
+    settings_helper_.Set(
+        kAccountsPrefDeviceLocalAccounts,
+        base::Value(base::Value::List().Append(
+            base::Value::Dict()
+                .Set(kAccountsPrefDeviceLocalAccountsKeyId, account_id)
+                .Set(kAccountsPrefDeviceLocalAccountsKeyType,
+                     static_cast<int>(type))
+                .Set(kAccountsPrefDeviceLocalAccountsKeyEphemeralMode,
+                     static_cast<int>(ephemeral_mode)))));
+  }
+
   void RetrieveTrustedDevicePolicies() {
     GetChromeUserManager()->RetrieveTrustedDevicePolicies();
   }
@@ -292,6 +312,89 @@
   EXPECT_FALSE(IsEphemeralAccountId(kOwnerAccountId));
 }
 
+// Tests that `IsEphemeralAccountId(account_id)` returns true when `account_id`
+// is a guest account id.
+TEST_F(UserManagerTest, IsEphemeralAccountIdTrueForGuestAccountId) {
+  EXPECT_TRUE(IsEphemeralAccountId(user_manager::GuestAccountId()));
+
+  SetDeviceSettings(
+      /* ephemeral_users_enabled= */ false,
+      /* owner= */ kOwnerAccountId.GetUserEmail());
+  RetrieveTrustedDevicePolicies();
+
+  EXPECT_TRUE(IsEphemeralAccountId(user_manager::GuestAccountId()));
+}
+
+// Tests that `IsEphemeralAccountId(account_id)` returns false when `account_id`
+// is a stub account id.
+TEST_F(UserManagerTest, IsEphemeralAccountIdFalseForStubAccountId) {
+  EXPECT_FALSE(IsEphemeralAccountId(user_manager::StubAccountId()));
+
+  SetDeviceSettings(
+      /* ephemeral_users_enabled= */ true,
+      /* owner= */ kOwnerAccountId.GetUserEmail());
+  RetrieveTrustedDevicePolicies();
+
+  EXPECT_FALSE(IsEphemeralAccountId(user_manager::StubAccountId()));
+}
+
+// Tests that `IsEphemeralAccountId(account_id)` returns false when `account_id`
+// is a stub ad account id.
+TEST_F(UserManagerTest, IsEphemeralAccountIdFalseForStubAdAccountId) {
+  EXPECT_FALSE(IsEphemeralAccountId(user_manager::StubAdAccountId()));
+
+  SetDeviceSettings(
+      /* ephemeral_users_enabled= */ true,
+      /* owner= */ kOwnerAccountId.GetUserEmail());
+  RetrieveTrustedDevicePolicies();
+
+  EXPECT_FALSE(IsEphemeralAccountId(user_manager::StubAdAccountId()));
+}
+
+// Tests that `IsEphemeralAccountId(account_id)` returns true when `account_id`
+// is a public account id.
+TEST_F(UserManagerTest, IsEphemeralAccountIdTrueForPublicAccountId) {
+  const AccountId public_accout_id = CreateDeviceLocalAccountId(
+      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION);
+
+  EXPECT_TRUE(IsEphemeralAccountId(public_accout_id));
+
+  // Set all ephemeral related policies to `false` to make sure that policies
+  // don't affect ephemeral mode of the public account.
+  SetDeviceSettings(
+      /* ephemeral_users_enabled= */ false,
+      /* owner= */ kOwnerAccountId.GetUserEmail());
+  SetDeviceLocalPublicAccount(
+      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION,
+      policy::DeviceLocalAccount::EphemeralMode::kDisable);
+  RetrieveTrustedDevicePolicies();
+
+  EXPECT_TRUE(IsEphemeralAccountId(public_accout_id));
+}
+
+// Tests that `IsEphemeralAccountId(account_id)` returns true when `account_id`
+// is a SAML public account id.
+TEST_F(UserManagerTest, IsEphemeralAccountIdTrueForSamlPublicAccountId) {
+  const AccountId saml_public_accout_id = CreateDeviceLocalAccountId(
+      kDeviceLocalAccountId,
+      policy::DeviceLocalAccount::TYPE_SAML_PUBLIC_SESSION);
+
+  EXPECT_TRUE(IsEphemeralAccountId(saml_public_accout_id));
+
+  // Set all ephemeral related policies to `false` to make sure that policies
+  // don't affect ephemeral mode of the SAML public account.
+  SetDeviceSettings(
+      /* ephemeral_users_enabled= */ false,
+      /* owner= */ kOwnerAccountId.GetUserEmail());
+  SetDeviceLocalPublicAccount(
+      kDeviceLocalAccountId,
+      policy::DeviceLocalAccount::TYPE_SAML_PUBLIC_SESSION,
+      policy::DeviceLocalAccount::EphemeralMode::kDisable);
+  RetrieveTrustedDevicePolicies();
+
+  EXPECT_TRUE(IsEphemeralAccountId(saml_public_accout_id));
+}
+
 // Tests that `UserManager` correctly parses device-wide ephemeral users policy
 // by calling `IsEphemeralAccountId(account_id)` function.
 TEST_F(UserManagerTest, IsEphemeralAccountIdUsesEphemeralUsersEnabledPolicy) {
@@ -310,8 +413,8 @@
 // `IsEphemeralAccountId(account_id)` function.
 TEST_F(UserManagerTest,
        IsEphemeralAccountIdRespectsFollowDeviceWidePolicyEphemeralMode) {
-  const AccountId account_id =
-      CreateDeviceLocalKioskAppAccountId(kDeviceLocalAccountId);
+  const AccountId account_id = CreateDeviceLocalAccountId(
+      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
 
   EXPECT_FALSE(IsEphemeralAccountId(account_id));
 
@@ -335,8 +438,8 @@
 // ephemeral mode equals to `kUnset` by calling
 // `IsEphemeralAccountId(account_id)` function.
 TEST_F(UserManagerTest, IsEphemeralAccountIdRespectsUnsetEphemeralMode) {
-  const AccountId account_id =
-      CreateDeviceLocalKioskAppAccountId(kDeviceLocalAccountId);
+  const AccountId account_id = CreateDeviceLocalAccountId(
+      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
 
   EXPECT_FALSE(IsEphemeralAccountId(account_id));
 
@@ -360,8 +463,8 @@
 // ephemeral mode equals to `kDisable` by calling
 // `IsEphemeralAccountId(account_id)` function.
 TEST_F(UserManagerTest, IsEphemeralAccountIdRespectsDisableEphemeralMode) {
-  const AccountId account_id =
-      CreateDeviceLocalKioskAppAccountId(kDeviceLocalAccountId);
+  const AccountId account_id = CreateDeviceLocalAccountId(
+      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
 
   EXPECT_FALSE(IsEphemeralAccountId(account_id));
 
@@ -380,9 +483,9 @@
 // Tests that `UserManager` correctly parses device-local accounts with
 // ephemeral mode equals to `kEnable` by calling
 // `IsEphemeralAccountId(account_id)` function.
-TEST_F(UserManagerTest, IsEphemeralAccountIdRespectsEnableEphemeralMode) {
-  const AccountId account_id =
-      CreateDeviceLocalKioskAppAccountId(kDeviceLocalAccountId);
+TEST_F(UserManagerTest, IsEphemeralAccountIdRespectssEnableEphemeralMode) {
+  const AccountId account_id = CreateDeviceLocalAccountId(
+      kDeviceLocalAccountId, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
 
   EXPECT_FALSE(IsEphemeralAccountId(account_id));
 
diff --git a/chrome/browser/ash/policy/dlp/files_policy_notification_manager.cc b/chrome/browser/ash/policy/dlp/files_policy_notification_manager.cc
index a086bb8..fd289f0 100644
--- a/chrome/browser/ash/policy/dlp/files_policy_notification_manager.cc
+++ b/chrome/browser/ash/policy/dlp/files_policy_notification_manager.cc
@@ -5,24 +5,42 @@
 #include "chrome/browser/ash/policy/dlp/files_policy_notification_manager.h"
 
 #include "base/check.h"
+#include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "chrome/browser/ash/file_manager/io_task.h"
+#include "chrome/browser/ash/file_manager/io_task_controller.h"
 #include "chrome/browser/ash/file_manager/url_util.h"
+#include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dialogs/files_policy_dialog.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "storage/browser/file_system/file_system_url.h"
 
 namespace policy {
 
 FilesPolicyNotificationManager::FilesPolicyNotificationManager(
-    content::BrowserContext* context) {
+    content::BrowserContext* context)
+    : context_(context) {
   DCHECK(context);
 
-  context_ = context;
+  file_manager::VolumeManager* const volume_manager =
+      file_manager::VolumeManager::Get(Profile::FromBrowserContext(context));
+  if (!volume_manager) {
+    LOG(ERROR) << "FilesPolicyNotificationManager failed to find "
+                  "file_manager::VolumeManager";
+    return;
+  }
+  auto* io_task_controller = volume_manager->io_task_controller();
+  if (!io_task_controller) {
+    LOG(ERROR) << "FilesPolicyNotificationManager failed to find "
+                  "file_manager::io_task::IOTaskController";
+    return;
+  }
+  io_tasks_observation_.Observe(io_task_controller);
 }
 
 FilesPolicyNotificationManager::~FilesPolicyNotificationManager() = default;
@@ -72,6 +90,32 @@
                                params);
 }
 
+bool FilesPolicyNotificationManager::HasIOTask(
+    file_manager::io_task::IOTaskId task_id) const {
+  return base::Contains(io_tasks_, task_id);
+}
+
+FilesPolicyNotificationManager::WarningInfo::WarningInfo(
+    std::vector<base::FilePath> files_paths,
+    Policy warning_reason)
+    : warning_reason(warning_reason) {
+  for (const auto& file_path : files_paths) {
+    files.emplace_back(file_path);
+  }
+}
+
+FilesPolicyNotificationManager::WarningInfo::WarningInfo(WarningInfo&& other) =
+    default;
+
+FilesPolicyNotificationManager::WarningInfo::~WarningInfo() = default;
+
+FilesPolicyNotificationManager::IOTaskInfo::IOTaskInfo() = default;
+
+FilesPolicyNotificationManager::IOTaskInfo::IOTaskInfo(IOTaskInfo&& other) =
+    default;
+
+FilesPolicyNotificationManager::IOTaskInfo::~IOTaskInfo() = default;
+
 void FilesPolicyNotificationManager::ShowFilesPolicyDialog(
     FilesDialogType type,
     absl::optional<policy::Policy> policy,
@@ -88,6 +132,11 @@
   // TODO(ayaelattar): Timeout after total 5 minutes.
 }
 
+void FilesPolicyNotificationManager::AddIOTask(
+    file_manager::io_task::IOTaskId task_id) {
+  io_tasks_.emplace(std::move(task_id), IOTaskInfo());
+}
+
 void FilesPolicyNotificationManager::OnBrowserSetLastActive(Browser* browser) {
   if (!ash::IsBrowserForSystemWebApp(browser,
                                      ash::SystemWebAppType::FILE_MANAGER)) {
@@ -106,4 +155,37 @@
   std::move(pending_callback_).Run(modal_parent);
 }
 
+void FilesPolicyNotificationManager::OnIOTaskStatus(
+    const file_manager::io_task::ProgressStatus& status) {
+  // Observe only Copy and Move tasks.
+  if (status.type != file_manager::io_task::OperationType::kCopy &&
+      status.type != file_manager::io_task::OperationType::kMove) {
+    return;
+  }
+
+  if (!HasIOTask(status.task_id) &&
+      status.state == file_manager::io_task::State::kQueued) {
+    AddIOTask(status.task_id);
+  } else if (HasIOTask(status.task_id) && status.IsCompleted()) {
+    // If it's in a terminal state, stop observing.
+    io_tasks_.erase(status.task_id);
+  }
+}
+
+std::vector<DlpConfidentialFile>
+FilesPolicyNotificationManager::GetWarningFiles(
+    file_manager::io_task::IOTaskId task_id,
+    Policy warning_reason) const {
+  if (!HasIOTask(task_id) || !io_tasks_.at(task_id).warning_info.has_value() ||
+      io_tasks_.at(task_id).warning_info->warning_reason != warning_reason) {
+    return {};
+  }
+  return io_tasks_.at(task_id).warning_info->files;
+}
+
+bool FilesPolicyNotificationManager::HasBlockedFiles(
+    file_manager::io_task::IOTaskId task_id) const {
+  return HasIOTask(task_id) && !io_tasks_.at(task_id).blocked_files.empty();
+}
+
 }  // namespace policy
diff --git a/chrome/browser/ash/policy/dlp/files_policy_notification_manager.h b/chrome/browser/ash/policy/dlp/files_policy_notification_manager.h
index fde9deb..dad31b02 100644
--- a/chrome/browser/ash/policy/dlp/files_policy_notification_manager.h
+++ b/chrome/browser/ash/policy/dlp/files_policy_notification_manager.h
@@ -6,7 +6,9 @@
 #define CHROME_BROWSER_ASH_POLICY_DLP_FILES_POLICY_NOTIFICATION_MANAGER_H_
 
 #include "base/functional/callback_forward.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/ash/file_manager/io_task.h"
+#include "chrome/browser/ash/file_manager/io_task_controller.h"
 #include "chrome/browser/chromeos/policy/dlp/dialogs/files_policy_dialog.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -21,8 +23,10 @@
 // FilesPolicyNotificationManager is responsible for showing block and warning
 // notifications/dialogs for files because of DLP and enterprise connectors
 // policies.
-class FilesPolicyNotificationManager : public KeyedService,
-                                       public BrowserListObserver {
+class FilesPolicyNotificationManager
+    : public KeyedService,
+      public BrowserListObserver,
+      public file_manager::io_task::IOTaskController::Observer {
  public:
   explicit FilesPolicyNotificationManager(content::BrowserContext* context);
 
@@ -39,23 +43,77 @@
                   FilesDialogType type,
                   absl::optional<policy::Policy> policy);
 
+  // Returns whether IO task is being tracked.
+  bool HasIOTask(file_manager::io_task::IOTaskId task_id) const;
+
  private:
+  // Holds all information related to IO task warning. Any extra information
+  // needed for custom messaging should be added here.
+  struct WarningInfo {
+    WarningInfo() = delete;
+    WarningInfo(std::vector<base::FilePath> files_paths, Policy warning_reason);
+    WarningInfo(WarningInfo&& other);
+    ~WarningInfo();
+
+    // Warning files.
+    std::vector<DlpConfidentialFile> files;
+    // Warning reason. There should be only one policy per warning as mixed
+    // warnings aren't supported.
+    Policy warning_reason;
+  };
+
+  // Holds needed information for each tracked IO task.
+  struct IOTaskInfo {
+    IOTaskInfo();
+    IOTaskInfo(IOTaskInfo&& other);
+    ~IOTaskInfo();
+
+    // Should have value only if there's warning.
+    absl::optional<WarningInfo> warning_info;
+    // A map of all files blocked to be transferred and the block reason for
+    // each.
+    std::map<DlpConfidentialFile, Policy> blocked_files;
+  };
+
   // Shows a FilesPolicyDialog.
   void ShowFilesPolicyDialog(FilesDialogType type,
                              absl::optional<policy::Policy> policy,
                              gfx::NativeWindow modal_parent);
 
+  // Starts tracking IO task with `task_id`.
+  void AddIOTask(file_manager::io_task::IOTaskId task_id);
+
   // BrowserListObserver overrides:
   // Called when opening a new Files App window to use as the modal parent for a
   // FilesPolicyDialog.
   void OnBrowserSetLastActive(Browser* browser) override;
 
+  // file_manager::io_task::IOTaskController::Observer overrides:
+  void OnIOTaskStatus(
+      const file_manager::io_task::ProgressStatus& status) override;
+
+  // Returns IO task warning files due to `warning_reason`.
+  std::vector<DlpConfidentialFile> GetWarningFiles(
+      file_manager::io_task::IOTaskId task_id,
+      Policy warning_reason) const;
+
+  // Returns whether IO task has any blocked file.
+  bool HasBlockedFiles(file_manager::io_task::IOTaskId task_id) const;
+
   // Callback to show a policy dialog after waiting to open a Files App window.
   base::OnceCallback<void(gfx::NativeWindow)> pending_callback_;
 
   // Context for which the FPNM is created.
   content::BrowserContext* context_;
 
+  // A map from tracked IO tasks ids to their info.
+  std::map<file_manager::io_task::IOTaskId, IOTaskInfo> io_tasks_;
+
+  // Observes IOTaskController to get updates about IO tasks.
+  base::ScopedObservation<file_manager::io_task::IOTaskController,
+                          file_manager::io_task::IOTaskController::Observer>
+      io_tasks_observation_{this};
+
   base::WeakPtrFactory<FilesPolicyNotificationManager> weak_factory_{this};
 };
 
diff --git a/chrome/browser/ash/policy/dlp/files_policy_notification_manager_unittest.cc b/chrome/browser/ash/policy/dlp/files_policy_notification_manager_unittest.cc
new file mode 100644
index 0000000..0b0f4e4
--- /dev/null
+++ b/chrome/browser/ash/policy/dlp/files_policy_notification_manager_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/policy/dlp/files_policy_notification_manager.h"
+
+#include "ash/webui/file_manager/url_constants.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/bind.h"
+#include "chrome/browser/ash/file_manager/copy_or_move_io_task.h"
+#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
+#include "chrome/browser/ash/file_manager/trash_io_task.h"
+#include "chrome/browser/ash/file_manager/volume_manager.h"
+#include "chrome/browser/ash/file_manager/volume_manager_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "storage/browser/quota/quota_manager_proxy.h"
+#include "storage/browser/test/test_file_system_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+bool CreateDummyFile(const base::FilePath& path) {
+  return WriteFile(path, "42", sizeof("42")) == sizeof("42");
+}
+
+}  // namespace
+
+class FilesPolicyNotificationManagerTest : public testing::Test {
+ public:
+  FilesPolicyNotificationManagerTest()
+      : profile_manager_(TestingBrowserProcess::GetGlobal()) {}
+  FilesPolicyNotificationManagerTest(
+      const FilesPolicyNotificationManagerTest&) = delete;
+  FilesPolicyNotificationManagerTest& operator=(
+      const FilesPolicyNotificationManagerTest&) = delete;
+  ~FilesPolicyNotificationManagerTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile("test-user");
+    file_manager::VolumeManagerFactory::GetInstance()->SetTestingFactory(
+        profile_,
+        base::BindLambdaForTesting([](content::BrowserContext* context) {
+          return std::unique_ptr<KeyedService>(
+              std::make_unique<file_manager::VolumeManager>(
+                  Profile::FromBrowserContext(context), nullptr, nullptr,
+                  ash::disks::DiskMountManager::GetInstance(), nullptr,
+                  file_manager::VolumeManager::GetMtpStorageInfoCallback()));
+        }));
+    ash::disks::DiskMountManager::InitializeForTesting(
+        new file_manager::FakeDiskMountManager);
+    file_manager::VolumeManager* const volume_manager =
+        file_manager::VolumeManager::Get(profile_);
+    ASSERT_TRUE(volume_manager);
+    io_task_controller_ = volume_manager->io_task_controller();
+    ASSERT_TRUE(io_task_controller_);
+    fpnm_ = std::make_unique<FilesPolicyNotificationManager>(profile_);
+
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    file_system_context_ = storage::CreateFileSystemContextForTesting(
+        /*quota_manager_proxy=*/nullptr, temp_dir_.GetPath());
+  }
+
+  void TearDown() override {
+    fpnm_ = nullptr;
+
+    profile_manager_.DeleteAllTestingProfiles();
+    ash::disks::DiskMountManager::Shutdown();
+  }
+
+  storage::FileSystemURL CreateFileSystemURL(const std::string& path) {
+    return storage::FileSystemURL::CreateForTest(
+        kTestStorageKey, storage::kFileSystemTypeLocal,
+        base::FilePath::FromUTF8Unsafe(path));
+  }
+
+ protected:
+  std::unique_ptr<FilesPolicyNotificationManager> fpnm_;
+  scoped_refptr<storage::FileSystemContext> file_system_context_;
+  raw_ptr<file_manager::io_task::IOTaskController> io_task_controller_;
+  content::BrowserTaskEnvironment task_environment_;
+  TestingProfileManager profile_manager_;
+  raw_ptr<TestingProfile> profile_;
+  base::ScopedTempDir temp_dir_;
+  const blink::StorageKey kTestStorageKey =
+      blink::StorageKey::CreateFromStringForTesting("chrome://abc");
+};
+
+TEST_F(FilesPolicyNotificationManagerTest, AddCopyTask) {
+  file_manager::io_task::IOTaskId task_id = 1;
+  base::FilePath src_file_path = temp_dir_.GetPath().AppendASCII("test1.txt");
+  ASSERT_TRUE(CreateDummyFile(src_file_path));
+  auto src_url = CreateFileSystemURL(src_file_path.value());
+  ASSERT_TRUE(src_url.is_valid());
+  auto dst_url = CreateFileSystemURL(temp_dir_.GetPath().value());
+
+  auto task = std::make_unique<file_manager::io_task::CopyOrMoveIOTask>(
+      file_manager::io_task::OperationType::kCopy,
+      std::vector<storage::FileSystemURL>({src_url}), dst_url, profile_,
+      file_system_context_);
+
+  io_task_controller_->Add(std::move(task));
+  EXPECT_TRUE(fpnm_->HasIOTask(task_id));
+
+  // Pause the task. It shouldn't be removed.
+  file_manager::io_task::PauseParams pause_params;
+  pause_params.policy_params.emplace(Policy::kDlp);
+  io_task_controller_->Pause(task_id, std::move(pause_params));
+  EXPECT_TRUE(fpnm_->HasIOTask(task_id));
+
+  // Once the task is complete, it should be removed.
+  io_task_controller_->Cancel(task_id);
+  EXPECT_FALSE(fpnm_->HasIOTask(task_id));
+}
+
+// Only Copy and move tasks are observed by FilesPolicyNotificationManager.
+TEST_F(FilesPolicyNotificationManagerTest, AddTrashTask) {
+  file_manager::io_task::IOTaskId task_id = 1;
+  base::FilePath src_file_path = temp_dir_.GetPath().AppendASCII("test1.txt");
+  ASSERT_TRUE(CreateDummyFile(src_file_path));
+  auto src_url = CreateFileSystemURL(src_file_path.value());
+  ASSERT_TRUE(src_url.is_valid());
+
+  auto task = std::make_unique<file_manager::io_task::TrashIOTask>(
+      std::vector<storage::FileSystemURL>({src_url}), profile_,
+      file_system_context_, base::FilePath());
+
+  io_task_controller_->Add(std::move(task));
+  EXPECT_FALSE(fpnm_->HasIOTask(task_id));
+
+  io_task_controller_->Cancel(task_id);
+  EXPECT_FALSE(fpnm_->HasIOTask(task_id));
+}
+
+}  // namespace policy
diff --git a/chrome/browser/ash/policy/login/login_profile_policy_provider.cc b/chrome/browser/ash/policy/login/login_profile_policy_provider.cc
index 15ccaa2..a598398 100644
--- a/chrome/browser/ash/policy/login/login_profile_policy_provider.cc
+++ b/chrome/browser/ash/policy/login/login_profile_policy_provider.cc
@@ -73,8 +73,14 @@
      key::kExtensionManifestV2Availability},
     {key::kDeviceLoginScreenPromptOnMultipleMatchingCertificates,
      key::kPromptOnMultipleMatchingCertificates},
+
+    // TODO(b:283960562): Remove the mapping to
+    // kContextAwareAccessSignalsAllowlist as part of the deprecation and
+    // cleanup of that policy.
     {key::kDeviceLoginScreenContextAwareAccessSignalsAllowlist,
      key::kContextAwareAccessSignalsAllowlist},
+    {key::kDeviceLoginScreenContextAwareAccessSignalsAllowlist,
+     key::kUserContextAwareAccessSignalsAllowlist},
 
     // key::kDeviceLoginScreenLocales maps to the ash::kDeviceLoginScreenLocales
     // CrosSetting elsewhere. Also map it to the key::kForcedLanguages policy in
diff --git a/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.cc b/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.cc
index 105a1407..27913703 100644
--- a/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.cc
+++ b/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.cc
@@ -30,8 +30,8 @@
   result->region_code = input->region_code;
   result->number_pad_present = Convert(input->number_pad_present);
   result->top_row_keys =
-      Convert<diagnostics::mojom::TopRowKey,
-              crosapi::mojom::TelemetryKeyboardTopRowKey>(input->top_row_keys);
+      ConvertVector<crosapi::mojom::TelemetryKeyboardTopRowKey>(
+          input->top_row_keys);
   result->top_right_key = Convert(input->top_right_key);
   result->has_assistant_key =
       crosapi::mojom::BoolValue::New(input->has_assistant_key);
diff --git a/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.h b/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.h
index fab7532..dc0e6f8 100644
--- a/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.h
+++ b/chrome/browser/ash/telemetry_extension/events/telemetry_event_service_converters.h
@@ -105,10 +105,10 @@
 cros_healthd::mojom::EventCategoryEnum Convert(
     crosapi::mojom::TelemetryEventCategoryEnum input);
 
-template <class InputT,
-          class OutputT,
+template <class OutputT,
+          class InputT,
           std::enable_if_t<std::is_enum_v<InputT>, bool> = true>
-std::vector<OutputT> Convert(std::vector<InputT> input) {
+std::vector<OutputT> ConvertVector(std::vector<InputT> input) {
   std::vector<OutputT> result;
   for (auto elem : input) {
     result.push_back(Convert(elem));
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index 33f1fa02..b500e26 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -33,7 +33,6 @@
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -382,50 +381,6 @@
             mLanguageCode = languageCode;
         }
 
-        /**
-         * Builds a profile with the given values, assuming those are reviewed by the user and thus
-         * are marked {@link VerificationStatus.USER_VERIFIED}.
-         */
-        public AutofillProfile(String guid, boolean isLocal, @Source int source,
-                String honorificPrefix, String fullName, String companyName, String streetAddress,
-                String region, String locality, String dependentLocality, String postalCode,
-                String sortingCode, String countryCode, String phoneNumber, String emailAddress,
-                String languageCode) {
-            this(guid, isLocal, source,
-                    new ValueWithStatus(honorificPrefix, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(fullName, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(companyName, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(streetAddress, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(region, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(locality, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(dependentLocality, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(postalCode, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(sortingCode, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(countryCode, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(phoneNumber, VerificationStatus.USER_VERIFIED),
-                    new ValueWithStatus(emailAddress, VerificationStatus.USER_VERIFIED),
-                    languageCode);
-        }
-
-        /**
-         * Builds an empty local profile with country code from the default locale.
-         * All other fields are empty strings with {@link VerificationStatus.NO_STATUS},
-         * because JNI does not handle null strings.
-         */
-        public AutofillProfile() {
-            this("" /* guid */, true /* isLocal */, Source.LOCAL_OR_SYNCABLE,
-                    ValueWithStatus.EMPTY /* honorificPrefix */,
-                    ValueWithStatus.EMPTY /* fullName */, ValueWithStatus.EMPTY /* companyName */,
-                    ValueWithStatus.EMPTY /* streetAddress */, ValueWithStatus.EMPTY /* region */,
-                    ValueWithStatus.EMPTY /* locality */,
-                    ValueWithStatus.EMPTY /* dependentLocality */,
-                    ValueWithStatus.EMPTY /* postalCode */, ValueWithStatus.EMPTY /* sortingCode */,
-                    new ValueWithStatus(Locale.getDefault().getCountry(),
-                            VerificationStatus.USER_VERIFIED) /* country */,
-                    ValueWithStatus.EMPTY /* phoneNumber */,
-                    ValueWithStatus.EMPTY /* emailAddress */, "" /* languageCode */);
-        }
-
         /* Builds an AutofillProfile that is an exact copy of the one passed as parameter. */
         public AutofillProfile(AutofillProfile profile) {
             mGUID = profile.getGUID();
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index 30b0f81e..979d030 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -427,8 +427,11 @@
       }
       if (source == FillingSource::AUTOFILL)
         continue;  // Autofill suggestions have no sheet.
-      absl::optional<AccessorySheetData> sheet =
-          GetControllerForFillingSource(source)->GetSheetData();
+      AccessoryController* controller = GetControllerForFillingSource(source);
+      if (!controller) {
+        continue;  // Most-likely, the controller was cleaned up already.
+      }
+      absl::optional<AccessorySheetData> sheet = controller->GetSheetData();
       if (sheet.has_value())
         view_->OnItemsAvailable(std::move(sheet.value()));
     }
diff --git a/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java b/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
index 3d905f3c1..5946d2b9 100644
--- a/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
+++ b/chrome/browser/autofill/test/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java
@@ -402,7 +402,7 @@
     @SmallTest
     @Feature({"Autofill"})
     public void testValuesSetInProfileGainUserVerifiedStatus() {
-        AutofillProfile profile = new AutofillProfile();
+        AutofillProfile profile = AutofillProfile.builder().build();
         Assert.assertEquals(VerificationStatus.NO_STATUS, profile.getFullNameStatus());
         Assert.assertEquals(VerificationStatus.NO_STATUS, profile.getStreetAddressStatus());
         Assert.assertEquals(VerificationStatus.NO_STATUS, profile.getLocalityStatus());
diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.cc b/chrome/browser/bookmarks/android/bookmark_bridge.cc
index d1514b0..251b078eb 100644
--- a/chrome/browser/bookmarks/android/bookmark_bridge.cc
+++ b/chrome/browser/bookmarks/android/bookmark_bridge.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <cmath>
 #include <memory>
 #include <queue>
 #include <string>
@@ -22,6 +23,7 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/i18n/string_compare.h"
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/uuid.h"
@@ -490,10 +492,16 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(partner_bookmarks_shim_->IsLoaded());
+  if (!partner_bookmarks_shim_->IsLoaded()) {
+    return nullptr;
+  }
 
   const BookmarkNode* partner_node =
       partner_bookmarks_shim_->GetPartnerBookmarksRoot();
+  if (!partner_node) {
+    return nullptr;
+  }
+
   ScopedJavaLocalRef<jobject> folder_id_obj = JavaBookmarkIdCreateBookmarkId(
       env, partner_node->id(), GetBookmarkType(partner_node));
   return folder_id_obj;
diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.cc b/chrome/browser/bookmarks/chrome_bookmark_client.cc
index f85ea28..247da10 100644
--- a/chrome/browser/bookmarks/chrome_bookmark_client.cc
+++ b/chrome/browser/bookmarks/chrome_bookmark_client.cc
@@ -119,6 +119,13 @@
   return managed_bookmark_service_->GetLoadManagedNodeCallback();
 }
 
+bookmarks::metrics::StorageStateForUma
+ChromeBookmarkClient::GetStorageStateForUma() {
+  return bookmark_sync_service_->IsTrackingMetadata()
+             ? bookmarks::metrics::StorageStateForUma::kSyncEnabled
+             : bookmarks::metrics::StorageStateForUma::kLocalOnly;
+}
+
 bool ChromeBookmarkClient::CanSetPermanentNodeTitle(
     const bookmarks::BookmarkNode* permanent_node) {
   return !managed_bookmark_service_ ||
diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.h b/chrome/browser/bookmarks/chrome_bookmark_client.h
index f62aeabb..5a38917b 100644
--- a/chrome/browser/bookmarks/chrome_bookmark_client.h
+++ b/chrome/browser/bookmarks/chrome_bookmark_client.h
@@ -60,6 +60,7 @@
   bool IsPermanentNodeVisibleWhenEmpty(
       bookmarks::BookmarkNode::Type type) override;
   bookmarks::LoadManagedNodeCallback GetLoadManagedNodeCallback() override;
+  bookmarks::metrics::StorageStateForUma GetStorageStateForUma() override;
   bool CanSetPermanentNodeTitle(
       const bookmarks::BookmarkNode* permanent_node) override;
   bool CanSyncNode(const bookmarks::BookmarkNode* node) override;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 63dc07a..1576082 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -43,9 +43,6 @@
         <include name="IDR_CHROME_APP_MANIFEST" file="resources\chrome_app\manifest.json" type="BINDATA" />
         <include name="IDR_URL_MOJOM_LITE_JS" file="${root_gen_dir}\url\mojom\url.mojom-lite.js" use_base_dir="false" type="BINDATA" />
 
-        <!-- Shared images for ChromeOS WebUIs -->
-        <include name="IDR_CHROME_OS_AN_ERROR_OCCURRED_SVG" file="resources\chromeos\images\an_error_occurred.svg" type="BINDATA" />
-        <include name="IDR_CHROME_OS_NO_NETWORK_SVG" file="resources\chromeos\images\no_network.svg" type="BINDATA" />
         <!-- Edu Coexistence account login resources -->
         <include name="IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_HTML" file="resources\chromeos\edu_coexistence\edu_coexistence.html" type="BINDATA" />
         <include name="IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_APP_JS" file="${root_gen_dir}\chrome\browser\resources\chromeos\edu_coexistence\edu_coexistence_app.js" use_base_dir="false" type="BINDATA" />
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 0bfc0c9..1b9db89 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -532,7 +532,7 @@
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 #include "chrome/browser/enterprise/chrome_browser_main_extra_parts_enterprise.h"
-#include "chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h"
 #include "chrome/browser/ui/webui/app_settings/web_app_settings_navigation_throttle.h"
 #endif
 
@@ -4993,7 +4993,7 @@
   MaybeAddThrottle(
       WebAppSettingsNavigationThrottle::MaybeCreateThrottleFor(handle),
       &throttles);
-  MaybeAddThrottle(profile_token_management::ProfileTokenNavigationThrottle::
+  MaybeAddThrottle(profile_management::ProfileManagementNavigationThrottle::
                        MaybeCreateThrottleFor(handle),
                    &throttles);
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/chrome_process_singleton.cc b/chrome/browser/chrome_process_singleton.cc
index bf6e58f6..c4755243 100644
--- a/chrome/browser/chrome_process_singleton.cc
+++ b/chrome/browser/chrome_process_singleton.cc
@@ -55,6 +55,7 @@
   const version_info::Channel channel = chrome::GetChannel();
   if (channel != version_info::Channel::CANARY &&
       channel != version_info::Channel::DEV &&
+      channel != version_info::Channel::BETA &&
       channel != version_info::Channel::UNKNOWN) {
     return kEarlySingletonDefaultGroup;
   }
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn b/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn
index 5b89b9de..e30ed74 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/telemetry/api/events/BUILD.gn
@@ -92,6 +92,7 @@
     "//base",
     "//chrome/common/chromeos/extensions/api",
     "//chromeos/crosapi/mojom",
+    "//third_party/abseil-cpp:absl",
   ]
 }
 
@@ -139,6 +140,7 @@
     ":event_converters",
     "//chrome/common/chromeos/extensions/api",
     "//chromeos/crosapi/mojom",
+    "//testing/gmock",
     "//testing/gtest",
   ]
 }
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.cc b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.cc
index 472cee46..23d831e 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.cc
@@ -4,9 +4,14 @@
 
 #include "chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.h"
 
+#include <cstdint>
+
 #include "base/notreached.h"
 #include "chrome/common/chromeos/extensions/api/events.h"
+#include "chromeos/crosapi/mojom/nullable_primitives.mojom.h"
 #include "chromeos/crosapi/mojom/telemetry_event_service.mojom.h"
+#include "chromeos/crosapi/mojom/telemetry_keyboard_event.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos::converters {
 
@@ -29,6 +34,48 @@
   return result;
 }
 
+cx_events::KeyboardInfo UncheckedConvertPtr(
+    crosapi::TelemetryKeyboardInfoPtr ptr) {
+  cx_events::KeyboardInfo result;
+
+  result.id = ConvertStructPtr<absl::optional<uint32_t>>(std::move(ptr->id));
+  result.connection_type = Convert(ptr->connection_type);
+  result.name = std::move(ptr->name);
+  result.physical_layout = Convert(ptr->physical_layout);
+  result.mechanical_layout = Convert(ptr->mechanical_layout);
+  result.region_code = std::move(ptr->region_code);
+  result.number_pad_present = Convert(ptr->number_pad_present);
+  if (ptr->top_row_keys) {
+    result.top_row_keys =
+        ConvertVector<cx_events::KeyboardTopRowKey>(ptr->top_row_keys.value());
+  }
+  result.top_right_key = Convert(ptr->top_right_key);
+  if (ptr->has_assistant_key) {
+    result.has_assistant_key = ptr->has_assistant_key->value;
+  }
+
+  return result;
+}
+
+cx_events::KeyboardDiagnosticEventInfo UncheckedConvertPtr(
+    crosapi::TelemetryKeyboardDiagnosticEventInfoPtr ptr) {
+  cx_events::KeyboardDiagnosticEventInfo result;
+
+  result.keyboard_info =
+      ConvertStructPtr<cx_events::KeyboardInfo>(std::move(ptr->keyboard_info));
+
+  if (ptr->tested_keys) {
+    result.tested_keys = ConvertVector<int>(ptr->tested_keys.value());
+  }
+
+  if (ptr->tested_top_row_keys) {
+    result.tested_top_row_keys =
+        ConvertVector<int>(ptr->tested_top_row_keys.value());
+  }
+
+  return result;
+}
+
 cx_events::LidEventInfo UncheckedConvertPtr(
     crosapi::TelemetryLidEventInfoPtr ptr) {
   cx_events::LidEventInfo result;
@@ -70,6 +117,10 @@
   return result;
 }
 
+absl::optional<uint32_t> UncheckedConvertPtr(crosapi::UInt32ValuePtr ptr) {
+  return ptr->value;
+}
+
 }  // namespace unchecked
 
 cx_events::AudioJackEvent Convert(
@@ -98,6 +149,140 @@
   NOTREACHED();
 }
 
+cx_events::KeyboardConnectionType Convert(
+    crosapi::TelemetryKeyboardConnectionType input) {
+  switch (input) {
+    case crosapi::TelemetryKeyboardConnectionType::kUnmappedEnumField:
+      return cx_events::KeyboardConnectionType::kNone;
+    case crosapi::TelemetryKeyboardConnectionType::kInternal:
+      return cx_events::KeyboardConnectionType::kInternal;
+    case crosapi::TelemetryKeyboardConnectionType::kUsb:
+      return cx_events::KeyboardConnectionType::kUsb;
+    case crosapi::TelemetryKeyboardConnectionType::kBluetooth:
+      return cx_events::KeyboardConnectionType::kBluetooth;
+    case crosapi::TelemetryKeyboardConnectionType::kUnknown:
+      return cx_events::KeyboardConnectionType::kUnknown;
+  }
+  NOTREACHED();
+}
+
+cx_events::PhysicalKeyboardLayout Convert(
+    crosapi::TelemetryKeyboardPhysicalLayout input) {
+  switch (input) {
+    case crosapi::TelemetryKeyboardPhysicalLayout::kUnmappedEnumField:
+      return cx_events::PhysicalKeyboardLayout::kNone;
+    case crosapi::TelemetryKeyboardPhysicalLayout::kUnknown:
+      return cx_events::PhysicalKeyboardLayout::kUnknown;
+    case crosapi::TelemetryKeyboardPhysicalLayout::kChromeOS:
+      return cx_events::PhysicalKeyboardLayout::kChromeOs;
+  }
+  NOTREACHED();
+}
+
+cx_events::MechanicalKeyboardLayout Convert(
+    crosapi::TelemetryKeyboardMechanicalLayout input) {
+  switch (input) {
+    case crosapi::TelemetryKeyboardMechanicalLayout::kUnmappedEnumField:
+      return cx_events::MechanicalKeyboardLayout::kNone;
+    case crosapi::TelemetryKeyboardMechanicalLayout::kUnknown:
+      return cx_events::MechanicalKeyboardLayout::kUnknown;
+    case crosapi::TelemetryKeyboardMechanicalLayout::kAnsi:
+      return cx_events::MechanicalKeyboardLayout::kAnsi;
+    case crosapi::TelemetryKeyboardMechanicalLayout::kIso:
+      return cx_events::MechanicalKeyboardLayout::kIso;
+    case crosapi::TelemetryKeyboardMechanicalLayout::kJis:
+      return cx_events::MechanicalKeyboardLayout::kJis;
+  }
+  NOTREACHED();
+}
+
+cx_events::KeyboardNumberPadPresence Convert(
+    crosapi::TelemetryKeyboardNumberPadPresence input) {
+  switch (input) {
+    case crosapi::TelemetryKeyboardNumberPadPresence::kUnmappedEnumField:
+      return cx_events::KeyboardNumberPadPresence::kNone;
+    case crosapi::TelemetryKeyboardNumberPadPresence::kUnknown:
+      return cx_events::KeyboardNumberPadPresence::kUnknown;
+    case crosapi::TelemetryKeyboardNumberPadPresence::kPresent:
+      return cx_events::KeyboardNumberPadPresence::kPresent;
+    case crosapi::TelemetryKeyboardNumberPadPresence::kNotPresent:
+      return cx_events::KeyboardNumberPadPresence::kNotPresent;
+  }
+  NOTREACHED();
+}
+
+cx_events::KeyboardTopRowKey Convert(
+    crosapi::TelemetryKeyboardTopRowKey input) {
+  switch (input) {
+    case crosapi::TelemetryKeyboardTopRowKey::kUnmappedEnumField:
+      return cx_events::KeyboardTopRowKey::kNone;
+    case crosapi::TelemetryKeyboardTopRowKey::kNone:
+      return cx_events::KeyboardTopRowKey::kNoKey;
+    case crosapi::TelemetryKeyboardTopRowKey::kUnknown:
+      return cx_events::KeyboardTopRowKey::kUnknown;
+    case crosapi::TelemetryKeyboardTopRowKey::kBack:
+      return cx_events::KeyboardTopRowKey::kBack;
+    case crosapi::TelemetryKeyboardTopRowKey::kForward:
+      return cx_events::KeyboardTopRowKey::kForward;
+    case crosapi::TelemetryKeyboardTopRowKey::kRefresh:
+      return cx_events::KeyboardTopRowKey::kRefresh;
+    case crosapi::TelemetryKeyboardTopRowKey::kFullscreen:
+      return cx_events::KeyboardTopRowKey::kFullscreen;
+    case crosapi::TelemetryKeyboardTopRowKey::kOverview:
+      return cx_events::KeyboardTopRowKey::kOverview;
+    case crosapi::TelemetryKeyboardTopRowKey::kScreenshot:
+      return cx_events::KeyboardTopRowKey::kScreenshot;
+    case crosapi::TelemetryKeyboardTopRowKey::kScreenBrightnessDown:
+      return cx_events::KeyboardTopRowKey::kScreenBrightnessDown;
+    case crosapi::TelemetryKeyboardTopRowKey::kScreenBrightnessUp:
+      return cx_events::KeyboardTopRowKey::kScreenBrightnessUp;
+    case crosapi::TelemetryKeyboardTopRowKey::kPrivacyScreenToggle:
+      return cx_events::KeyboardTopRowKey::kPrivacyScreenToggle;
+    case crosapi::TelemetryKeyboardTopRowKey::kMicrophoneMute:
+      return cx_events::KeyboardTopRowKey::kMicrophoneMute;
+    case crosapi::TelemetryKeyboardTopRowKey::kVolumeMute:
+      return cx_events::KeyboardTopRowKey::kVolumeMute;
+    case crosapi::TelemetryKeyboardTopRowKey::kVolumeDown:
+      return cx_events::KeyboardTopRowKey::kVolumeDown;
+    case crosapi::TelemetryKeyboardTopRowKey::kVolumeUp:
+      return cx_events::KeyboardTopRowKey::kVolumeUp;
+    case crosapi::TelemetryKeyboardTopRowKey::kKeyboardBacklightToggle:
+      return cx_events::KeyboardTopRowKey::kKeyboardBacklightToggle;
+    case crosapi::TelemetryKeyboardTopRowKey::kKeyboardBacklightDown:
+      return cx_events::KeyboardTopRowKey::kKeyboardBacklightDown;
+    case crosapi::TelemetryKeyboardTopRowKey::kKeyboardBacklightUp:
+      return cx_events::KeyboardTopRowKey::kKeyboardBacklightUp;
+    case crosapi::TelemetryKeyboardTopRowKey::kNextTrack:
+      return cx_events::KeyboardTopRowKey::kNextTrack;
+    case crosapi::TelemetryKeyboardTopRowKey::kPreviousTrack:
+      return cx_events::KeyboardTopRowKey::kPreviousTrack;
+    case crosapi::TelemetryKeyboardTopRowKey::kPlayPause:
+      return cx_events::KeyboardTopRowKey::kPlayPause;
+    case crosapi::TelemetryKeyboardTopRowKey::kScreenMirror:
+      return cx_events::KeyboardTopRowKey::kScreenMirror;
+    case crosapi::TelemetryKeyboardTopRowKey::kDelete:
+      return cx_events::KeyboardTopRowKey::kDelete;
+  }
+  NOTREACHED();
+}
+
+cx_events::KeyboardTopRightKey Convert(
+    crosapi::TelemetryKeyboardTopRightKey input) {
+  switch (input) {
+    case crosapi::TelemetryKeyboardTopRightKey::kUnmappedEnumField:
+      return cx_events::KeyboardTopRightKey::kNone;
+    case crosapi::TelemetryKeyboardTopRightKey::kUnknown:
+      return cx_events::KeyboardTopRightKey::kUnknown;
+    case crosapi::TelemetryKeyboardTopRightKey::kPower:
+      return cx_events::KeyboardTopRightKey::kPower;
+    case crosapi::TelemetryKeyboardTopRightKey::kLock:
+      return cx_events::KeyboardTopRightKey::kLock;
+    case crosapi::TelemetryKeyboardTopRightKey::kControlPanel:
+      return cx_events::KeyboardTopRightKey::kControlPanel;
+  }
+  NOTREACHED();
+}
+
 cx_events::LidEvent Convert(crosapi::TelemetryLidEventInfo::State state) {
   switch (state) {
     case crosapi::TelemetryLidEventInfo_State::kUnmappedEnumField:
@@ -164,8 +349,14 @@
       return crosapi::TelemetryEventCategoryEnum::kSdCard;
     case cx_events::EventCategory::kPower:
       return crosapi::TelemetryEventCategoryEnum::kPower;
+    case cx_events::EventCategory::kKeyboardDiagnostic:
+      return crosapi::TelemetryEventCategoryEnum::kKeyboardDiagnostic;
   }
   NOTREACHED();
 }
 
+int Convert(uint32_t input) {
+  return static_cast<int>(input);
+}
+
 }  // namespace chromeos::converters
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.h b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.h
index cca6a59..313cd53 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.h
+++ b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.h
@@ -5,10 +5,15 @@
 #ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_TELEMETRY_API_EVENTS_EVENTS_API_CONVERTERS_H_
 #define CHROME_BROWSER_CHROMEOS_EXTENSIONS_TELEMETRY_API_EVENTS_EVENTS_API_CONVERTERS_H_
 
+#include <cstdint>
+#include <type_traits>
 #include <utility>
 
 #include "chrome/common/chromeos/extensions/api/events.h"
+#include "chromeos/crosapi/mojom/nullable_primitives.mojom.h"
 #include "chromeos/crosapi/mojom/telemetry_event_service.mojom.h"
+#include "chromeos/crosapi/mojom/telemetry_keyboard_event.mojom.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace chromeos::converters {
 
@@ -17,6 +22,12 @@
 api::os_events::AudioJackEventInfo UncheckedConvertPtr(
     crosapi::mojom::TelemetryAudioJackEventInfoPtr ptr);
 
+api::os_events::KeyboardInfo UncheckedConvertPtr(
+    crosapi::mojom::TelemetryKeyboardInfoPtr ptr);
+
+api::os_events::KeyboardDiagnosticEventInfo UncheckedConvertPtr(
+    crosapi::mojom::TelemetryKeyboardDiagnosticEventInfoPtr ptr);
+
 api::os_events::LidEventInfo UncheckedConvertPtr(
     crosapi::mojom::TelemetryLidEventInfoPtr ptr);
 
@@ -29,6 +40,9 @@
 api::os_events::PowerEventInfo UncheckedConvertPtr(
     crosapi::mojom::TelemetryPowerEventInfoPtr ptr);
 
+absl::optional<uint32_t> UncheckedConvertPtr(
+    crosapi::mojom::UInt32ValuePtr ptr);
+
 }  // namespace unchecked
 
 api::os_events::AudioJackEvent Convert(
@@ -37,6 +51,24 @@
 api::os_events::AudioJackDeviceType Convert(
     crosapi::mojom::TelemetryAudioJackEventInfo::DeviceType device_type);
 
+api::os_events::KeyboardConnectionType Convert(
+    crosapi::mojom::TelemetryKeyboardConnectionType input);
+
+api::os_events::PhysicalKeyboardLayout Convert(
+    crosapi::mojom::TelemetryKeyboardPhysicalLayout input);
+
+api::os_events::MechanicalKeyboardLayout Convert(
+    crosapi::mojom::TelemetryKeyboardMechanicalLayout input);
+
+api::os_events::KeyboardNumberPadPresence Convert(
+    crosapi::mojom::TelemetryKeyboardNumberPadPresence input);
+
+api::os_events::KeyboardTopRowKey Convert(
+    crosapi::mojom::TelemetryKeyboardTopRowKey input);
+
+api::os_events::KeyboardTopRightKey Convert(
+    crosapi::mojom::TelemetryKeyboardTopRightKey input);
+
 api::os_events::LidEvent Convert(
     crosapi::mojom::TelemetryLidEventInfo::State state);
 
@@ -52,6 +84,20 @@
 crosapi::mojom::TelemetryEventCategoryEnum Convert(
     api::os_events::EventCategory input);
 
+int Convert(uint32_t input);
+
+template <class OutputT,
+          class InputT,
+          std::enable_if_t<std::is_enum_v<InputT> || std::is_integral_v<InputT>,
+                           bool> = true>
+std::vector<OutputT> ConvertVector(std::vector<InputT> input) {
+  std::vector<OutputT> output;
+  for (auto elem : input) {
+    output.push_back(Convert(std::move(elem)));
+  }
+  return output;
+}
+
 template <class OutputT, class InputT>
 OutputT ConvertStructPtr(InputT input) {
   return (!input.is_null()) ? unchecked::UncheckedConvertPtr(std::move(input))
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters_unittest.cc b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters_unittest.cc
index 0c962c9..e919b57 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters_unittest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters_unittest.cc
@@ -5,7 +5,10 @@
 #include "chrome/browser/chromeos/extensions/telemetry/api/events/events_api_converters.h"
 
 #include "chrome/common/chromeos/extensions/api/events.h"
+#include "chromeos/crosapi/mojom/nullable_primitives.mojom.h"
 #include "chromeos/crosapi/mojom/telemetry_event_service.mojom.h"
+#include "chromeos/crosapi/mojom/telemetry_keyboard_event.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos::converters {
@@ -45,6 +48,162 @@
       cx_events::AudioJackDeviceType::kMicrophone);
 }
 
+TEST(TelemetryEventServiceConvertersTest, ConvertKeyboardConnectionType) {
+  EXPECT_EQ(
+      Convert(crosapi::TelemetryKeyboardConnectionType::kUnmappedEnumField),
+      cx_events::KeyboardConnectionType::kNone);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardConnectionType::kInternal),
+            cx_events::KeyboardConnectionType::kInternal);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardConnectionType::kUsb),
+            cx_events::KeyboardConnectionType::kUsb);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardConnectionType::kBluetooth),
+            cx_events::KeyboardConnectionType::kBluetooth);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardConnectionType::kUnknown),
+            cx_events::KeyboardConnectionType::kUnknown);
+}
+
+TEST(TelemetryEventServiceConvertersTest, ConvertKeyboardPhysicalLayout) {
+  EXPECT_EQ(
+      Convert(crosapi::TelemetryKeyboardPhysicalLayout::kUnmappedEnumField),
+      cx_events::PhysicalKeyboardLayout::kNone);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardPhysicalLayout::kUnknown),
+            cx_events::PhysicalKeyboardLayout::kUnknown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardPhysicalLayout::kChromeOS),
+            cx_events::PhysicalKeyboardLayout::kChromeOs);
+}
+
+TEST(TelemetryEventServiceConvertersTest, ConvertKeyboardMechanicalLayout) {
+  EXPECT_EQ(
+      Convert(crosapi::TelemetryKeyboardMechanicalLayout::kUnmappedEnumField),
+      cx_events::MechanicalKeyboardLayout::kNone);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardMechanicalLayout::kUnknown),
+            cx_events::MechanicalKeyboardLayout::kUnknown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardMechanicalLayout::kAnsi),
+            cx_events::MechanicalKeyboardLayout::kAnsi);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardMechanicalLayout::kIso),
+            cx_events::MechanicalKeyboardLayout::kIso);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardMechanicalLayout::kJis),
+            cx_events::MechanicalKeyboardLayout::kJis);
+}
+
+TEST(TelemetryEventServiceConvertersTest, ConvertKeyboardNumberPadPresence) {
+  EXPECT_EQ(
+      Convert(crosapi::TelemetryKeyboardNumberPadPresence::kUnmappedEnumField),
+      cx_events::KeyboardNumberPadPresence::kNone);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardNumberPadPresence::kUnknown),
+            cx_events::KeyboardNumberPadPresence::kUnknown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardNumberPadPresence::kPresent),
+            cx_events::KeyboardNumberPadPresence::kPresent);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardNumberPadPresence::kNotPresent),
+            cx_events::KeyboardNumberPadPresence::kNotPresent);
+}
+
+TEST(TelemetryEventServiceConvertersTest, ConvertKeyboardTopRowKey) {
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kUnmappedEnumField),
+            cx_events::KeyboardTopRowKey::kNone);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kNone),
+            cx_events::KeyboardTopRowKey::kNoKey);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kUnknown),
+            cx_events::KeyboardTopRowKey::kUnknown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kBack),
+            cx_events::KeyboardTopRowKey::kBack);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kForward),
+            cx_events::KeyboardTopRowKey::kForward);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kRefresh),
+            cx_events::KeyboardTopRowKey::kRefresh);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kFullscreen),
+            cx_events::KeyboardTopRowKey::kFullscreen);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kOverview),
+            cx_events::KeyboardTopRowKey::kOverview);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kScreenshot),
+            cx_events::KeyboardTopRowKey::kScreenshot);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kScreenBrightnessDown),
+            cx_events::KeyboardTopRowKey::kScreenBrightnessDown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kScreenBrightnessUp),
+            cx_events::KeyboardTopRowKey::kScreenBrightnessUp);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kPrivacyScreenToggle),
+            cx_events::KeyboardTopRowKey::kPrivacyScreenToggle);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kMicrophoneMute),
+            cx_events::KeyboardTopRowKey::kMicrophoneMute);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kVolumeMute),
+            cx_events::KeyboardTopRowKey::kVolumeMute);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kVolumeDown),
+            cx_events::KeyboardTopRowKey::kVolumeDown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kVolumeUp),
+            cx_events::KeyboardTopRowKey::kVolumeUp);
+
+  EXPECT_EQ(
+      Convert(crosapi::TelemetryKeyboardTopRowKey::kKeyboardBacklightToggle),
+      cx_events::KeyboardTopRowKey::kKeyboardBacklightToggle);
+
+  EXPECT_EQ(
+      Convert(crosapi::TelemetryKeyboardTopRowKey::kKeyboardBacklightDown),
+      cx_events::KeyboardTopRowKey::kKeyboardBacklightDown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kKeyboardBacklightUp),
+            cx_events::KeyboardTopRowKey::kKeyboardBacklightUp);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kNextTrack),
+            cx_events::KeyboardTopRowKey::kNextTrack);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kPreviousTrack),
+            cx_events::KeyboardTopRowKey::kPreviousTrack);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kPlayPause),
+            cx_events::KeyboardTopRowKey::kPlayPause);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kScreenMirror),
+            cx_events::KeyboardTopRowKey::kScreenMirror);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRowKey::kDelete),
+            cx_events::KeyboardTopRowKey::kDelete);
+}
+
+TEST(TelemetryEventServiceConvertersTest, ConvertKeyboardTopRightKey) {
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRightKey::kUnmappedEnumField),
+            cx_events::KeyboardTopRightKey::kNone);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRightKey::kUnknown),
+            cx_events::KeyboardTopRightKey::kUnknown);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRightKey::kPower),
+            cx_events::KeyboardTopRightKey::kPower);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRightKey::kLock),
+            cx_events::KeyboardTopRightKey::kLock);
+
+  EXPECT_EQ(Convert(crosapi::TelemetryKeyboardTopRightKey::kControlPanel),
+            cx_events::KeyboardTopRightKey::kControlPanel);
+}
+
 TEST(TelemetryExtensionEventsApiConvertersUnitTest, ConvertLidState) {
   EXPECT_EQ(Convert(crosapi::TelemetryLidEventInfo::State::kUnmappedEnumField),
             cx_events::LidEvent::kNone);
@@ -115,6 +274,134 @@
 
   EXPECT_EQ(Convert(cx_events::EventCategory::kPower),
             crosapi::TelemetryEventCategoryEnum::kPower);
+
+  EXPECT_EQ(Convert(cx_events::EventCategory::kKeyboardDiagnostic),
+            crosapi::TelemetryEventCategoryEnum::kKeyboardDiagnostic);
+}
+
+TEST(TelemetryExtensionEventsApiConvertersUnitTest, ConvertKeyboardInfo) {
+  constexpr int kId = 1;
+  constexpr char kName[] = "TESTNAME";
+  constexpr char kRegionCode[] = "de";
+
+  auto input = crosapi::TelemetryKeyboardInfo::New();
+  input->id = crosapi::UInt32Value::New(kId);
+  input->connection_type = crosapi::TelemetryKeyboardConnectionType::kBluetooth;
+  input->name = kName;
+  input->physical_layout = crosapi::TelemetryKeyboardPhysicalLayout::kChromeOS;
+  input->mechanical_layout = crosapi::TelemetryKeyboardMechanicalLayout::kAnsi;
+  input->region_code = kRegionCode;
+  input->number_pad_present =
+      crosapi::TelemetryKeyboardNumberPadPresence::kPresent;
+  input->top_row_keys = {crosapi::TelemetryKeyboardTopRowKey::kBack,
+                         crosapi::TelemetryKeyboardTopRowKey::kForward};
+  input->top_right_key = crosapi::TelemetryKeyboardTopRightKey::kPower;
+  input->has_assistant_key = crosapi::BoolValue::New(true);
+
+  auto result = ConvertStructPtr<cx_events::KeyboardInfo>(std::move(input));
+
+  ASSERT_TRUE(result.id.has_value());
+  EXPECT_EQ(*result.id, kId);
+
+  EXPECT_EQ(result.connection_type,
+            cx_events::KeyboardConnectionType::kBluetooth);
+
+  ASSERT_TRUE(result.name.has_value());
+  EXPECT_EQ(*result.name, kName);
+
+  EXPECT_EQ(result.physical_layout,
+            cx_events::PhysicalKeyboardLayout::kChromeOs);
+  EXPECT_EQ(result.mechanical_layout,
+            cx_events::MechanicalKeyboardLayout::kAnsi);
+
+  ASSERT_TRUE(result.region_code.has_value());
+  EXPECT_EQ(*result.region_code, kRegionCode);
+
+  EXPECT_EQ(result.number_pad_present,
+            cx_events::KeyboardNumberPadPresence::kPresent);
+
+  ASSERT_EQ(result.top_row_keys.size(), 2UL);
+  EXPECT_THAT(result.top_row_keys,
+              testing::ElementsAre(cx_events::KeyboardTopRowKey::kBack,
+                                   cx_events::KeyboardTopRowKey::kForward));
+
+  EXPECT_EQ(result.top_right_key, cx_events::KeyboardTopRightKey::kPower);
+
+  ASSERT_TRUE(result.has_assistant_key.has_value());
+  EXPECT_TRUE(result.has_assistant_key.value());
+}
+
+TEST(TelemetryExtensionEventsApiConvertersUnitTest,
+     ConvertKeyboardDiagnosticEventInfo) {
+  constexpr int kId = 1;
+  constexpr char kName[] = "TESTNAME";
+  constexpr char kRegionCode[] = "de";
+
+  const std::vector<uint32_t> kTestedKeys = {1, 2, 3, 4, 5, 6};
+  const std::vector<uint32_t> kTestedTopRowKeys = {7, 8, 9, 10, 11, 12};
+
+  auto keyboard = crosapi::TelemetryKeyboardInfo::New();
+  keyboard->id = crosapi::UInt32Value::New(kId);
+  keyboard->connection_type =
+      crosapi::TelemetryKeyboardConnectionType::kBluetooth;
+  keyboard->name = kName;
+  keyboard->physical_layout =
+      crosapi::TelemetryKeyboardPhysicalLayout::kChromeOS;
+  keyboard->mechanical_layout =
+      crosapi::TelemetryKeyboardMechanicalLayout::kAnsi;
+  keyboard->region_code = kRegionCode;
+  keyboard->number_pad_present =
+      crosapi::TelemetryKeyboardNumberPadPresence::kPresent;
+  keyboard->top_row_keys = {crosapi::TelemetryKeyboardTopRowKey::kBack,
+                            crosapi::TelemetryKeyboardTopRowKey::kForward};
+  keyboard->top_right_key = crosapi::TelemetryKeyboardTopRightKey::kPower;
+  keyboard->has_assistant_key = crosapi::BoolValue::New(true);
+
+  auto input = crosapi::TelemetryKeyboardDiagnosticEventInfo::New();
+  input->keyboard_info = std::move(keyboard);
+  input->tested_keys = kTestedKeys;
+  input->tested_top_row_keys = kTestedTopRowKeys;
+
+  auto result = ConvertStructPtr<cx_events::KeyboardDiagnosticEventInfo>(
+      std::move(input));
+
+  ASSERT_TRUE(result.keyboard_info.has_value());
+
+  auto keyboard_info_result = std::move(result.keyboard_info.value());
+  ASSERT_TRUE(keyboard_info_result.id.has_value());
+  EXPECT_EQ(*keyboard_info_result.id, kId);
+
+  EXPECT_EQ(keyboard_info_result.connection_type,
+            cx_events::KeyboardConnectionType::kBluetooth);
+
+  ASSERT_TRUE(keyboard_info_result.name.has_value());
+  EXPECT_EQ(*keyboard_info_result.name, kName);
+
+  EXPECT_EQ(keyboard_info_result.physical_layout,
+            cx_events::PhysicalKeyboardLayout::kChromeOs);
+  EXPECT_EQ(keyboard_info_result.mechanical_layout,
+            cx_events::MechanicalKeyboardLayout::kAnsi);
+
+  ASSERT_TRUE(keyboard_info_result.region_code.has_value());
+  EXPECT_EQ(*keyboard_info_result.region_code, kRegionCode);
+
+  EXPECT_EQ(keyboard_info_result.number_pad_present,
+            cx_events::KeyboardNumberPadPresence::kPresent);
+
+  ASSERT_EQ(keyboard_info_result.top_row_keys.size(), 2UL);
+  EXPECT_THAT(keyboard_info_result.top_row_keys,
+              testing::ElementsAre(cx_events::KeyboardTopRowKey::kBack,
+                                   cx_events::KeyboardTopRowKey::kForward));
+
+  EXPECT_EQ(keyboard_info_result.top_right_key,
+            cx_events::KeyboardTopRightKey::kPower);
+
+  ASSERT_TRUE(keyboard_info_result.has_assistant_key.has_value());
+  EXPECT_TRUE(keyboard_info_result.has_assistant_key.value());
+
+  EXPECT_THAT(result.tested_keys, testing::ElementsAre(1, 2, 3, 4, 5, 6));
+  EXPECT_THAT(result.tested_top_row_keys,
+              testing::ElementsAre(7, 8, 9, 10, 11, 12));
 }
 
 TEST(TelemetryExtensionEventsApiConvertersUnitTest, ConvertAudioJackEventInfo) {
diff --git a/chrome/browser/companion/core/companion_metrics_logger.cc b/chrome/browser/companion/core/companion_metrics_logger.cc
index d811bb9..d135848f 100644
--- a/chrome/browser/companion/core/companion_metrics_logger.cc
+++ b/chrome/browser/companion/core/companion_metrics_logger.cc
@@ -24,6 +24,9 @@
 // surface can have for UMA/UKM collection.
 const int kMaxNumChildElements = 10;
 
+// The max count of text queries to report up to for UKM.
+const int kMaxNumTextSearches = 10;
+
 // Helper method to determine whether a UI surface is a list surface. List
 // surfaces are surfaces that take the form of a list with one or more items
 // inside it, e.g page entities.
@@ -191,7 +194,9 @@
   // Text search.
   auto iter = ui_surface_metrics_.find(UiSurface::kSearchBox);
   if (iter != ui_surface_metrics_.end()) {
-    ukm_builder.SetTextSearchCount(iter->second.click_count);
+    ukm_builder.SetTextSearchCount(
+        std::clamp(iter->second.click_count, 0u,
+                   static_cast<unsigned int>(kMaxNumTextSearches)));
   }
 
   // Region search.
diff --git a/chrome/browser/companion/core/companion_metrics_logger.h b/chrome/browser/companion/core/companion_metrics_logger.h
index 816695eb..e05db27 100644
--- a/chrome/browser/companion/core/companion_metrics_logger.h
+++ b/chrome/browser/companion/core/companion_metrics_logger.h
@@ -59,7 +59,7 @@
   size_t child_element_shown_count = kInvalidNumChildren;
 
   // The number of times user clicked on the surface.
-  size_t click_count = 0;
+  uint32_t click_count = 0;
 
   // The position of the clicked UI element within its parent list. Applicable
   // to surfaces that show a list.
diff --git a/chrome/browser/companion/core/companion_metrics_logger_unittest.cc b/chrome/browser/companion/core/companion_metrics_logger_unittest.cc
index 6c70a28..d1cf0752 100644
--- a/chrome/browser/companion/core/companion_metrics_logger_unittest.cc
+++ b/chrome/browser/companion/core/companion_metrics_logger_unittest.cc
@@ -89,6 +89,14 @@
   ExpectUkmEntry(ukm::builders::Companion_PageView::kTextSearchCountName, 1);
 }
 
+TEST_F(CompanionMetricsLoggerTest, TextSearchMaxClamp) {
+  for (auto i = 1; i <= 11; i++) {
+    logger_->RecordUiSurfaceClicked(UiSurface::kSearchBox, kInvalidPosition);
+  }
+  logger_.reset();
+  ExpectUkmEntry(ukm::builders::Companion_PageView::kTextSearchCountName, 10);
+}
+
 TEST_F(CompanionMetricsLoggerTest, RegionSearchClicks) {
   base::HistogramTester histogram_tester;
 
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index a29bc27..60186ac 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -2683,16 +2683,9 @@
 // This tests checks that window is correctly initialized when DevTools is
 // opened while navigation through history with forward and back actions.
 // (crbug.com/627407)
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_LACROS)
-// Flaky on MAC and lacros https://crbug.com/1443360
-#define MAYBE_TestWindowInitializedOnNavigateBack \
-  DISABLED_TestWindowInitializedOnNavigateBack
-#else
-#define MAYBE_TestWindowInitializedOnNavigateBack \
-  TestWindowInitializedOnNavigateBack
-#endif
+// TODO(https://crbug.com/1443360): Deflake and re-enable this test.
 IN_PROC_BROWSER_TEST_F(DevToolsTest,
-                       MAYBE_TestWindowInitializedOnNavigateBack) {
+                       DISABLED_TestWindowInitializedOnNavigateBack) {
   TestChromeWebUIControllerFactory test_factory;
   content::ScopedWebUIControllerFactoryRegistration factory_registration(
       &test_factory);
diff --git a/chrome/browser/download/download_item_model_unittest.cc b/chrome/browser/download/download_item_model_unittest.cc
index ab79d34..1b3dab0 100644
--- a/chrome/browser/download/download_item_model_unittest.cc
+++ b/chrome/browser/download/download_item_model_unittest.cc
@@ -566,6 +566,15 @@
         .WillByDefault(Return(test_case.mixed_content_status));
     EXPECT_EQ(base::UTF16ToUTF8(model().GetStatusText()),
               test_case.expected_bubble_status_msg);
+#if !BUILDFLAG(IS_ANDROID)
+    // Android doesn't have BubbleUI info.
+    // Whether it's v2 or not doesn't affect the primary button, so it doesn't
+    // matter what we pass here.
+    EXPECT_EQ(model()
+                  .GetBubbleUIInfo(/*is_download_bubble_v2=*/false)
+                  .primary_button_command.value(),
+              DownloadCommands::Command::KEEP);
+#endif  // !BUILDFLAG(IS_ANDROID)
   }
 
   const struct DangerTypeTestCase {
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index e74adb8..6e4195f 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -952,6 +952,7 @@
     case download::DownloadItem::InsecureDownloadStatus::BLOCK:
     case download::DownloadItem::InsecureDownloadStatus::WARN:
       return DownloadUIModel::BubbleUIInfo()
+          .AddPrimaryButton(DownloadCommands::Command::KEEP)
           .AddSubpageSummary(l10n_util::GetStringUTF16(
               IDS_DOWNLOAD_BUBBLE_WARNING_SUBPAGE_SUMMARY_INSECURE))
           .AddIconAndColor(vector_icons::kNotSecureWarningIcon,
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn
index 85a49c3..fc592724 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/core/network/BUILD.gn
@@ -2,50 +2,67 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("network") {
+source_set("common") {
   public = [
     "key_network_delegate.h",
     "key_upload_request.h",
-    "mojo_key_network_delegate.h",
     "util.h",
   ]
 
   sources = [
     "key_upload_request.cc",
-    "mojo_key_network_delegate.cc",
     "util.cc",
   ]
 
   public_deps = [
+    "//base",
     "//third_party/abseil-cpp:absl",
     "//url",
   ]
 
   deps = [
-    "//base",
-    "//chrome/browser/enterprise/connectors/device_trust/common",
     "//chrome/browser/enterprise/connectors/device_trust/key_management/core",
     "//components/policy/proto",
     "//crypto",
+  ]
+}
+
+source_set("network") {
+  public = [ "mojo_key_network_delegate.h" ]
+
+  sources = [ "mojo_key_network_delegate.cc" ]
+
+  public_deps = [
+    ":common",
+    "//base",
+    "//net",
+    "//url",
+  ]
+
+  deps = [
+    "//chrome/browser/enterprise/connectors/device_trust/common",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/platform",
     "//services/network/public/cpp",
     "//services/network/public/mojom",
-    "//url",
   ]
+}
 
-  if (is_win) {
-    public += [ "win_key_network_delegate.h" ]
+if (is_win) {
+  source_set("win_key_network_delegate") {
+    public = [ "win_key_network_delegate.h" ]
 
-    sources += [ "win_key_network_delegate.cc" ]
+    sources = [ "win_key_network_delegate.cc" ]
 
-    public_deps += [ "//components/winhttp" ]
-
-    deps += [
-      "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network/fetcher",
+    public_deps = [
+      ":common",
+      "//base",
+      "//components/winhttp",
       "//net",
       "//url",
     ]
+
+    deps = [ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network/fetcher" ]
   }
 }
 
@@ -56,7 +73,7 @@
   sources = [ "mock_key_network_delegate.cc" ]
 
   public_deps = [
-    ":network",
+    ":common",
     "//testing/gmock",
     "//url",
   ]
@@ -67,6 +84,7 @@
   sources = [ "mojo_key_network_delegate_unittest.cc" ]
 
   deps = [
+    ":common",
     ":network",
     ":test_support",
     "//base",
@@ -80,6 +98,9 @@
   if (is_win) {
     sources += [ "win_key_network_delegate_unittest.cc" ]
 
-    deps += [ "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network/fetcher:test_support" ]
+    deps += [
+      ":win_key_network_delegate",
+      "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network/fetcher:test_support",
+    ]
   }
 }
diff --git a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/BUILD.gn b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/BUILD.gn
index ab43e31..f8b79cae 100644
--- a/chrome/browser/enterprise/connectors/device_trust/key_management/installer/BUILD.gn
+++ b/chrome/browser/enterprise/connectors/device_trust/key_management/installer/BUILD.gn
@@ -18,7 +18,7 @@
 
   deps = [
     "//chrome/browser/enterprise/connectors/device_trust/key_management/core",
-    "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network",
+    "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network:common",
     "//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence",
     "//components/policy/proto",
     "//crypto",
diff --git a/chrome/browser/enterprise/profile_management/profile_management_features.cc b/chrome/browser/enterprise/profile_management/profile_management_features.cc
index e9e5dddf..b6f43a0a 100644
--- a/chrome/browser/enterprise/profile_management/profile_management_features.cc
+++ b/chrome/browser/enterprise/profile_management/profile_management_features.cc
@@ -6,10 +6,14 @@
 
 #include "build/build_config.h"
 
-namespace profile_management {
+namespace profile_management::features {
 
 BASE_FEATURE(kThirdPartyProfileManagement,
              "ThirdPartyProfileManagement",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-}  // namespace profile_management
+BASE_FEATURE(kEnableProfileTokenManagement,
+             "EnableProfileTokenManagement",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+}  // namespace profile_management::features
diff --git a/chrome/browser/enterprise/profile_management/profile_management_features.h b/chrome/browser/enterprise/profile_management/profile_management_features.h
index 8d11dc2..ce15f9a 100644
--- a/chrome/browser/enterprise/profile_management/profile_management_features.h
+++ b/chrome/browser/enterprise/profile_management/profile_management_features.h
@@ -8,11 +8,14 @@
 #include "base/feature_list.h"
 #include "build/build_config.h"
 
-namespace profile_management {
+namespace profile_management::features {
 
 // Controls whether third-party profile management is enabled.
 BASE_DECLARE_FEATURE(kThirdPartyProfileManagement);
 
-}  // namespace profile_management
+// Controls whether token-based profile management is enabled.
+BASE_DECLARE_FEATURE(kEnableProfileTokenManagement);
+
+}  // namespace profile_management::features
 
 #endif  // CHROME_BROWSER_ENTERPRISE_PROFILE_MANAGEMENT_PROFILE_MANAGEMENT_FEATURES_H_
diff --git a/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.cc b/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.cc
similarity index 71%
rename from chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.cc
rename to chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.cc
index 0949084..7add5c4 100644
--- a/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.cc
+++ b/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h"
 
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/enterprise/profile_token_management/token_management_features.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_features.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/profile_token_web_signin_interceptor.h"
@@ -21,7 +21,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
-namespace profile_token_management {
+namespace profile_management {
 
 constexpr char kTestHost[] = "www.google.com";
 
@@ -32,7 +32,7 @@
 };
 
 class QueryParamTokenInfoGetter
-    : public ProfileTokenNavigationThrottle::TokenInfoGetter {
+    : public ProfileManagementNavigationThrottle::TokenInfoGetter {
  public:
   QueryParamTokenInfoGetter() = default;
   ~QueryParamTokenInfoGetter() override = default;
@@ -54,11 +54,12 @@
 
 }  // namespace
 
-ProfileTokenNavigationThrottle::TokenInfoGetter::~TokenInfoGetter() = default;
+ProfileManagementNavigationThrottle::TokenInfoGetter::~TokenInfoGetter() =
+    default;
 
 // static
-std::unique_ptr<ProfileTokenNavigationThrottle>
-ProfileTokenNavigationThrottle::MaybeCreateThrottleFor(
+std::unique_ptr<ProfileManagementNavigationThrottle>
+ProfileManagementNavigationThrottle::MaybeCreateThrottleFor(
     content::NavigationHandle* navigation_handle) {
   if (!base::FeatureList::IsEnabled(features::kEnableProfileTokenManagement) ||
       !g_browser_process->local_state() ||
@@ -66,11 +67,11 @@
     return nullptr;
   }
 
-  return std::make_unique<ProfileTokenNavigationThrottle>(
+  return std::make_unique<ProfileManagementNavigationThrottle>(
       navigation_handle, std::make_unique<QueryParamTokenInfoGetter>());
 }
 
-ProfileTokenNavigationThrottle::ProfileTokenNavigationThrottle(
+ProfileManagementNavigationThrottle::ProfileManagementNavigationThrottle(
     content::NavigationHandle* navigation_handle,
     std::unique_ptr<TokenInfoGetter> token_info_getter)
     : NavigationThrottle(navigation_handle),
@@ -78,26 +79,28 @@
   DCHECK(token_info_getter_);
 }
 
-ProfileTokenNavigationThrottle::~ProfileTokenNavigationThrottle() = default;
+ProfileManagementNavigationThrottle::~ProfileManagementNavigationThrottle() =
+    default;
 
 content::NavigationThrottle::ThrottleCheckResult
-ProfileTokenNavigationThrottle::WillProcessResponse() {
+ProfileManagementNavigationThrottle::WillProcessResponse() {
   auto host = navigation_handle()->GetURL().host();
   if (base::Contains(kSupportedHosts, host)) {
     token_info_getter_->GetTokenInfo(
         navigation_handle(),
-        base::BindOnce(&ProfileTokenNavigationThrottle::OnTokenInfoReceived,
-                       weak_ptr_factory_.GetWeakPtr()));
+        base::BindOnce(
+            &ProfileManagementNavigationThrottle::OnTokenInfoReceived,
+            weak_ptr_factory_.GetWeakPtr()));
     return DEFER;
   }
   return PROCEED;
 }
 
-const char* ProfileTokenNavigationThrottle::GetNameForLogging() {
-  return "ProfileTokenNavigationThrottle";
+const char* ProfileManagementNavigationThrottle::GetNameForLogging() {
+  return "ProfileManagementNavigationThrottle";
 }
 
-void ProfileTokenNavigationThrottle::OnTokenInfoReceived(
+void ProfileManagementNavigationThrottle::OnTokenInfoReceived(
     const std::string& id,
     const std::string& management_token) {
   if (!management_token.empty()) {
@@ -111,4 +114,4 @@
   Resume();
 }
 
-}  // namespace profile_token_management
+}  // namespace profile_management
diff --git a/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h b/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h
new file mode 100644
index 0000000..4e882c0
--- /dev/null
+++ b/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h
@@ -0,0 +1,73 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_PROFILE_MANAGEMENT_PROFILE_MANAGEMENT_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_ENTERPRISE_PROFILE_MANAGEMENT_PROFILE_MANAGEMENT_NAVIGATION_THROTTLE_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+class NavigationHandle;
+}
+
+namespace profile_management {
+
+extern const char kTestHost[];
+
+// This throttle looks for profile management data in certain HTTP responses
+// from supported hosts. If a response from a supported host is received, the
+// navigation is deferred while trying to retrieve the data from the response
+// body. If profile management data is found, this throttle triggers signin
+// interception.
+class ProfileManagementNavigationThrottle : public content::NavigationThrottle {
+ public:
+  class TokenInfoGetter {
+   public:
+    virtual ~TokenInfoGetter();
+    // Gets the profile id and enrollment token from `navigation_handle` and
+    // and calls `callback` with them. Both values will be empty strings if no
+    // info is found.
+    virtual void GetTokenInfo(
+        content::NavigationHandle* navigation_handle,
+        base::OnceCallback<void(const std::string&, const std::string&)>
+            callback) = 0;
+  };
+
+  // Create a navigation throttle for the given navigation if third-party
+  // profile management is enabled. Returns nullptr if no throttling should be
+  // done.
+  static std::unique_ptr<ProfileManagementNavigationThrottle>
+  MaybeCreateThrottleFor(content::NavigationHandle* navigation_handle);
+
+  ProfileManagementNavigationThrottle(
+      content::NavigationHandle* navigation_handle,
+      std::unique_ptr<TokenInfoGetter> token_info_getter);
+  ~ProfileManagementNavigationThrottle() override;
+  ProfileManagementNavigationThrottle(
+      const ProfileManagementNavigationThrottle&) = delete;
+  ProfileManagementNavigationThrottle& operator=(
+      const ProfileManagementNavigationThrottle&) = delete;
+
+  // content::NavigationThrottle implementation:
+  // Looks for profile management data in the navigation response if the host is
+  // supported.
+  content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
+      override;
+
+  const char* GetNameForLogging() override;
+
+ private:
+  void OnTokenInfoReceived(const std::string& id,
+                           const std::string& management_token);
+  std::unique_ptr<TokenInfoGetter> token_info_getter_;
+  base::WeakPtrFactory<ProfileManagementNavigationThrottle> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace profile_management
+
+#endif  // CHROME_BROWSER_ENTERPRISE_PROFILE_MANAGEMENT_PROFILE_MANAGEMENT_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle_unittest.cc b/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle_unittest.cc
similarity index 74%
rename from chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle_unittest.cc
rename to chrome/browser/enterprise/profile_management/profile_management_navigation_throttle_unittest.cc
index c5827801..fcf055f 100644
--- a/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle_unittest.cc
+++ b/chrome/browser/enterprise/profile_management/profile_management_navigation_throttle_unittest.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_navigation_throttle.h"
 
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/enterprise/profile_token_management/token_management_features.h"
+#include "chrome/browser/enterprise/profile_management/profile_management_features.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
@@ -16,14 +16,14 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace profile_token_management {
+namespace profile_management {
 
 using testing::_;
 
 namespace {
 
 class MockTokenInfoGetter
-    : public ProfileTokenNavigationThrottle::TokenInfoGetter {
+    : public ProfileManagementNavigationThrottle::TokenInfoGetter {
  public:
   MockTokenInfoGetter() = default;
   ~MockTokenInfoGetter() override = default;
@@ -38,11 +38,12 @@
 
 }  // namespace
 
-class ProfileTokenNavigationThrottleTest : public BrowserWithTestWindowTest {
+class ProfileManagementNavigationThrottleTest
+    : public BrowserWithTestWindowTest {
  public:
-  ProfileTokenNavigationThrottleTest() = default;
+  ProfileManagementNavigationThrottleTest() = default;
 
-  ~ProfileTokenNavigationThrottleTest() override = default;
+  ~ProfileManagementNavigationThrottleTest() override = default;
 
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
@@ -59,7 +60,7 @@
   }
 };
 
-TEST_F(ProfileTokenNavigationThrottleTest,
+TEST_F(ProfileManagementNavigationThrottleTest,
        ProfileTokenManagementFeatureDisabled) {
   base::test::ScopedFeatureList features;
   features.InitAndDisableFeature(features::kEnableProfileTokenManagement);
@@ -68,11 +69,11 @@
                                             main_frame());
 
   auto throttle =
-      ProfileTokenNavigationThrottle::MaybeCreateThrottleFor(&test_handle);
+      ProfileManagementNavigationThrottle::MaybeCreateThrottleFor(&test_handle);
   ASSERT_EQ(nullptr, throttle.get());
 }
 
-TEST_F(ProfileTokenNavigationThrottleTest, ProfileCreationDisallowed) {
+TEST_F(ProfileManagementNavigationThrottleTest, ProfileCreationDisallowed) {
   base::test::ScopedFeatureList features(
       features::kEnableProfileTokenManagement);
   g_browser_process->local_state()->SetBoolean(prefs::kBrowserAddPersonEnabled,
@@ -83,11 +84,12 @@
                                             main_frame());
 
   auto throttle =
-      ProfileTokenNavigationThrottle::MaybeCreateThrottleFor(&test_handle);
+      ProfileManagementNavigationThrottle::MaybeCreateThrottleFor(&test_handle);
   ASSERT_EQ(nullptr, throttle.get());
 }
 
-TEST_F(ProfileTokenNavigationThrottleTest, NoThrottlingWithUnsupportedHost) {
+TEST_F(ProfileManagementNavigationThrottleTest,
+       NoThrottlingWithUnsupportedHost) {
   base::test::ScopedFeatureList features(
       features::kEnableProfileTokenManagement);
   content::MockNavigationHandle test_handle(GURL("https://notasupported.host/"),
@@ -96,14 +98,14 @@
       std::make_unique<MockTokenInfoGetter>();
   EXPECT_CALL(*mock_info_getter, GetTokenInfo(_, _)).Times(0);
 
-  auto throttle = std::make_unique<ProfileTokenNavigationThrottle>(
+  auto throttle = std::make_unique<ProfileManagementNavigationThrottle>(
       &test_handle, std::move(mock_info_getter));
 
   EXPECT_EQ(content::NavigationThrottle::PROCEED,
             throttle->WillProcessResponse().action());
 }
 
-TEST_F(ProfileTokenNavigationThrottleTest, ThrottlingWithSupportedHost) {
+TEST_F(ProfileManagementNavigationThrottleTest, ThrottlingWithSupportedHost) {
   base::test::ScopedFeatureList features(
       features::kEnableProfileTokenManagement);
   content::MockNavigationHandle test_handle(
@@ -112,11 +114,11 @@
       std::make_unique<MockTokenInfoGetter>();
   EXPECT_CALL(*mock_info_getter, GetTokenInfo(&test_handle, _)).Times(1);
 
-  auto throttle = std::make_unique<ProfileTokenNavigationThrottle>(
+  auto throttle = std::make_unique<ProfileManagementNavigationThrottle>(
       &test_handle, std::move(mock_info_getter));
 
   EXPECT_EQ(content::NavigationThrottle::DEFER,
             throttle->WillProcessResponse().action());
 }
 
-}  // namespace profile_token_management
+}  // namespace profile_management
diff --git a/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.h b/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.h
deleted file mode 100644
index 5facce7..0000000
--- a/chrome/browser/enterprise/profile_token_management/profile_token_navigation_throttle.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ENTERPRISE_PROFILE_TOKEN_MANAGEMENT_PROFILE_TOKEN_NAVIGATION_THROTTLE_H_
-#define CHROME_BROWSER_ENTERPRISE_PROFILE_TOKEN_MANAGEMENT_PROFILE_TOKEN_NAVIGATION_THROTTLE_H_
-
-#include <memory>
-
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/navigation_throttle.h"
-
-namespace content {
-class NavigationHandle;
-}
-
-namespace profile_token_management {
-
-extern const char kTestHost[];
-
-// This throttle looks for an enrollment token in a web page from a supported
-// host. If a navigation matches a supported webhost, the navigation is deferred
-// while trying to get an enrollment token and profile id. Whether an
-// enrollement token is found the navigation continues and a signin interception
-// is triggered if an enrollment token is found.
-class ProfileTokenNavigationThrottle : public content::NavigationThrottle {
- public:
-  class TokenInfoGetter {
-   public:
-    virtual ~TokenInfoGetter();
-    // Gets the profile id and enrollment token from `navigation_handle` and
-    // and calls `callback` with them. Both values will be empty strings if no
-    // info is found.
-    virtual void GetTokenInfo(
-        content::NavigationHandle* navigation_handle,
-        base::OnceCallback<void(const std::string&, const std::string&)>
-            callback) = 0;
-  };
-
-  // Create a navigation throttle for the given navigation if device trust is
-  // enabled. Returns nullptr if no throttling should be done.
-  static std::unique_ptr<ProfileTokenNavigationThrottle> MaybeCreateThrottleFor(
-      content::NavigationHandle* navigation_handle);
-
-  ProfileTokenNavigationThrottle(
-      content::NavigationHandle* navigation_handle,
-      std::unique_ptr<TokenInfoGetter> token_info_getter);
-  ~ProfileTokenNavigationThrottle() override;
-  ProfileTokenNavigationThrottle(const ProfileTokenNavigationThrottle&) =
-      delete;
-  ProfileTokenNavigationThrottle& operator=(
-      const ProfileTokenNavigationThrottle&) = delete;
-
-  // content::NavigationThrottle implementation:
-  // Looks for an enrollment token in the navigation if the host is supported.
-  content::NavigationThrottle::ThrottleCheckResult WillProcessResponse()
-      override;
-
-  const char* GetNameForLogging() override;
-
- private:
-  void OnTokenInfoReceived(const std::string& id,
-                           const std::string& management_token);
-  std::unique_ptr<TokenInfoGetter> token_info_getter_;
-  base::WeakPtrFactory<ProfileTokenNavigationThrottle> weak_ptr_factory_{this};
-};
-
-}  // namespace profile_token_management
-
-#endif  // CHROME_BROWSER_ENTERPRISE_PROFILE_TOKEN_MANAGEMENT_PROFILE_TOKEN_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/enterprise/profile_token_management/token_management_features.cc b/chrome/browser/enterprise/profile_token_management/token_management_features.cc
deleted file mode 100644
index f8a2d97..0000000
--- a/chrome/browser/enterprise/profile_token_management/token_management_features.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/enterprise/profile_token_management/token_management_features.h"
-
-#include "base/feature_list.h"
-
-namespace profile_token_management::features {
-
-BASE_FEATURE(kEnableProfileTokenManagement,
-             "EnableProfileTokenManagement",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
-}  // namespace profile_token_management::features
diff --git a/chrome/browser/enterprise/profile_token_management/token_management_features.h b/chrome/browser/enterprise/profile_token_management/token_management_features.h
deleted file mode 100644
index 6289633d..0000000
--- a/chrome/browser/enterprise/profile_token_management/token_management_features.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2023 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_ENTERPRISE_PROFILE_TOKEN_MANAGEMENT_TOKEN_MANAGEMENT_FEATURES_H_
-#define CHROME_BROWSER_ENTERPRISE_PROFILE_TOKEN_MANAGEMENT_TOKEN_MANAGEMENT_FEATURES_H_
-
-#include "base/feature_list.h"
-
-namespace profile_token_management::features {
-
-BASE_DECLARE_FEATURE(kEnableProfileTokenManagement);
-
-}  // namespace profile_token_management::features
-
-#endif  // CHROME_BROWSER_ENTERPRISE_PROFILE_TOKEN_MANAGEMENT_TOKEN_MANAGEMENT_FEATURES_H_
diff --git a/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.cc b/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.cc
new file mode 100644
index 0000000..97ce5bc
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.cc
@@ -0,0 +1,78 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h"
+
+#include "base/no_destructor.h"
+#include "base/time/time.h"
+#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_report_generator.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_selections.h"
+
+namespace enterprise_reporting {
+
+LegacyTechService::LegacyTechService(Profile* profile,
+                                     LegacyTechReportTrigger trigger)
+    : url_matcher_(profile), trigger_(trigger) {
+  DCHECK(trigger_);
+}
+
+LegacyTechService::~LegacyTechService() = default;
+
+void LegacyTechService::ReportEvent(const std::string& type,
+                                    const GURL& url,
+                                    const std::string& filename,
+                                    uint64_t line,
+                                    uint64_t column) const {
+  absl::optional<std::string> matched_url = url_matcher_.GetMatchedURL(url);
+  if (!matched_url) {
+    return;
+  }
+
+  LegacyTechReportGenerator::LegacyTechData data = {
+      type,
+      /*timestamp=*/base::Time::Now(),
+      url,
+      *matched_url,
+      filename,
+      line,
+      column};
+
+  trigger_.Run(data);
+}
+
+// static
+LegacyTechServiceFactory* LegacyTechServiceFactory::GetInstance() {
+  static base::NoDestructor<LegacyTechServiceFactory> instance;
+  return instance.get();
+}
+
+// static
+LegacyTechService* LegacyTechServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<LegacyTechService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, /*create=*/true));
+}
+
+KeyedService* LegacyTechServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+
+  // Legacy tech report is always enabled and the callback must be set before
+  // any report created.
+  // Report uploading will be decided individually for every single report.
+  return new LegacyTechService(profile, trigger_);
+}
+
+void LegacyTechServiceFactory::SetReportTrigger(
+    LegacyTechReportTrigger&& trigger) {
+  trigger_ = std::move(trigger);
+}
+
+LegacyTechServiceFactory::LegacyTechServiceFactory()
+    : ProfileKeyedServiceFactory(
+          "LegacyTechReporting",
+          ProfileSelections::BuildRedirectedInIncognito()) {}
+LegacyTechServiceFactory::~LegacyTechServiceFactory() = default;
+
+}  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h b/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h
new file mode 100644
index 0000000..84f88ff
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h
@@ -0,0 +1,64 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ENTERPRISE_REPORTING_LEGACY_TECH_LEGACY_TECH_SERVICE_H_
+#define CHROME_BROWSER_ENTERPRISE_REPORTING_LEGACY_TECH_LEGACY_TECH_SERVICE_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_report_generator.h"
+#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_url_matcher.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace enterprise_reporting {
+
+using LegacyTechReportTrigger = base::RepeatingCallback<void(
+    const LegacyTechReportGenerator::LegacyTechData& data)>;
+
+// A `KeyedService` provides an API that allows content layor to upload report.
+// It will trigger a report if the event URL matches the policy setting.
+class LegacyTechService : public KeyedService {
+ public:
+  LegacyTechService(Profile* profile, LegacyTechReportTrigger trigger);
+  LegacyTechService(const LegacyTechService&) = delete;
+  LegacyTechService& operator=(const LegacyTechService&) = delete;
+  ~LegacyTechService() override;
+
+  void ReportEvent(const std::string& type,
+                   const GURL& url,
+                   const std::string& filename,
+                   uint64_t line,
+                   uint64_t column) const;
+
+ private:
+  LegacyTechURLMatcher url_matcher_;
+  LegacyTechReportTrigger trigger_;
+};
+
+class LegacyTechServiceFactory : public ProfileKeyedServiceFactory {
+ public:
+  static LegacyTechServiceFactory* GetInstance();
+  static LegacyTechService* GetForProfile(Profile* profile);
+
+  LegacyTechServiceFactory(const LegacyTechServiceFactory&) = delete;
+  LegacyTechServiceFactory& operator=(const LegacyTechServiceFactory&) = delete;
+
+  void SetReportTrigger(LegacyTechReportTrigger&& trigger);
+
+ protected:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+
+ private:
+  friend base::NoDestructor<LegacyTechServiceFactory>;
+
+  LegacyTechServiceFactory();
+  ~LegacyTechServiceFactory() override;
+
+  LegacyTechReportTrigger trigger_;
+};
+
+}  // namespace enterprise_reporting
+
+#endif  // CHROME_BROWSER_ENTERPRISE_REPORTING_LEGACY_TECH_LEGACY_TECH_SERVICE_H_
diff --git a/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service_unittest.cc b/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service_unittest.cc
new file mode 100644
index 0000000..bac0568
--- /dev/null
+++ b/chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_service.h"
+#include "chrome/browser/enterprise/reporting/legacy_tech/legacy_tech_report_generator.h"
+
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "chrome/browser/enterprise/reporting/prefs.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using ::testing::_;
+
+namespace enterprise_reporting {
+
+class LegacyTechServiceTest : public ::testing::Test {
+ public:
+  LegacyTechServiceTest() = default;
+  ~LegacyTechServiceTest() override = default;
+
+  void SetUp() override {
+    LegacyTechServiceFactory::GetInstance()->SetReportTrigger(
+        mock_trigger_.Get());
+  }
+
+  void SetPolicy(const std::vector<std::string>& urls) {
+    base::Value::List policy;
+    for (const auto& url : urls) {
+      policy.Append(base::Value(url));
+    }
+    profile_.GetTestingPrefService()->SetManagedPref(
+        kCloudLegacyTechReportAllowlist,
+        std::make_unique<base::Value>(std::move(policy)));
+  }
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  TestingProfile profile_;
+  ::testing::StrictMock<base::MockCallback<LegacyTechReportTrigger>>
+      mock_trigger_;
+};
+
+TEST_F(LegacyTechServiceTest, Disabled) {
+  EXPECT_CALL(mock_trigger_, Run(_)).Times(0);
+  LegacyTechServiceFactory::GetForProfile(&profile_)->ReportEvent(
+      "type", GURL("https://example.com"), "filename",
+      /*line=*/10, /*column=*/42);
+}
+
+TEST_F(LegacyTechServiceTest, NoMatched) {
+  EXPECT_CALL(mock_trigger_, Run(_)).Times(0);
+  SetPolicy({"www.example.com"});
+  LegacyTechServiceFactory::GetForProfile(&profile_)->ReportEvent(
+      "type", GURL("https://example.com"), "filename",
+      /*line=*/10, /*column=*/42);
+}
+
+TEST_F(LegacyTechServiceTest, MatchedAndUpload) {
+  LegacyTechReportGenerator::LegacyTechData expected_data = {
+      "type",        base::Time::Now(), GURL("https://example.com"),
+      "example.com", "filename",        /*line=*/10,
+      /*column=*/42};
+
+  EXPECT_CALL(mock_trigger_, Run(_)).Times(1);
+  SetPolicy({"example.com"});
+  LegacyTechServiceFactory::GetForProfile(&profile_)->ReportEvent(
+      "type", GURL("https://example.com"), "filename",
+      /*line=*/10, /*column=*/42);
+}
+
+}  // namespace enterprise_reporting
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index a07f812..ae453c6 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -480,6 +480,7 @@
       this);
   external_install_manager_->Shutdown();
   corrupted_extension_reinstaller_.Shutdown();
+  extension_registrar_.Shutdown();
 }
 
 void ExtensionService::Init() {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index bf18ce4..4fdae2c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -7404,7 +7404,7 @@
   {
     "name": "ui-enable-shared-image-cache-for-gpu",
     "owners": [ "rjkroege", "khushalsagar", "msisov@igalia.com" ],
-    "expiry_milestone": 115
+    "expiry_milestone": 121
   },
   {
     "name": "ui-slow-animations",
@@ -7865,6 +7865,11 @@
     "expiry_milestone": 130
   },
   {
+    "name": "windows11-mica-titlebar",
+    "owners": [ "j@jloeffler.com", "pkasting" ],
+    "expiry_milestone": 120
+  },
+  {
     "name": "xsurface-metrics-reporting",
     "owners": [ "//chrome/android/feed/OWNERS", "feed@chromium.org" ],
     "expiry_milestone": 115
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 75acccc..730db4c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4893,6 +4893,11 @@
     "Use Windows Runtime MIDI API for WebMIDI (effective only on Windows 10 or "
     "later).";
 
+const char kWindows11MicaTitlebarName[] = "Windows 11 Mica titlebar";
+const char kWindows11MicaTitlebarDescription[] =
+    "Use the DWM system-drawn Mica titlebar on Windows 11, version 22H2 (build "
+    "22621) and above.";
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 const char kLaunchWindowsNativeHostsDirectlyName[] =
     "Force Native Host Executables to Launch Directly";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 77c58ac..7b439a34 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2799,6 +2799,9 @@
 extern const char kUseWinrtMidiApiName[];
 extern const char kUseWinrtMidiApiDescription[];
 
+extern const char kWindows11MicaTitlebarName[];
+extern const char kWindows11MicaTitlebarDescription[];
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kLaunchWindowsNativeHostsDirectlyName[];
 extern const char kLaunchWindowsNativeHostsDirectlyDescription[];
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImpl.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImpl.java
index f2a29aa..2362032 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImpl.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImpl.java
@@ -17,14 +17,13 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.SaveInstanceStateObserver;
 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.TabLaunchType;
-import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -179,6 +178,11 @@
      * fullscreen re-auth. Back presses done from tab switcher re-auth screen, is handled elsewhere.
      */
     private final @NonNull Runnable mBackPressInReauthFullScreenRunnable;
+    /**
+     * A supplier to indicate if the re-auth was pending in the previous Chrome session before it
+     * was destroyed.
+     */
+    private final @NonNull Supplier<Boolean> mIsIncognitoReauthPendingOnRestoreSupplier;
 
     // No strong reference to this should be made outside of this class because
     // we set this to null in hideDialogIfShowing for it to be garbage collected.
@@ -198,6 +202,9 @@
      *         used to determine the current {@link LayoutType} which is shown.
      * @param profileSupplier A Observable Supplier of {@link Profile} which is used to query the
      *         preference value of the Incognito lock setting.
+     * @param incognitoReauthPendingOnRestoreSupplier Supplier to indicate where the {@link
+     *         IncognitoReauthControllerImpl#KEY_IS_INCOGNITO_REAUTH_PENDING} was set to true in the
+     * saved instance state.
      * @param taskId The task Id of the {@link ChromeActivity} associated with this controller.
      */
     public IncognitoReauthControllerImpl(@NonNull TabModelSelector tabModelSelector,
@@ -205,7 +212,7 @@
             @NonNull OneshotSupplier<LayoutStateProvider> layoutStateProviderOneshotSupplier,
             @NonNull ObservableSupplier<Profile> profileSupplier,
             @NonNull IncognitoReauthCoordinatorFactory incognitoReauthCoordinatorFactory,
-            int taskId) {
+            @NonNull Supplier<Boolean> incognitoReauthPendingOnRestoreSupplier, int taskId) {
         mTabModelSelector = tabModelSelector;
         mActivityLifecycleDispatcher = dispatcher;
         mProfileObservableSupplier = profileSupplier;
@@ -215,6 +222,7 @@
         mBackPressInReauthFullScreenRunnable =
                 incognitoReauthCoordinatorFactory.getBackPressRunnable();
         mTaskId = taskId;
+        mIsIncognitoReauthPendingOnRestoreSupplier = incognitoReauthPendingOnRestoreSupplier;
 
         layoutStateProviderOneshotSupplier.onAvailable(
                 mLayoutStateProviderCallbackController.makeCancelable(layoutStateProvider -> {
@@ -394,18 +402,8 @@
     // Tab state initialized is called when creating tabs from launcher shortcut or restore. Re-auth
     // dialogs should be shown iff any Incognito tabs were restored.
     private void onTabStateInitializedForReauth() {
-        boolean hasRestoredIncognitoTabs = false;
-        TabModel incognitoTabModel = mTabModelSelector.getModel(/*incognito=*/true);
-        for (int i = 0; i < incognitoTabModel.getCount(); ++i) {
-            @TabLaunchType
-            Integer tabLaunchType = CriticalPersistedTabData.from(incognitoTabModel.getTabAt(i))
-                                            .getTabLaunchTypeAtCreation();
-            if (tabLaunchType == TabLaunchType.FROM_RESTORE) {
-                hasRestoredIncognitoTabs = true;
-                break;
-            }
-        }
-        mIncognitoReauthPending = hasRestoredIncognitoTabs;
+        mIncognitoReauthPending = mTabModelSelector.getModel(/*incognito=*/true).getCount() > 0
+                && mIsIncognitoReauthPendingOnRestoreSupplier.get();
         showDialogIfRequired();
     }
 }
diff --git a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImplTest.java b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImplTest.java
index 5e45ad8..a2cc7e14 100644
--- a/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImplTest.java
+++ b/chrome/browser/incognito/android/java/src/org/chromium/chrome/browser/incognito/reauth/IncognitoReauthControllerImplTest.java
@@ -10,7 +10,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -30,7 +29,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.LooperMode;
 
-import org.chromium.base.UserDataHost;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -40,10 +38,6 @@
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.chrome.browser.tab.TabImpl;
-import org.chromium.chrome.browser.tab.TabLaunchType;
-import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -99,6 +93,8 @@
     private OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderOneshotSupplier;
     private ObservableSupplierImpl<Profile> mProfileObservableSupplier;
 
+    private boolean mIsIncognitoReauthPendingOnRestore;
+
     private void switchToIncognitoTabModel() {
         doReturn(true).when(mTabModelSelectorMock).isIncognitoSelected();
         mIncognitoReauthController.onBeforeIncognitoTabModelSelected();
@@ -109,21 +105,6 @@
         mIncognitoReauthController.onAfterRegularTabModelChanged();
     }
 
-    private Tab prepareTabForRestoreOrLauncherShortcut(boolean isRestore) {
-        TabImpl tab = mock(TabImpl.class);
-        doReturn(true).when(tab).isInitialized();
-        UserDataHost userDataHost = new UserDataHost();
-        CriticalPersistedTabData criticalPersistedTabData = mock(CriticalPersistedTabData.class);
-        userDataHost.setUserData(CriticalPersistedTabData.class, criticalPersistedTabData);
-        doReturn(userDataHost).when(tab).getUserDataHost();
-        doReturn(0).when(criticalPersistedTabData).getRootId();
-        @TabLaunchType
-        Integer launchType =
-                isRestore ? TabLaunchType.FROM_RESTORE : TabLaunchType.FROM_LAUNCHER_SHORTCUT;
-        doReturn(launchType).when(criticalPersistedTabData).getTabLaunchTypeAtCreation();
-        return tab;
-    }
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -164,7 +145,8 @@
 
         mIncognitoReauthController = new IncognitoReauthControllerImpl(mTabModelSelectorMock,
                 mActivityLifecycleDispatcherMock, mLayoutStateProviderOneshotSupplier,
-                mProfileObservableSupplier, mIncognitoReauthCoordinatorFactoryMock, TASK_ID);
+                mProfileObservableSupplier, mIncognitoReauthCoordinatorFactoryMock,
+                () -> mIsIncognitoReauthPendingOnRestore, TASK_ID);
         mProfileObservableSupplier.set(mProfileMock);
 
         verify(mLayoutStateProviderMock, times(1))
@@ -281,9 +263,7 @@
         // Pretend there's one incognito tab.
         doReturn(1).when(mIncognitoTabModelMock).getCount();
         switchToIncognitoTabModel();
-        Tab incognitoTab = prepareTabForRestoreOrLauncherShortcut(/*isRestore=*/true);
-        doReturn(true).when(incognitoTab).isIncognito();
-        doReturn(incognitoTab).when(mIncognitoTabModelMock).getTabAt(0);
+        mIsIncognitoReauthPendingOnRestore = true;
 
         mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
         assertTrue("IncognitoReauthCoordinator should be created for restored"
@@ -298,9 +278,7 @@
         // Pretend there's one incognito tab.
         doReturn(1).when(mIncognitoTabModelMock).getCount();
         switchToIncognitoTabModel();
-        Tab incognitoTab = prepareTabForRestoreOrLauncherShortcut(/*isRestore=*/false);
-        doReturn(true).when(incognitoTab).isIncognito();
-        doReturn(incognitoTab).when(mIncognitoTabModelMock).getTabAt(0);
+        mIsIncognitoReauthPendingOnRestore = false;
 
         mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
         assertFalse("IncognitoReauthCoordinator should not be created for Incognito tabs"
diff --git a/chrome/browser/lacros/browser_service_lacros_browsertest.cc b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
index 6fba66f..0421fa8 100644
--- a/chrome/browser/lacros/browser_service_lacros_browsertest.cc
+++ b/chrome/browser/lacros/browser_service_lacros_browsertest.cc
@@ -333,8 +333,14 @@
   }
 };
 
+// TODO(crbug.com/1447850) This test constantly fail for internal builder.
+#if BUILDFLAG(IS_CHROMEOS_LACROS) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#define MAYBE_HandlesUncleanExit DISABLED_HandlesUncleanExit
+#else
+#define MAYBE_HandlesUncleanExit HandlesUncleanExit
+#endif
 IN_PROC_BROWSER_TEST_F(BrowserServiceLacrosWindowlessBrowserTest,
-                       HandlesUncleanExit) {
+                       MAYBE_HandlesUncleanExit) {
   // Browser launch should be suppressed with the kNoStartupWindow switch.
   ASSERT_FALSE(browser());
 
diff --git a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
index 5c1c466..249eacd 100644
--- a/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/page_content_annotations_service_browsertest.cc
@@ -40,7 +40,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
-#include "chrome/browser/ash/login/test/scoped_policy_update.h"
+#include "chrome/browser/ash/login/test/guest_session_mixin.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #endif
 
@@ -167,22 +167,16 @@
         /*disabled_features=*/{});
   }
 
-  void SetUpInProcessBrowserTestFixture() override {
-    MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();
+  ~PageContentAnnotationsServiceEphemeralProfileBrowserTest() override {}
 
-    std::unique_ptr<ash::ScopedDevicePolicyUpdate> device_policy_update =
-        device_state_.RequestDevicePolicyUpdate();
-    device_policy_update->policy_payload()
-        ->mutable_ephemeral_users_enabled()
-        ->set_ephemeral_users_enabled(true);
-  }
-
- protected:
-  ash::DeviceStateMixin device_state_{
-      &mixin_host_,
-      ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
+  PageContentAnnotationsServiceEphemeralProfileBrowserTest(
+      const PageContentAnnotationsServiceEphemeralProfileBrowserTest&) = delete;
+  PageContentAnnotationsServiceEphemeralProfileBrowserTest& operator=(
+      const PageContentAnnotationsServiceEphemeralProfileBrowserTest&) = delete;
 
  private:
+  ash::GuestSessionMixin guest_session_{&mixin_host_};
+
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
diff --git a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactTest.java b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactTest.java
index a8640e5e..8fbbd62 100644
--- a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactTest.java
+++ b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactTest.java
@@ -87,7 +87,7 @@
 
     @Test
     public void test() {
-        AutofillProfile profile = new AutofillProfile();
+        AutofillProfile profile = AutofillProfile.builder().build();
         AutofillContact contact =
                 new AutofillContact(mContext, profile, mPayerName, mPayerPhone, mPayerEmail,
                         mIsComplete ? ContactEditor.COMPLETE
diff --git a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactUnitTest.java b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactUnitTest.java
index c0911dfd..6957fff 100644
--- a/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactUnitTest.java
+++ b/chrome/browser/payments/android/java/src/org/chromium/chrome/browser/payments/AutofillContactUnitTest.java
@@ -32,7 +32,7 @@
 
     @Test
     public void testIsEqualOrSupersetOf_RequestAllFields() {
-        AutofillProfile dummyProfile = new AutofillProfile();
+        AutofillProfile dummyProfile = AutofillProfile.builder().build();
         Context mockContext = spy(RuntimeEnvironment.application);
         doReturn(MESSAGE).when(mockContext).getString(anyInt());
 
@@ -73,7 +73,7 @@
 
     @Test
     public void testIsEqualOrSupersetOf_RequestSomeFields() {
-        AutofillProfile dummyProfile = new AutofillProfile();
+        AutofillProfile dummyProfile = AutofillProfile.builder().build();
         Context mockContext = spy(RuntimeEnvironment.application);
         doReturn(MESSAGE).when(mockContext).getString(anyInt());
 
@@ -99,7 +99,7 @@
 
     @Test
     public void testGetRelevanceScore_RequestAllFields() {
-        AutofillProfile dummyProfile = new AutofillProfile();
+        AutofillProfile dummyProfile = AutofillProfile.builder().build();
         Context mockContext = spy(RuntimeEnvironment.application);
         doReturn(MESSAGE).when(mockContext).getString(anyInt());
 
@@ -156,7 +156,7 @@
 
     @Test
     public void testGetRelevanceScore_RequestSomeFields() {
-        AutofillProfile dummyProfile = new AutofillProfile();
+        AutofillProfile dummyProfile = AutofillProfile.builder().build();
         Context mockContext = spy(RuntimeEnvironment.application);
         doReturn(MESSAGE).when(mockContext).getString(anyInt());
 
@@ -191,4 +191,4 @@
                 false /* requestName */, true /* requestPhone */, true /* requestEmail */);
         Assert.assertEquals(0, contact.getRelevanceScore());
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/platform_util_mac.mm b/chrome/browser/platform_util_mac.mm
index 64e04dad..f7530d9f 100644
--- a/chrome/browser/platform_util_mac.mm
+++ b/chrome/browser/platform_util_mac.mm
@@ -6,6 +6,7 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
@@ -16,15 +17,36 @@
 #include "chrome/browser/platform_util_internal.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
 #include "net/base/mac/url_conversions.h"
 #include "ui/views/widget/widget.h"
 #include "url/gurl.h"
 
 namespace platform_util {
 
+// Returns true if revealing file paths in the Finder should be skipped
+// because it's not needed while running a test.
+bool WorkspacePathRevealDisabledForTest() {
+  // Note: the kTestType switch is only added on browser tests, but not unit
+  // tests. Unit tests need to add the switch manually:
+  //
+  //   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  //   command_line->AppendSwitch(switches::kTestType);
+  //
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType);
+}
+
 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) {
   DCHECK([NSThread isMainThread]);
   NSURL* url = base::mac::FilePathToNSURL(full_path);
+
+  // The Finder creates a new window on each `full_path` reveal. Skip
+  // revealing the path during testing to avoid an avalanche of new
+  // Finder windows.
+  if (WorkspacePathRevealDisabledForTest()) {
+    return;
+  }
+
   [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[ url ]];
 }
 
diff --git a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java
index 4affb5a..dceb043 100644
--- a/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java
+++ b/chrome/browser/quick_delete/android/javatests/src/org/chromium/chrome/browser/quick_delete/QuickDeleteControllerTest.java
@@ -30,6 +30,7 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge;
@@ -194,6 +195,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "https://crbug.com/1448217")
     public void testBrowsingDataDeletion_onClickedDelete() throws Exception {
         resetCookies();
         loadSiteDataUrl();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index c75a578..7e25c4f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -3557,13 +3557,12 @@
   if (add_separator)
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
 #if BUILDFLAG(IS_MAC)
-  menu_model_.AddItem(
-      IDC_SEND_TAB_TO_SELF,
-      l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF));
+  menu_model_.AddItem(IDC_SEND_TAB_TO_SELF,
+                      l10n_util::GetStringUTF16(IDS_MENU_SEND_TAB_TO_SELF));
 #else
   menu_model_.AddItemWithIcon(
       IDC_SEND_TAB_TO_SELF,
-      l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF),
+      l10n_util::GetStringUTF16(IDS_MENU_SEND_TAB_TO_SELF),
       ui::ImageModel::FromVectorIcon(kLaptopAndSmartphoneIcon));
 #endif
 }
diff --git a/chrome/browser/resources/chromeos/images/an_error_occurred.svg b/chrome/browser/resources/chromeos/images/an_error_occurred.svg
deleted file mode 100644
index 930d159..0000000
--- a/chrome/browser/resources/chromeos/images/an_error_occurred.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="520" height="320" viewBox="0 0 520 320" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M0 0h520v320H0z"/><path fill="#fff" d="M0 24h520v272H0z"/><path d="m380.619 121.819-27.137 48.879a25.925 25.925 0 0 1-15.396 12.537 25.757 25.757 0 0 1-19.7-2.079 25.912 25.912 0 0 1-7.77-6.493 26.074 26.074 0 0 1-4.706-8.99 26.146 26.146 0 0 1 2.066-19.821l27.137-48.866a25.922 25.922 0 0 1 15.393-12.545 25.755 25.755 0 0 1 19.703 2.074 26.015 26.015 0 0 1 12.471 15.484 26.183 26.183 0 0 1-2.061 19.82z" fill="#E8E9EC"/><path d="M263.119 34.172h-60.524a11.208 11.208 0 0 0-5.583 1.537 11.289 11.289 0 0 0-4.085 4.125l-30.263 52.729a11.34 11.34 0 0 0 0 11.31l30.263 52.73a11.284 11.284 0 0 0 4.119 4.144 11.213 11.213 0 0 0 5.628 1.518h60.445a11.224 11.224 0 0 0 5.627-1.52 11.304 11.304 0 0 0 4.12-4.142l30.263-52.73a11.337 11.337 0 0 0 0-11.31l-30.236-52.73a11.308 11.308 0 0 0-4.132-4.149 11.221 11.221 0 0 0-5.642-1.512z" stroke="#EA4335" stroke-width="5.063" stroke-miterlimit="10" stroke-dasharray="19.75 19.75"/><path d="m213.031 78.242 39.705 39.953m0-39.953-39.705 39.953" stroke="#EA4335" stroke-width="5.063" stroke-miterlimit="10"/><path d="M252.06 278.101a5.348 5.348 0 0 1-6.184-1.462 5.414 5.414 0 0 1-1.073-1.988 54.596 54.596 0 0 1 2.886-38.658c5.658-12.09 15.549-21.657 27.78-26.871a53.684 53.684 0 0 1 38.514-1.386c12.571 4.32 23.116 13.151 29.619 24.803a5.458 5.458 0 0 1-.744 6.356 5.404 5.404 0 0 1-1.839 1.305l-88.959 37.901z" stroke="#E4E4E4" stroke-width="5.063" stroke-miterlimit="10"/><path d="m146.004 152.816-18.712 22.432c-2.36 2.829-1.993 7.047.82 9.421l22.035 18.6a6.623 6.623 0 0 0 9.367-.824l18.711-22.432c2.36-2.829 1.994-7.048-.819-9.422l-22.036-18.599a6.621 6.621 0 0 0-9.366.824z" fill="#F882FF"/><path d="M220.553 259.65c.406-5.051.534-10.121.384-15.187a26.4 26.4 0 0 1 7.748-18.558 109.419 109.419 0 0 0 8.118-8.34c8.503-10.591 6.331-26.471-5.642-33.478-10.515-6.155-25.163-2.318-30.553 8.885-2.516 5.169-3.179 10.991-5.218 16.334a26.197 26.197 0 0 1-11.363 13.548c-4.517 2.598-9.271 4.796-13.469 7.994a31.078 31.078 0 0 0-9.523 11.83 31.853 31.853 0 0 0-1.179 23.507 31.629 31.629 0 0 0 15.267 17.838 31.327 31.327 0 0 0 23.285 2.321 31.521 31.521 0 0 0 18.45-14.477 30.933 30.933 0 0 0 3.695-12.217z" fill="#BBC0C7"/><path d="M377.731 224.173c9.648 0 17.469-7.867 17.469-17.572 0-9.705-7.821-17.572-17.469-17.572-9.647 0-17.468 7.867-17.468 17.572 0 9.705 7.821 17.572 17.468 17.572z" stroke="#FBBC05" stroke-width="5.063" stroke-miterlimit="10"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/images/no_network.svg b/chrome/browser/resources/chromeos/images/no_network.svg
deleted file mode 100644
index 469d6ca..0000000
--- a/chrome/browser/resources/chromeos/images/no_network.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="520" height="320" viewBox="0 0 520 320" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#a)"><path fill="#fff" d="M0 0h520v320H0z"/><g clip-path="url(#b)"><path fill="#fff" d="M-17.703 25h555.405v274H-17.703z"/><path d="m177.978 217.427-3.573-10.43a3.639 3.639 0 0 0-4.621-2.263l-10.43 3.573a3.639 3.639 0 0 0-2.262 4.621l3.573 10.429a3.637 3.637 0 0 0 4.62 2.263l10.43-3.573a3.638 3.638 0 0 0 2.263-4.62z" fill="#4285F4"/><path d="M124.852 151.197a16.392 16.392 0 0 0-3.703-.564l-3.99-.611a13.951 13.951 0 0 1-8.461-4.851c-3.101-3.536-5.156-7.877-10.182-8.914a11.417 11.417 0 0 0-10.812 3.527 10.03 10.03 0 0 0-.407.472 11.428 11.428 0 0 0-.232 13.788 11.437 11.437 0 0 0 3.666 3.244c1.75.982 3.703 1.685 5.49 2.49a13.475 13.475 0 0 1 7.136 7.406l1.472 3.758a16.223 16.223 0 0 0 19.793 11.598 16.228 16.228 0 0 0 11.612-19.785 16.227 16.227 0 0 0-11.382-11.558v0z" stroke="#E6E6E6" stroke-width="2.777" stroke-linecap="round" stroke-linejoin="round"/><path d="M497.446 151.42a6.712 6.712 0 1 0-.001-13.423 6.712 6.712 0 0 0 .001 13.423z" fill="#E6E7EA"/><path d="M414.014 206.719a4.054 4.054 0 1 0 0-8.108 4.054 4.054 0 0 0 0 8.108zM18.149 124.677a2.87 2.87 0 1 0 0-5.74 2.87 2.87 0 0 0 0 5.74z" fill="#4285F4"/><path d="m349.124 191.955-10.182 6.757a5.478 5.478 0 0 0-2.481 4.943l.74 12.229a5.478 5.478 0 0 0 3.018 4.563l10.969 5.425a5.464 5.464 0 0 0 5.462-.324l10.182-6.758a5.493 5.493 0 0 0 2.444-4.897l-.74-12.237a5.508 5.508 0 0 0-.9-2.685 5.505 5.505 0 0 0-2.118-1.879l-10.932-5.47a5.476 5.476 0 0 0-5.462.333v0z" stroke="#E6E6E6" stroke-width="2.777" stroke-linecap="round" stroke-linejoin="round"/><path d="M50.557 174.395H32.543a1.084 1.084 0 0 0-.937.557 1.092 1.092 0 0 0 .012 1.09l8.997 15.58a1.08 1.08 0 0 0 .926.52 1.085 1.085 0 0 0 .926-.52l9.006-15.598a1.092 1.092 0 0 0-.916-1.629zm352.081-15.57a1.93 1.93 0 0 0-.56 1.509 1.924 1.924 0 0 0 .782 1.407 15.03 15.03 0 0 0 19.566-1.345 15.035 15.035 0 0 0 1.585-19.548 1.93 1.93 0 0 0-2.215-.693 1.948 1.948 0 0 0-.701.434l-18.457 18.236z" fill="#D2E3FC"/><path d="M428.298 151.984a15.513 15.513 0 0 1-11.636-2.394 15.496 15.496 0 0 1-6.526-9.927l-.047-.249a15.527 15.527 0 0 1 12.321-18.144v0a15.535 15.535 0 0 1 18.171 12.321l.047.25a15.517 15.517 0 0 1-12.33 18.143v0z" stroke="#E6E7EA" stroke-width="2.777" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3.63 7.26"/><path d="M428.298 151.984a15.406 15.406 0 0 1-11.658-2.362 15.409 15.409 0 0 1-6.504-9.959l-.047-.249a15.527 15.527 0 0 1 12.321-18.144v0a15.535 15.535 0 0 1 18.171 12.321l.047.25a15.517 15.517 0 0 1-12.33 18.143v0z" stroke="#4285F4" stroke-width="2.777" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3.63 7.27"/><path d="m339.201 141.931-10.914-10.923a96.576 96.576 0 0 0-136.574 0l-10.914 10.923a9.005 9.005 0 0 0-1.952 9.809 8.998 8.998 0 0 0 1.952 2.919l18.754 18.764c.252-.371.537-.718.852-1.037l8.211-8.211a72.665 72.665 0 0 1 102.75 0l8.211 8.211c.314.319.599.666.851 1.037l18.754-18.764a9.01 9.01 0 0 0 2.647-6.36 9.006 9.006 0 0 0-2.628-6.368z" fill="#F7F8F9"/><path d="M311.385 164.175a72.667 72.667 0 0 0-102.75 0l-8.211 8.211c-.315.319-.6.666-.852 1.037l25.549 25.604 2.129-2.129a46.287 46.287 0 0 1 50.477-10.05 46.28 46.28 0 0 1 15.024 10.05l2.129 2.129 25.567-25.567a7.366 7.366 0 0 0-.852-1.037l-8.21-8.248z" fill="#E8E9EC"/><path d="m266.369 227.501 28.511-28.511-2.13-2.129A46.266 46.266 0 0 0 260 183.282a46.287 46.287 0 0 0-32.75 13.579l-2.129 2.129 28.51 28.511a9.007 9.007 0 0 0 12.738 0z" fill="#BBC0C7"/></g></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h520v320H0z"/></clipPath><clipPath id="b"><path fill="#fff" transform="translate(-17.703 25)" d="M0 0h555.405v274H0z"/></clipPath></defs></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/kerberos/kerberos_in_browser_dialog.html b/chrome/browser/resources/chromeos/kerberos/kerberos_in_browser_dialog.html
index 5d6be878..e01924b 100644
--- a/chrome/browser/resources/chromeos/kerberos/kerberos_in_browser_dialog.html
+++ b/chrome/browser/resources/chromeos/kerberos/kerberos_in_browser_dialog.html
@@ -3,7 +3,8 @@
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
   </head>
   <body>
-    <kerberos-settings-redirect></kerberos-settings-redirect>
+    <kerberos-settings-redirect id="redirect-dialog">
+    </kerberos-settings-redirect>
     <script type="module" src="kerberos_settings_redirect.js"></script>
   </body>
 </html>
diff --git a/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.html b/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.html
index 5f802a0..073bb5a 100644
--- a/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.html
+++ b/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.html
@@ -16,11 +16,13 @@
   <div slot="body">$i18n{kerberosInBrowserDescription}</div>
 
   <div slot="button-container">
-    <cr-button class="cancel-button" on-click="onVisitWithoutTicket_">
-      $i18n{kerberosInBrowserVisitWithoutTicket}
+    <cr-button class="cancel-button" id="cancel-button"
+        on-click="onCancelButtonClicked_">
+        $i18n{kerberosInBrowserVisitWithoutTicket}
     </cr-button>
-    <cr-button class="action-button" on-click="onManageTickets_">
-      $i18n{kerberosInBrowserManageTickets}
+    <cr-button class="action-button" id="settings-button"
+        on-click="onManageTickets_">
+        $i18n{kerberosInBrowserManageTickets}
     </cr-button>
   </div>
 </cr-dialog>
diff --git a/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.ts b/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.ts
index 546ebc1a..5394b735 100644
--- a/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.ts
+++ b/chrome/browser/resources/chromeos/kerberos/kerberos_settings_redirect.ts
@@ -19,7 +19,7 @@
     return getTemplate();
   }
 
-  private onVisitWithoutTicket_(): void {
+  private onCancelButtonClicked_(): void {
     chrome.send('dialogClose');
   }
 
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_screens_list.html b/chrome/browser/resources/chromeos/login/components/oobe_screens_list.html
index e53c1c1..473aa0f 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_screens_list.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_screens_list.html
@@ -132,11 +132,12 @@
           <iron-icon class="screen-icon" icon="[[screen.icon]]"></iron-icon>
         </div>
         <div class="text-container">
-          <div class="screen-title">[[screen.title]]</div>
-          <div class="screen-subtitle">[[screen.subtitle]]</div>
+          <div class="screen-title"> [[i18nDynamic(locale, screen.title)]]</div>
+          <div class="screen-subtitle">[[getSubtitile_(locale)]]</div>
         </div>
         <div class="info-icon">
-          <iron-icon hidden="[[isSyncedIconHidden(screen.synced , screen.selected)]]"
+          <iron-icon hidden="[[isSyncedIconHidden(screen.is_synced ,
+                                            screen.selected)]]"
               id="sync" icon="oobe-20:sync"></iron-icon>
           <iron-icon hidden="[[!screen.selected]]"
               id="selected" icon="oobe-20:selected"></iron-icon>
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_screens_list.js b/chrome/browser/resources/chromeos/login/components/oobe_screens_list.js
index a12e17d0..b7ac1de 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_screens_list.js
+++ b/chrome/browser/resources/chromeos/login/components/oobe_screens_list.js
@@ -104,6 +104,13 @@
     this.notifyPath('screensList_');
   }
 
+  getSubtitile_(locale, screen_subtitle) {
+    if (screen_subtitle) {
+      return this.i18nDynamic(locale, screen_subtitle);
+    }
+    return '';
+  }
+
   isScreenDisabled(is_revisitable, is_completed) {
     return (!is_revisitable) && is_completed;
   }
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
index a5745dad..dfc123f 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmark_row.html
@@ -29,10 +29,17 @@
   }
 
   iron-icon {
+    --iron-icon-fill-color: var(--google-grey-700);
     height: 16px;
     width: 16px;
   }
 
+  @media (prefers-color-scheme: dark) {
+    iron-icon {
+      --iron-icon-fill-color: var(--google-grey-100);
+    }
+  }
+
   :host-context([chrome-refresh-2023]) .sp-labelless-input:hover {
     --cr-input-hover-background-color: transparent;
   }
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.html
index 36f2dff..874665fe 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.html
@@ -1,6 +1,7 @@
 <style include="cr-icons cr-hidden-style">
   hr {
-    border: var(--cr-hairline);
+    border: none;
+    border-top: var(--cr-hairline);
   }
 
   .dropdown-item {
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_edit_dialog.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_edit_dialog.html
index 0f89e26..d01823c 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_edit_dialog.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_edit_dialog.html
@@ -9,7 +9,6 @@
   .body {
     display: flex;
     flex-direction: column;
-    gap: 8px;
   }
 
   .button-row {
@@ -55,6 +54,7 @@
     border: 1px solid var(--scrollable-border-color);
     border-radius: 2px;
     height: 160px;
+    margin-top: 8px;
     overflow: auto;
   }
 
@@ -82,6 +82,10 @@
     gap: 8px;
   }
 
+  :host-context([chrome-refresh-2023]) .input-section {
+    margin-bottom: 16px;
+  }
+
   .name-input {
     --cr-input-error-display: none;
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index e39c8e6..a549df2 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -132,6 +132,7 @@
   // AXNodeID can be used to access the other.
   private domNodeToAxNodeIdMap_: TwoWayMap = new TwoWayMap();
 
+  private scrollingOnSelection_: boolean;
   private hasContent_: boolean;
   private emptyStateImagePath_: string;
   private emptyStateDarkImagePath_: string;
@@ -165,6 +166,11 @@
           anchorNodeId, anchorOffset, focusNodeId, focusOffset);
     };
 
+    document.onscroll = () => {
+      chrome.readAnything.onScroll(this.scrollingOnSelection_);
+      this.scrollingOnSelection_ = false;
+    };
+
     // Pass copy commands to main page. Copy commands will not work if they are
     // disabled on the main page.
     document.oncopy = () => {
@@ -320,6 +326,7 @@
     if (!startElement) {
       return;
     }
+    this.scrollingOnSelection_ = true;
     startElement.scrollIntoViewIfNeeded();
   }
 
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index 8748c6e..47e3125 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -68,6 +68,9 @@
     // shortcuts.
     function onCopy(): void;
 
+    // Called when the Read Anything panel is scrolled.
+    function onScroll(onSelection: boolean): void;
+
     // Called when a user clicks a link. NodeID is an AXNodeID which identifies
     // the link's corresponding AXNode in the main pane.
     function onLinkClicked(nodeId: number): void;
diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
index fc408dd..dada8e13 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc
@@ -724,7 +724,7 @@
 
 // TODO(https://crbug.com/1399454): Test is flaky.
 IN_PROC_BROWSER_TEST_F(V4SafeBrowsingServiceTest,
-                       DISABLED_SubResourceHitWithMainFrameReferrer) {
+                       SubResourceHitWithMainFrameReferrer) {
   GURL first_url = embedded_test_server()->GetURL(kEmptyPage);
   GURL second_url = embedded_test_server()->GetURL(kMalwarePage);
   GURL bad_url = embedded_test_server()->GetURL(kMalwareImg);
diff --git a/chrome/browser/sharing_hub/sharing_hub_model.cc b/chrome/browser/sharing_hub/sharing_hub_model.cc
index a3c6c80..806908b 100644
--- a/chrome/browser/sharing_hub/sharing_hub_model.cc
+++ b/chrome/browser/sharing_hub/sharing_hub_model.cc
@@ -70,7 +70,7 @@
     std::vector<SharingHubAction>* list) {
   for (const auto& action : first_party_action_list_) {
     if (action.command_id == IDC_SEND_TAB_TO_SELF) {
-      if (DoShowSendTabToSelfForWebContents(web_contents)) {
+      if (send_tab_to_self::ShouldDisplayEntryPoint(web_contents)) {
         list->push_back(action);
       }
     } else if (action.command_id == IDC_QRCODE_GENERATOR) {
@@ -114,8 +114,7 @@
   }
 
   first_party_action_list_.emplace_back(
-      IDC_SEND_TAB_TO_SELF,
-      l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF),
+      IDC_SEND_TAB_TO_SELF, l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF),
       &kLaptopAndSmartphoneIcon, "SharingHubDesktop.SendTabToSelfSelected", 0);
 
   first_party_action_list_.emplace_back(
@@ -146,9 +145,4 @@
       &kSavePageIcon, "SharingHubDesktop.SavePageSelected", 0);
 }
 
-bool SharingHubModel::DoShowSendTabToSelfForWebContents(
-    content::WebContents* web_contents) {
-  return send_tab_to_self::ShouldDisplayEntryPoint(web_contents);
-}
-
 }  // namespace sharing_hub
diff --git a/chrome/browser/sharing_hub/sharing_hub_model.h b/chrome/browser/sharing_hub/sharing_hub_model.h
index 012f7cdd..842934f 100644
--- a/chrome/browser/sharing_hub/sharing_hub_model.h
+++ b/chrome/browser/sharing_hub/sharing_hub_model.h
@@ -77,8 +77,6 @@
   void PopulateFirstPartyActions();
   void PopulateThirdPartyActions();
 
-  bool DoShowSendTabToSelfForWebContents(content::WebContents* web_contents);
-
   // A list of Sharing Hub first party actions in order in which they appear.
   std::vector<SharingHubAction> first_party_action_list_;
 
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index e2a0a75..5380cb5 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -866,6 +866,7 @@
   }
 #endif
 
+  DLOG(INFO) << "SyncTest::SetupSync() completed.";
   return true;
 }
 
diff --git a/chrome/browser/touch_to_fill/OWNERS b/chrome/browser/touch_to_fill/OWNERS
index 6b954e4..032fe21 100644
--- a/chrome/browser/touch_to_fill/OWNERS
+++ b/chrome/browser/touch_to_fill/OWNERS
@@ -1,3 +1,4 @@
 fhorschig@chromium.org
+ioanap@chromium.org
 
 file://chrome/browser/password_manager/OWNERS
diff --git a/chrome/browser/touch_to_fill/password_generation/OWNERS b/chrome/browser/touch_to_fill/password_generation/OWNERS
new file mode 100644
index 0000000..be2bfa2
--- /dev/null
+++ b/chrome/browser/touch_to_fill/password_generation/OWNERS
@@ -0,0 +1 @@
+atsvirchkova@google.com
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 8e0ef2e..bd2941b 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1976,6 +1976,7 @@
       "//components/services/app_service/public/cpp:preferred_apps",
       "//components/services/app_service/public/cpp:run_on_os_login",
       "//components/services/app_service/public/cpp:types",
+      "//components/supervised_user/core/common",
       "//components/ui_metrics",
       "//components/url_formatter",
       "//components/user_education/common",
@@ -2058,10 +2059,7 @@
       "webui/family_link_user_internals/family_link_user_internals_ui.cc",
       "webui/family_link_user_internals/family_link_user_internals_ui.h",
     ]
-    deps += [
-      "//components/supervised_user/core/browser",
-      "//components/supervised_user/core/common",
-    ]
+    deps += [ "//components/supervised_user/core/browser" ]
   }
 
   if (enable_supervised_users && enable_extensions) {
@@ -4545,6 +4543,8 @@
       "views/autofill/payments/local_card_migration_icon_view.h",
       "views/autofill/payments/manage_saved_iban_bubble_view.cc",
       "views/autofill/payments/manage_saved_iban_bubble_view.h",
+      "views/autofill/payments/mandatory_reauth_confirmation_bubble_view.cc",
+      "views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h",
       "views/autofill/payments/mandatory_reauth_icon_view.cc",
       "views/autofill/payments/mandatory_reauth_icon_view.h",
       "views/autofill/payments/mandatory_reauth_opt_in_bubble_view.cc",
@@ -5644,6 +5644,7 @@
       "//services/media_session/public/mojom",
       "//ui/base/dragdrop:types",
       "//ui/gfx/geometry",
+      "//ui/lottie",
       "//ui/views:buildflags",
     ]
     public_deps += [ "//ui/base/dragdrop/mojom:mojom_headers" ]
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
index ddd5146..5e98c0d 100644
--- a/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/values/dimens.xml
@@ -45,12 +45,14 @@
     <dimen name="location_bar_status_icon_holding_space_size">8dp</dimen>
     <dimen name="location_bar_icon_end_padding">0dp</dimen>
     <dimen name="location_bar_icon_end_padding_focused">12dp</dimen>
+    <dimen name="location_bar_icon_end_padding_focused_smaller">10dp</dimen>
     <dimen name="location_bar_verbose_start_padding_verbose_text">4dp</dimen>
     <dimen name="location_bar_status_extra_padding">4dp</dimen>
     <dimen name="location_bar_url_action_offset">-8dp</dimen>
     <dimen name="location_bar_url_action_padding">8dp</dimen>
 
-    <dimen name="location_bar_status_view_left_space_width">8dp</dimen>
+    <dimen name="location_bar_status_view_left_space_width">10dp</dimen>
+    <dimen name="location_bar_status_view_left_space_width_bigger">12dp</dimen>
 
     <dimen name="tablet_toolbar_start_padding">4dp</dimen>
 
@@ -87,14 +89,11 @@
     <dimen name="omnibox_suggestion_36dp_icon_size">36dp</dimen>
     <dimen name="omnibox_suggestion_24dp_icon_size">24dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_start">10dp</dimen>
-    <dimen name="omnibox_suggestion_36dp_icon_margin_start_smaller">9dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_start_smallest">8dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_end">10dp</dimen>
-    <dimen name="omnibox_suggestion_36dp_icon_margin_end_smaller">9dp</dimen>
     <dimen name="omnibox_suggestion_36dp_icon_margin_end_smallest">8dp</dimen>
     <dimen name="omnibox_suggestion_24dp_icon_margin_start">16dp</dimen>
-    <dimen name="omnibox_suggestion_24dp_icon_margin_start_modern">12dp</dimen>
-    <dimen name="omnibox_suggestion_24dp_icon_margin_end">16dp</dimen>
+    <dimen name="omnibox_suggestion_24dp_icon_margin_start_modern_bigger">14dp</dimen>
     <dimen name="omnibox_suggestion_favicon_size">24dp</dimen>
     <dimen name="omnibox_suggestion_decoration_image_size">36dp</dimen>
     <dimen name="omnibox_suggestion_icon_area_size">56dp</dimen>
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
index 3cf684f..5e81efc7 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarLayout.java
@@ -25,6 +25,7 @@
 
 import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
 import org.chromium.chrome.browser.omnibox.status.StatusView;
+import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.components.browser_ui.widget.CompositeTouchDelegate;
 import org.chromium.ui.base.DeviceFormFactor;
@@ -307,7 +308,11 @@
 
     /** Returns the increase in StatusView end padding, when the Url bar is focused. */
     public int getEndPaddingPixelSizeOnFocusDelta() {
-        return getResources().getDimensionPixelSize(R.dimen.location_bar_icon_end_padding_focused)
+        int focusedPaddingDimen = OmniboxFeatures.shouldShowModernizeVisualUpdate(getContext())
+                        && OmniboxFeatures.shouldShowSmallBottomMargin()
+                ? R.dimen.location_bar_icon_end_padding_focused_smaller
+                : R.dimen.location_bar_icon_end_padding_focused;
+        return getResources().getDimensionPixelSize(focusedPaddingDimen)
                 - getResources().getDimensionPixelSize(R.dimen.location_bar_icon_end_padding);
     }
 
@@ -334,9 +339,11 @@
 
         // Set the left space expansion width.
         ViewGroup.LayoutParams leftSpacingParams = mStatusViewLeftSpace.getLayoutParams();
-        leftSpacingParams.width = (int) (getResources().getDimensionPixelSize(
-                                                 R.dimen.location_bar_status_view_left_space_width)
-                * percent);
+        int dimen = OmniboxResourceProvider.selectMarginDimen(
+                R.dimen.location_bar_status_view_left_space_width,
+                R.dimen.location_bar_status_view_left_space_width_bigger,
+                R.dimen.location_bar_status_view_left_space_width_bigger);
+        leftSpacingParams.width = (int) (getResources().getDimensionPixelSize(dimen) * percent);
         mStatusViewLeftSpace.setLayoutParams(leftSpacingParams);
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
index 63af05c..a04a844 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/styles/OmniboxResourceProvider.java
@@ -391,24 +391,24 @@
     /** Gets the start padding for an omnibox suggestion's decoration icon. */
     public static @Px int getIconStartPadding(Context context) {
         return context.getResources().getDimensionPixelSize(
-                selectMarginDimen(R.dimen.omnibox_suggestion_24dp_icon_margin_start_modern,
-                        R.dimen.omnibox_suggestion_24dp_icon_margin_start_modern,
+                selectMarginDimen(R.dimen.omnibox_suggestion_24dp_icon_margin_start_modern_bigger,
+                        R.dimen.omnibox_suggestion_24dp_icon_margin_start,
                         R.dimen.omnibox_suggestion_24dp_icon_margin_start));
     }
 
     /** Gets the start padding for a large omnibox suggestion decoration icon. */
     public static @Px int getLargeIconStartPadding(Context context) {
         return context.getResources().getDimensionPixelSize(
-                selectMarginDimen(R.dimen.omnibox_suggestion_36dp_icon_margin_start_smaller,
-                        R.dimen.omnibox_suggestion_36dp_icon_margin_start_smallest,
+                selectMarginDimen(R.dimen.omnibox_suggestion_36dp_icon_margin_start_smallest,
+                        R.dimen.omnibox_suggestion_36dp_icon_margin_start,
                         R.dimen.omnibox_suggestion_36dp_icon_margin_start));
     }
 
     /** Gets the end padding for a large omnibox suggestion decoration icon. */
     public static @Px int getLargeIconEndPadding(Context context) {
         return context.getResources().getDimensionPixelSize(
-                selectMarginDimen(R.dimen.omnibox_suggestion_36dp_icon_margin_end_smaller,
-                        R.dimen.omnibox_suggestion_36dp_icon_margin_end_smallest,
+                selectMarginDimen(R.dimen.omnibox_suggestion_36dp_icon_margin_end_smallest,
+                        R.dimen.omnibox_suggestion_36dp_icon_margin_end,
                         R.dimen.omnibox_suggestion_36dp_icon_margin_end));
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index 81f45dbc..48dac0f1 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -73,9 +73,8 @@
 
     private static boolean sDimensionsInitialized;
     private static int sIconWidthPx;
-    private static int sPaddingStart;
+    private static int sPaddingSmallIcon;
     private static int sPaddingStartLargeIcon;
-    private static int sPaddingEnd;
     private static int sPaddingEndLargeIcon;
     private static int sEdgeSize;
     private static int sEdgeSizeLargeIcon;
@@ -208,8 +207,8 @@
                     ViewGroup.LayoutParams.WRAP_CONTENT,
                     SuggestionLayout.LayoutParams.SuggestionViewType.DECORATION));
 
-            int paddingStart = sds.isLarge ? sPaddingStartLargeIcon : sPaddingStart;
-            int paddingEnd = sds.isLarge ? sPaddingEndLargeIcon : sPaddingEnd;
+            int paddingStart = sds.isLarge ? sPaddingStartLargeIcon : sPaddingSmallIcon;
+            int paddingEnd = sds.isLarge ? sPaddingEndLargeIcon : sPaddingSmallIcon;
             int edgeSize = sds.isLarge ? sEdgeSizeLargeIcon : sEdgeSize;
             rciv.setPadding(paddingStart, 0, paddingEnd, 0);
             rciv.setMinimumHeight(edgeSize);
@@ -392,7 +391,8 @@
         view.setClipToOutline(true);
     }
 
-    private static void initializeDimensions(Context context) {
+    @VisibleForTesting
+    static void initializeDimensions(Context context) {
         boolean showModernizeVisualUpdate =
                 OmniboxFeatures.shouldShowModernizeVisualUpdate(context);
         Resources resources = context.getResources();
@@ -400,12 +400,10 @@
                         ? R.dimen.omnibox_suggestion_icon_area_size_modern
                         : R.dimen.omnibox_suggestion_icon_area_size);
 
-        sPaddingStart = showModernizeVisualUpdate
+        sPaddingSmallIcon = showModernizeVisualUpdate
                 ? OmniboxResourceProvider.getIconStartPadding(context)
                 : resources.getDimensionPixelSize(
                         R.dimen.omnibox_suggestion_24dp_icon_margin_start);
-        sPaddingEnd =
-                resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_24dp_icon_margin_end);
         sPaddingStartLargeIcon = OmniboxResourceProvider.getLargeIconStartPadding(context);
         sPaddingEndLargeIcon = OmniboxResourceProvider.getLargeIconEndPadding(context);
         sEdgeSize = resources.getDimensionPixelSize(R.dimen.omnibox_suggestion_24dp_icon_size);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
index 5b5d242..4e6f018 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinderUnitTest.java
@@ -28,6 +28,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
@@ -37,11 +38,15 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.omnibox.OmniboxFeatures;
 import org.chromium.chrome.browser.omnibox.suggestions.DropdownCommonProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.SuggestionCommonProperties;
 import org.chromium.chrome.browser.omnibox.suggestions.base.BaseSuggestionViewProperties.Action;
 import org.chromium.chrome.browser.omnibox.test.R;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.RoundedCornerOutlineProvider;
 import org.chromium.ui.base.TestActivity;
@@ -60,6 +65,7 @@
     @Rule
     public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
             new ActivityScenarioRule<>(TestActivity.class);
+    public @Rule TestRule mFeatures = new Features.JUnitProcessor();
 
     @Mock
     ImageView mIconView;
@@ -92,6 +98,7 @@
         PropertyModelChangeProcessor.create(mModel, mBaseView,
                 new BaseSuggestionViewBinder(
                         (m, v, p) -> { Assert.assertEquals(mContentView, v); }));
+        BaseSuggestionViewBinder.initializeDimensions(mActivity);
     }
 
     @Test
@@ -133,6 +140,63 @@
     }
 
     @Test
+    public void decorIcon_padding() {
+        SuggestionDrawableState state =
+                SuggestionDrawableState.Builder.forColor(0).setUseRoundedCorners(true).build();
+        mModel.set(BaseSuggestionViewProperties.ICON, state);
+        int padding = mResources.getDimensionPixelSize(
+                org.chromium.chrome.browser.omnibox.R.dimen
+                        .omnibox_suggestion_24dp_icon_margin_start);
+
+        verify(mIconView).setPadding(padding, 0, padding, 0);
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE)
+    public void decorIcon_padding_small() {
+        OmniboxFeatures.MODERNIZE_VISUAL_UPDATE_SMALL_BOTTOM_MARGIN.setForTesting(true);
+        BaseSuggestionViewBinder.initializeDimensions(mActivity);
+        SuggestionDrawableState state =
+                SuggestionDrawableState.Builder.forColor(0).setUseRoundedCorners(true).build();
+        mModel.set(BaseSuggestionViewProperties.ICON, state);
+        int padding = mResources.getDimensionPixelSize(
+                org.chromium.chrome.browser.omnibox.R.dimen
+                        .omnibox_suggestion_24dp_icon_margin_start_modern_bigger);
+
+        verify(mIconView).setPadding(padding, 0, padding, 0);
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE)
+    public void decorIcon_padding_smaller() {
+        OmniboxFeatures.MODERNIZE_VISUAL_UPDATE_SMALLER_MARGINS.setForTesting(true);
+        BaseSuggestionViewBinder.initializeDimensions(mActivity);
+        SuggestionDrawableState state =
+                SuggestionDrawableState.Builder.forColor(0).setUseRoundedCorners(true).build();
+        mModel.set(BaseSuggestionViewProperties.ICON, state);
+        int padding = mResources.getDimensionPixelSize(
+                org.chromium.chrome.browser.omnibox.R.dimen
+                        .omnibox_suggestion_24dp_icon_margin_start);
+
+        verify(mIconView).setPadding(padding, 0, padding, 0);
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.OMNIBOX_MODERNIZE_VISUAL_UPDATE)
+    public void decorIcon_padding_smallest() {
+        OmniboxFeatures.MODERNIZE_VISUAL_UPDATE_SMALLEST_MARGINS.setForTesting(true);
+        BaseSuggestionViewBinder.initializeDimensions(mActivity);
+        SuggestionDrawableState state =
+                SuggestionDrawableState.Builder.forColor(0).setUseRoundedCorners(true).build();
+        mModel.set(BaseSuggestionViewProperties.ICON, state);
+        int padding = mResources.getDimensionPixelSize(
+                org.chromium.chrome.browser.omnibox.R.dimen
+                        .omnibox_suggestion_24dp_icon_margin_start);
+
+        verify(mIconView).setPadding(padding, 0, padding, 0);
+    }
+
+    @Test
     public void actionIcon_showIcon() {
         Runnable callback = mock(Runnable.class);
         List<Action> list = Arrays.asList(
diff --git a/chrome/browser/ui/autofill/autofill_bubble_handler.h b/chrome/browser/ui/autofill/autofill_bubble_handler.h
index adbe679..7bff267 100644
--- a/chrome/browser/ui/autofill/autofill_bubble_handler.h
+++ b/chrome/browser/ui/autofill/autofill_bubble_handler.h
@@ -23,6 +23,7 @@
 class VirtualCardEnrollBubbleController;
 class MandatoryReauthBubbleController;
 enum class IbanBubbleType;
+enum class MandatoryReauthBubbleType;
 
 // TODO(crbug.com/1337392): consider removing this class and give the logic back
 // to each bubble's controller. This class serves also the avatar button /
@@ -90,7 +91,8 @@
   virtual AutofillBubbleBase* ShowMandatoryReauthBubble(
       content::WebContents* web_contents,
       MandatoryReauthBubbleController* controller,
-      bool is_user_gesture) = 0;
+      bool is_user_gesture,
+      MandatoryReauthBubbleType bubble_type) = 0;
 
   // TODO(crbug.com/964127): Wait for the integration with sign in after local
   // save to be landed to see if we need to merge password saved and credit card
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller.h b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller.h
index eb51c75..a5be71c 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller.h
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller.h
@@ -32,8 +32,6 @@
   virtual std::u16string GetCancelButtonText() const = 0;
   virtual std::u16string GetExplanationText() const = 0;
 
-  virtual void OnAcceptButton() = 0;
-  virtual void OnCancelButton() = 0;
   virtual void OnBubbleClosed(PaymentsBubbleClosedReason closed_reason) = 0;
 
   // Returns the current bubble view. Can return nullptr if bubble is not
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
index d58995d..f1dcaae5 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
@@ -56,14 +56,28 @@
     return;
   }
 
-  CHECK(accept_mandatory_reauth_callback_ &&
-        cancel_mandatory_reauth_callback_ && close_mandatory_reauth_callback_);
+  // We don't run any callbacks in the confirmation view, so there's no need to
+  // ensure they exist.
+  if (current_bubble_type_ == MandatoryReauthBubbleType::kOptIn) {
+    CHECK(accept_mandatory_reauth_callback_ &&
+          cancel_mandatory_reauth_callback_ &&
+          close_mandatory_reauth_callback_);
+  }
 
   Show();
 }
 
 std::u16string MandatoryReauthBubbleControllerImpl::GetWindowTitle() const {
-  return l10n_util::GetStringUTF16(IDS_AUTOFILL_MANDATORY_REAUTH_OPT_IN_TITLE);
+  switch (current_bubble_type_) {
+    case MandatoryReauthBubbleType::kOptIn:
+      return l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_MANDATORY_REAUTH_OPT_IN_TITLE);
+    case MandatoryReauthBubbleType::kConfirmation:
+      return l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_TITLE);
+    case MandatoryReauthBubbleType::kInactive:
+      return std::u16string();
+  }
 }
 
 std::u16string MandatoryReauthBubbleControllerImpl::GetAcceptButtonText()
@@ -78,29 +92,34 @@
 }
 
 std::u16string MandatoryReauthBubbleControllerImpl::GetExplanationText() const {
-  return l10n_util::GetStringUTF16(
-      IDS_AUTOFILL_MANDATORY_REAUTH_OPT_IN_EXPLANATION);
-}
-
-void MandatoryReauthBubbleControllerImpl::OnAcceptButton() {
-  std::move(accept_mandatory_reauth_callback_).Run();
-}
-
-void MandatoryReauthBubbleControllerImpl::OnCancelButton() {
-  std::move(cancel_mandatory_reauth_callback_).Run();
+  switch (current_bubble_type_) {
+    case MandatoryReauthBubbleType::kOptIn:
+      return l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_MANDATORY_REAUTH_OPT_IN_EXPLANATION);
+    case MandatoryReauthBubbleType::kConfirmation:
+      return l10n_util::GetStringUTF16(
+          IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_EXPLANATION);
+    case MandatoryReauthBubbleType::kInactive:
+      return std::u16string();
+  }
 }
 
 void MandatoryReauthBubbleControllerImpl::OnBubbleClosed(
     PaymentsBubbleClosedReason closed_reason) {
   set_bubble_view(nullptr);
 
-  if (closed_reason == PaymentsBubbleClosedReason::kCancelled ||
-      closed_reason == PaymentsBubbleClosedReason::kAccepted) {
-    // If the user explicitly cancelled or accepted the dialog, we don't want to
-    // display it anymore so we set it to inactive.
-    current_bubble_type_ = MandatoryReauthBubbleType::kInactive;
+  if (current_bubble_type_ == MandatoryReauthBubbleType::kOptIn) {
+    if (closed_reason == PaymentsBubbleClosedReason::kAccepted) {
+      std::move(accept_mandatory_reauth_callback_).Run();
+      current_bubble_type_ = MandatoryReauthBubbleType::kConfirmation;
+    } else if (closed_reason == PaymentsBubbleClosedReason::kCancelled) {
+      std::move(cancel_mandatory_reauth_callback_).Run();
+      current_bubble_type_ = MandatoryReauthBubbleType::kInactive;
+    } else if (closed_reason == PaymentsBubbleClosedReason::kClosed) {
+      close_mandatory_reauth_callback_.Run();
+    }
   } else {
-    close_mandatory_reauth_callback_.Run();
+    current_bubble_type_ = MandatoryReauthBubbleType::kInactive;
   }
 
   UpdatePageActionIcon();
@@ -130,7 +149,7 @@
   AutofillBubbleHandler* autofill_bubble_handler =
       browser->window()->GetAutofillBubbleHandler();
   set_bubble_view(autofill_bubble_handler->ShowMandatoryReauthBubble(
-      web_contents(), this, /*is_user_gesture=*/false));
+      web_contents(), this, /*is_user_gesture=*/false, current_bubble_type_));
 #endif  // !BUILDFLAG(IS_ANDROID)
 }
 
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
index b568a98..dd6340c 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
@@ -35,8 +35,6 @@
   std::u16string GetAcceptButtonText() const override;
   std::u16string GetCancelButtonText() const override;
   std::u16string GetExplanationText() const override;
-  void OnAcceptButton() override;
-  void OnCancelButton() override;
   void OnBubbleClosed(PaymentsBubbleClosedReason closed_reason) override;
   AutofillBubbleBase* GetBubbleView() override;
   bool IsIconVisible() override;
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl_unittest.cc
index 061308a..22da98f 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl_unittest.cc
@@ -52,12 +52,10 @@
   }
 
   void ClickAcceptButton() {
-    controller()->OnAcceptButton();
     controller()->OnBubbleClosed(PaymentsBubbleClosedReason::kAccepted);
   }
 
   void ClickCancelButton() {
-    controller()->OnCancelButton();
     controller()->OnBubbleClosed(PaymentsBubbleClosedReason::kCancelled);
   }
 
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_ui.h b/chrome/browser/ui/autofill/payments/mandatory_reauth_ui.h
index 18d9cca..458b334 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_ui.h
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_ui.h
@@ -15,6 +15,9 @@
 
   // Bubble prompting the user to enable mandatory reauth.
   kOptIn = 1,
+
+  // Bubble confirming that the user has opted into mandatory reauth.
+  kConfirmation = 2,
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
index d1f12e80..2c8ce83 100644
--- a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
+++ b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
@@ -32,16 +32,6 @@
   return iban_bubble_view_.get();
 }
 
-AutofillBubbleBase* TestAutofillBubbleHandler::ShowMandatoryReauthBubble(
-    content::WebContents* web_contents,
-    MandatoryReauthBubbleController* controller,
-    bool is_user_gesture) {
-  if (!mandatory_reauth_bubble_view_) {
-    mandatory_reauth_bubble_view_ = std::make_unique<TestAutofillBubble>();
-  }
-  return mandatory_reauth_bubble_view_.get();
-}
-
 AutofillBubbleBase* TestAutofillBubbleHandler::ShowLocalCardMigrationBubble(
     content::WebContents* web_contents,
     LocalCardMigrationBubbleController* controller,
@@ -119,6 +109,17 @@
   return virtual_card_enroll_bubble_view_.get();
 }
 
+AutofillBubbleBase* TestAutofillBubbleHandler::ShowMandatoryReauthBubble(
+    content::WebContents* web_contents,
+    MandatoryReauthBubbleController* controller,
+    bool is_user_gesture,
+    MandatoryReauthBubbleType bubble_type) {
+  if (!mandatory_reauth_bubble_view_) {
+    mandatory_reauth_bubble_view_ = std::make_unique<TestAutofillBubble>();
+  }
+  return mandatory_reauth_bubble_view_.get();
+}
+
 void TestAutofillBubbleHandler::OnPasswordSaved() {}
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
index 016fb825..f217798 100644
--- a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
+++ b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
@@ -14,6 +14,7 @@
 namespace autofill {
 
 enum class IbanBubbleType;
+enum class MandatoryReauthBubbleType;
 
 class TestAutofillBubble final : public AutofillBubbleBase {
   void Hide() override {}
@@ -75,7 +76,8 @@
   AutofillBubbleBase* ShowMandatoryReauthBubble(
       content::WebContents* web_contents,
       MandatoryReauthBubbleController* controller,
-      bool is_user_gesture) override;
+      bool is_user_gesture,
+      MandatoryReauthBubbleType bubble_type) override;
   void OnPasswordSaved() override;
 
  private:
diff --git a/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc b/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc
index 8c5e999a..cf3d90b 100644
--- a/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc
+++ b/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc
@@ -104,7 +104,7 @@
     mixer[ui::kColorFrameActive] = {color.value()};
   } else if (dwm_frame_color_) {
     mixer[ui::kColorFrameActive] = {dwm_frame_color_.value()};
-  } else if (ShouldDefaultThemeUseMicaTitlebar() && !key.app_controller) {
+  } else if (ShouldDefaultThemeUseMicaTitlebar()) {
     mixer[ui::kColorFrameActive] = {key.color_mode == ColorMode::kDark
                                         ? kSystemMicaDarkFrameColor
                                         : kSystemMicaLightFrameColor};
@@ -114,7 +114,7 @@
     mixer[ui::kColorFrameInactive] = {color.value()};
   } else if (dwm_inactive_frame_color_) {
     mixer[ui::kColorFrameInactive] = {dwm_inactive_frame_color_.value()};
-  } else if (ShouldDefaultThemeUseMicaTitlebar() && !key.app_controller) {
+  } else if (ShouldDefaultThemeUseMicaTitlebar()) {
     mixer[ui::kColorFrameInactive] = {key.color_mode == ColorMode::kDark
                                           ? kSystemMicaDarkFrameColor
                                           : kSystemMicaLightFrameColor};
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index f97201a..140fa0c 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -25,8 +25,11 @@
 #include "components/policy/core/common/management/management_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/vector_icons/vector_icons.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/ui_base_features.h"
+#include "ui/gfx/vector_icon_types.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
@@ -125,6 +128,20 @@
 }
 
 #if !BUILDFLAG(IS_ANDROID)
+
+const gfx::VectorIcon& GetManagedUiIcon(Profile* profile) {
+  CHECK(ShouldDisplayManagedUi(profile));
+
+  if (enterprise_util::IsBrowserManaged(profile)) {
+    return features::IsChromeRefresh2023()
+               ? vector_icons::kBusinessChromeRefreshIcon
+               : vector_icons::kBusinessIcon;
+  }
+
+  CHECK(ShouldDisplayManagedByParentUi(profile));
+  return vector_icons::kFamilyLinkIcon;
+}
+
 std::u16string GetManagedUiMenuItemLabel(Profile* profile) {
   CHECK(ShouldDisplayManagedUi(profile));
   absl::optional<std::string> manager = GetAccountManagerIdentity(profile);
diff --git a/chrome/browser/ui/managed_ui.h b/chrome/browser/ui/managed_ui.h
index dc712561..5e7a049 100644
--- a/chrome/browser/ui/managed_ui.h
+++ b/chrome/browser/ui/managed_ui.h
@@ -13,6 +13,10 @@
 
 class Profile;
 
+namespace gfx {
+struct VectorIcon;
+}
+
 namespace chrome {
 
 // Returns true if a 'Managed by <...>' message should appear in
@@ -31,6 +35,9 @@
 bool ShouldDisplayManagedUi(Profile* profile);
 
 #if !BUILDFLAG(IS_ANDROID)
+// The icon to use in the Managed UI.
+const gfx::VectorIcon& GetManagedUiIcon(Profile* profile);
+
 // The label for the App Menu item for Managed UI.
 //
 // Must only be called if ShouldDisplayManagedUi(profile) is true.
diff --git a/chrome/browser/ui/managed_ui_browsertest.cc b/chrome/browser/ui/managed_ui_browsertest.cc
index 9240e59..2ee5562 100644
--- a/chrome/browser/ui/managed_ui_browsertest.cc
+++ b/chrome/browser/ui/managed_ui_browsertest.cc
@@ -23,8 +23,10 @@
 #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "components/supervised_user/core/common/features.h"
+#include "components/vector_icons/vector_icons.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "ui/gfx/vector_icon_types.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
@@ -129,6 +131,45 @@
 // On ChromeOS we don't display the management UI for enterprise or supervised
 // users.
 #if !BUILDFLAG(IS_CHROMEOS)
+IN_PROC_BROWSER_TEST_P(ManagedUiTest, GetManagedUiIconEnterprise) {
+  // Simulate a managed device.
+  AddEnterpriseManagedPolicies();
+  policy::ScopedManagementServiceOverrideForTesting browser_management(
+      policy::ManagementServiceFactory::GetForProfile(browser()->profile()),
+      policy::EnterpriseManagementAuthority::CLOUD);
+
+  // An un-supervised profile.
+  TestingProfile::Builder builder;
+  auto profile = builder.Build();
+
+  // Simulate a supervised profile.
+  TestingProfile::Builder builder_supervised;
+  builder_supervised.SetIsSupervisedProfile();
+  std::unique_ptr<TestingProfile> profile_supervised =
+      builder_supervised.Build();
+
+  EXPECT_EQ(vector_icons::kBusinessIcon.name,
+            chrome::GetManagedUiIcon(profile.get()).name);
+  // Enterprise management takes precedence over supervision in the management
+  // UI.
+  EXPECT_EQ(vector_icons::kBusinessIcon.name,
+            chrome::GetManagedUiIcon(profile_supervised.get()).name);
+}
+
+IN_PROC_BROWSER_TEST_P(ManagedUiTest, GetManagedUiIconSupervised) {
+  if (!IsManagedUiEnabledForSupervisedUsers()) {
+    return;
+  }
+
+  // Simulate a supervised profile.
+  TestingProfile::Builder builder;
+  builder.SetIsSupervisedProfile();
+  std::unique_ptr<TestingProfile> profile = builder.Build();
+
+  EXPECT_EQ(vector_icons::kFamilyLinkIcon.name,
+            chrome::GetManagedUiIcon(profile.get()).name);
+}
+
 IN_PROC_BROWSER_TEST_P(ManagedUiTest, GetManagedUiMenuItemLabelEnterprise) {
   // Simulate a managed profile.
   AddEnterpriseManagedPolicies();
diff --git a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc
index 953110d..b5b8b49 100644
--- a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc
+++ b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper.cc
@@ -125,6 +125,12 @@
   auto* browser =
       chrome::FindBrowserWithWebContents(navigation_handle->GetWebContents());
 
+  // If a sign-in dialog is being currently displayed, the prompt should
+  // not be shown to avoid conflict.
+  if (browser->signin_view_controller()->ShowsModalDialog()) {
+    return;
+  }
+
   // If a Privacy Sandbox prompt already exists for this browser, do not attempt
   // to open another one.
   if (auto* privacy_sandbox_service =
diff --git a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc
index ba1dd3a..71d420c7 100644
--- a/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc
+++ b/chrome/browser/ui/privacy_sandbox/privacy_sandbox_prompt_helper_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
+#include "components/signin/public/base/signin_buildflags.h"
 #include "components/sync/test/test_sync_service.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test.h"
@@ -260,6 +261,25 @@
                                     0);
 }
 
+#if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+IN_PROC_BROWSER_TEST_P(PrivacySandboxPromptHelperTestWithParam,
+                       NoPromptProfileSetup) {
+  // Check when profile setup is in progress, that no prompt is shown.
+  base::HistogramTester histogram_tester;
+  EXPECT_CALL(*mock_privacy_sandbox_service(),
+              PromptOpenedForBrowser(browser()))
+      .Times(0);
+  // Show the profile customization dialog.
+  browser()->signin_view_controller()->ShowModalProfileCustomizationDialog(
+      /*is_local_profile_creation=*/true);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), GURL(chrome::kChromeUINewTabPageURL)));
+  base::RunLoop().RunUntilIdle();
+  histogram_tester.ExpectTotalCount(kPrivacySandboxDialogDisplayHostHistogram,
+                                    0);
+}
+#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
+
 IN_PROC_BROWSER_TEST_P(PrivacySandboxPromptHelperTestWithParam, UnsuitableUrl) {
   // Check that no prompt is shown for navigations to unsuitable URLs.
   base::HistogramTester histogram_tester;
diff --git a/chrome/browser/ui/side_panel/companion/companion_utils.cc b/chrome/browser/ui/side_panel/companion/companion_utils.cc
index 71d60d7..117a4fa 100644
--- a/chrome/browser/ui/side_panel/companion/companion_utils.cc
+++ b/chrome/browser/ui/side_panel/companion/companion_utils.cc
@@ -75,8 +75,7 @@
 
   Browser* const browser = chrome::FindBrowserWithWebContents(web_contents);
   PrefService* const pref_service = browser->profile()->GetPrefs();
-  if (base::FeatureList::IsEnabled(companion::features::kSidePanelCompanion) &&
-      pref_service &&
+  if (IsCompanionFeatureEnabled() && pref_service &&
       pref_service->GetBoolean(
           prefs::kSidePanelCompanionEntryPinnedToToolbar)) {
     browser->window()->MaybeShowFeaturePromo(
diff --git a/chrome/browser/ui/tabs/tab_menu_model.cc b/chrome/browser/ui/tabs/tab_menu_model.cc
index 8a079c2..56692985 100644
--- a/chrome/browser/ui/tabs/tab_menu_model.cc
+++ b/chrome/browser/ui/tabs/tab_menu_model.cc
@@ -153,12 +153,11 @@
     AddSeparator(ui::NORMAL_SEPARATOR);
 #if BUILDFLAG(IS_MAC)
     AddItem(TabStripModel::CommandSendTabToSelf,
-            l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF));
+            l10n_util::GetStringUTF16(IDS_MENU_SEND_TAB_TO_SELF));
 #else
-    AddItemWithIcon(
-        TabStripModel::CommandSendTabToSelf,
-        l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF),
-        ui::ImageModel::FromVectorIcon(kLaptopAndSmartphoneIcon));
+    AddItemWithIcon(TabStripModel::CommandSendTabToSelf,
+                    l10n_util::GetStringUTF16(IDS_MENU_SEND_TAB_TO_SELF),
+                    ui::ImageModel::FromVectorIcon(kLaptopAndSmartphoneIcon));
 #endif
   }
 
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 538d314b..295a934 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -1259,13 +1259,13 @@
           IDC_SHOW_MANAGEMENT_PAGE,
           chrome::GetManagedUiMenuItemLabel(browser_->profile()),
           ui::ImageModel::FromVectorIcon(
-              vector_icons::kBusinessIcon,
+              chrome::GetManagedUiIcon(browser_->profile()),
               ui::kColorMenuItemForegroundHighlighted, kIconSize));
     } else {
       AddItemWithIcon(IDC_SHOW_MANAGEMENT_PAGE,
                       chrome::GetManagedUiMenuItemLabel(browser_->profile()),
                       ui::ImageModel::FromVectorIcon(
-                          vector_icons::kBusinessChromeRefreshIcon,
+                          chrome::GetManagedUiIcon(browser_->profile()),
                           ui::kColorMenuIcon, kDefaultIconSize));
     }
   }
diff --git a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
index c419711..5219b6eb 100644
--- a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
+++ b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/views/autofill/payments/local_card_migration_bubble_views.h"
 #include "chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.h"
 #include "chrome/browser/ui/views/autofill/payments/manage_saved_iban_bubble_view.h"
+#include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h"
 #include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h"
 #include "chrome/browser/ui/views/autofill/payments/offer_notification_bubble_views.h"
 #include "chrome/browser/ui/views/autofill/payments/offer_notification_icon_view.h"
@@ -311,7 +312,8 @@
 AutofillBubbleBase* AutofillBubbleHandlerImpl::ShowMandatoryReauthBubble(
     content::WebContents* web_contents,
     MandatoryReauthBubbleController* controller,
-    bool is_user_gesture) {
+    bool is_user_gesture,
+    MandatoryReauthBubbleType bubble_type) {
   PageActionIconView* icon_view =
       toolbar_button_provider_->GetPageActionIconView(
           PageActionIconType::kMandatoryReauth);
@@ -319,16 +321,30 @@
   views::View* anchor_view = toolbar_button_provider_->GetAnchorView(
       PageActionIconType::kMandatoryReauth);
 
-  MandatoryReauthOptInBubbleView* bubble =
-      new MandatoryReauthOptInBubbleView(anchor_view, web_contents, controller);
-
-  DCHECK(bubble);
-  bubble->SetHighlightedButton(icon_view);
-
-  views::BubbleDialogDelegateView::CreateBubble(bubble);
-  bubble->Show(is_user_gesture ? LocationBarBubbleDelegateView::USER_GESTURE
-                               : LocationBarBubbleDelegateView::AUTOMATIC);
-  return bubble;
+  switch (bubble_type) {
+    case MandatoryReauthBubbleType::kOptIn: {
+      MandatoryReauthOptInBubbleView* bubble =
+          new MandatoryReauthOptInBubbleView(anchor_view, web_contents,
+                                             controller);
+      bubble->SetHighlightedButton(icon_view);
+      views::BubbleDialogDelegateView::CreateBubble(bubble);
+      bubble->Show(is_user_gesture ? LocationBarBubbleDelegateView::USER_GESTURE
+                                   : LocationBarBubbleDelegateView::AUTOMATIC);
+      return bubble;
+    }
+    case MandatoryReauthBubbleType::kConfirmation: {
+      MandatoryReauthConfirmationBubbleView* bubble =
+          new MandatoryReauthConfirmationBubbleView(anchor_view, web_contents,
+                                                    controller);
+      bubble->SetHighlightedButton(icon_view);
+      views::BubbleDialogDelegateView::CreateBubble(bubble);
+      bubble->Show(is_user_gesture ? LocationBarBubbleDelegateView::USER_GESTURE
+                                   : LocationBarBubbleDelegateView::AUTOMATIC);
+      return bubble;
+    }
+    case MandatoryReauthBubbleType::kInactive:
+      NOTREACHED_NORETURN();
+  }
 }
 
 void AutofillBubbleHandlerImpl::OnPasswordSaved() {}
diff --git a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h
index 8c7d350..8d4bdec6e 100644
--- a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h
+++ b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h
@@ -83,7 +83,8 @@
   AutofillBubbleBase* ShowMandatoryReauthBubble(
       content::WebContents* web_contents,
       MandatoryReauthBubbleController* controller,
-      bool is_user_gesture) override;
+      bool is_user_gesture,
+      MandatoryReauthBubbleType bubble_type) override;
 
   void OnPasswordSaved() override;
 
diff --git a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc
index 614555e..f96755e9 100644
--- a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc
+++ b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_bubble_view_uitest.cc
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/functional/bind.h"
+#include "base/test/mock_callback.h"
 #include "chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h"
+#include "chrome/browser/ui/autofill/payments/mandatory_reauth_ui.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_icon_view.h"
 #include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h"
@@ -35,8 +38,18 @@
 
   void ShowBubble() {
     MandatoryReauthBubbleControllerImpl* controller = GetController();
-    controller->ShowBubble(base::DoNothing(), base::DoNothing(),
-                           base::DoNothing());
+    controller->ShowBubble(accept_callback.Get(), cancel_callback.Get(),
+                           close_callback.Get());
+    views::test::WidgetVisibleWaiter visible_waiter(
+        static_cast<MandatoryReauthOptInBubbleView*>(
+            controller->GetBubbleView())
+            ->GetWidget());
+    visible_waiter.Wait();
+  }
+
+  void ReshowBubble() {
+    MandatoryReauthBubbleControllerImpl* controller = GetController();
+    controller->ReshowBubble();
     views::test::WidgetVisibleWaiter visible_waiter(
         static_cast<MandatoryReauthOptInBubbleView*>(
             controller->GetBubbleView())
@@ -56,7 +69,7 @@
         browser()->tab_strip_model()->GetActiveWebContents());
   }
 
-  views::BubbleDialogDelegate* GetOptInBubble() {
+  views::BubbleDialogDelegate* GetReauthBubble() {
     return GetIconView()->GetBubble();
   }
 
@@ -109,29 +122,95 @@
     ClickOnViewAndWait(cancel_button, mandatory_reauth_bubble);
   }
 
+  void ClickOnCloseButton(
+      views::BubbleDialogDelegate* mandatory_reauth_bubble) {
+    views::View* close_button = mandatory_reauth_bubble->GetBubbleFrameView()
+                                    ->GetCloseButtonForTesting();
+    ClickOnViewAndWait(close_button, mandatory_reauth_bubble);
+  }
+
+  base::MockOnceClosure accept_callback;
+  base::MockOnceClosure cancel_callback;
+  base::MockRepeatingClosure close_callback;
+
  protected:
   test::AutofillBrowserTestEnvironment autofill_test_environment_;
 };
 
 IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ShowBubble) {
   ShowBubble();
-  EXPECT_TRUE(GetOptInBubble());
+  EXPECT_TRUE(GetReauthBubble());
   EXPECT_TRUE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kOptIn);
 }
 
 IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest,
                        ClickOptInCancelButton) {
   ShowBubble();
-  ClickOnCancelButton(GetOptInBubble());
-  EXPECT_FALSE(GetOptInBubble());
+  EXPECT_CALL(cancel_callback, Run).Times(1);
+  ClickOnCancelButton(GetReauthBubble());
+  EXPECT_FALSE(GetReauthBubble());
   EXPECT_FALSE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kInactive);
 }
 
 IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ClickOptInOkButton) {
   ShowBubble();
-  ClickOnOkButton(GetOptInBubble());
-  EXPECT_FALSE(GetOptInBubble());
+  EXPECT_CALL(accept_callback, Run).Times(1);
+  ClickOnOkButton(GetReauthBubble());
+  EXPECT_FALSE(GetReauthBubble());
+  EXPECT_TRUE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kConfirmation);
+}
+
+IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ClickOptInCloseButton) {
+  ShowBubble();
+  EXPECT_CALL(close_callback, Run).Times(1);
+  ClickOnCloseButton(GetReauthBubble());
+  EXPECT_FALSE(GetReauthBubble());
+  EXPECT_TRUE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kOptIn);
+}
+
+IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest, ReshowOptInBubble) {
+  ShowBubble();
+  ClickOnCloseButton(GetReauthBubble());
+  ReshowBubble();
+  EXPECT_TRUE(GetReauthBubble());
+  EXPECT_TRUE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kOptIn);
+}
+
+IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest,
+                       ReshowConfirmationBubble) {
+  ShowBubble();
+  ClickOnOkButton(GetReauthBubble());
+  ReshowBubble();
+  EXPECT_TRUE(GetReauthBubble());
+  EXPECT_TRUE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kConfirmation);
+}
+
+IN_PROC_BROWSER_TEST_F(MandatoryReauthBubbleViewUiTest,
+                       ClickConfirmationCloseButton) {
+  ShowBubble();
+  ClickOnOkButton(GetReauthBubble());
+  ReshowBubble();
+  EXPECT_TRUE(GetReauthBubble());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kConfirmation);
+  EXPECT_CALL(close_callback, Run).Times(0);
+  ClickOnCloseButton(GetReauthBubble());
+  EXPECT_FALSE(GetReauthBubble());
   EXPECT_FALSE(IsIconVisible());
+  EXPECT_EQ(GetController()->GetBubbleType(),
+            MandatoryReauthBubbleType::kInactive);
 }
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.cc b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.cc
new file mode 100644
index 0000000..1f81db85
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.cc
@@ -0,0 +1,108 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h"
+#include "chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h"
+#include "chrome/browser/ui/views/autofill/payments/dialog_view_ids.h"
+#include "chrome/browser/ui/views/autofill/payments/payments_view_util.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "chrome/grit/theme_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/vector_icon_utils.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/styled_label.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/view_class_properties.h"
+
+namespace autofill {
+
+MandatoryReauthConfirmationBubbleView::MandatoryReauthConfirmationBubbleView(
+    views::View* anchor_view,
+    content::WebContents* web_contents,
+    MandatoryReauthBubbleController* controller)
+    : LocationBarBubbleDelegateView(anchor_view, web_contents),
+      controller_(controller) {
+  SetButtons(ui::DIALOG_BUTTON_NONE);
+  SetShowCloseButton(true);
+  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
+}
+
+void MandatoryReauthConfirmationBubbleView::Show(DisplayReason reason) {
+  ShowForReason(reason);
+}
+
+void MandatoryReauthConfirmationBubbleView::Hide() {
+  CloseBubble();
+  WindowClosing();
+  controller_ = nullptr;
+}
+
+void MandatoryReauthConfirmationBubbleView::AddedToWidget() {
+  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+  auto* mandatory_reauth_opt_in_banner =
+      bundle.GetImageSkiaNamed(IDR_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION);
+  GetBubbleFrameView()->SetHeaderView(
+      std::make_unique<ThemeTrackingNonAccessibleImageView>(
+          *mandatory_reauth_opt_in_banner, *mandatory_reauth_opt_in_banner,
+          base::BindRepeating(&views::BubbleDialogDelegate::GetBackgroundColor,
+                              base::Unretained(this))));
+}
+
+std::u16string MandatoryReauthConfirmationBubbleView::GetWindowTitle() const {
+  return controller_ ? controller_->GetWindowTitle() : std::u16string();
+}
+
+void MandatoryReauthConfirmationBubbleView::WindowClosing() {
+  if (controller_) {
+    controller_->OnBubbleClosed(
+        GetPaymentsBubbleClosedReasonFromWidget(GetWidget()));
+    controller_ = nullptr;
+  }
+}
+
+MandatoryReauthConfirmationBubbleView::
+    ~MandatoryReauthConfirmationBubbleView() = default;
+
+void MandatoryReauthConfirmationBubbleView::OnSettingsLinkClicked() {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+  chrome::ShowSettingsSubPage(browser, chrome::kPaymentsSubPage);
+}
+
+void MandatoryReauthConfirmationBubbleView::Init() {
+  SetID(DialogViewId::MAIN_CONTENT_VIEW_LOCAL);
+  SetProperty(views::kMarginsKey, gfx::Insets());
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical));
+  std::u16string link_text = l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_SETTINGS_LINK);
+  size_t link_offset;
+  std::u16string explanation_text = l10n_util::GetStringFUTF16(
+      IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_EXPLANATION, link_text,
+      &link_offset);
+  views::StyledLabel::RangeStyleInfo style_info =
+      views::StyledLabel::RangeStyleInfo::CreateForLink(base::BindRepeating(
+          &MandatoryReauthConfirmationBubbleView::OnSettingsLinkClicked,
+          weak_ptr_factory_.GetWeakPtr()));
+  AddChildView(views::Builder<views::StyledLabel>()
+                   .SetText(explanation_text)
+                   .SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT)
+                   .SetDefaultTextStyle(views::style::STYLE_SECONDARY)
+                   .SetHorizontalAlignment(gfx::ALIGN_LEFT)
+                   .AddStyleRange(gfx::Range(link_offset,
+                                             link_offset + link_text.length()),
+                                  style_info)
+                   .Build());
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h
new file mode 100644
index 0000000..bfe8a32
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_confirmation_bubble_view.h
@@ -0,0 +1,55 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_MANDATORY_REAUTH_CONFIRMATION_BUBBLE_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_MANDATORY_REAUTH_CONFIRMATION_BUBBLE_VIEW_H_
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
+
+namespace autofill {
+
+class MandatoryReauthBubbleController;
+
+class MandatoryReauthConfirmationBubbleView
+    : public AutofillBubbleBase,
+      public LocationBarBubbleDelegateView {
+ public:
+  MandatoryReauthConfirmationBubbleView(
+      views::View* anchor_view,
+      content::WebContents* web_contents,
+      MandatoryReauthBubbleController* controller);
+  MandatoryReauthConfirmationBubbleView(
+      const MandatoryReauthConfirmationBubbleView&) = delete;
+  MandatoryReauthConfirmationBubbleView& operator=(
+      const MandatoryReauthConfirmationBubbleView&) = delete;
+
+  void Show(DisplayReason reason);
+
+  // AutofillBubbleBase:
+  void Hide() override;
+
+  // LocationBarBubbleDelegateView:
+  void AddedToWidget() override;
+  std::u16string GetWindowTitle() const override;
+  void WindowClosing() override;
+
+ private:
+  ~MandatoryReauthConfirmationBubbleView() override;
+
+  void OnSettingsLinkClicked();
+
+  // LocationBarBubbleDelegateView:
+  void Init() override;
+
+  raw_ptr<MandatoryReauthBubbleController> controller_;
+
+  base::WeakPtrFactory<MandatoryReauthConfirmationBubbleView> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_MANDATORY_REAUTH_CONFIRMATION_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.cc b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.cc
index 11cc495..59249a0 100644
--- a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.cc
@@ -29,12 +29,6 @@
       controller_(controller) {
   SetButtonLabel(ui::DIALOG_BUTTON_OK, controller->GetAcceptButtonText());
   SetButtonLabel(ui::DIALOG_BUTTON_CANCEL, controller->GetCancelButtonText());
-  SetCancelCallback(
-      base::BindOnce(&MandatoryReauthOptInBubbleView::OnDialogCancelled,
-                     base::Unretained(this)));
-  SetAcceptCallback(
-      base::BindOnce(&MandatoryReauthOptInBubbleView::OnDialogAccepted,
-                     base::Unretained(this)));
   SetShowCloseButton(true);
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
@@ -75,18 +69,6 @@
 
 MandatoryReauthOptInBubbleView::~MandatoryReauthOptInBubbleView() = default;
 
-void MandatoryReauthOptInBubbleView::OnDialogAccepted() {
-  if (controller_) {
-    controller_->OnAcceptButton();
-  }
-}
-
-void MandatoryReauthOptInBubbleView::OnDialogCancelled() {
-  if (controller_) {
-    controller_->OnCancelButton();
-  }
-}
-
 void MandatoryReauthOptInBubbleView::Init() {
   SetID(DialogViewId::MAIN_CONTENT_VIEW_LOCAL);
   SetProperty(views::kMarginsKey, gfx::Insets());
diff --git a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h
index dfca64fa..e5ab227 100644
--- a/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h
+++ b/chrome/browser/ui/views/autofill/payments/mandatory_reauth_opt_in_bubble_view.h
@@ -36,9 +36,6 @@
  protected:
   ~MandatoryReauthOptInBubbleView() override;
 
-  void OnDialogAccepted();
-  void OnDialogCancelled();
-
   // LocationBarBubbleDelegateView:
   void Init() override;
 
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
index 302e68ce..d0086d6 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -317,8 +317,8 @@
 }
 
 void SavedTabGroupBar::HandleDrop() {
-  // TODO(tbergquist): go through the controller/service
-  saved_tab_group_model_->Reorder(drag_data_->guid(), GetDropIndex().value());
+  saved_tab_group_model_->ReorderGroupLocally(drag_data_->guid(),
+                                              GetDropIndex().value());
   drag_data_.release();
   HideOverflowMenu();
   SchedulePaint();
@@ -420,29 +420,11 @@
 }
 
 void SavedTabGroupBar::SavedTabGroupReorderedLocally() {
-  // Selection sort the buttons to match the model's order.
-  std::unordered_map<std::string, SavedTabGroupButton*> buttons_by_guid;
-  for (views::View* child : children()) {
-    SavedTabGroupButton* button =
-        views::AsViewClass<SavedTabGroupButton>(child);
-    if (button) {
-      buttons_by_guid[button->guid().AsLowercaseString()] = button;
-    }
-  }
+  SavedTabGroupReordered();
+}
 
-  int i = 0;
-  for (SavedTabGroup group : saved_tab_group_model_->saved_tab_groups()) {
-    views::View* const button =
-        buttons_by_guid[group.saved_guid().AsLowercaseString()];
-    ReorderChildView(button, i);
-    button->SetVisible(i < kMaxVisibleButtons);
-
-    i++;
-  }
-
-  // Ensure the overflow button is the last button in the view hierarchy.
-  ReorderChildView(overflow_button_, children().size());
-  PreferredSizeChanged();
+void SavedTabGroupBar::SavedTabGroupReorderedFromSync() {
+  SavedTabGroupReordered();
 }
 
 void SavedTabGroupBar::SavedTabGroupAddedFromSync(const base::Uuid& guid) {
@@ -558,6 +540,32 @@
   SchedulePaint();
 }
 
+void SavedTabGroupBar::SavedTabGroupReordered() {
+  // Selection sort the buttons to match the model's order.
+  std::unordered_map<std::string, SavedTabGroupButton*> buttons_by_guid;
+  for (views::View* child : children()) {
+    SavedTabGroupButton* button =
+        views::AsViewClass<SavedTabGroupButton>(child);
+    if (button) {
+      buttons_by_guid[button->guid().AsLowercaseString()] = button;
+    }
+  }
+
+  int i = 0;
+  for (SavedTabGroup group : saved_tab_group_model_->saved_tab_groups()) {
+    views::View* const button =
+        buttons_by_guid[group.saved_guid().AsLowercaseString()];
+    ReorderChildView(button, i);
+    button->SetVisible(i < kMaxVisibleButtons);
+
+    i++;
+  }
+
+  // Ensure the overflow button is the last button in the view hierarchy.
+  ReorderChildView(overflow_button_, children().size());
+  PreferredSizeChanged();
+}
+
 void SavedTabGroupBar::LoadAllButtonsFromModel() {
   const std::vector<SavedTabGroup>& saved_tab_groups =
       saved_tab_group_model_->saved_tab_groups();
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h
index c14251bc..02bac22 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.h
@@ -74,6 +74,7 @@
       const base::Uuid& group_guid,
       const absl::optional<base::Uuid>& tab_guid = absl::nullopt) override;
   void SavedTabGroupReorderedLocally() override;
+  void SavedTabGroupReorderedFromSync() override;
   void SavedTabGroupAddedFromSync(const base::Uuid& guid) override;
   void SavedTabGroupRemovedFromSync(
       const SavedTabGroup* removed_group) override;
@@ -104,6 +105,10 @@
   // `SavedTabGroupBar` if the `guid` exists in `saved_tab_group_model_`.
   void SavedTabGroupUpdated(const base::Uuid& guid);
 
+  // Reorders all groups in the bookmarks to match the state of
+  // `saved_tab_group_model_`.
+  void SavedTabGroupReordered();
+
   // Adds the button to the child views for a new tab group at a specific index.
   // This function then verifies if the added button and overflow button should
   // be visible/hidden. Also adds a button ptr to the tab_group_buttons_ list.
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
index f4ee55e..8b3338f2 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
@@ -293,11 +293,11 @@
   saved_tab_group_model()->Add(kSavedTabGroup3);
 
   ASSERT_THAT(get_button_guids(), testing::ElementsAre(guid_1, guid_2, guid_3));
-  saved_tab_group_model()->Reorder(kSavedTabGroup2.saved_guid(), 2);
+  saved_tab_group_model()->ReorderGroupLocally(kSavedTabGroup2.saved_guid(), 2);
   EXPECT_THAT(get_button_guids(), testing::ElementsAre(guid_1, guid_3, guid_2));
-  saved_tab_group_model()->Reorder(kSavedTabGroup2.saved_guid(), 0);
+  saved_tab_group_model()->ReorderGroupLocally(kSavedTabGroup2.saved_guid(), 0);
   EXPECT_THAT(get_button_guids(), testing::ElementsAre(guid_2, guid_1, guid_3));
-  saved_tab_group_model()->Reorder(kSavedTabGroup2.saved_guid(), 1);
+  saved_tab_group_model()->ReorderGroupLocally(kSavedTabGroup2.saved_guid(), 1);
   EXPECT_THAT(get_button_guids(), testing::ElementsAre(guid_1, guid_2, guid_3));
 }
 
diff --git a/chrome/browser/ui/views/desktop_capture/share_this_tab_dialog_views.cc b/chrome/browser/ui/views/desktop_capture/share_this_tab_dialog_views.cc
index 0da7bff..7b5525e 100644
--- a/chrome/browser/ui/views/desktop_capture/share_this_tab_dialog_views.cc
+++ b/chrome/browser/ui/views/desktop_capture/share_this_tab_dialog_views.cc
@@ -112,6 +112,7 @@
       l10n_util::GetStringFUTF16(IDS_SHARE_THIS_TAB_DIALOG_TITLE, app_name_));
   // TODO(crbug.com/1448008): Prevent non-initial focus of the title label.
   title_label->SetFocusBehavior(View::FocusBehavior::ALWAYS);
+  SetInitiallyFocusedView(title_label);
 
   views::Label* description_label =
       AddChildView(std::make_unique<views::Label>());
@@ -165,6 +166,10 @@
   SetButtonLabel(ui::DIALOG_BUTTON_OK,
                  l10n_util::GetStringUTF16(IDS_SHARE_THIS_TAB_DIALOG_ALLOW));
   SetButtonEnabled(ui::DIALOG_BUTTON_OK, false);
+
+  // Simply pressing ENTER without tab-key navigating to the button
+  // must not accept the dialog, or else that'd be a security issue.
+  SetDefaultButton(ui::DialogButton::DIALOG_BUTTON_NONE);
 }
 
 ShareThisTabDialogView::~ShareThisTabDialogView() = default;
diff --git a/chrome/browser/ui/views/frame/browser_frame.cc b/chrome/browser/ui/views/frame/browser_frame.cc
index 9fc1913..24b85357 100644
--- a/chrome/browser/ui/views/frame/browser_frame.cc
+++ b/chrome/browser/ui/views/frame/browser_frame.cc
@@ -440,6 +440,25 @@
              : views::Widget::GetUserColor();
 }
 
+ui::ColorProviderManager::ColorMode BrowserFrame::GetColorMode() const {
+  // Currently the incognito browser is implemented as unthemed dark mode.
+  if (IsIncognitoBrowser()) {
+    return ui::ColorProviderManager::ColorMode::kDark;
+  }
+
+  const auto* theme_service =
+      ThemeServiceFactory::GetForProfile(browser_view_->browser()->profile());
+  const auto browser_color_scheme = theme_service->GetBrowserColorScheme();
+
+  if (browser_color_scheme == ThemeService::BrowserColorScheme::kSystem) {
+    return Widget::GetColorMode();
+  }
+
+  return browser_color_scheme == ThemeService::BrowserColorScheme::kLight
+             ? ui::ColorProviderManager::ColorMode::kLight
+             : ui::ColorProviderManager::ColorMode::kDark;
+}
+
 void BrowserFrame::OnMenuClosed() {
   menu_runner_.reset();
 }
@@ -461,15 +480,9 @@
 }
 
 void BrowserFrame::SelectNativeTheme() {
-  // Select between regular, dark and Linux toolkit themes.
+  // Select between regular and Linux toolkit themes.
   ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
 
-  if (IsIncognitoBrowser()) {
-    // Incognito browsers should always use the dark NativeTheme instance.
-    SetNativeTheme(ui::NativeTheme::GetInstanceForDarkUI());
-    return;
-  }
-
 #if BUILDFLAG(IS_LINUX)
   const auto* linux_ui_theme =
       ui::LinuxUiTheme::GetForWindow(GetNativeWindow());
diff --git a/chrome/browser/ui/views/frame/browser_frame.h b/chrome/browser/ui/views/frame/browser_frame.h
index bd75762..795f057 100644
--- a/chrome/browser/ui/views/frame/browser_frame.h
+++ b/chrome/browser/ui/views/frame/browser_frame.h
@@ -183,6 +183,7 @@
   void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
   ui::ColorProviderManager::Key GetColorProviderKey() const override;
   absl::optional<SkColor> GetUserColor() const override;
+  ui::ColorProviderManager::ColorMode GetColorMode() const override;
 
  private:
   void OnTouchUiChanged();
diff --git a/chrome/browser/ui/views/frame/browser_frame_browsertest.cc b/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
index 35d4d4c..3ad314f 100644
--- a/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_browsertest.cc
@@ -4,19 +4,29 @@
 
 #include "chrome/browser/ui/views/frame/browser_frame.h"
 
+#include "base/test/bind.h"
 #include "build/build_config.h"
 #include "chrome/browser/devtools/devtools_window_testing.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/chrome_views_delegate.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "ui/color/color_id.h"
+#include "ui/color/color_mixer.h"
+#include "ui/color/color_provider.h"
+#include "ui/color/color_provider_manager.h"
+#include "ui/color/color_recipe.h"
 #include "ui/views/views_delegate.h"
 
 class BrowserFrameBoundsChecker : public ChromeViewsDelegate {
@@ -58,3 +68,97 @@
   ASSERT_TRUE(app_browser->is_type_app());
   app_browser->window()->Close();
 }
+
+class BrowserFrameColorModeTest : public BrowserFrameTest {
+ public:
+  static constexpr SkColor kLightColor = SK_ColorWHITE;
+  static constexpr SkColor kDarkColor = SK_ColorBLACK;
+
+  // BrowserFrameTest:
+  void SetUpOnMainThread() override {
+    BrowserFrameTest::SetUpOnMainThread();
+
+    // Force a light / dark color to be returned for `kColorSysPrimary`
+    // depending on the ColorMode.
+    ui::ColorProviderManager::ResetForTesting();
+    ui::ColorProviderManager::GetForTesting().AppendColorProviderInitializer(
+        base::BindRepeating(&AddColor));
+
+    // Set the default browser pref to follow system color mode.
+    profile()->GetPrefs()->SetInteger(
+        prefs::kBrowserColorScheme,
+        static_cast<int>(ThemeService::BrowserColorScheme::kSystem));
+  }
+
+ protected:
+  static void AddColor(ui::ColorProvider* provider,
+                       const ui::ColorProviderManager::Key& key) {
+    // Add a postprocessing mixer to ensure it is appended to the end of the
+    // pipeline.
+    ui::ColorMixer& mixer = provider->AddPostprocessingMixer();
+    mixer[ui::kColorSysPrimary] = {
+        key.color_mode == ui::ColorProviderManager::ColorMode::kDark
+            ? kDarkColor
+            : kLightColor};
+  }
+
+  // Sets the `kBrowserColorScheme` pref for the `profile`.
+  void SetBrowserColorScheme(Profile* profile,
+                             ThemeService::BrowserColorScheme color_scheme) {
+    profile->GetPrefs()->SetInteger(prefs::kBrowserColorScheme,
+                                    static_cast<int>(color_scheme));
+  }
+
+  Profile* profile() { return browser()->profile(); }
+};
+
+// Verifies the BrowserFrame honors the BrowserColorScheme pref.
+IN_PROC_BROWSER_TEST_F(BrowserFrameColorModeTest, TracksBrowserColorScheme) {
+  // Assert the browser follows the system color mode. Simulate the system color
+  // mode by setting the widget level color mode override.
+  views::Widget* browser_frame =
+      BrowserView::GetBrowserViewForBrowser(browser())->GetWidget();
+  browser_frame->SetColorModeOverride(
+      ui::ColorProviderManager::ColorMode::kLight);
+  EXPECT_EQ(kLightColor,
+            browser_frame->GetColorProvider()->GetColor(ui::kColorSysPrimary));
+
+  browser_frame->SetColorModeOverride(
+      ui::ColorProviderManager::ColorMode::kDark);
+  EXPECT_EQ(kDarkColor,
+            browser_frame->GetColorProvider()->GetColor(ui::kColorSysPrimary));
+
+  // Set the BrowserColorScheme pref. The BrowserFrame should ignore the system
+  // color mode.
+  browser_frame->SetColorModeOverride(
+      ui::ColorProviderManager::ColorMode::kLight);
+  SetBrowserColorScheme(profile(), ThemeService::BrowserColorScheme::kDark);
+  EXPECT_EQ(kDarkColor,
+            browser_frame->GetColorProvider()->GetColor(ui::kColorSysPrimary));
+
+  browser_frame->SetColorModeOverride(
+      ui::ColorProviderManager::ColorMode::kDark);
+  SetBrowserColorScheme(profile(), ThemeService::BrowserColorScheme::kLight);
+  EXPECT_EQ(kLightColor,
+            browser_frame->GetColorProvider()->GetColor(ui::kColorSysPrimary));
+}
+
+// Verifies incognito browsers will always use the dark ColorMode.
+IN_PROC_BROWSER_TEST_F(BrowserFrameColorModeTest, IncognitoAlwaysDarkMode) {
+  // Create an incognito browser.
+  Browser* incognito_browser = CreateIncognitoBrowser(profile());
+  views::Widget* incognito_browser_frame =
+      BrowserView::GetBrowserViewForBrowser(incognito_browser)->GetWidget();
+
+  // The incognito browser should reflect the dark color mode irrespective of
+  // the current BrowserColorScheme.
+  SetBrowserColorScheme(incognito_browser->profile(),
+                        ThemeService::BrowserColorScheme::kLight);
+  EXPECT_EQ(kDarkColor, incognito_browser_frame->GetColorProvider()->GetColor(
+                            ui::kColorSysPrimary));
+
+  SetBrowserColorScheme(incognito_browser->profile(),
+                        ThemeService::BrowserColorScheme::kDark);
+  EXPECT_EQ(kDarkColor, incognito_browser_frame->GetColorProvider()->GetColor(
+                            ui::kColorSysPrimary));
+}
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index a8465a95..a284925 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -1947,7 +1947,7 @@
 
   menu_contents->InsertItemAt(
       index, IDC_SEND_TAB_TO_SELF,
-      l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF));
+      l10n_util::GetStringUTF16(IDS_MENU_SEND_TAB_TO_SELF));
 #if !BUILDFLAG(IS_MAC)
   menu_contents->SetIcon(
       index, ui::ImageModel::FromVectorIcon(kLaptopAndSmartphoneIcon));
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc
index 04dd532b..899f3f99 100644
--- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_device_picker_bubble_view.cc
@@ -77,7 +77,7 @@
 }
 
 std::u16string SendTabToSelfDevicePickerBubbleView::GetWindowTitle() const {
-  return l10n_util::GetStringUTF16(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF);
+  return l10n_util::GetStringUTF16(IDS_SEND_TAB_TO_SELF);
 }
 
 void SendTabToSelfDevicePickerBubbleView::WindowClosing() {
diff --git a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_promo_bubble_view.cc b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_promo_bubble_view.cc
index 65eba45b..e0cc849 100644
--- a/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_promo_bubble_view.cc
+++ b/chrome/browser/ui/views/send_tab_to_self/send_tab_to_self_promo_bubble_view.cc
@@ -33,7 +33,7 @@
   DCHECK(controller_);
 
   SetShowCloseButton(true);
-  SetTitle(IDS_CONTEXT_MENU_SEND_TAB_TO_SELF);
+  SetTitle(IDS_SEND_TAB_TO_SELF);
 
   auto* provider = ChromeLayoutProvider::Get();
   set_fixed_width(
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 11e8647..ecfb2235 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -71,6 +71,7 @@
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
@@ -1162,6 +1163,12 @@
     return false;
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (chromeos::features::IsJellyrollEnabled()) {
+    return false;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // The tabstrip normally avoids strokes and relies on the active tab
   // contrasting sufficiently with the frame background.  When there isn't
   // enough contrast, fall back to a stroke.  Always compute the contrast ratio
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
index 983f907..d4913dc4 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "cc/paint/skottie_wrapper.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -18,12 +19,15 @@
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/resource/resource_bundle.h"
 #include "ui/color/color_provider.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/lottie/animation.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/animated_image_view.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
 #include "ui/views/controls/image_view.h"
@@ -33,12 +37,19 @@
 
 namespace {
 
-// Height of the progress bar style activity indicator shown at the top of some
-// sheets.
-constexpr int kActivityIndicatorHeight = 4;
+// Margin between the top of the dialog and the start of any illustration.
+constexpr int kImageMarginTop = 22;
 
 using ImageColorScheme = AuthenticatorRequestSheetModel::ImageColorScheme;
 
+template <typename T>
+void ConfigureHeaderIllustration(T* illustration, gfx::Size header_size) {
+  illustration->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets::TLBR(kImageMarginTop, 0, kImageMarginTop, 0)));
+  illustration->SetSize(header_size);
+  illustration->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
+}
+
 }  // namespace
 
 using views::BoxLayout;
@@ -84,34 +95,41 @@
 
 std::unique_ptr<views::View>
 AuthenticatorRequestSheetView::CreateIllustrationWithOverlays() {
-  // Some sheets do not have an illustration.
-  const gfx::VectorIcon& illustration =
-      model()->GetStepIllustration(ImageColorScheme::kLight);
-  if (&illustration == &gfx::kNoneIcon) {
+  constexpr int kImageHeight = 112, kImageMarginBottom = 2;
+  constexpr int kHeaderHeight =
+      kImageHeight + kImageMarginTop + kImageMarginBottom;
+  const int dialog_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
+  const gfx::Size header_size(dialog_width, kHeaderHeight);
+
+  // The actual illustration image is set in `UpdateIconImageFromModel`, below,
+  // because it's not until that point that we know whether the light or dark
+  // illustration should be used.
+  View* illustration;
+  if (model()->lottie_illustration_light_id()) {
+    auto animation = std::make_unique<views::AnimatedImageView>();
+    animation->SetPreferredSize(gfx::Size(dialog_width, kImageHeight));
+    ConfigureHeaderIllustration(animation.get(), header_size);
+    step_illustration_animation_ = animation.get();
+    illustration = animation.release();
+  } else if (&model()->GetStepIllustration(ImageColorScheme::kLight) !=
+             &gfx::kNoneIcon) {
+    auto image_view = std::make_unique<NonAccessibleImageView>();
+    ConfigureHeaderIllustration(image_view.get(), header_size);
+    step_illustration_image_ = image_view.get();
+    illustration = image_view.release();
+  } else {
     return std::make_unique<views::View>();
   }
 
-  const int dialog_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
-      views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
-  constexpr int kImageHeight = 112, kImageMarginTop = 22,
-                kImageMarginBottom = 2;
-  const int header_height = kImageHeight + kImageMarginTop + kImageMarginBottom;
-  const gfx::Size image_view_size(dialog_width, header_height);
-
   // The container view has no layout, so its preferred size is hardcoded to
-  // match the size of the image, and all overlays are absolutely positioned.
+  // match the size of the header, and all overlays are absolutely positioned.
   auto header_view = std::make_unique<views::View>();
-  header_view->SetPreferredSize(image_view_size);
-
-  auto image_view = std::make_unique<NonAccessibleImageView>();
-  step_illustration_ = image_view.get();
-  image_view->SetBorder(views::CreateEmptyBorder(
-      gfx::Insets::TLBR(kImageMarginTop, 0, kImageMarginTop, 0)));
-  image_view->SetSize(image_view_size);
-  image_view->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
-  header_view->AddChildView(image_view.release());
+  header_view->SetPreferredSize(header_size);
+  header_view->AddChildView(illustration);
 
   if (model()->IsActivityIndicatorVisible()) {
+    constexpr int kActivityIndicatorHeight = 4;
     auto activity_indicator = std::make_unique<views::ProgressBar>(
         kActivityIndicatorHeight, false /* allow_round_corner */);
     activity_indicator->SetValue(-1 /* inifinite animation */);
@@ -213,14 +231,23 @@
 }
 
 void AuthenticatorRequestSheetView::UpdateIconImageFromModel() {
-  if (!step_illustration_) {
-    return;
+  const bool is_dark = GetNativeTheme()->ShouldUseDarkColors();
+  if (step_illustration_image_) {
+    gfx::IconDescription icon_description(model()->GetStepIllustration(
+        is_dark ? ImageColorScheme::kDark : ImageColorScheme::kLight));
+    step_illustration_image_->SetImage(gfx::CreateVectorIcon(icon_description));
+  } else if (step_illustration_animation_) {
+    const int lottie_id = is_dark ? *model()->lottie_illustration_dark_id()
+                                  : *model()->lottie_illustration_light_id();
+    absl::optional<std::vector<uint8_t>> lottie_bytes =
+        ui::ResourceBundle::GetSharedInstance().GetLottieData(lottie_id);
+    scoped_refptr<cc::SkottieWrapper> skottie =
+        cc::SkottieWrapper::CreateSerializable(std::move(*lottie_bytes));
+    step_illustration_animation_->SetAnimatedImage(
+        std::make_unique<lottie::Animation>(skottie));
+    step_illustration_animation_->SizeToPreferredSize();
+    step_illustration_animation_->Play();
   }
-
-  gfx::IconDescription icon_description(model()->GetStepIllustration(
-      GetNativeTheme()->ShouldUseDarkColors() ? ImageColorScheme::kDark
-                                              : ImageColorScheme::kLight));
-  step_illustration_->SetImage(gfx::CreateVectorIcon(icon_description));
 }
 
 void AuthenticatorRequestSheetView::UpdateIconColors() {
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h
index 1dcf6b17..9178b2fe 100644
--- a/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h
@@ -14,6 +14,7 @@
 #include "ui/views/view.h"
 
 namespace views {
+class AnimatedImageView;
 class Label;
 }  // namespace views
 
@@ -118,7 +119,8 @@
   raw_ptr<views::ImageButton> close_button_ = nullptr;
   raw_ptr<views::View, DanglingUntriaged> step_specific_content_ = nullptr;
   AutoFocus should_focus_step_specific_content_ = AutoFocus::kNo;
-  raw_ptr<NonAccessibleImageView> step_illustration_ = nullptr;
+  raw_ptr<NonAccessibleImageView> step_illustration_image_ = nullptr;
+  raw_ptr<views::AnimatedImageView> step_illustration_animation_ = nullptr;
   raw_ptr<views::Label, DanglingUntriaged> error_label_ = nullptr;
 };
 
diff --git a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
index fa55616..3b003d26 100644
--- a/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
+++ b/chrome/browser/ui/views/webauthn/sheet_view_factory.cc
@@ -209,6 +209,18 @@
       sheet_view = std::make_unique<AuthenticatorQRSheetView>(
           std::make_unique<AuthenticatorQRSheetModel>(dialog_model));
       break;
+    case Step::kCableV2Connecting:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          std::make_unique<AuthenticatorConnectingSheetModel>(dialog_model));
+      break;
+    case Step::kCableV2Connected:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          std::make_unique<AuthenticatorConnectedSheetModel>(dialog_model));
+      break;
+    case Step::kCableV2Error:
+      sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
+          std::make_unique<AuthenticatorCableErrorSheetModel>(dialog_model));
+      break;
     case Step::kClientPinChange:
       sheet_view = std::make_unique<AuthenticatorClientPinEntrySheetView>(
           std::make_unique<AuthenticatorClientPinEntrySheetModel>(
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
index 4657739..e828f3933 100644
--- a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -130,6 +130,15 @@
           /*contact_phone_callback=*/base::DoNothing(), "fido://qrcode");
       model_->SetCurrentStepForTesting(
           AuthenticatorRequestDialogModel::Step::kCableV2QRCode);
+    } else if (name == "cable_v2_connecting") {
+      model_->SetCurrentStepForTesting(
+          AuthenticatorRequestDialogModel::Step::kCableV2Connecting);
+    } else if (name == "cable_v2_connected") {
+      model_->SetCurrentStepForTesting(
+          AuthenticatorRequestDialogModel::Step::kCableV2Connected);
+    } else if (name == "cable_v2_error") {
+      model_->SetCurrentStepForTesting(
+          AuthenticatorRequestDialogModel::Step::kCableV2Error);
     } else if (name == "phone_aoa") {
       model_->SetCurrentStepForTesting(
           AuthenticatorRequestDialogModel::Step::kAndroidAccessory);
@@ -409,6 +418,18 @@
   ShowAndVerifyUi();
 }
 
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_cable_v2_connecting) {
+  ShowAndVerifyUi();
+}
+
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_cable_v2_connected) {
+  ShowAndVerifyUi();
+}
+
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_cable_v2_error) {
+  ShowAndVerifyUi();
+}
+
 IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_phone_aoa) {
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h b/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h
index 9e05b11..0314a371 100644
--- a/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h
+++ b/chrome/browser/ui/webauthn/authenticator_request_sheet_model.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace gfx {
 struct VectorIcon;
 }
@@ -65,6 +67,18 @@
   virtual void OnAccept() = 0;
   virtual void OnCancel() = 0;
   virtual void OnManageDevices();
+
+  absl::optional<int> lottie_illustration_light_id() const {
+    return lottie_illustration_light_id_;
+  }
+
+  absl::optional<int> lottie_illustration_dark_id() const {
+    return lottie_illustration_dark_id_;
+  }
+
+ protected:
+  absl::optional<int> lottie_illustration_light_id_;
+  absl::optional<int> lottie_illustration_dark_id_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_MODEL_H_
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index 2573349..ed402484 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -18,6 +18,7 @@
 #include "build/build_config.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/webauthn/webauthn_ui_helpers.h"
+#include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/elide_url.h"
@@ -1367,6 +1368,88 @@
   }
 }
 
+// AuthenticatorConnectingSheetModel ------------------------------------------
+
+AuthenticatorConnectingSheetModel::AuthenticatorConnectingSheetModel(
+    AuthenticatorRequestDialogModel* dialog_model)
+    : AuthenticatorSheetModelBase(dialog_model,
+                                  OtherMechanismButtonVisibility::kHidden) {
+  lottie_illustration_light_id_ = IDR_WEBAUTHN_HYBRID_CONNECTING_LIGHT;
+  lottie_illustration_dark_id_ = IDR_WEBAUTHN_HYBRID_CONNECTING_DARK;
+}
+
+AuthenticatorConnectingSheetModel::~AuthenticatorConnectingSheetModel() =
+    default;
+
+const gfx::VectorIcon& AuthenticatorConnectingSheetModel::GetStepIllustration(
+    ImageColorScheme color_scheme) const {
+  // Uses Lottie instead.
+  return gfx::kNoneIcon;
+}
+
+std::u16string AuthenticatorConnectingSheetModel::GetStepTitle() const {
+  return u"Connecting with your device (UNTRANSLATED)";
+}
+
+std::u16string AuthenticatorConnectingSheetModel::GetStepDescription() const {
+  return u"This will just take a moment (UNTRANSLATED)";
+}
+
+// AuthenticatorConnectedSheetModel ------------------------------------------
+
+AuthenticatorConnectedSheetModel::AuthenticatorConnectedSheetModel(
+    AuthenticatorRequestDialogModel* dialog_model)
+    : AuthenticatorSheetModelBase(dialog_model,
+                                  OtherMechanismButtonVisibility::kHidden) {}
+
+AuthenticatorConnectedSheetModel::~AuthenticatorConnectedSheetModel() = default;
+
+bool AuthenticatorConnectedSheetModel::IsActivityIndicatorVisible() const {
+  return false;
+}
+
+const gfx::VectorIcon& AuthenticatorConnectedSheetModel::GetStepIllustration(
+    ImageColorScheme color_scheme) const {
+  return color_scheme == ImageColorScheme::kDark ? kPasskeyPhoneDarkIcon
+                                                 : kPasskeyPhoneIcon;
+}
+
+std::u16string AuthenticatorConnectedSheetModel::GetStepTitle() const {
+  return u"Check your device (UNTRANSLATED)";
+}
+
+std::u16string AuthenticatorConnectedSheetModel::GetStepDescription() const {
+  return u"Follow the steps described on your device (UNTRANSLATED)";
+}
+
+// AuthenticatorCableErrorSheetModel ------------------------------------------
+
+AuthenticatorCableErrorSheetModel::AuthenticatorCableErrorSheetModel(
+    AuthenticatorRequestDialogModel* dialog_model)
+    : AuthenticatorSheetModelBase(dialog_model,
+                                  OtherMechanismButtonVisibility::kHidden) {}
+
+AuthenticatorCableErrorSheetModel::~AuthenticatorCableErrorSheetModel() =
+    default;
+
+bool AuthenticatorCableErrorSheetModel::IsOtherMechanismButtonVisible() const {
+  return false;
+}
+
+const gfx::VectorIcon& AuthenticatorCableErrorSheetModel::GetStepIllustration(
+    ImageColorScheme color_scheme) const {
+  return color_scheme == ImageColorScheme::kDark ? kPasskeyErrorDarkIcon
+                                                 : kPasskeyErrorIcon;
+}
+
+std::u16string AuthenticatorCableErrorSheetModel::GetStepTitle() const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE);
+}
+
+std::u16string AuthenticatorCableErrorSheetModel::GetStepDescription() const {
+  return u"Something went wrong (UNTRANSLATED)";
+}
+
 // AuthenticatorCreatePasskeySheetModel
 // --------------------------------------------------
 
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 4310d620..abd5b3d6 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -588,6 +588,50 @@
   std::u16string GetStepDescription() const override;
 };
 
+class AuthenticatorConnectingSheetModel : public AuthenticatorSheetModelBase {
+ public:
+  explicit AuthenticatorConnectingSheetModel(
+      AuthenticatorRequestDialogModel* dialog_model);
+  ~AuthenticatorConnectingSheetModel() override;
+
+ private:
+  // AuthenticatorSheetModelBase:
+  const gfx::VectorIcon& GetStepIllustration(
+      ImageColorScheme color_scheme) const override;
+  std::u16string GetStepTitle() const override;
+  std::u16string GetStepDescription() const override;
+};
+
+class AuthenticatorConnectedSheetModel : public AuthenticatorSheetModelBase {
+ public:
+  explicit AuthenticatorConnectedSheetModel(
+      AuthenticatorRequestDialogModel* dialog_model);
+  ~AuthenticatorConnectedSheetModel() override;
+
+ private:
+  // AuthenticatorSheetModelBase:
+  bool IsActivityIndicatorVisible() const override;
+  const gfx::VectorIcon& GetStepIllustration(
+      ImageColorScheme color_scheme) const override;
+  std::u16string GetStepTitle() const override;
+  std::u16string GetStepDescription() const override;
+};
+
+class AuthenticatorCableErrorSheetModel : public AuthenticatorSheetModelBase {
+ public:
+  explicit AuthenticatorCableErrorSheetModel(
+      AuthenticatorRequestDialogModel* dialog_model);
+  ~AuthenticatorCableErrorSheetModel() override;
+
+ private:
+  // AuthenticatorSheetModelBase:
+  bool IsOtherMechanismButtonVisible() const override;
+  const gfx::VectorIcon& GetStepIllustration(
+      ImageColorScheme color_scheme) const override;
+  std::u16string GetStepTitle() const override;
+  std::u16string GetStepDescription() const override;
+};
+
 class AuthenticatorCreatePasskeySheetModel
     : public AuthenticatorSheetModelBase {
  public:
diff --git a/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc b/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc
new file mode 100644
index 0000000..17ebc4b
--- /dev/null
+++ b/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc
@@ -0,0 +1,144 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.h"
+
+#include <string>
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/login/test/js_checker.h"
+#include "chrome/browser/ash/login/test/test_predicate_waiter.h"
+#include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/test/base/chrome_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/base/features.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+namespace {
+constexpr test::UIPath kCancelButtonPath = {"redirect-dialog", "cancel-button"};
+constexpr test::UIPath kOpenSettingsButtonPath = {"redirect-dialog",
+                                                  "settings-button"};
+
+bool IsSettingsWindowOpened() {
+  auto* browser_list = BrowserList::GetInstance();
+  return base::ranges::count_if(*browser_list, [](Browser* browser) {
+           return ash::IsBrowserForSystemWebApp(
+               browser, ash::SystemWebAppType::SETTINGS);
+         }) != 0;
+}
+}  // namespace
+
+class KerberosInBrowserDialogButtonTest : public InProcessBrowserTest {
+ public:
+  KerberosInBrowserDialogButtonTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        net::features::kKerberosInBrowserRedirect);
+  }
+
+  KerberosInBrowserDialogButtonTest(const KerberosInBrowserDialogButtonTest&) =
+      delete;
+  KerberosInBrowserDialogButtonTest& operator=(
+      const KerberosInBrowserDialogButtonTest&) = delete;
+
+  ~KerberosInBrowserDialogButtonTest() override = default;
+
+ protected:
+  void SetUpOnMainThread() override {
+    ash::SystemWebAppManager::GetForTest(browser()->profile())
+        ->InstallSystemAppsForTesting();
+  }
+
+  void PressButton(const test::UIPath& button_path) {
+    auto* frame = webui_->GetRenderFrameHost();
+    ASSERT_TRUE(frame);
+
+    // Waiting for the DOM to be fully loaded.
+    std::make_unique<test::TestPredicateWaiter>(
+        base::BindRepeating(&content::RenderFrameHost::IsDOMContentLoaded,
+                            base::Unretained(frame)))
+        ->Wait();
+
+    test::JSChecker checker = test::JSChecker(frame);
+    checker.CreateVisibilityWaiter(/*visibility=*/true, button_path)->Wait();
+    checker.ExpectValidPath(button_path);
+    checker.ClickOnPath(button_path);
+  }
+
+  void EnsureWebUIAvailable() {
+    auto* dialog = ash::KerberosInBrowserDialog::GetDialogForTesting();
+    ASSERT_TRUE(dialog);
+    webui_ = dialog->GetWebUIForTest();
+    ASSERT_TRUE(webui_);
+  }
+
+  void WaitUntilDialogIsClosed() {
+    std::make_unique<test::TestPredicateWaiter>(base::BindRepeating([]() {
+      return !KerberosInBrowserDialog::IsShown();
+    }))->Wait();
+  }
+
+  content::WebUI* webui_;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(KerberosInBrowserDialogButtonTest, CancelButton) {
+  ash::KerberosInBrowserDialog::Show();
+  EXPECT_TRUE(ash::KerberosInBrowserDialog::IsShown());
+  EXPECT_FALSE(IsSettingsWindowOpened());
+
+  EnsureWebUIAvailable();
+  PressButton(kCancelButtonPath);
+  WaitUntilDialogIsClosed();
+
+  // Pressing the cancel button should not open a new settings window.
+  EXPECT_FALSE(IsSettingsWindowOpened());
+}
+
+IN_PROC_BROWSER_TEST_F(KerberosInBrowserDialogButtonTest, SettingsButton) {
+  ash::KerberosInBrowserDialog::Show();
+  EXPECT_TRUE(ash::KerberosInBrowserDialog::IsShown());
+  EXPECT_FALSE(IsSettingsWindowOpened());
+
+  EnsureWebUIAvailable();
+  PressButton(kOpenSettingsButtonPath);
+  // After pressing the button OS Settings should be opened.
+  ui_test_utils::WaitForBrowserToOpen();
+  EXPECT_TRUE(IsSettingsWindowOpened());
+
+  WaitUntilDialogIsClosed();
+}
+
+class KerberosInBrowserDialogFeatureDisabledTest
+    : public KerberosInBrowserDialogButtonTest {
+ public:
+  KerberosInBrowserDialogFeatureDisabledTest() {
+    scoped_feature_list_.InitAndDisableFeature(
+        net::features::kKerberosInBrowserRedirect);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(KerberosInBrowserDialogFeatureDisabledTest, Smoke) {
+  ash::KerberosInBrowserDialog::Show();
+
+  // If the feature is disabled the system dialog is created anyway, but the
+  // WebUI is not loaded.
+  EXPECT_TRUE(ash::KerberosInBrowserDialog::IsShown());
+  auto* dialog = ash::KerberosInBrowserDialog::GetDialogForTesting();
+  ASSERT_TRUE(dialog);
+  webui_ = dialog->GetWebUIForTest();
+  ASSERT_FALSE(webui_);
+}
+
+}  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.cc b/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.cc
index c779aea..8fd231c 100644
--- a/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.cc
+++ b/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.cc
@@ -27,11 +27,6 @@
 constexpr int kKerberosInBrowserDialogHeight = 155;
 }  // namespace
 
-// static
-bool KerberosInBrowserDialog::IsShown() {
-  return g_dialog != nullptr;
-}
-
 void KerberosInBrowserDialog::AdjustWidgetInitParams(
     views::Widget::InitParams* params) {
   params->z_order = ui::ZOrderLevel::kNormal;
@@ -93,4 +88,14 @@
       ->SetBackdropType(WindowBackdrop::BackdropType::kSemiOpaque);
 }
 
+// static
+bool KerberosInBrowserDialog::IsShown() {
+  return g_dialog != nullptr;
+}
+
+// static
+KerberosInBrowserDialog* KerberosInBrowserDialog::GetDialogForTesting() {
+  return g_dialog;
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.h b/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.h
index dfc3dab..da12d60a 100644
--- a/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.h
+++ b/chrome/browser/ui/webui/ash/kerberos/kerberos_in_browser_dialog.h
@@ -17,14 +17,16 @@
   KerberosInBrowserDialog(const KerberosInBrowserDialog&) = delete;
   KerberosInBrowserDialog& operator=(const KerberosInBrowserDialog&) = delete;
 
-  static bool IsShown();
+  // ui::SystemWebDialogDelegate overrides.
+  void AdjustWidgetInitParams(views::Widget::InitParams* params) override;
 
   // Displays the dialog.
   // |close_dialog_closure| will be called when the dialog is closed.
   static void Show(base::OnceClosure close_dialog_closure = base::DoNothing());
 
-  // ui::SystemWebDialogDelegate overrides.
-  void AdjustWidgetInitParams(views::Widget::InitParams* params) override;
+  static bool IsShown();
+
+  static KerberosInBrowserDialog* GetDialogForTesting();
 
  protected:
   explicit KerberosInBrowserDialog(
diff --git a/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc b/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc
index da99d4be..31cc39e 100644
--- a/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/choobe_screen_handler.cc
@@ -35,6 +35,8 @@
     screen_dict.Set("title", base::Value(screen.title_id));
     screen_dict.Set("icon", base::Value(screen.icon_id));
     screen_dict.Set("selected", false);
+    screen_dict.Set("is_revisitable", screen.is_revisitable);
+    screen_dict.Set("is_synced", screen.is_synced);
     screens_list.Append(std::move(screen_dict));
   }
 
diff --git a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
index 0cb35f0..8371e09 100644
--- a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
+++ b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -103,7 +103,6 @@
      IDS_PASSWORD_MANAGER_UI_ALREADY_CHANGED_PASSWORD},
     {"appsLabel", IDS_PASSWORD_MANAGER_UI_APPS_LABEL},
     {"authTimedOut", IDS_PASSWORD_MANAGER_UI_AUTH_TIMED_OUT},
-    {"autosigninDescription", IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC},
     {"autosigninLabel", IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_LABEL},
     {"backToCheckup", IDS_PASSWORD_MANAGER_UI_BACK_TO_CHECKUP_ARIA_DESCRIPTION},
     {"backToPasswords",
@@ -390,6 +389,10 @@
                     InsertBrandedPasswordManager(
                         IDS_PASSWORD_MANAGER_UI_ADD_SHORTCUT_DESCRIPTION));
 
+  source->AddString("autosigninDescription",
+                    InsertBrandedPasswordManager(
+                        IDS_PASSWORD_MANAGER_UI_AUTOSIGNIN_TOGGLE_DESC));
+
   source->AddString(
       "emptyStateImportAccountStore",
       InsertBrandedPasswordManager(
diff --git a/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc b/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc
index 44042f83..f8f5a6c 100644
--- a/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/ash/privacy_hub_handler_unittest.cc
@@ -90,9 +90,7 @@
       }
 
       // Assume that the data is stored in the last valid arg.
-      auto data_span =
-          base::make_span(data->args().begin(), data->args().end());
-      for (const auto& arg : base::Reversed(data_span)) {
+      for (const auto& arg : base::Reversed(data->args())) {
         if (&arg != data->arg1())
           return arg.Clone();
       }
diff --git a/chrome/browser/ui/webui/signin/inline_login_ui.cc b/chrome/browser/ui/webui/signin/inline_login_ui.cc
index dd19d0d..d6ba94c 100644
--- a/chrome/browser/ui/webui/signin/inline_login_ui.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_ui.cc
@@ -166,8 +166,6 @@
     {"edu_coexistence_template.js",
      IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_TEMPLATE_JS},
     {"edu_coexistence_css.js", IDR_EDU_COEXISTENCE_EDU_COEXISTENCE_CSS_JS},
-    {"an_error_occurred.svg", IDR_CHROME_OS_AN_ERROR_OCCURRED_SVG},
-    {"no_network.svg", IDR_CHROME_OS_NO_NETWORK_SVG},
     {"account_manager_signin_blocked_by_policy.svg",
      IDS_ACCOUNT_MANAGER_SIGNIN_BLOCKED_BY_POLICY_SVG},
 
diff --git a/chrome/browser/web_applications/externally_managed_app_manager.cc b/chrome/browser/web_applications/externally_managed_app_manager.cc
index 3dfd90a2..3beba41 100644
--- a/chrome/browser/web_applications/externally_managed_app_manager.cc
+++ b/chrome/browser/web_applications/externally_managed_app_manager.cc
@@ -70,7 +70,9 @@
     : callback(std::move(callback)),
       remaining_install_requests(pending_installs.size()),
       pending_installs(std::move(pending_installs)),
-      remaining_uninstall_requests(remaining_uninstall_requests) {}
+      remaining_uninstall_requests(remaining_uninstall_requests) {
+  CHECK(this->callback);
+}
 
 ExternallyManagedAppManager::SynchronizeRequest::~SynchronizeRequest() =
     default;
@@ -164,6 +166,7 @@
     std::vector<ExternalInstallOptions> desired_apps_install_options,
     ExternalInstallSource install_source,
     SynchronizeCallback callback) {
+  CHECK(callback);
   DCHECK(base::ranges::all_of(
       desired_apps_install_options,
       [&install_source](const ExternalInstallOptions& install_options) {
@@ -494,6 +497,7 @@
     ExternalInstallSource install_source,
     SynchronizeCallback callback,
     AllAppsLock& lock) {
+  CHECK(callback);
   base::Value::Dict debug_info;
   debug_info.Set("install_source", base::ToString(install_source));
   base::Value::List* desired_installs =
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index 3259387..89210a8 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -109,6 +109,9 @@
     kCableActivate,
     kAndroidAccessory,
     kCableV2QRCode,
+    kCableV2Connecting,
+    kCableV2Connected,
+    kCableV2Error,
 
     // Authenticator Client PIN.
     kClientPinChange,
diff --git a/chrome/browser/win/titlebar_config.cc b/chrome/browser/win/titlebar_config.cc
index c270ecf4..92d0e816 100644
--- a/chrome/browser/win/titlebar_config.cc
+++ b/chrome/browser/win/titlebar_config.cc
@@ -11,13 +11,11 @@
 #include "ui/color/win/accent_color_observer.h"
 #include "ui/native_theme/native_theme.h"
 
-namespace {
 // Allows the titlebar to be drawn by the system using the Mica material
 // on Windows 11, version 22H2 and above.
 BASE_FEATURE(kWindows11MicaTitlebar,
              "Windows11MicaTitlebar",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-}  // namespace
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 bool ShouldBrowserCustomDrawTitlebar(BrowserView* browser_view) {
   return !ShouldDefaultThemeUseMicaTitlebar() ||
diff --git a/chrome/browser/win/titlebar_config.h b/chrome/browser/win/titlebar_config.h
index db976dd..48f4137 100644
--- a/chrome/browser/win/titlebar_config.h
+++ b/chrome/browser/win/titlebar_config.h
@@ -5,8 +5,12 @@
 #ifndef CHROME_BROWSER_WIN_TITLEBAR_CONFIG_H_
 #define CHROME_BROWSER_WIN_TITLEBAR_CONFIG_H_
 
+#include "base/feature_list.h"
+
 class BrowserView;
 
+BASE_DECLARE_FEATURE(kWindows11MicaTitlebar);
+
 // Returns whether we should custom draw the titlebar for a browser window.
 bool ShouldBrowserCustomDrawTitlebar(BrowserView* browser_view);
 
diff --git a/chrome/build/lacros-arm.pgo.txt b/chrome/build/lacros-arm.pgo.txt
index a0362b7..1b858d8 100644
--- a/chrome/build/lacros-arm.pgo.txt
+++ b/chrome/build/lacros-arm.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-arm-generic-main-1684799991-1a0d500b4d3fa7d53791cf1ab9e5c4f74d8bafc8.profdata
+chrome-chromeos-arm-generic-main-1684843186-dd341c1dae4901f683bab1a2faae7dd03715bdfd.profdata
diff --git a/chrome/build/lacros-arm64.pgo.txt b/chrome/build/lacros-arm64.pgo.txt
index f5af917..a1160e0 100644
--- a/chrome/build/lacros-arm64.pgo.txt
+++ b/chrome/build/lacros-arm64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-arm64-generic-main-1684800790-2eb37f1b962e98a82da74d41c53ff52e0905d5e0.profdata
+chrome-chromeos-arm64-generic-main-1684843186-54b0745b334fc6d936c55e4bdbc777627914ee75.profdata
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt
index eed2954e..2d006aa 100644
--- a/chrome/build/lacros64.pgo.txt
+++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@
-chrome-chromeos-amd64-generic-main-1684828565-da55a67400c66191134b5be6bcc58b651680f74d.profdata
+chrome-chromeos-amd64-generic-main-1684843186-0b7ea4613220a6150a5b4743b8289af87653634d.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 51844db..5384c2e7 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1684835895-594fa5abf492c3035948c270458992b8b645adac.profdata
+chrome-mac-arm-main-1684857571-07a51ba45bc74a4663a0be33b2abec569c33f563.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 1a157f0..b52c6c98 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1684821564-325844468c7319669ba9e236032f23ba915553c4.profdata
+chrome-mac-main-1684843186-20275422f87fd4060045add080e17b05083a03e5.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 98ccade..15fe2e1 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1684821564-e32a42907879d19978a43e3e0d3b4f61b083bb28.profdata
+chrome-win32-main-1684843186-b56da8f73c9e56f6052e109a34cd6288e928d19a.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 0ccfdc4..c2d0ea5 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1684832237-bd930cc333c15c98a05320355de7216e9aa7b9cb.profdata
+chrome-win64-main-1684843186-f6524f6aadef092f7ede3c5339550e687a405caf.profdata
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index 1ef05c9..d8ce74e 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -224,7 +224,6 @@
     "//components/services/app_service/public/cpp:app_types",
     "//components/services/heap_profiling/public/cpp",
     "//components/strings",
-    "//components/supervised_user/core/common",
     "//components/supervised_user/core/common:buildflags",
     "//components/translate/content/common",
     "//components/translate/core/common",
@@ -271,6 +270,10 @@
     "//components/password_manager/content/common",
   ]
 
+  if (enable_supervised_users) {
+    public_deps += [ "//components/supervised_user/core/common" ]
+  }
+
   if (enable_pdf) {
     deps += [ "//components/pdf/common" ]
   }
diff --git a/chrome/common/accessibility/read_anything_constants.cc b/chrome/common/accessibility/read_anything_constants.cc
index d22e830..18807e4 100644
--- a/chrome/common/accessibility/read_anything_constants.cc
+++ b/chrome/common/accessibility/read_anything_constants.cc
@@ -20,6 +20,8 @@
 const char kFontScaleHistogramName[] = "Accessibility.ReadAnything.FontScale";
 const char kSettingsChangeHistogramName[] =
     "Accessibility.ReadAnything.SettingsChange";
+const char kScrollEventHistogramName[] =
+    "Accessibility.ReadAnything.ScrollEvent";
 
 const std::set<std::string> GetNonSelectableUrls() {
   return {
diff --git a/chrome/common/accessibility/read_anything_constants.h b/chrome/common/accessibility/read_anything_constants.h
index 3ac360a..ca0efda 100644
--- a/chrome/common/accessibility/read_anything_constants.h
+++ b/chrome/common/accessibility/read_anything_constants.h
@@ -20,6 +20,7 @@
 extern const char kFontNameHistogramName[];
 extern const char kFontScaleHistogramName[];
 extern const char kSettingsChangeHistogramName[];
+extern const char kScrollEventHistogramName[];
 
 extern const std::set<std::string> GetNonSelectableUrls();
 
@@ -64,6 +65,17 @@
   kMaxValue = kLetterSpacingChange,
 };
 
+// Enum for logging how a scroll occurs.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ReadAnythingScrollEvent {
+  kSelectedSidePanel = 0,
+  kSelectedMainPanel = 1,
+  kScrolledSidePanel = 2,
+  kScrolledMainPanel = 3,
+  kMaxValue = kScrolledMainPanel,
+};
+
 }  // namespace
 
 #endif  // CHROME_COMMON_ACCESSIBILITY_READ_ANYTHING_CONSTANTS_H_
diff --git a/chrome/common/chromeos/extensions/api/events.idl b/chrome/common/chromeos/extensions/api/events.idl
index bfd22d9..dc7b2de 100644
--- a/chrome/common/chromeos/extensions/api/events.idl
+++ b/chrome/common/chromeos/extensions/api/events.idl
@@ -10,7 +10,8 @@
         lid,
         usb,
         sd_card,
-        power
+        power,
+        keyboard_diagnostic
     };
 
     enum EventSupportStatus {
@@ -39,6 +40,114 @@
         microphone
     };
 
+    enum KeyboardConnectionType {
+        // Includes devices connected over USB that are on fully internal busses, as
+        // well as the keyboards/touchpads for detachables.
+        internal,
+        usb,
+        bluetooth,
+        // An unknown device is most likely to be internal.
+        unknown
+    };
+
+    enum PhysicalKeyboardLayout {
+        unknown,
+        // A typical Chrome OS keyboard with action keys on the top row, reduced
+        // navigation keys, etc.
+        // TODO: Should we map Wilco and drallion to chromeos?
+        chrome_os
+    };
+
+    // The international standard that the layout follows.
+    enum MechanicalKeyboardLayout {
+        unknown,
+        ansi,
+        iso,
+        jis
+    };
+
+    enum KeyboardNumberPadPresence {
+        // Unknown indicates there is no reliable evidence whether a numberpad is
+        // present. This is common for external keyboards.
+        unknown,
+        present,
+        not_present
+    };
+
+    enum KeyboardTopRowKey {
+        // Either no key at all, or no special action key at this position.
+        no_key,
+        // Marker for keys which cannot be decoded, but have some action.
+        unknown,
+        back,
+        forward,
+        refresh,
+        fullscreen,
+        overview,
+        screenshot,
+        screen_brightness_down,
+        screen_brightness_up,
+        privacy_screen_toggle,
+        microphone_mute,
+        volume_mute,
+        volume_down,
+        volume_up,
+        keyboard_backlight_toggle,
+        keyboard_backlight_down,
+        keyboard_backlight_up,
+        next_track,
+        previous_track,
+        play_pause,
+        screen_mirror,
+        delete
+    };
+
+    enum KeyboardTopRightKey {
+        unknown,
+        power,
+        lock,
+        control_panel
+    };
+
+    // Describes a connected keyboard.
+    dictionary KeyboardInfo {
+        // The number of the keyboard's /dev/input/event* node.
+        long? id;
+        KeyboardConnectionType? connectionType;
+        DOMString? name;
+        PhysicalKeyboardLayout? physicalLayout;
+        MechanicalKeyboardLayout? mechanicalLayout;
+        // For internal keyboards, the region code of the device (from which the
+        // visual layout can be determined).
+        DOMString? regionCode;
+        KeyboardNumberPadPresence? numberPadPresent;
+        // List of ChromeOS specific action keys in the top row. This list excludes
+        // the left-most Escape key, and right-most key (usually Power/Lock).
+        // If a keyboard has F11-F15 keys beyond the rightmost action key, they may
+        // not be included in this list (even as "none").
+        KeyboardTopRowKey[] topRowKeys;
+        // For CrOS keyboards, the glyph shown on the key at the far right end of the
+        // top row. This data may not be completely reliable.
+        KeyboardTopRightKey? topRightKey;
+        // Only applicable to CrOS keyboards.
+        boolean? hasAssistantKey;
+    };
+
+    // Keyboard diagnostics event info. It is fired when users completed a keyboard
+    // diagnostic in the Diagnostics App.
+    dictionary KeyboardDiagnosticEventInfo {
+        // The keyboard which has been tested.
+        KeyboardInfo? keyboardInfo;
+        // Keys which have been tested. It is an array of the evdev key code.
+        long[] testedKeys;
+        // Top row keys which have been tested. They are positions of the key on the
+        // top row after escape (0 is leftmost, 1 is next to the right, etc.).
+        // Generally, 0 is F1, in some fashion.
+        // NOTE: This position may exceed the length of keyboard_info->top_row_keys,
+        // for external keyboards with keys in the F11-F15 range.
+        long[] testedTopRowKeys;
+    };
+
     enum LidEvent {
         // The lid was closed.
         closed,
diff --git a/chrome/installer/mini_installer/mini_file.cc b/chrome/installer/mini_installer/mini_file.cc
index f4d66af..367ca57 100644
--- a/chrome/installer/mini_installer/mini_file.cc
+++ b/chrome/installer/mini_installer/mini_file.cc
@@ -28,7 +28,8 @@
     return false;
   handle_ =
       ::CreateFileW(path_.get(), DELETE | GENERIC_WRITE, FILE_SHARE_DELETE,
-                    nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+                    nullptr, CREATE_ALWAYS,
+                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
   if (handle_ != INVALID_HANDLE_VALUE)
     return true;
   path_.clear();
diff --git a/chrome/installer/mini_installer/mini_file.h b/chrome/installer/mini_installer/mini_file.h
index 0d83dcb9..e38e9555 100644
--- a/chrome/installer/mini_installer/mini_file.h
+++ b/chrome/installer/mini_installer/mini_file.h
@@ -29,7 +29,9 @@
   MiniFile& operator=(MiniFile&& other) noexcept;
 
   // Creates a new file at |path| for exclusive writing. Returns true if the
-  // file was created, in which case IsValid() will return true.
+  // file was created, in which case IsValid() will return true. Consumers are
+  // expected to write sequentially to the file. This expectation is for the
+  // sake of performance rather than correctness.
   bool Create(const wchar_t* path);
 
   // Returns true if this object has a path and a handle to an open file.
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn
index c88c4ae..3bd87929 100644
--- a/chrome/installer/setup/BUILD.gn
+++ b/chrome/installer/setup/BUILD.gn
@@ -37,7 +37,7 @@
       "//base",
       "//build:branding_buildflags",
       "//build/win:default_exe_manifest",
-      "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network",
+      "//chrome/browser/enterprise/connectors/device_trust/key_management/core/network:win_key_network_delegate",
       "//chrome/browser/enterprise/connectors/device_trust/key_management/installer:elevated_rotation",
       "//chrome/browser/enterprise/connectors/device_trust/key_management/installer/management_service:rotate_util",
       "//chrome/chrome_elf:constants",
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index 24db0cc..9646144 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -572,6 +572,7 @@
       .SetMethod("isOverline", &ReadAnythingAppController::IsOverline)
       .SetMethod("onConnected", &ReadAnythingAppController::OnConnected)
       .SetMethod("onCopy", &ReadAnythingAppController::OnCopy)
+      .SetMethod("onScroll", &ReadAnythingAppController::OnScroll)
       .SetMethod("onLinkClicked", &ReadAnythingAppController::OnLinkClicked)
       .SetMethod("isSelectable", &ReadAnythingAppController::isSelectable)
       .SetMethod("onSelectionChange",
@@ -752,6 +753,10 @@
   page_handler_->OnCopy();
 }
 
+void ReadAnythingAppController::OnScroll(bool on_selection) const {
+  model_.OnScroll(on_selection, /* from_reading_mode= */ true);
+}
+
 void ReadAnythingAppController::OnLinkClicked(ui::AXNodeID ax_node_id) const {
   DCHECK_NE(model_.active_tree_id(), ui::AXTreeIDUnknown());
   // Prevent link clicks while distillation is in progress, as it means that the
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 39a1243d..28f1f65 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -117,6 +117,7 @@
   bool IsOverline(ui::AXNodeID ax_node_id) const;
   void OnConnected();
   void OnCopy() const;
+  void OnScroll(bool on_selection) const;
   void OnLinkClicked(ui::AXNodeID ax_node_id) const;
   void OnSelectionChange(ui::AXNodeID anchor_node_id,
                          int anchor_offset,
diff --git a/chrome/renderer/accessibility/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything_app_model.cc
index 6e95f97d..8bf8de4 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/containers/contains.h"
+#include "base/metrics/histogram_functions.h"
 #include "ui/accessibility/ax_enums.mojom-shared.h"
 #include "ui/accessibility/ax_node.h"
 #include "ui/accessibility/ax_role_properties.h"
@@ -462,6 +463,24 @@
   EraseTree(tree_id);
 }
 
+void ReadAnythingAppModel::OnScroll(bool on_selection,
+                                    bool from_reading_mode) const {
+  if (on_selection) {
+    // If the scroll event came from the side panel because of a selection, then
+    // this means the main panel was selected, causing the side panel to scroll
+    // & vice versa.
+    base::UmaHistogramEnumeration(
+        string_constants::kScrollEventHistogramName,
+        from_reading_mode ? ReadAnythingScrollEvent::kSelectedMainPanel
+                          : ReadAnythingScrollEvent::kSelectedSidePanel);
+  } else {
+    base::UmaHistogramEnumeration(
+        string_constants::kScrollEventHistogramName,
+        from_reading_mode ? ReadAnythingScrollEvent::kScrolledSidePanel
+                          : ReadAnythingScrollEvent::kScrolledMainPanel);
+  }
+}
+
 void ReadAnythingAppModel::ProcessNonGeneratedEvents(
     const std::vector<ui::AXEvent>& events) {
   // Note that this list of events may overlap with generated events in the
@@ -557,6 +576,11 @@
       case ui::AXEventGenerator::Event::ALERT:
         requires_distillation_ = true;
         break;
+      case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
+        OnScroll(event.event_params.event_from_action ==
+                     ax::mojom::Action::kSetSelection,
+                 /* from_reading_mode= */ false);
+        break;
 
       // Audit these events e.g. to trigger distillation.
       case ui::AXEventGenerator::Event::NONE:
@@ -622,7 +646,6 @@
       case ui::AXEventGenerator::Event::ROLE_CHANGED:
       case ui::AXEventGenerator::Event::ROW_COUNT_CHANGED:
       case ui::AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED:
-      case ui::AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED:
       case ui::AXEventGenerator::Event::SELECTED_CHANGED:
       case ui::AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED:
       case ui::AXEventGenerator::Event::SELECTED_VALUE_CHANGED:
diff --git a/chrome/renderer/accessibility/read_anything_app_model.h b/chrome/renderer/accessibility/read_anything_app_model.h
index 25cc93f..6ba7f2f3 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.h
+++ b/chrome/renderer/accessibility/read_anything_app_model.h
@@ -86,6 +86,7 @@
   bool IsNodeIgnoredForReadAnything(ui::AXNodeID ax_node_id) const;
   bool NodeIsContentNode(ui::AXNodeID ax_node_id) const;
   void OnThemeChanged(read_anything::mojom::ReadAnythingThemePtr new_theme);
+  void OnScroll(bool on_selection, bool from_reading_mode) const;
 
   void Reset(const std::vector<ui::AXNodeID>& content_node_ids);
   bool PostProcessSelection();
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 8282edeb..13df3f6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -356,6 +356,7 @@
     "//components/subresource_filter/content/browser",
     "//components/subresource_filter/content/browser:test_support",
     "//components/subresource_filter/core/common",
+    "//components/supervised_user/core/common",
     "//components/sync:test_support",
     "//components/sync_preferences",
     "//components/sync_preferences:test_support",
@@ -1720,6 +1721,7 @@
       "//ui/base:test_support",
       "//ui/base/clipboard:clipboard_test_support",
       "//ui/base/dragdrop/mojom",
+      "//ui/color:color",
       "//ui/compositor:test_support",
       "//ui/display:test_support",
       "//ui/native_theme:test_support",
@@ -4344,6 +4346,7 @@
         "../browser/ui/webui/ash/cros_components_browsertest.cc",
         "../browser/ui/webui/ash/crostini_upgrader/crostini_upgrader_dialog_browsertest.cc",
         "../browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler_browsertest.cc",
+        "../browser/ui/webui/ash/kerberos/kerberos_in_browser_browsertest.cc",
         "../browser/ui/webui/ash/login/oobe_display_chooser_browsertest.cc",
         "../browser/ui/webui/ash/login/testapi/oobe_test_api_browsertest.cc",
         "../browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog_browsertest.cc",
@@ -5679,6 +5682,7 @@
     "../browser/enterprise/identifiers/profile_id_service_factory_unittest.cc",
     "../browser/enterprise/reporting/browser_report_generator_unittest.cc",
     "../browser/enterprise/reporting/legacy_tech/legacy_tech_report_generator_unittest.cc",
+    "../browser/enterprise/reporting/legacy_tech/legacy_tech_service_unittest.cc",
     "../browser/enterprise/reporting/legacy_tech/legacy_tech_url_matcher_unittest.cc",
     "../browser/enterprise/reporting/policy_info_unittest.cc",
     "../browser/enterprise/reporting/profile_report_generator_unittest.cc",
@@ -6406,6 +6410,8 @@
     "//components/strings",
     "//components/subresource_filter/core/browser",
     "//components/subresource_filter/core/browser:test_support",
+    "//components/supervised_user/core/common",
+    "//components/supervised_user/core/common:buildflags",
     "//components/sync:test_support",
     "//components/sync_bookmarks",
     "//components/sync_device_info:test_support",
@@ -8905,8 +8911,8 @@
       "../browser/enterprise/connectors/device_trust/attestation/browser/profile_attester_unittest.cc",
       "../browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc",
       "../browser/enterprise/connectors/device_trust/browser/signing_key_policy_observer.cc",
+      "../browser/enterprise/profile_management/profile_management_navigation_throttle_unittest.cc",
       "../browser/enterprise/profile_management/saml_response_parser_unittest.cc",
-      "../browser/enterprise/profile_token_management/profile_token_navigation_throttle_unittest.cc",
       "../browser/enterprise/remote_commands/rotate_attestation_credential_job_unittest.cc",
       "../browser/net/disk_cache_dir_policy_handler_unittest.cc",
       "../browser/signin/profile_token_web_signin_interceptor_unittest.cc",
diff --git a/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js
index 3ad91bf..27db3db5 100644
--- a/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js
@@ -195,7 +195,11 @@
       counter++;
     };
     apnSubpage.close();
+    await flushTasks();
+
     apnSubpage.close();
+    await flushTasks();
+
     assertEquals(1, counter);
     Router.getInstance().navigateToPreviousRoute = navigateToPreviousRoute;
   });
diff --git a/chrome/updater/lock_linux.cc b/chrome/updater/lock_linux.cc
index 5e5ec358..281dd8bc 100644
--- a/chrome/updater/lock_linux.cc
+++ b/chrome/updater/lock_linux.cc
@@ -31,6 +31,10 @@
 
 namespace updater {
 
+// A global preferences lock for Linux implemented with pthread mutexes in
+// shared memory. Note that the shared memory region is leaked once per lock
+// name for reasons described in `//docs/updater/design_doc.md`. The size of the
+// leaked region is ~40 bytes.
 class ScopedLockImpl {
  public:
   ScopedLockImpl(const ScopedLockImpl&) = delete;
@@ -138,7 +142,6 @@
 }
 
 ScopedLockImpl::~ScopedLockImpl() {
-  // TODO(crbug.com/1370140): Fix shared memory leak.
   if (mutex_) {
     pthread_mutex_unlock(mutex_.get());
     munmap(mutex_.get(), sizeof(pthread_mutex_t));
diff --git a/chromeos/ash/components/quick_start/BUILD.gn b/chromeos/ash/components/quick_start/BUILD.gn
index e3bb4231..e71e67c 100644
--- a/chromeos/ash/components/quick_start/BUILD.gn
+++ b/chromeos/ash/components/quick_start/BUILD.gn
@@ -15,6 +15,7 @@
     "quick_start_requests.h",
   ]
   deps = [
+    "proto",
     "//base",
     "//components/cbor",
     "//crypto",
@@ -27,6 +28,7 @@
   testonly = true
   public_deps = [ ":quick_start" ]
   deps = [
+    "proto",
     "//base",
     "//chrome/browser/nearby_sharing/public/cpp",
     "//chromeos/ash/services/nearby/public/mojom:mojom",
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/proto/BUILD.gn b/chromeos/ash/components/quick_start/proto/BUILD.gn
similarity index 100%
rename from chrome/browser/ash/login/oobe_quick_start/connectivity/proto/BUILD.gn
rename to chromeos/ash/components/quick_start/proto/BUILD.gn
diff --git a/chrome/browser/ash/login/oobe_quick_start/connectivity/proto/aes_gcm_authentication_message.proto b/chromeos/ash/components/quick_start/proto/aes_gcm_authentication_message.proto
similarity index 96%
rename from chrome/browser/ash/login/oobe_quick_start/connectivity/proto/aes_gcm_authentication_message.proto
rename to chromeos/ash/components/quick_start/proto/aes_gcm_authentication_message.proto
index e026c82..8e174ce4 100644
--- a/chrome/browser/ash/login/oobe_quick_start/connectivity/proto/aes_gcm_authentication_message.proto
+++ b/chromeos/ash/components/quick_start/proto/aes_gcm_authentication_message.proto
@@ -3,7 +3,7 @@
 
 syntax = "proto2";
 
-package ash.quick_start.proto;
+package quick_start.proto;
 
 option optimize_for = LITE_RUNTIME;
 
diff --git a/chromeos/ash/components/quick_start/quick_start_requests.cc b/chromeos/ash/components/quick_start/quick_start_requests.cc
index a34af32..70bc624 100644
--- a/chromeos/ash/components/quick_start/quick_start_requests.cc
+++ b/chromeos/ash/components/quick_start/quick_start_requests.cc
@@ -5,6 +5,7 @@
 #include "quick_start_requests.h"
 #include "base/base64.h"
 #include "base/json/json_writer.h"
+#include "chromeos/ash/components/quick_start/proto/aes_gcm_authentication_message.pb.h"
 #include "chromeos/ash/components/quick_start/quick_start_message.h"
 #include "chromeos/ash/components/quick_start/quick_start_message_type.h"
 #include "components/cbor/values.h"
@@ -18,6 +19,8 @@
 
 namespace {
 
+namespace proto = ::quick_start::proto;
+
 // bootstrapOptions key telling the phone how to handle
 // challenge UI in case of fallback.
 constexpr char kFlowTypeKey[] = "flowType";
@@ -64,6 +67,10 @@
 // ID
 constexpr char kSessionIdKey[] = "SESSION_ID";
 
+// The role that should be used for the target device. See this enum:
+// http://google3/java/com/google/android/gms/smartdevice/d2d/proto/aes_gcm_authentication_message.proto;l=26;rcl=489093041
+constexpr int32_t kAuthPayloadTargetDeviceRole = 1;
+
 // Boolean in NotifySourceOfUpdateMessage indicating target device requires an
 // update.
 constexpr char kNotifySourceOfUpdateMessageKey[] = "forced_update_required";
@@ -173,6 +180,38 @@
   return request_bytes;
 }
 
+std::vector<uint8_t> BuildTargetDeviceHandshakeMessage(
+    const std::string& authentication_token,
+    std::array<uint8_t, 32> secret,
+    std::array<uint8_t, 12> nonce) {
+  proto::V1Message::AuthenticationPayload auth_payload;
+  auth_payload.set_role(kAuthPayloadTargetDeviceRole);
+  auth_payload.set_auth_string(authentication_token);
+
+  std::string unencrypted_payload;
+  auth_payload.SerializeToString(&unencrypted_payload);
+
+  crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
+  aead.Init(secret);
+  std::vector<uint8_t> encrypted_payload =
+      aead.Seal(std::vector<uint8_t>(unencrypted_payload.begin(),
+                                     unencrypted_payload.end()),
+                nonce, /*additional_data=*/std::vector<uint8_t>());
+
+  proto::AesGcmAuthenticationMessage auth_message;
+  auth_message.set_version(proto::AesGcmAuthenticationMessage::V1);
+  proto::V1Message* v1_message = auth_message.mutable_v1();
+  v1_message->set_nonce(std::string(nonce.begin(), nonce.end()));
+  v1_message->set_payload(
+      std::string(encrypted_payload.begin(), encrypted_payload.end()));
+
+  std::string auth_message_serialized;
+  auth_message.SerializeToString(&auth_message_serialized);
+
+  return std::vector<uint8_t>(auth_message_serialized.begin(),
+                              auth_message_serialized.end());
+}
+
 std::unique_ptr<QuickStartMessage> BuildNotifySourceOfUpdateMessage(
     int32_t session_id,
     std::string& shared_secret) {
diff --git a/chromeos/ash/components/quick_start/quick_start_requests.h b/chromeos/ash/components/quick_start/quick_start_requests.h
index 05e7f58..50461d3 100644
--- a/chromeos/ash/components/quick_start/quick_start_requests.h
+++ b/chromeos/ash/components/quick_start/quick_start_requests.h
@@ -33,6 +33,11 @@
 
 cbor::Value GenerateGetAssertionRequest(const std::string& challenge_b64url);
 
+std::vector<uint8_t> BuildTargetDeviceHandshakeMessage(
+    const std::string& authentication_token,
+    std::array<uint8_t, 32> secret,
+    std::array<uint8_t, 12> nonce);
+
 std::unique_ptr<QuickStartMessage> BuildNotifySourceOfUpdateMessage(
     int32_t session_id,
     std::string& shared_secret);
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 746da8e..2e59c42 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -582,6 +582,15 @@
   <message name="IDS_AUTOFILL_MANDATORY_REAUTH_ICON_TOOLTIP" desc="The tooltip message for the omnibox icon for the mandatory reauth opt-in bubble on Desktop. This bubble prompts users if they would like to enroll in mandatory reauth. Mandatory reauth requires the user to unlock their device or pass a biometric auth when autofilling payment method information.">
     Enable mandatory re-authentication
   </message>
+  <message name="IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_TITLE" desc="Title text for the mandatory reauth confirmation prompt that is displayed to the user after opting into mandatory reauth. Mandatory reauth requires the user to unlock their device or pass a biometric auth when autofilling payment method information.">
+    You're all set
+  </message>
+  <message name="IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_EXPLANATION" desc="Explanation text for the mandatory reauth confirmation prompt that is displayed to the user after opting into mandatory reauth. Mandatory reauth requires the user to unlock their device or pass a biometric auth when autofilling payment method information.">
+    Chrome will now verify it's you before filling in payment methods. You can update this at any time in <ph name="IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_SETTINGS_LINK">$1<ex>settings</ex></ph>.
+  </message>
+  <message name="IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_SETTINGS_LINK" desc="A link to the settings page for the mandatory reauth confirmation prompt that is displayed to the user after opting into mandatory reauth. Mandatory reauth requires the user to unlock their device or pass a biometric auth when autofilling payment method information.">
+    settings
+  </message>
 
   <!-- virtual cards related strings - start -->
   <if expr="not is_ios and not is_android">
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_EXPLANATION.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_EXPLANATION.png.sha1
new file mode 100644
index 0000000..1536756
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_EXPLANATION.png.sha1
@@ -0,0 +1 @@
+78441dc412e145dc3f98260fd06cabe1b9a8b195
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_SETTINGS_LINK.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_SETTINGS_LINK.png.sha1
new file mode 100644
index 0000000..1536756
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_SETTINGS_LINK.png.sha1
@@ -0,0 +1 @@
+78441dc412e145dc3f98260fd06cabe1b9a8b195
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_TITLE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_TITLE.png.sha1
new file mode 100644
index 0000000..9086e6a
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_MANDATORY_REAUTH_CONFIRMATION_TITLE.png.sha1
@@ -0,0 +1 @@
+6f973090fca4ad5016326e47e16295e86231551b
\ No newline at end of file
diff --git a/components/bookmarks/browser/bookmark_client.h b/components/bookmarks/browser/bookmark_client.h
index fd342898..3bd0d0f 100644
--- a/components/bookmarks/browser/bookmark_client.h
+++ b/components/bookmarks/browser/bookmark_client.h
@@ -13,6 +13,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/task/cancelable_task_tracker.h"
 #include "components/bookmarks/browser/bookmark_node.h"
+#include "components/bookmarks/common/bookmark_metrics.h"
 #include "components/favicon_base/favicon_callback.h"
 #include "components/keyed_service/core/keyed_service.h"
 
@@ -69,6 +70,9 @@
   // will be invoked in the Profile's IO task runner.
   virtual LoadManagedNodeCallback GetLoadManagedNodeCallback() = 0;
 
+  // Returns the current storage state to be added as suffix to metrics.
+  virtual metrics::StorageStateForUma GetStorageStateForUma() = 0;
+
   // Returns true if the |permanent_node| can have its title updated.
   virtual bool CanSetPermanentNodeTitle(const BookmarkNode* permanent_node) = 0;
 
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index e1a95ca..9d11bbb6 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -870,7 +870,8 @@
     const std::u16string& title,
     const GURL& url,
     const BookmarkNode::MetaInfoMap* meta_info) {
-  metrics::RecordUrlBookmarkAdded(GetFolderType(parent));
+  metrics::RecordUrlBookmarkAdded(GetFolderType(parent),
+                                  client_->GetStorageStateForUma());
   return AddURL(parent, index, title, url, meta_info, absl::nullopt,
                 absl::nullopt, true);
 }
diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h
index 101de9e..115faeb 100644
--- a/components/bookmarks/browser/bookmark_model.h
+++ b/components/bookmarks/browser/bookmark_model.h
@@ -90,7 +90,7 @@
   // most heavy-lifting taking place in a background sequence. Upon completion,
   // loaded() will return true and observers will be notified via
   // BookmarkModelLoaded(). Uses different files depending on
-  // |sync_storage_type| to support local and account storages.
+  // |storage_type| to support local and account storages.
   // Please note that for the time being the local storage is also used when
   // sync is on.
   // TODO(crbug.com/1422201): Update the note above when the local storage is
diff --git a/components/bookmarks/browser/bookmark_model_unittest.cc b/components/bookmarks/browser/bookmark_model_unittest.cc
index e2471666..a446c46 100644
--- a/components/bookmarks/browser/bookmark_model_unittest.cc
+++ b/components/bookmarks/browser/bookmark_model_unittest.cc
@@ -558,82 +558,128 @@
 }
 
 TEST_F(BookmarkModelTest, AddURL) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
 
-  const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
+  const BookmarkNode* new_node =
+      model_->AddURL(bookmark_bar_node, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
-  ASSERT_EQ(1u, root->children().size());
+  ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(title, new_node->GetTitle());
   ASSERT_TRUE(url == new_node->url());
   ASSERT_TRUE(new_node->uuid().is_valid());
   ASSERT_EQ(BookmarkNode::URL, new_node->type());
   ASSERT_EQ(new_node, model_->GetMostRecentlyAddedUserNodeForURL(url));
 
-  EXPECT_TRUE(new_node->id() != root->id() &&
-              new_node->id() != model_->other_node()->id() &&
-              new_node->id() != model_->mobile_node()->id());
+  EXPECT_THAT(new_node->id(),
+              testing::AllOf(testing::Ne(model_->bookmark_bar_node()->id()),
+                             testing::Ne(model_->other_node()->id()),
+                             testing::Ne(model_->mobile_node()->id())));
 }
 
 TEST_F(BookmarkModelTest, AddNewURL) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
 
-  const BookmarkNode* new_node = model_->AddNewURL(root, 0, title, url);
-  ASSERT_EQ(1, user_action_tester()->GetActionCount("Bookmarks.Added"));
+  const BookmarkNode* new_node =
+      model_->AddNewURL(bookmark_bar_node, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 true);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), true);
 
-  ASSERT_EQ(1u, root->children().size());
+  ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(title, new_node->GetTitle());
   ASSERT_TRUE(url == new_node->url());
   ASSERT_TRUE(new_node->uuid().is_valid());
   ASSERT_EQ(BookmarkNode::URL, new_node->type());
   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
 
-  EXPECT_TRUE(new_node->id() != root->id() &&
-              new_node->id() != model_->other_node()->id() &&
-              new_node->id() != model_->mobile_node()->id());
+  EXPECT_THAT(new_node->id(),
+              testing::AllOf(testing::Ne(model_->bookmark_bar_node()->id()),
+                             testing::Ne(model_->other_node()->id()),
+                             testing::Ne(model_->mobile_node()->id())));
+}
+
+// Tests recording user action when adding bookmarks in account storage.
+TEST_F(BookmarkModelTest, AddNewURLAccountStorage) {
+  auto client = std::make_unique<TestBookmarkClient>();
+  client->SetStorageStateForUma(metrics::StorageStateForUma::kAccount);
+  model_ = TestBookmarkClient::CreateModelWithClient(std::move(client));
+
+  model_->AddNewURL(model_->bookmark_bar_node(), 0, u"title",
+                    GURL("http://foo.com"));
+  EXPECT_EQ(1, user_action_tester()->GetActionCount("Bookmarks.Added"));
+  EXPECT_EQ(1, user_action_tester()->GetActionCount(
+                   "Bookmarks.Added.AccountStorage"));
+}
+
+// Tests recording user action when adding bookmarks in local storage not
+// syncing.
+TEST_F(BookmarkModelTest, AddNewURLLocalStorageNotSyncing) {
+  auto client = std::make_unique<TestBookmarkClient>();
+  client->SetStorageStateForUma(metrics::StorageStateForUma::kLocalOnly);
+  model_ = TestBookmarkClient::CreateModelWithClient(std::move(client));
+
+  model_->AddNewURL(model_->bookmark_bar_node(), 0, u"title",
+                    GURL("http://foo.com"));
+  EXPECT_EQ(1, user_action_tester()->GetActionCount("Bookmarks.Added"));
+  EXPECT_EQ(
+      1, user_action_tester()->GetActionCount("Bookmarks.Added.LocalStorage"));
+}
+
+// Tests recording user action when adding bookmarks in local storage syncing.
+TEST_F(BookmarkModelTest, AddNewURLLocalStorageSyncing) {
+  auto client = std::make_unique<TestBookmarkClient>();
+  client->SetStorageStateForUma(metrics::StorageStateForUma::kSyncEnabled);
+  model_ = TestBookmarkClient::CreateModelWithClient(std::move(client));
+
+  model_->AddNewURL(model_->bookmark_bar_node(), 0, u"title",
+                    GURL("http://foo.com"));
+  EXPECT_EQ(1, user_action_tester()->GetActionCount("Bookmarks.Added"));
+  EXPECT_EQ(1, user_action_tester()->GetActionCount(
+                   "Bookmarks.Added.LocalStorageSyncing"));
 }
 
 TEST_F(BookmarkModelTest, AddURLWithUnicodeTitle) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(
       u"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053");
   const GURL url("https://www.baidu.com/");
 
-  const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
+  const BookmarkNode* new_node =
+      model_->AddURL(bookmark_bar_node, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
-  ASSERT_EQ(1u, root->children().size());
+  ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(title, new_node->GetTitle());
   ASSERT_TRUE(url == new_node->url());
   ASSERT_EQ(BookmarkNode::URL, new_node->type());
   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
 
-  EXPECT_TRUE(new_node->id() != root->id() &&
-              new_node->id() != model_->other_node()->id() &&
-              new_node->id() != model_->mobile_node()->id());
+  EXPECT_THAT(new_node->id(),
+              testing::AllOf(testing::Ne(model_->bookmark_bar_node()->id()),
+                             testing::Ne(model_->other_node()->id()),
+                             testing::Ne(model_->mobile_node()->id())));
 }
 
 TEST_F(BookmarkModelTest, AddURLWithWhitespaceTitle) {
   for (size_t i = 0; i < std::size(url_whitespace_test_cases); ++i) {
-    const BookmarkNode* root = model_->bookmark_bar_node();
+    const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
     const std::u16string title(
         ASCIIToUTF16(url_whitespace_test_cases[i].input_title));
     const GURL url("http://foo.com");
 
-    const BookmarkNode* new_node = model_->AddURL(root, i, title, url);
+    const BookmarkNode* new_node =
+        model_->AddURL(bookmark_bar_node, i, title, url);
 
-    EXPECT_EQ(i + 1, root->children().size());
+    EXPECT_EQ(i + 1, bookmark_bar_node->children().size());
     EXPECT_EQ(ASCIIToUTF16(url_whitespace_test_cases[i].expected_title),
               new_node->GetTitle());
     EXPECT_EQ(BookmarkNode::URL, new_node->type());
@@ -641,7 +687,7 @@
 }
 
 TEST_F(BookmarkModelTest, AddURLWithCreationTimeAndMetaInfo) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
   const Time time = Time::Now() - base::Days(1);
@@ -649,12 +695,12 @@
   meta_info["foo"] = "bar";
 
   const BookmarkNode* new_node =
-      model_->AddURL(root, 0, title, url, &meta_info, time);
+      model_->AddURL(bookmark_bar_node, 0, title, url, &meta_info, time);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
-  ASSERT_EQ(1u, root->children().size());
+  ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(title, new_node->GetTitle());
   ASSERT_TRUE(url == new_node->url());
   ASSERT_TRUE(new_node->uuid().is_valid());
@@ -664,92 +710,95 @@
   ASSERT_EQ(meta_info, *new_node->GetMetaInfoMap());
   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
 
-  EXPECT_TRUE(new_node->id() != root->id() &&
-              new_node->id() != model_->other_node()->id() &&
-              new_node->id() != model_->mobile_node()->id());
+  EXPECT_THAT(new_node->id(),
+              testing::AllOf(testing::Ne(model_->bookmark_bar_node()->id()),
+                             testing::Ne(model_->other_node()->id()),
+                             testing::Ne(model_->mobile_node()->id())));
 }
 
 TEST_F(BookmarkModelTest, AddURLWithGUID) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
   const Time time = Time::Now() - base::Days(1);
   BookmarkNode::MetaInfoMap meta_info;
   const base::Uuid guid = base::Uuid::GenerateRandomV4();
 
-  const BookmarkNode* new_node =
-      model_->AddURL(root, /*index=*/0, title, url, &meta_info, time, guid);
+  const BookmarkNode* new_node = model_->AddURL(
+      bookmark_bar_node, /*index=*/0, title, url, &meta_info, time, guid);
 
   EXPECT_EQ(guid, new_node->uuid());
 }
 
 TEST_F(BookmarkModelTest, AddURLToMobileBookmarks) {
-  const BookmarkNode* root = model_->mobile_node();
+  const BookmarkNode* mobile_node = model_->mobile_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
 
-  const BookmarkNode* new_node = model_->AddURL(root, 0, title, url);
+  const BookmarkNode* new_node = model_->AddURL(mobile_node, 0, title, url);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(mobile_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
-  ASSERT_EQ(1u, root->children().size());
+  ASSERT_EQ(1u, mobile_node->children().size());
   ASSERT_EQ(title, new_node->GetTitle());
   ASSERT_TRUE(url == new_node->url());
   ASSERT_EQ(BookmarkNode::URL, new_node->type());
   ASSERT_TRUE(new_node == model_->GetMostRecentlyAddedUserNodeForURL(url));
 
-  EXPECT_TRUE(new_node->id() != root->id() &&
-              new_node->id() != model_->other_node()->id() &&
-              new_node->id() != model_->mobile_node()->id());
+  EXPECT_THAT(new_node->id(),
+              testing::AllOf(testing::Ne(model_->bookmark_bar_node()->id()),
+                             testing::Ne(model_->other_node()->id()),
+                             testing::Ne(model_->mobile_node()->id())));
 }
 
 TEST_F(BookmarkModelTest, AddFolder) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
 
-  const BookmarkNode* new_node = model_->AddFolder(root, 0, title);
+  const BookmarkNode* new_node = model_->AddFolder(bookmark_bar_node, 0, title);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
-  ASSERT_EQ(1u, root->children().size());
+  ASSERT_EQ(1u, bookmark_bar_node->children().size());
   ASSERT_EQ(title, new_node->GetTitle());
   ASSERT_TRUE(new_node->uuid().is_valid());
   ASSERT_EQ(BookmarkNode::FOLDER, new_node->type());
 
-  EXPECT_TRUE(new_node->id() != root->id() &&
-              new_node->id() != model_->other_node()->id() &&
-              new_node->id() != model_->mobile_node()->id());
+  EXPECT_THAT(new_node->id(),
+              testing::AllOf(testing::Ne(model_->bookmark_bar_node()->id()),
+                             testing::Ne(model_->other_node()->id()),
+                             testing::Ne(model_->mobile_node()->id())));
 
   // Add another folder, just to make sure folder_ids are incremented correctly.
   ClearCounts();
-  model_->AddFolder(root, 0, title);
+  model_->AddFolder(bookmark_bar_node, 0, title);
   AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 }
 
 TEST_F(BookmarkModelTest, AddFolderWithCreationTime) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   BookmarkNode::MetaInfoMap meta_info;
   const base::Time creation_time(base::Time::Now() - base::Days(1));
 
-  const BookmarkNode* new_node =
-      model_->AddFolder(root, /*index=*/0, title, &meta_info, creation_time);
+  const BookmarkNode* new_node = model_->AddFolder(
+      bookmark_bar_node, /*index=*/0, title, &meta_info, creation_time);
 
   EXPECT_EQ(creation_time, new_node->date_added());
 }
 
 TEST_F(BookmarkModelTest, AddFolderWithGUID) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   BookmarkNode::MetaInfoMap meta_info;
   const base::Uuid guid = base::Uuid::GenerateRandomV4();
 
   const BookmarkNode* new_node =
-      model_->AddFolder(root, /*index=*/0, title, &meta_info,
+      model_->AddFolder(bookmark_bar_node, /*index=*/0, title, &meta_info,
                         /*creation_time=*/Time::Now(), guid);
 
   EXPECT_EQ(guid, new_node->uuid());
@@ -757,13 +806,14 @@
 
 TEST_F(BookmarkModelTest, AddFolderWithWhitespaceTitle) {
   for (size_t i = 0; i < std::size(title_whitespace_test_cases); ++i) {
-    const BookmarkNode* root = model_->bookmark_bar_node();
+    const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
     const std::u16string title(
         ASCIIToUTF16(title_whitespace_test_cases[i].input_title));
 
-    const BookmarkNode* new_node = model_->AddFolder(root, i, title);
+    const BookmarkNode* new_node =
+        model_->AddFolder(bookmark_bar_node, i, title);
 
-    EXPECT_EQ(i + 1, root->children().size());
+    EXPECT_EQ(i + 1, bookmark_bar_node->children().size());
     EXPECT_EQ(ASCIIToUTF16(title_whitespace_test_cases[i].expected_title),
               new_node->GetTitle());
     EXPECT_EQ(BookmarkNode::FOLDER, new_node->type());
@@ -771,30 +821,30 @@
 }
 
 TEST_F(BookmarkModelTest, RemoveURL) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
-  model_->AddURL(root, 0, title, url);
+  model_->AddURL(bookmark_bar_node, 0, title, url);
   ClearCounts();
 
-  model_->Remove(root->children().front().get(),
+  model_->Remove(bookmark_bar_node->children().front().get(),
                  bookmarks::metrics::BookmarkEditSource::kOther);
-  ASSERT_EQ(0u, root->children().size());
+  ASSERT_EQ(0u, bookmark_bar_node->children().size());
   histogram_tester()->ExpectTotalCount("Bookmarks.RemovedSource", 1);
   histogram_tester()->ExpectBucketCount(
       "Bookmarks.RemovedSource",
       static_cast<int>(metrics::BookmarkEditSource::kOther), 1);
   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
   // Make sure there is no mapping for the URL.
   ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
 }
 
 TEST_F(BookmarkModelTest, RemoveFolder) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
-  const BookmarkNode* folder = model_->AddFolder(root, 0, u"foo");
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
+  const BookmarkNode* folder = model_->AddFolder(bookmark_bar_node, 0, u"foo");
 
   ClearCounts();
 
@@ -806,16 +856,16 @@
   ClearCounts();
 
   // Now remove the folder.
-  model_->Remove(root->children().front().get(),
+  model_->Remove(bookmark_bar_node->children().front().get(),
                  bookmarks::metrics::BookmarkEditSource::kOther);
-  ASSERT_EQ(0u, root->children().size());
+  ASSERT_EQ(0u, bookmark_bar_node->children().size());
   histogram_tester()->ExpectTotalCount("Bookmarks.RemovedSource", 1);
   histogram_tester()->ExpectBucketCount(
       "Bookmarks.RemovedSource",
       static_cast<int>(metrics::BookmarkEditSource::kOther), 1);
   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
 
   // Make sure there is no mapping for the URL.
   ASSERT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
@@ -957,10 +1007,10 @@
 }
 
 TEST_F(BookmarkModelTest, SetTitle) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   std::u16string title(u"foo");
   const GURL url("http://url.com");
-  const BookmarkNode* node = model_->AddURL(root, 0, title, url);
+  const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, title, url);
 
   ClearCounts();
 
@@ -985,10 +1035,10 @@
 
 TEST_F(BookmarkModelTest, SetTitleWithWhitespace) {
   for (size_t i = 0; i < std::size(title_whitespace_test_cases); ++i) {
-    const BookmarkNode* root = model_->bookmark_bar_node();
+    const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
     std::u16string title(u"dummy");
     const GURL url("http://foo.com");
-    const BookmarkNode* node = model_->AddURL(root, 0, title, url);
+    const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, title, url);
 
     title = ASCIIToUTF16(title_whitespace_test_cases[i].input_title);
     model_->SetTitle(node, title, metrics::BookmarkEditSource::kOther);
@@ -998,8 +1048,9 @@
 }
 
 TEST_F(BookmarkModelTest, SetFolderTitle) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
-  const BookmarkNode* folder = model_->AddFolder(root, 0, u"folder");
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
+  const BookmarkNode* folder =
+      model_->AddFolder(bookmark_bar_node, 0, u"folder");
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
   const BookmarkNode* node = model_->AddURL(folder, 0, title, url);
@@ -1008,8 +1059,8 @@
   model_->SetTitle(folder, u"golder", metrics::BookmarkEditSource::kOther);
 
   // Should not change the hierarchy.
-  EXPECT_EQ(root->children().size(), 1u);
-  EXPECT_EQ(root->children().front().get(), folder);
+  EXPECT_EQ(bookmark_bar_node->children().size(), 1u);
+  EXPECT_EQ(bookmark_bar_node->children().front().get(), folder);
   EXPECT_EQ(folder->children().size(), 1u);
   EXPECT_EQ(folder->children().front().get(), node);
   EXPECT_EQ(node->parent(), folder);
@@ -1028,10 +1079,10 @@
 }
 
 TEST_F(BookmarkModelTest, SetURL) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   GURL url("http://foo.com");
-  const BookmarkNode* node = model_->AddURL(root, 0, title, url);
+  const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, title, url);
 
   ClearCounts();
 
@@ -1046,10 +1097,10 @@
 }
 
 TEST_F(BookmarkModelTest, SetDateAdded) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   GURL url("http://foo.com");
-  const BookmarkNode* node = model_->AddURL(root, 0, title, url);
+  const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, title, url);
 
   ClearCounts();
 
@@ -1061,20 +1112,21 @@
 }
 
 TEST_F(BookmarkModelTest, Move) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
-  const BookmarkNode* node = model_->AddURL(root, 0, title, url);
-  const BookmarkNode* folder1 = model_->AddFolder(root, 0, u"folder");
+  const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, title, url);
+  const BookmarkNode* folder1 =
+      model_->AddFolder(bookmark_bar_node, 0, u"folder");
   ClearCounts();
 
   model_->Move(node, folder1, 0);
 
   AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
-  observer_details_.ExpectEquals(root, folder1, 1, 0, false);
+  observer_details_.ExpectEquals(bookmark_bar_node, folder1, 1, 0, false);
   EXPECT_TRUE(folder1 == node->parent());
-  EXPECT_EQ(1u, root->children().size());
-  EXPECT_EQ(folder1, root->children().front().get());
+  EXPECT_EQ(1u, bookmark_bar_node->children().size());
+  EXPECT_EQ(folder1, bookmark_bar_node->children().front().get());
   EXPECT_EQ(1u, folder1->children().size());
   EXPECT_EQ(node, folder1->children().front().get());
 
@@ -1084,13 +1136,13 @@
 
   // And remove the folder.
   ClearCounts();
-  model_->Remove(root->children().front().get(),
+  model_->Remove(bookmark_bar_node->children().front().get(),
                  bookmarks::metrics::BookmarkEditSource::kOther);
   AssertObserverCount(0, 0, 1, 0, 0, 1, 0, 0, 0);
-  observer_details_.ExpectEquals(root, nullptr, 0, static_cast<size_t>(-1),
-                                 false);
+  observer_details_.ExpectEquals(bookmark_bar_node, nullptr, 0,
+                                 static_cast<size_t>(-1), false);
   EXPECT_TRUE(model_->GetMostRecentlyAddedUserNodeForURL(url) == nullptr);
-  EXPECT_EQ(0u, root->children().size());
+  EXPECT_EQ(0u, bookmark_bar_node->children().size());
 
   matches = model_->GetBookmarksMatching(
       u"foo", /*max_count=*/1, query_parser::MatchingAlgorithm::DEFAULT);
@@ -1098,25 +1150,28 @@
 }
 
 TEST_F(BookmarkModelTest, NonMovingMoveCall) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
   const base::Time old_date(base::Time::Now() - base::Days(1));
 
-  const BookmarkNode* node = model_->AddURL(root, 0, title, url);
-  model_->SetDateFolderModified(root, old_date);
+  const BookmarkNode* node = model_->AddURL(bookmark_bar_node, 0, title, url);
+  model_->SetDateFolderModified(bookmark_bar_node, old_date);
 
-  // Since |node| is already at the index 0 of |root|, this is no-op.
-  model_->Move(node, root, 0);
+  // Since |node| is already at the index 0 of |bookmark_bar_node|, this is
+  // no-op.
+  model_->Move(node, bookmark_bar_node, 0);
 
   // Check that the modification date is kept untouched.
-  EXPECT_EQ(old_date, root->date_folder_modified());
+  EXPECT_EQ(old_date, bookmark_bar_node->date_folder_modified());
 }
 
 TEST_F(BookmarkModelTest, MoveURLFromFolder) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
-  const BookmarkNode* folder1 = model_->AddFolder(root, 0, u"folder");
-  const BookmarkNode* folder2 = model_->AddFolder(root, 0, u"golder");
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
+  const BookmarkNode* folder1 =
+      model_->AddFolder(bookmark_bar_node, 0, u"folder");
+  const BookmarkNode* folder2 =
+      model_->AddFolder(bookmark_bar_node, 0, u"golder");
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
   const BookmarkNode* node = model_->AddURL(folder1, 0, title, url);
@@ -1127,7 +1182,7 @@
   // Should update the hierarchy.
   AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
   observer_details_.ExpectEquals(folder1, folder2, 0, 0, false);
-  EXPECT_EQ(root->children().size(), 2u);
+  EXPECT_EQ(bookmark_bar_node->children().size(), 2u);
   EXPECT_EQ(folder1->children().size(), 0u);
   EXPECT_EQ(folder2->children().size(), 1u);
   EXPECT_EQ(folder2->children().front().get(), node);
@@ -1147,7 +1202,7 @@
   // Should update the hierarchy.
   AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
   observer_details_.ExpectEquals(folder2, folder1, 0, 0, false);
-  EXPECT_EQ(root->children().size(), 2u);
+  EXPECT_EQ(bookmark_bar_node->children().size(), 2u);
   EXPECT_EQ(folder1->children().size(), 1u);
   EXPECT_EQ(folder2->children().size(), 0u);
   EXPECT_EQ(folder1->children().front().get(), node);
@@ -1162,9 +1217,11 @@
 }
 
 TEST_F(BookmarkModelTest, MoveFolder) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
-  const BookmarkNode* folder1 = model_->AddFolder(root, 0, u"folder");
-  const BookmarkNode* folder2 = model_->AddFolder(root, 1, u"golder");
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
+  const BookmarkNode* folder1 =
+      model_->AddFolder(bookmark_bar_node, 0, u"folder");
+  const BookmarkNode* folder2 =
+      model_->AddFolder(bookmark_bar_node, 1, u"golder");
   const BookmarkNode* folder3 = model_->AddFolder(folder1, 0, u"holder");
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
@@ -1176,9 +1233,9 @@
   // Should update the hierarchy.
   AssertObserverCount(0, 1, 0, 0, 0, 0, 0, 0, 0);
   observer_details_.ExpectEquals(folder1, folder2, 0, 0, false);
-  EXPECT_EQ(root->children().size(), 2u);
-  EXPECT_EQ(root->children()[0].get(), folder1);
-  EXPECT_EQ(root->children()[1].get(), folder2);
+  EXPECT_EQ(bookmark_bar_node->children().size(), 2u);
+  EXPECT_EQ(bookmark_bar_node->children()[0].get(), folder1);
+  EXPECT_EQ(bookmark_bar_node->children()[1].get(), folder2);
   EXPECT_EQ(folder1->children().size(), 0u);
   EXPECT_EQ(folder2->children().size(), 1u);
   EXPECT_EQ(folder2->children()[0].get(), folder3);
@@ -1200,55 +1257,56 @@
 }
 
 TEST_F(BookmarkModelTest, Copy) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   static const std::string model_string("a 1:[ b c ] d 2:[ e f g ] h ");
-  test::AddNodesFromModelString(model_.get(), root, model_string);
+  test::AddNodesFromModelString(model_.get(), bookmark_bar_node, model_string);
 
   // Validate initial model.
-  std::string actual_model_string = test::ModelStringFromNode(root);
+  std::string actual_model_string =
+      test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ(model_string, actual_model_string);
 
   // Copy 'd' to be after '1:b': URL item from bar to folder.
-  const BookmarkNode* node_to_copy = root->children()[2].get();
-  const BookmarkNode* destination = root->children()[1].get();
+  const BookmarkNode* node_to_copy = bookmark_bar_node->children()[2].get();
+  const BookmarkNode* destination = bookmark_bar_node->children()[1].get();
   model_->Copy(node_to_copy, destination, 1);
-  actual_model_string = test::ModelStringFromNode(root);
+  actual_model_string = test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ("a 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string);
 
   // Copy '1:d' to be after 'a': URL item from folder to bar.
-  const BookmarkNode* folder = root->children()[1].get();
+  const BookmarkNode* folder = bookmark_bar_node->children()[1].get();
   node_to_copy = folder->children()[1].get();
-  model_->Copy(node_to_copy, root, 1);
-  actual_model_string = test::ModelStringFromNode(root);
+  model_->Copy(node_to_copy, bookmark_bar_node, 1);
+  actual_model_string = test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e f g ] h ", actual_model_string);
 
   // Copy '1' to be after '2:e': Folder from bar to folder.
-  node_to_copy = root->children()[2].get();
-  destination = root->children()[4].get();
+  node_to_copy = bookmark_bar_node->children()[2].get();
+  destination = bookmark_bar_node->children()[4].get();
   model_->Copy(node_to_copy, destination, 1);
-  actual_model_string = test::ModelStringFromNode(root);
+  actual_model_string = test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f g ] h ",
             actual_model_string);
 
   // Copy '2:1' to be after '2:f': Folder within same folder.
-  folder = root->children()[4].get();
+  folder = bookmark_bar_node->children()[4].get();
   node_to_copy = folder->children()[1].get();
   model_->Copy(node_to_copy, folder, 3);
-  actual_model_string = test::ModelStringFromNode(root);
+  actual_model_string = test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h ",
             actual_model_string);
 
   // Copy first 'd' to be after 'h': URL item within the bar.
-  node_to_copy = root->children()[1].get();
-  model_->Copy(node_to_copy, root, 6);
-  actual_model_string = test::ModelStringFromNode(root);
+  node_to_copy = bookmark_bar_node->children()[1].get();
+  model_->Copy(node_to_copy, bookmark_bar_node, 6);
+  actual_model_string = test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ("a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
             actual_model_string);
 
   // Copy '2' to be after 'a': Folder within the bar.
-  node_to_copy = root->children()[4].get();
-  model_->Copy(node_to_copy, root, 1);
-  actual_model_string = test::ModelStringFromNode(root);
+  node_to_copy = bookmark_bar_node->children()[4].get();
+  model_->Copy(node_to_copy, bookmark_bar_node, 1);
+  actual_model_string = test::ModelStringFromNode(bookmark_bar_node);
   EXPECT_EQ(
       "a 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] d 1:[ b d c ] "
       "d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
@@ -1517,11 +1575,11 @@
 }
 
 TEST_F(BookmarkModelTest, MobileNodeVisibleWithChildren) {
-  const BookmarkNode* root = model_->mobile_node();
+  const BookmarkNode* mobile_node = model_->mobile_node();
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
 
-  model_->AddURL(root, 0, title, url);
+  model_->AddURL(mobile_node, 0, title, url);
   EXPECT_TRUE(model_->mobile_node()->IsVisible());
 }
 
@@ -1619,8 +1677,9 @@
 }
 
 TEST_F(BookmarkModelTest, GetBookmarksMatching) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
-  const BookmarkNode* folder = model_->AddFolder(root, 0, u"folder");
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
+  const BookmarkNode* folder =
+      model_->AddFolder(bookmark_bar_node, 0, u"folder");
   const std::u16string title(u"foo");
   const GURL url("http://foo.com");
   const BookmarkNode* node = model_->AddURL(folder, 0, title, url);
@@ -1640,16 +1699,16 @@
 TEST_F(BookmarkModelTest, TitledUrlIndexUpdatedOnRemove) {
   const std::u16string title = u"Title";
   const GURL url("http://google.com");
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
 
-  model_->AddURL(root, 0, title, url);
+  model_->AddURL(bookmark_bar_node, 0, title, url);
   ASSERT_EQ(1U, model_
                     ->GetBookmarksMatching(
                         title, 1, query_parser::MatchingAlgorithm::DEFAULT)
                     .size());
 
   // Remove the node and make sure we don't get back any results.
-  model_->Remove(root->children().front().get(),
+  model_->Remove(bookmark_bar_node->children().front().get(),
                  bookmarks::metrics::BookmarkEditSource::kOther);
   EXPECT_EQ(0U, model_
                     ->GetBookmarksMatching(
@@ -1662,9 +1721,9 @@
   const std::u16string initial_title = u"Initial";
   const std::u16string new_title = u"New";
   const GURL url("http://google.com");
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
 
-  model_->AddURL(root, 0, initial_title, url);
+  model_->AddURL(bookmark_bar_node, 0, initial_title, url);
   ASSERT_EQ(1U,
             model_
                 ->GetBookmarksMatching(initial_title, 1,
@@ -1676,7 +1735,7 @@
                     .size());
 
   // Change the title.
-  model_->SetTitle(root->children().front().get(), new_title,
+  model_->SetTitle(bookmark_bar_node->children().front().get(), new_title,
                    metrics::BookmarkEditSource::kOther);
 
   // Verify that we only get results for the new title.
@@ -1696,9 +1755,9 @@
   const std::u16string title = u"Title";
   const GURL initial_url("http://initial");
   const GURL new_url("http://new");
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
 
-  model_->AddURL(root, 0, title, initial_url);
+  model_->AddURL(bookmark_bar_node, 0, title, initial_url);
   ASSERT_EQ(1U, model_
                     ->GetBookmarksMatching(
                         u"initial", 1, query_parser::MatchingAlgorithm::DEFAULT)
@@ -1709,7 +1768,7 @@
                     .size());
 
   // Change the URL.
-  model_->SetURL(root->children().front().get(), new_url,
+  model_->SetURL(bookmark_bar_node->children().front().get(), new_url,
                  metrics::BookmarkEditSource::kOther);
 
   // Verify that we only get results for the new URL.
@@ -1904,7 +1963,7 @@
 // (e.g. http://www.google.com) or a matching icon URL
 // (e.g. http://www.google.com/favicon.ico).
 TEST_F(BookmarkModelFaviconTest, FaviconsChangedObserver) {
-  const BookmarkNode* root = model_->bookmark_bar_node();
+  const BookmarkNode* bookmark_bar_node = model_->bookmark_bar_node();
   std::u16string kTitle(u"foo");
   GURL kPageURL1("http://www.google.com");
   GURL kPageURL2("http://www.google.ca");
@@ -1912,10 +1971,14 @@
   GURL kFaviconURL12("http://www.google.com/favicon.ico");
   GURL kFaviconURL3("http://www.amazon.com/favicon.ico");
 
-  const BookmarkNode* node1 = model_->AddURL(root, 0, kTitle, kPageURL1);
-  const BookmarkNode* node2 = model_->AddURL(root, 0, kTitle, kPageURL2);
-  const BookmarkNode* node3 = model_->AddURL(root, 0, kTitle, kPageURL3);
-  const BookmarkNode* node4 = model_->AddURL(root, 0, kTitle, kPageURL3);
+  const BookmarkNode* node1 =
+      model_->AddURL(bookmark_bar_node, 0, kTitle, kPageURL1);
+  const BookmarkNode* node2 =
+      model_->AddURL(bookmark_bar_node, 0, kTitle, kPageURL2);
+  const BookmarkNode* node3 =
+      model_->AddURL(bookmark_bar_node, 0, kTitle, kPageURL3);
+  const BookmarkNode* node4 =
+      model_->AddURL(bookmark_bar_node, 0, kTitle, kPageURL3);
 
   {
     OnFaviconLoaded(AsMutable(node1), kFaviconURL12);
@@ -2010,9 +2073,9 @@
 };
 
 TEST_F(BookmarkDualModelTest, MoveToOtherModel) {
-  const BookmarkNode* root = local_or_syncable_model_->mobile_node();
+  const BookmarkNode* mobile_node = local_or_syncable_model_->mobile_node();
   const BookmarkNode* folder =
-      local_or_syncable_model_->AddFolder(root, 0, u"folder");
+      local_or_syncable_model_->AddFolder(mobile_node, 0, u"folder");
   local_or_syncable_model_->AddURL(folder, 0, u"foo", GURL("http://foo.com"));
   local_or_syncable_model_->AddURL(folder, 1, u"bar", GURL("http://bar.com"));
   base::Uuid folder_uuid_before_move = folder->uuid();
@@ -2020,14 +2083,14 @@
   ASSERT_TRUE(dest_folder->children().empty());
 
   testing::Sequence local_or_syncable_sequence;
-  EXPECT_CALL(
-      local_or_syncable_observer_,
-      OnWillRemoveBookmarks(local_or_syncable_model_.get(), root, 0, folder))
+  EXPECT_CALL(local_or_syncable_observer_,
+              OnWillRemoveBookmarks(local_or_syncable_model_.get(), mobile_node,
+                                    0, folder))
       .InSequence(local_or_syncable_sequence);
   std::set<GURL> removed_urls{GURL("http://foo.com"), GURL("http://bar.com")};
   EXPECT_CALL(local_or_syncable_observer_,
-              BookmarkNodeRemoved(local_or_syncable_model_.get(), root, 0,
-                                  folder, removed_urls))
+              BookmarkNodeRemoved(local_or_syncable_model_.get(), mobile_node,
+                                  0, folder, removed_urls))
       .InSequence(local_or_syncable_sequence);
 
   testing::Sequence account_sequence;
diff --git a/components/bookmarks/common/bookmark_metrics.cc b/components/bookmarks/common/bookmark_metrics.cc
index c9bb2b7..c6f9ec1e 100644
--- a/components/bookmarks/common/bookmark_metrics.cc
+++ b/components/bookmarks/common/bookmark_metrics.cc
@@ -4,9 +4,12 @@
 
 #include "components/bookmarks/common/bookmark_metrics.h"
 
+#include <string>
+
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
+#include "base/strings/strcat.h"
 #include "components/bookmarks/common/url_load_stats.h"
 
 namespace {
@@ -17,12 +20,29 @@
     bookmarks::metrics::BookmarkFolderTypeForUMA parent) {
   base::UmaHistogramEnumeration("Bookmarks.ParentFolderType", parent);
 }
+
+std::string GetStorageStateSuffixForMetrics(
+    bookmarks::metrics::StorageStateForUma storage_state) {
+  switch (storage_state) {
+    case bookmarks::metrics::StorageStateForUma::kAccount:
+      return std::string(".AccountStorage");
+    case bookmarks::metrics::StorageStateForUma::kLocalOnly:
+      return std::string(".LocalStorage");
+    case bookmarks::metrics::StorageStateForUma::kSyncEnabled:
+      return std::string(".LocalStorageSyncing");
+  }
+  NOTREACHED_NORETURN();
 }
 
+}  // namespace
+
 namespace bookmarks::metrics {
 
-void RecordUrlBookmarkAdded(BookmarkFolderTypeForUMA parent) {
+void RecordUrlBookmarkAdded(BookmarkFolderTypeForUMA parent,
+                            StorageStateForUma storage_state) {
   base::RecordAction(base::UserMetricsAction("Bookmarks.Added"));
+  base::RecordComputedAction(base::StrCat(
+      {"Bookmarks.Added", GetStorageStateSuffixForMetrics(storage_state)}));
   RecordBookmarkParentFolderType(parent);
 }
 
diff --git a/components/bookmarks/common/bookmark_metrics.h b/components/bookmarks/common/bookmark_metrics.h
index 96f0ba6..9df412a 100644
--- a/components/bookmarks/common/bookmark_metrics.h
+++ b/components/bookmarks/common/bookmark_metrics.h
@@ -36,8 +36,20 @@
   kMaxValue = kOther,
 };
 
+// An enum class to add storage state as a suffix to metrics.
+enum class StorageStateForUma {
+  // Account storage.
+  kAccount,
+  // Local storage that is not being synced at the time the metric is
+  // recorded.
+  kLocalOnly,
+  // Local storage that is being synced at the time the metric is recorded.
+  kSyncEnabled,
+};
+
 // Records when a bookmark is added by the user.
-void RecordUrlBookmarkAdded(BookmarkFolderTypeForUMA parent);
+void RecordUrlBookmarkAdded(BookmarkFolderTypeForUMA parent,
+                            StorageStateForUma storage_state);
 
 // Records when a bookmark folder is added by the user.
 void RecordBookmarkFolderAdded(BookmarkFolderTypeForUMA parent);
diff --git a/components/bookmarks/test/test_bookmark_client.cc b/components/bookmarks/test/test_bookmark_client.cc
index 3b74c2b..4b8e0f00 100644
--- a/components/bookmarks/test/test_bookmark_client.cc
+++ b/components/bookmarks/test/test_bookmark_client.cc
@@ -97,6 +97,11 @@
   return !requests_per_page_url_.empty();
 }
 
+void TestBookmarkClient::SetStorageStateForUma(
+    metrics::StorageStateForUma storage_state) {
+  storage_state_for_uma_ = storage_state;
+}
+
 bool TestBookmarkClient::IsPermanentNodeVisibleWhenEmpty(
     BookmarkNode::Type type) {
   switch (type) {
@@ -120,6 +125,10 @@
                         std::move(managed_node_));
 }
 
+metrics::StorageStateForUma TestBookmarkClient::GetStorageStateForUma() {
+  return storage_state_for_uma_;
+}
+
 bool TestBookmarkClient::CanSetPermanentNodeTitle(
     const BookmarkNode* permanent_node) {
   return IsManagedNodeRoot(permanent_node);
diff --git a/components/bookmarks/test/test_bookmark_client.h b/components/bookmarks/test/test_bookmark_client.h
index 6c84bf8..07be238 100644
--- a/components/bookmarks/test/test_bookmark_client.h
+++ b/components/bookmarks/test/test_bookmark_client.h
@@ -66,9 +66,13 @@
   // Returns true if there is at least one active favicon loading task.
   bool HasFaviconLoadTasks() const;
 
+  // Sets |storage_state_for_uma_| returned by |GetStorageStateForUma()|.
+  void SetStorageStateForUma(metrics::StorageStateForUma storage_state);
+
   // BookmarkClient:
   bool IsPermanentNodeVisibleWhenEmpty(BookmarkNode::Type type) override;
   LoadManagedNodeCallback GetLoadManagedNodeCallback() override;
+  metrics::StorageStateForUma GetStorageStateForUma() override;
   bool CanSetPermanentNodeTitle(const BookmarkNode* permanent_node) override;
   bool CanSyncNode(const BookmarkNode* node) override;
   bool CanBeEditedByUser(const BookmarkNode* node) override;
@@ -100,6 +104,9 @@
   base::CancelableTaskTracker::TaskId next_task_id_ = 1;
   std::map<GURL, std::list<favicon_base::FaviconImageCallback>>
       requests_per_page_url_;
+
+  metrics::StorageStateForUma storage_state_for_uma_ =
+      metrics::StorageStateForUma::kLocalOnly;
 };
 
 }  // namespace bookmarks
diff --git a/components/exo/wayland/fuzzer/harness.cc.tmpl b/components/exo/wayland/fuzzer/harness.cc.tmpl
index e672b29..ddcd0ce 100644
--- a/components/exo/wayland/fuzzer/harness.cc.tmpl
+++ b/components/exo/wayland/fuzzer/harness.cc.tmpl
@@ -177,16 +177,11 @@
 }
 
 Harness::~Harness() {
-  {% for interface in interfaces %}
-    for (auto ifc : {{interface.name}}_list_) {
-      if (ifc)
-        {% if interface.name == "wl_display" %}
-          wl_display_disconnect(ifc);
-        {% else %}
-          free(ifc);
-        {% endif %}
+  for (auto ifc : wl_display_list_) {
+    if (ifc) {
+      wl_display_disconnect(ifc);
     }
-  {% endfor %}
+  }
 }
 
 void Harness::Run(const actions::actions& all_steps) {
diff --git a/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters_for_ui.cc b/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters_for_ui.cc
index 9e3af01..e39cfff1 100644
--- a/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters_for_ui.cc
+++ b/components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters_for_ui.cc
@@ -108,6 +108,21 @@
                                  .annotated_visit.visit_row.visit_time,
                              true, false, true, false};
 
+  if (!clusters.empty()) {
+    int time_horizon_hours = (continuation_params_.continuation_time -
+                              continuation_params.continuation_time)
+                                 .InHours();
+    base::UmaHistogramCounts1000(
+        "History.Clusters.Backend.GetMostRecentClustersForUI."
+        "GetMostRecentPersistedClustersTimeHorizon",
+        time_horizon_hours);
+    base::UmaHistogramCounts1000(
+        "History.Clusters.Backend.GetMostRecentClustersForUI."
+        "GetMostRecentPersistedClustersTimeHorizon" +
+            GetHistogramNameSliceForRequestSource(clustering_request_source_),
+        time_horizon_hours);
+  }
+
   // Prune out synced clusters if feature not enabled.
   if (!GetConfig().include_synced_visits) {
     auto it = clusters.begin();
diff --git a/components/keyed_service/content/browser_context_keyed_service_factory.h b/components/keyed_service/content/browser_context_keyed_service_factory.h
index bd911736..18e1668 100644
--- a/components/keyed_service/content/browser_context_keyed_service_factory.h
+++ b/components/keyed_service/content/browser_context_keyed_service_factory.h
@@ -21,8 +21,9 @@
 }
 
 // Base class for Factories that take a BrowserContext object and return some
-// service on a one-to-one mapping. Each factory that derives from this class
-// *must* be a Singleton (only unit tests don't do that).
+// service on a one-to-one mapping. Barring unit tests, each factory that
+// derives from this class *must* be a singleton (base::NoDestructor is
+// recommended over base::Singleton).
 //
 // We do this because services depend on each other and we need to control
 // shutdown/destruction order. In each derived classes' constructors, the
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 938f41f..7a0ce1e 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -911,8 +911,11 @@
     if (match.takeover_action) {
       ExecuteAction(selection, disposition, timestamp);
     } else {
-      OpenMatch(match, disposition, GURL(), std::u16string(), selection.line,
-                timestamp);
+      GURL alternate_nav_url = AutocompleteResult::ComputeAlternateNavUrl(
+          input_, match,
+          autocomplete_controller()->autocomplete_provider_client());
+      OpenMatch(match, disposition, alternate_nav_url, std::u16string(),
+                selection.line, timestamp);
     }
   }
 }
diff --git a/components/policy/test/data/policy_test_cases.json b/components/policy/test/data/policy_test_cases.json
index 8a8c8764..933cd970 100644
--- a/components/policy/test/data/policy_test_cases.json
+++ b/components/policy/test/data/policy_test_cases.json
@@ -14410,6 +14410,10 @@
           "enterprise_connectors.device_trust.origins": {
             "default_value": [],
             "location": "signin_profile"
+          },
+          "enterprise_connectors.device_trust_user.origins": {
+            "default_value": [],
+            "location": "signin_profile"
           }
         }
       },
@@ -14430,6 +14434,14 @@
               "https://foo.example3.com/path"
             ],
             "location": "signin_profile"
+          },
+          "enterprise_connectors.device_trust_user.origins": {
+            "value": [
+              "https://example1.com",
+              "https://[*.]example2.com",
+              "https://foo.example3.com/path"
+            ],
+            "location": "signin_profile"
           }
         }
       }
diff --git a/components/reading_list/core/reading_list_sync_bridge_unittest.cc b/components/reading_list/core/reading_list_sync_bridge_unittest.cc
index ecbdbab..865df72 100644
--- a/components/reading_list/core/reading_list_sync_bridge_unittest.cc
+++ b/components/reading_list/core/reading_list_sync_bridge_unittest.cc
@@ -143,9 +143,10 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
   base::SimpleTestClock clock_;
   testing::NiceMock<syncer::MockModelTypeChangeProcessor> processor_;
+  std::unique_ptr<ReadingListModelImpl> model_;
+
   // ModelTypeStore is owned by |model_|.
   raw_ptr<syncer::ModelTypeStore> underlying_in_memory_store_ = nullptr;
-  std::unique_ptr<ReadingListModelImpl> model_;
 };
 
 TEST_F(ReadingListSyncBridgeTest, SaveOneRead) {
diff --git a/components/reporting/util/wrapped_rate_limiter_unittest.cc b/components/reporting/util/wrapped_rate_limiter_unittest.cc
index 7ebed67..6740500 100644
--- a/components/reporting/util/wrapped_rate_limiter_unittest.cc
+++ b/components/reporting/util/wrapped_rate_limiter_unittest.cc
@@ -35,10 +35,10 @@
 
   base::test::TaskEnvironment task_environment_;
 
-  raw_ptr<MockRateLimiter> mock_rate_limiter_;
   WrappedRateLimiter::SmartPtr wrapped_rate_limiter_{
       nullptr, base::OnTaskRunnerDeleter(nullptr)};
   WrappedRateLimiter::AsyncAcquireCb async_acquire_cb_;
+  raw_ptr<MockRateLimiter> mock_rate_limiter_;
 };
 
 TEST_F(WrappedRateLimiterTest, SuccessfulAcquire) {
@@ -91,6 +91,7 @@
 
 TEST_F(WrappedRateLimiterTest, AcquireAfterDestruction) {
   test::TestEvent<bool> acuire_event;
+  mock_rate_limiter_ = nullptr;
   wrapped_rate_limiter_.reset();
   async_acquire_cb_.Run(123U, acuire_event.cb());
   EXPECT_FALSE(acuire_event.result());
diff --git a/components/saved_tab_groups/saved_tab_group.cc b/components/saved_tab_groups/saved_tab_group.cc
index bda6bc4ad..be95932 100644
--- a/components/saved_tab_groups/saved_tab_group.cc
+++ b/components/saved_tab_groups/saved_tab_group.cc
@@ -191,8 +191,21 @@
   return *this;
 }
 
-SavedTabGroup& SavedTabGroup::MoveTab(const base::Uuid& saved_tab_guid,
-                                      size_t new_index) {
+SavedTabGroup& SavedTabGroup::MoveTabLocally(const base::Uuid& saved_tab_guid,
+                                             size_t new_index) {
+  MoveTabImpl(saved_tab_guid, new_index);
+  UpdateTabPositionsImpl();
+  return *this;
+}
+
+SavedTabGroup& SavedTabGroup::MoveTabFromSync(const base::Uuid& saved_tab_guid,
+                                              size_t new_index) {
+  MoveTabImpl(saved_tab_guid, new_index);
+  return *this;
+}
+
+void SavedTabGroup::MoveTabImpl(const base::Uuid& saved_tab_guid,
+                                size_t new_index) {
   absl::optional<size_t> curr_index = GetIndexOfTab(saved_tab_guid);
   CHECK(curr_index.has_value());
   CHECK_GE(curr_index.value(), 0u);
@@ -211,15 +224,6 @@
         saved_tabs_.rbegin() + ((saved_tabs_.size() - 1) - curr_index.value()) +
             1);
   }
-  UpdateTabPositionsImpl();
-  SetUpdateTimeWindowsEpochMicros(base::Time::Now());
-  return *this;
-}
-
-void SavedTabGroup::UpdateTabPositionsImpl() {
-  for (size_t i = 0; i < saved_tabs_.size(); ++i) {
-    saved_tabs_[i].SetPosition(i);
-  }
 }
 
 void SavedTabGroup::InsertTabImpl(SavedTabGroupTab tab) {
@@ -263,6 +267,14 @@
   saved_tabs_.push_back(std::move(tab));
 }
 
+void SavedTabGroup::UpdateTabPositionsImpl() {
+  for (size_t i = 0; i < saved_tabs_.size(); ++i) {
+    saved_tabs_[i].SetPosition(i);
+  }
+
+  SetUpdateTimeWindowsEpochMicros(base::Time::Now());
+}
+
 bool SavedTabGroup::ShouldMergeGroup(
     const sync_pb::SavedTabGroupSpecifics& sync_specific) const {
   bool sync_update_is_latest =
@@ -280,6 +292,7 @@
   if (ShouldMergeGroup(sync_specific)) {
     SetTitle(base::UTF8ToUTF16(sync_specific.group().title()));
     SetColor(SyncColorToTabGroupColor(sync_specific.group().color()));
+    SetPosition(sync_specific.group().position());
     SetUpdateTimeWindowsEpochMicros(base::Time::FromDeltaSinceWindowsEpoch(
         base::Microseconds(sync_specific.update_time_windows_epoch_micros())));
   }
diff --git a/components/saved_tab_groups/saved_tab_group.h b/components/saved_tab_groups/saved_tab_group.h
index f01deab..625093e 100644
--- a/components/saved_tab_groups/saved_tab_group.h
+++ b/components/saved_tab_groups/saved_tab_group.h
@@ -111,9 +111,16 @@
   // replacement tab already exists. In this case we CHECK.
   SavedTabGroup& ReplaceTabAt(const base::Uuid& saved_tab_guid,
                               SavedTabGroupTab tab);
+
   // Moves the tab denoted by `tab_id` from its current index to the
-  // `new_index`.
-  SavedTabGroup& MoveTab(const base::Uuid& saved_tab_guid, size_t new_index);
+  // `new_index`. If the tab was moved locally update the positions of all tabs
+  // in the group. Otherwise, leave the order of the tabs as is. We do this
+  // because sync does not guarantee all the data will be sent, in the order the
+  // changes were made.
+  SavedTabGroup& MoveTabLocally(const base::Uuid& saved_tab_guid,
+                                size_t new_index);
+  SavedTabGroup& MoveTabFromSync(const base::Uuid& saved_tab_guid,
+                                 size_t new_index);
 
   // Merges this groups data with a specific from sync and returns the newly
   // merged specific. Side effect: Updates the values of this group.
@@ -144,6 +151,9 @@
       const sync_pb::SavedTabGroup::SavedTabGroupColor color);
 
  private:
+  // Moves the tab denoted by `saved_tab_guid` to the position `new_index`.
+  void MoveTabImpl(const base::Uuid& saved_tab_guid, size_t new_index);
+
   // Insert `tab` into sorted order based on its position compared to already
   // stored tabs in its group. It should be noted that the list of tabs in each
   // group must already be in sorted order for this function to work as
diff --git a/components/saved_tab_groups/saved_tab_group_model.cc b/components/saved_tab_groups/saved_tab_group_model.cc
index 16a288d9..50dbfd2 100644
--- a/components/saved_tab_groups/saved_tab_group_model.cc
+++ b/components/saved_tab_groups/saved_tab_group_model.cc
@@ -4,6 +4,7 @@
 
 #include "components/saved_tab_groups/saved_tab_group_model.h"
 
+#include <algorithm>
 #include <cstddef>
 #include <memory>
 #include <vector>
@@ -370,7 +371,7 @@
   // Copy `tab_id` to prevent uaf when ungrouping a saved tab: crbug/1401965.
   const base::Uuid copy_tab_id = tab_id;
   absl::optional<int> index = GetIndexOf(group_id);
-  saved_tab_groups_[index.value()].MoveTab(tab_id, new_index);
+  saved_tab_groups_[index.value()].MoveTabLocally(tab_id, new_index);
 
   for (auto& observer : observers_) {
     observer.SavedTabGroupUpdatedLocally(group_id, copy_tab_id);
@@ -383,12 +384,16 @@
 
   DCHECK(Contains(group_id));
 
-  int index = GetIndexOf(group_id).value();
+  const int index = GetIndexOf(group_id).value();
+  const int preferred_index = sync_specific.group().position();
+
   saved_tab_groups_[index].MergeGroup(std::move(sync_specific));
 
-  int preferred_index = Get(group_id)->position();
   if (index != preferred_index) {
-    Reorder(group_id, preferred_index);
+    const int num_groups = Count();
+    const int new_index =
+        preferred_index < num_groups ? preferred_index : num_groups - 1;
+    ReorderGroupFromSync(group_id, std::max(new_index, 0));
   }
 
   for (auto& observer : observers_) {
@@ -402,90 +407,45 @@
     const sync_pb::SavedTabGroupSpecifics& sync_specific) {
   const base::Uuid& group_guid =
       base::Uuid::ParseLowercase(sync_specific.tab().group_guid());
-  absl::optional<int> group_index = GetIndexOf(group_guid);
-  CHECK(group_index.has_value());
 
   const base::Uuid& tab_guid = base::Uuid::ParseLowercase(sync_specific.guid());
-  CHECK(saved_tab_groups_[group_index.value()].ContainsTab(tab_guid));
+  SavedTabGroup* const group = GetGroupContainingTab(tab_guid);
+  CHECK(group);
 
-  SavedTabGroupTab merged_tab(*Get(group_guid)->GetTab(tab_guid));
-  merged_tab.MergeTab(std::move(sync_specific));
-  saved_tab_groups_[group_index.value()].ReplaceTabAt(tab_guid, merged_tab);
+  const absl::optional<int> index = group->GetIndexOfTab(tab_guid);
+  const int preferred_index = sync_specific.tab().position();
 
-  for (auto& observer : observers_) {
-    observer.SavedTabGroupUpdatedFromSync(group_guid,
-                                          merged_tab.saved_tab_guid());
+  group->GetTab(tab_guid)->MergeTab(std::move(sync_specific));
+
+  if (index != preferred_index) {
+    const int num_tabs = group->saved_tabs().size();
+    const int new_index =
+        preferred_index < num_tabs ? preferred_index : num_tabs - 1;
+    group->MoveTabFromSync(tab_guid, std::max(new_index, 0));
   }
 
-  return merged_tab.ToSpecifics();
+  for (auto& observer : observers_) {
+    observer.SavedTabGroupUpdatedFromSync(group_guid, tab_guid);
+  }
+
+  return group->GetTab(tab_guid)->ToSpecifics();
 }
 
-void SavedTabGroupModel::Reorder(const base::Uuid& id, int new_index) {
-  CHECK_GE(new_index, 0);
-  CHECK_LT(new_index, Count());
-
-  absl::optional<int> index = GetIndexOf(id);
-  CHECK(index.has_value());
-  CHECK_GE(index.value(), 0);
-
-  SavedTabGroup group = saved_tab_groups_[index.value()];
-
-  saved_tab_groups_.erase(saved_tab_groups_.begin() + index.value());
-  saved_tab_groups_.emplace(saved_tab_groups_.begin() + new_index,
-                            std::move(group));
-
+void SavedTabGroupModel::ReorderGroupLocally(const base::Uuid& id,
+                                             int new_index) {
+  ReorderGroupImpl(id, new_index);
   UpdateGroupPositionsImpl();
-
   for (auto& observer : observers_) {
     observer.SavedTabGroupReorderedLocally();
   }
 }
 
-void SavedTabGroupModel::UpdateGroupPositionsImpl() {
-  for (size_t i = 0; i < saved_tab_groups_.size(); ++i)
-    saved_tab_groups_[i].SetPosition(i);
-}
-
-void SavedTabGroupModel::InsertGroupImpl(const SavedTabGroup& group) {
-  // We can always safely insert the first group.
-  if (saved_tab_groups_.empty()) {
-    saved_tab_groups_.emplace_back(std::move(group));
-    return;
+void SavedTabGroupModel::ReorderGroupFromSync(const base::Uuid& id,
+                                              int new_index) {
+  ReorderGroupImpl(id, new_index);
+  for (auto& observer : observers_) {
+    observer.SavedTabGroupReorderedFromSync();
   }
-
-  // Because saved_tab_groups_ must be in sorted order, we can immediately place
-  // the group at the end of the vector if `group` is the largest
-  // element we have seen yet.
-  if (saved_tab_groups_[saved_tab_groups_.size() - 1].position() <
-      group.position()) {
-    saved_tab_groups_.emplace_back(std::move(group));
-    return;
-  }
-
-  // Insert `group` in front of an element if one of these criteria
-  // are met:
-  // 1. The current index is larger than `group`.
-  // 2. The current index has the same position as `group` and is not
-  // the most recently updated position.
-  for (size_t index = 0; index < saved_tab_groups_.size(); ++index) {
-    const SavedTabGroup& curr_group = saved_tab_groups_[index];
-    bool curr_position_larger = curr_group.position() > group.position();
-    bool curr_position_same = curr_group.position() == group.position();
-    bool curr_position_least_recently_updated =
-        curr_group.update_time_windows_epoch_micros() <=
-        group.update_time_windows_epoch_micros();
-
-    if (curr_position_larger ||
-        (curr_position_same && curr_position_least_recently_updated)) {
-      saved_tab_groups_.insert(saved_tab_groups_.begin() + index,
-                               std::move(group));
-      return;
-    }
-  }
-
-  // This can happen when the last element of the vector has the same position
-  // as `group` and was more recently updated.
-  saved_tab_groups_.emplace_back(std::move(group));
 }
 
 std::vector<sync_pb::SavedTabGroupSpecifics>
@@ -556,6 +516,77 @@
   }
 }
 
+void SavedTabGroupModel::AddObserver(SavedTabGroupModelObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void SavedTabGroupModel::RemoveObserver(SavedTabGroupModelObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void SavedTabGroupModel::ReorderGroupImpl(const base::Uuid& id, int new_index) {
+  DCHECK_GE(new_index, 0);
+  DCHECK_LT(new_index, Count());
+
+  absl::optional<int> index = GetIndexOf(id);
+  CHECK(index.has_value());
+  CHECK_GE(index.value(), 0);
+
+  SavedTabGroup group = saved_tab_groups_[index.value()];
+
+  saved_tab_groups_.erase(saved_tab_groups_.begin() + index.value());
+  saved_tab_groups_.emplace(saved_tab_groups_.begin() + new_index,
+                            std::move(group));
+}
+
+void SavedTabGroupModel::UpdateGroupPositionsImpl() {
+  for (size_t i = 0; i < saved_tab_groups_.size(); ++i) {
+    saved_tab_groups_[i].SetPosition(i);
+  }
+}
+
+void SavedTabGroupModel::InsertGroupImpl(const SavedTabGroup& group) {
+  // We can always safely insert the first group.
+  if (saved_tab_groups_.empty()) {
+    saved_tab_groups_.emplace_back(std::move(group));
+    return;
+  }
+
+  // Because saved_tab_groups_ must be in sorted order, we can immediately place
+  // the group at the end of the vector if `group` is the largest
+  // element we have seen yet.
+  if (saved_tab_groups_[saved_tab_groups_.size() - 1].position() <
+      group.position()) {
+    saved_tab_groups_.emplace_back(std::move(group));
+    return;
+  }
+
+  // Insert `group` in front of an element if one of these criteria
+  // are met:
+  // 1. The current index is larger than `group`.
+  // 2. The current index has the same position as `group` and is not
+  // the most recently updated position.
+  for (size_t index = 0; index < saved_tab_groups_.size(); ++index) {
+    const SavedTabGroup& curr_group = saved_tab_groups_[index];
+    bool curr_position_larger = curr_group.position() > group.position();
+    bool curr_position_same = curr_group.position() == group.position();
+    bool curr_position_least_recently_updated =
+        curr_group.update_time_windows_epoch_micros() <=
+        group.update_time_windows_epoch_micros();
+
+    if (curr_position_larger ||
+        (curr_position_same && curr_position_least_recently_updated)) {
+      saved_tab_groups_.insert(saved_tab_groups_.begin() + index,
+                               std::move(group));
+      return;
+    }
+  }
+
+  // This can happen when the last element of the vector has the same position
+  // as `group` and was more recently updated.
+  saved_tab_groups_.emplace_back(std::move(group));
+}
+
 std::unique_ptr<SavedTabGroup> SavedTabGroupModel::RemoveImpl(int index) {
   CHECK_GE(index, 0);
   std::unique_ptr<SavedTabGroup> removed_group =
@@ -575,11 +606,3 @@
   saved_group.SetTitle(visual_data->title());
   saved_group.SetColor(visual_data->color());
 }
-
-void SavedTabGroupModel::AddObserver(SavedTabGroupModelObserver* observer) {
-  observers_.AddObserver(observer);
-}
-
-void SavedTabGroupModel::RemoveObserver(SavedTabGroupModelObserver* observer) {
-  observers_.RemoveObserver(observer);
-}
diff --git a/components/saved_tab_groups/saved_tab_group_model.h b/components/saved_tab_groups/saved_tab_group_model.h
index acaec944..60d78ef 100644
--- a/components/saved_tab_groups/saved_tab_group_model.h
+++ b/components/saved_tab_groups/saved_tab_group_model.h
@@ -127,8 +127,11 @@
       const sync_pb::SavedTabGroupSpecifics& sync_specific);
 
   // Changes the index of a given tab group by id. The new index provided is the
-  // expected index after the group is removed.
-  void Reorder(const base::Uuid& id, int new_index);
+  // expected index after the group is removed. Notify local observers if the
+  // group was reordered locally, and sync observers if the group was reordered
+  // from sync.
+  void ReorderGroupLocally(const base::Uuid& id, int new_index);
+  void ReorderGroupFromSync(const base::Uuid& id, int new_index);
 
   // Loads the entries (a sync_pb::SavedTabGroupSpecifics can be a group or a
   // tab) saved locally in the model type store (local storage) and attempts to
@@ -149,6 +152,9 @@
   void RemoveObserver(SavedTabGroupModelObserver* observer);
 
  private:
+  // Moves the group denoted by `id` to the position `new_index`.
+  void ReorderGroupImpl(const base::Uuid& id, int new_index);
+
   // Updates all group positions to match the index they are currently stored
   // at.
   void UpdateGroupPositionsImpl();
diff --git a/components/saved_tab_groups/saved_tab_group_model_observer.h b/components/saved_tab_groups/saved_tab_group_model_observer.h
index 9ee180f..35725fd 100644
--- a/components/saved_tab_groups/saved_tab_group_model_observer.h
+++ b/components/saved_tab_groups/saved_tab_group_model_observer.h
@@ -38,10 +38,11 @@
       const absl::optional<base::Uuid>& tab_guid = absl::nullopt) {}
 
   // Called when the order of saved tab groups in the bookmark bar are changed.
-  // TODO(crbug/1372052): Figure out if we can maintain ordering of groups and
-  // tabs in sync.
   virtual void SavedTabGroupReorderedLocally() {}
 
+  // Happens when a group is reordered from sync.
+  virtual void SavedTabGroupReorderedFromSync() {}
+
   // Called when sync / ModelTypeStore updates data.
   virtual void SavedTabGroupAddedFromSync(const base::Uuid& guid) {}
 
diff --git a/components/saved_tab_groups/saved_tab_group_model_unittest.cc b/components/saved_tab_groups/saved_tab_group_model_unittest.cc
index 36e69d2..786886af 100644
--- a/components/saved_tab_groups/saved_tab_group_model_unittest.cc
+++ b/components/saved_tab_groups/saved_tab_group_model_unittest.cc
@@ -464,15 +464,15 @@
   ASSERT_EQ(0, saved_tab_group_model_->GetIndexOf(id_1_));
   ASSERT_EQ(1, saved_tab_group_model_->GetIndexOf(id_2_));
   ASSERT_EQ(2, saved_tab_group_model_->GetIndexOf(id_3_));
-  saved_tab_group_model_->Reorder(id_2_, 2);
+  saved_tab_group_model_->ReorderGroupLocally(id_2_, 2);
   EXPECT_EQ(0, saved_tab_group_model_->GetIndexOf(id_1_));
   EXPECT_EQ(1, saved_tab_group_model_->GetIndexOf(id_3_));
   EXPECT_EQ(2, saved_tab_group_model_->GetIndexOf(id_2_));
-  saved_tab_group_model_->Reorder(id_2_, 0);
+  saved_tab_group_model_->ReorderGroupLocally(id_2_, 0);
   EXPECT_EQ(0, saved_tab_group_model_->GetIndexOf(id_2_));
   EXPECT_EQ(1, saved_tab_group_model_->GetIndexOf(id_1_));
   EXPECT_EQ(2, saved_tab_group_model_->GetIndexOf(id_3_));
-  saved_tab_group_model_->Reorder(id_2_, 1);
+  saved_tab_group_model_->ReorderGroupLocally(id_2_, 1);
   EXPECT_EQ(0, saved_tab_group_model_->GetIndexOf(id_1_));
   EXPECT_EQ(1, saved_tab_group_model_->GetIndexOf(id_2_));
   EXPECT_EQ(2, saved_tab_group_model_->GetIndexOf(id_3_));
@@ -947,7 +947,7 @@
   saved_tab_group_model_->Add(stg_2);
   saved_tab_group_model_->Add(stg_3);
 
-  saved_tab_group_model_->Reorder(stg_2.saved_guid(), 2);
+  saved_tab_group_model_->ReorderGroupLocally(stg_2.saved_guid(), 2);
 
   EXPECT_TRUE(reordered_called_);
   EXPECT_EQ(2, saved_tab_group_model_->GetIndexOf(stg_2.saved_guid()));
diff --git a/components/saved_tab_groups/saved_tab_group_tab.cc b/components/saved_tab_groups/saved_tab_group_tab.cc
index 68fd40d..acc39d6 100644
--- a/components/saved_tab_groups/saved_tab_group_tab.cc
+++ b/components/saved_tab_groups/saved_tab_group_tab.cc
@@ -54,6 +54,7 @@
   if (ShouldMergeTab(sync_specific)) {
     SetURL(GURL(sync_specific.tab().url()));
     SetTitle(base::UTF8ToUTF16(sync_specific.tab().title()));
+    SetPosition(sync_specific.tab().position());
     SetUpdateTimeWindowsEpochMicros(base::Time::FromDeltaSinceWindowsEpoch(
         base::Microseconds(sync_specific.update_time_windows_epoch_micros())));
   }
diff --git a/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrlService.java b/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrlService.java
index 127dd3b..7609da4 100644
--- a/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrlService.java
+++ b/components/search_engines/android/java/src/org/chromium/components/search_engines/TemplateUrlService.java
@@ -360,13 +360,6 @@
                 TemplateUrlService.this, name, keyword, searchUrl, suggestUrl, faviconUrl,
                 setAsDefault);
     }
-    // TODO(crbug/1002271): This API is called from clank repo. Helper function below will be
-    // removed once clank repo is updated.
-    public boolean setPlayAPISearchEngine(
-            String name, String keyword, String searchUrl, String suggestUrl, String faviconUrl) {
-        return TemplateUrlServiceJni.get().setPlayAPISearchEngine(mNativeTemplateUrlServiceAndroid,
-                TemplateUrlService.this, name, keyword, searchUrl, suggestUrl, faviconUrl, true);
-    }
 
     @VisibleForTesting
     public String addSearchEngineForTesting(String keyword, int ageInDays) {
diff --git a/components/supervised_user_strings.grdp b/components/supervised_user_strings.grdp
index fa5beaa..078f999 100644
--- a/components/supervised_user_strings.grdp
+++ b/components/supervised_user_strings.grdp
@@ -91,5 +91,11 @@
       <message name="IDS_SUPERVISED_USER_NOT_SIGNED_IN" desc="Message to be shown to a supervised user who is not signed in to Chrome">
         Please start and sign in to Chrome so that Chrome can check whether you are allowed to access this site.
       </message>
+      <message name="IDS_PARENT_BLOCKED_SITE_BANNER_TITLE" desc="The banner title for the first time a user is blocked from a website because of parental controls">
+        New: Family Link choices for Chrome apply here
+      </message>
+      <message name="IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE" desc="The banner message for the first time a user is blocked from a website because of parental controls">
+        The settings your parent chose are now keeping you safer online
+      </message>
 
 </grit-part>
diff --git a/components/supervised_user_strings_grdp/IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE.png.sha1 b/components/supervised_user_strings_grdp/IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE.png.sha1
new file mode 100644
index 0000000..3e51eae
--- /dev/null
+++ b/components/supervised_user_strings_grdp/IDS_PARENT_BLOCKED_SITE_BANNER_MESSAGE.png.sha1
@@ -0,0 +1 @@
+e82ded6cfb662c79db03cac57bd94dad38c3f93a
\ No newline at end of file
diff --git a/components/supervised_user_strings_grdp/IDS_PARENT_BLOCKED_SITE_BANNER_TITLE.png.sha1 b/components/supervised_user_strings_grdp/IDS_PARENT_BLOCKED_SITE_BANNER_TITLE.png.sha1
new file mode 100644
index 0000000..2ff30ef
--- /dev/null
+++ b/components/supervised_user_strings_grdp/IDS_PARENT_BLOCKED_SITE_BANNER_TITLE.png.sha1
@@ -0,0 +1 @@
+5f9a84b98137fc4a11cbee08a74fcc9dbf45c850
\ No newline at end of file
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h
index 4e9e370..3d44ea9 100644
--- a/components/sync/engine/sync_engine.h
+++ b/components/sync/engine/sync_engine.h
@@ -182,6 +182,11 @@
   // Enables/Disables invalidations for session sync related datatypes.
   virtual void SetInvalidationsForSessionsEnabled(bool enabled) = 0;
 
+  // Returns whether the poll interval elapsed since the last known poll time.
+  // If returns true, there will likely be the next PERIODIC sync cycle soon but
+  // it's not guaranteed, see SyncSchedulerImpl for details.
+  virtual bool IsNextPollTimeInThePast() const = 0;
+
   // Returns a Value::List representing Nigori node.
   virtual void GetNigoriNodeForDebugging(AllNodesCallback callback) = 0;
 };
diff --git a/components/sync/engine/sync_scheduler_impl.cc b/components/sync/engine/sync_scheduler_impl.cc
index 3be722a..e4b65487 100644
--- a/components/sync/engine/sync_scheduler_impl.cc
+++ b/components/sync/engine/sync_scheduler_impl.cc
@@ -176,6 +176,7 @@
     // actually have miss the real poll, unless the client is restarted.
     // Fixing that would require using an AlarmTimer though, which is only
     // supported on certain platforms.
+    // TODO(crbug.com/1448012): introduce a helper to deal with poll times.
     last_poll_reset_ =
         TimeTicks::Now() -
         (now - ComputeLastPollOnStart(last_poll_time, GetPollInterval(), now));
diff --git a/components/sync/service/glue/sync_engine_impl.cc b/components/sync/service/glue/sync_engine_impl.cc
index 4e80ec8..8c1f916 100644
--- a/components/sync/service/glue/sync_engine_impl.cc
+++ b/components/sync/service/glue/sync_engine_impl.cc
@@ -17,6 +17,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/rand_util.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/public/invalidation_handler.h"
@@ -238,6 +239,7 @@
 
 void SyncEngineImpl::StartSyncingWithServer() {
   DVLOG(1) << name_ << ": SyncEngineImpl::StartSyncingWithServer called.";
+  // TODO(crbug.com/1448012): introduce a helper to deal with poll times.
   base::Time last_poll_time = prefs_->GetLastPollTime();
   // If there's no known last poll time (e.g. on initial start-up), we treat
   // this as if a poll just happened.
@@ -575,11 +577,29 @@
 }
 
 void SyncEngineImpl::SetInvalidationsForSessionsEnabled(bool enabled) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   sessions_invalidation_enabled_ = enabled;
   SendInterestedTopicsToInvalidator();
 }
 
+bool SyncEngineImpl::IsNextPollTimeInThePast() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(crbug.com/1448012): introduce a helper to deal with poll times.
+  base::Time last_poll_time = prefs_->GetLastPollTime();
+  base::TimeDelta poll_interval = prefs_->GetPollInterval();
+  if (last_poll_time.is_null() || poll_interval.is_zero()) {
+    // It's likely the first startup so the very first poll interval is just
+    // starting.
+    return false;
+  }
+
+  base::Time now = base::Time::Now();
+  return now >= last_poll_time + poll_interval;
+}
+
 void SyncEngineImpl::GetNigoriNodeForDebugging(AllNodesCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(backend_);
   sync_task_runner_->PostTask(
       FROM_HERE,
@@ -588,6 +608,7 @@
 }
 
 void SyncEngineImpl::OnInvalidatorClientIdChange(const std::string& client_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   sync_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&SyncEngineBackend::DoOnInvalidatorClientIdChange,
diff --git a/components/sync/service/glue/sync_engine_impl.h b/components/sync/service/glue/sync_engine_impl.h
index 092a5d1..b902de33 100644
--- a/components/sync/service/glue/sync_engine_impl.h
+++ b/components/sync/service/glue/sync_engine_impl.h
@@ -103,6 +103,7 @@
   void OnCookieJarChanged(bool account_mismatch,
                           base::OnceClosure callback) override;
   void SetInvalidationsForSessionsEnabled(bool enabled) override;
+  bool IsNextPollTimeInThePast() const override;
   void GetNigoriNodeForDebugging(AllNodesCallback callback) override;
 
   // InvalidationHandler implementation.
diff --git a/components/sync/service/glue/sync_engine_impl_unittest.cc b/components/sync/service/glue/sync_engine_impl_unittest.cc
index 04e6c40..80c50ae 100644
--- a/components/sync/service/glue/sync_engine_impl_unittest.cc
+++ b/components/sync/service/glue/sync_engine_impl_unittest.cc
@@ -34,6 +34,7 @@
 #include "components/sync/base/features.h"
 #include "components/sync/base/invalidation_helper.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 #include "components/sync/engine/net/http_bridge.h"
 #include "components/sync/engine/sync_engine_host.h"
 #include "components/sync/engine/sync_manager_factory.h"
@@ -59,6 +60,8 @@
 static const base::FilePath::CharType kTestSyncDir[] =
     FILE_PATH_LITERAL("sync-test");
 constexpr char kTestGaiaId[] = "test_gaia_id";
+constexpr char kTestCacheGuid[] = "test_cache_guid";
+constexpr char kTestBirthday[] = "test_birthday";
 
 class MockSyncEngineHost : public SyncEngineHost {
  public:
@@ -822,9 +825,6 @@
 }
 
 TEST_F(SyncEngineImplTest, ShouldPopulateAccountIdCachedInPrefs) {
-  const std::string kTestCacheGuid = "test_cache_guid";
-  const std::string kTestBirthday = "test_birthday";
-
   SyncTransportDataPrefs transport_data_prefs(&pref_service_);
   transport_data_prefs.SetCacheGuid(kTestCacheGuid);
   transport_data_prefs.SetBirthday(kTestBirthday);
@@ -837,9 +837,6 @@
 
 TEST_F(SyncEngineImplTest,
        ShouldNotPopulateAccountIdCachedInPrefsWithLocalSync) {
-  const std::string kTestCacheGuid = "test_cache_guid";
-  const std::string kTestBirthday = "test_birthday";
-
   SyncTransportDataPrefs transport_data_prefs(&pref_service_);
   transport_data_prefs.SetCacheGuid(kTestCacheGuid);
   transport_data_prefs.SetBirthday(kTestBirthday);
@@ -851,9 +848,6 @@
 }
 
 TEST_F(SyncEngineImplTest, ShouldLoadSyncDataUponInitialization) {
-  const std::string kTestCacheGuid = "test_cache_guid";
-  const std::string kTestBirthday = "test_birthday";
-
   SyncTransportDataPrefs transport_data_prefs(&pref_service_);
   transport_data_prefs.SetCacheGuid(kTestCacheGuid);
   transport_data_prefs.SetBirthday(kTestBirthday);
@@ -871,9 +865,6 @@
 // between the account ID cached in SyncPrefs and the actual one.
 TEST_F(SyncEngineImplTest,
        ShouldClearLocalSyncTransportDataDueToAccountIdMismatch) {
-  const std::string kTestCacheGuid = "test_cache_guid";
-  const std::string kTestBirthday = "test_birthday";
-
   SyncTransportDataPrefs transport_data_prefs(&pref_service_);
   transport_data_prefs.SetCacheGuid(kTestCacheGuid);
   transport_data_prefs.SetBirthday(kTestBirthday);
@@ -915,6 +906,37 @@
   fake_manager_->WaitForSyncThread();
 }
 
+TEST_F(SyncEngineImplTest, ShouldReturnWhetherNextPollTimePassed) {
+  SyncTransportDataPrefs transport_data_prefs(&pref_service_);
+  transport_data_prefs.SetCacheGuid(kTestCacheGuid);
+  transport_data_prefs.SetBirthday(kTestBirthday);
+
+  transport_data_prefs.SetLastPollTime(base::Time::Now() - base::Hours(5));
+  transport_data_prefs.SetPollInterval(base::Hours(4));
+
+  InitializeBackend();
+  ConfigureDataTypes();
+
+  EXPECT_TRUE(backend_->IsNextPollTimeInThePast());
+
+  // Mimic a finished PERIODIC sync cycle.
+  SyncCycleSnapshot snapshot(
+      /*birthday=*/std::string(),
+      /*bag_of_chips=*/std::string(), ModelNeutralState(), ProgressMarkerMap(),
+      /*is_silenced=*/false,
+      /*num_server_conflicts=*/0,
+      /*notifications_enabled=*/true,
+      /*sync_start_time=*/base::Time::Now(),
+      /*poll_finish_time=*/base::Time::Now(),
+      /*get_updates_origin=*/sync_pb::SyncEnums::PERIODIC,
+      /*poll_interval=*/base::Hours(4),
+      /*has_remaining_local_changes=*/false);
+  fake_manager_->NotifySyncCycleCompleted(snapshot);
+  fake_manager_->WaitForSyncThread();
+
+  EXPECT_FALSE(backend_->IsNextPollTimeInThePast());
+}
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/sync/service/sync_service_impl.cc b/components/sync/service/sync_service_impl.cc
index 9bd4b87..be1fad6 100644
--- a/components/sync/service/sync_service_impl.cc
+++ b/components/sync/service/sync_service_impl.cc
@@ -1358,6 +1358,8 @@
   DCHECK(engine_);
   DCHECK(engine_->IsInitialized());
   DCHECK(!engine_->GetCacheGuid().empty());
+  DVLOG(1) << "Started DataTypeManager configuration, reason: "
+           << static_cast<int>(reason);
 
   ConfigureContext configure_context;
   configure_context.authenticated_account_id = GetAccountInfo().account_id;
@@ -1847,39 +1849,46 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!auth_manager_->IsActiveAccountInfoFullyLoaded()) {
-    // Wait for refresh tokens to be loaded from the disk. GetDisableReasons()
-    // won't be empty until then.
+    DVLOG(1) << "Waiting for refresh tokens to be loaded from the disk";
+    // GetDisableReasons() won't be empty until then.
     return ModelTypeDownloadStatus::kWaitingForUpdates;
   }
 
   // TODO(crbug.com/1425026): check whether this works when local sync is
   // enabled.
   if (!GetDisableReasons().Empty() || !GetDataTypesToConfigure().Has(type)) {
-    // Sync is disabled hence updates won't be downloaded from the server.
+    DVLOG(1)
+        << "Sync or " << ModelTypeToDebugString(type)
+        << " is disabled hence updates won't be downloaded from the server";
     return ModelTypeDownloadStatus::kError;
   }
 
   if (!data_type_manager_) {
-    // Wait for the sync engine to be fully initialized.
+    DVLOG(1) << "Waiting for the sync engine to be fully initialized";
     return ModelTypeDownloadStatus::kWaitingForUpdates;
   }
   CHECK(engine_);
 
   if (data_type_manager_->GetDataTypesWithPermanentErrors().Has(type)) {
+    DVLOG(1) << "Permanent error for " << ModelTypeToDebugString(type);
     return ModelTypeDownloadStatus::kError;
   }
 
   if (!GetActiveDataTypes().Has(type) ||
       !engine_->GetDetailedStatus().notifications_enabled) {
+    DVLOG(1) << "Waiting for invalidations to be initialized";
     return ModelTypeDownloadStatus::kWaitingForUpdates;
   }
 
-  if (engine_->GetDetailedStatus().invalidated_data_types.Has(type)) {
+  // If there are any incoming invalidations or poll time elapsed, there can be
+  // new updates to download from the server.
+  if (engine_->GetDetailedStatus().invalidated_data_types.Has(type) ||
+      engine_->IsNextPollTimeInThePast()) {
+    DVLOG(1)
+        << "Waiting for updates due to incoming invalidations or poll request";
     return ModelTypeDownloadStatus::kWaitingForUpdates;
   }
 
-  // TODO(crbug.com/1425026): check for upcoming polling request.
-  NOTREACHED() << "Download status API is not fully implemented yet.";
   return ModelTypeDownloadStatus::kUpToDate;
 }
 
diff --git a/components/sync/service/sync_service_impl_unittest.cc b/components/sync/service/sync_service_impl_unittest.cc
index b5b6286..aad19f9 100644
--- a/components/sync/service/sync_service_impl_unittest.cc
+++ b/components/sync/service/sync_service_impl_unittest.cc
@@ -200,6 +200,12 @@
     }
   }
 
+  void SetInvalidationsEnabled() {
+    SyncStatus status = engine()->GetDetailedStatus();
+    status.notifications_enabled = true;
+    engine()->SetDetailedStatus(status);
+  }
+
   void TriggerPassphraseRequired() {
     service_->GetEncryptionObserverForTest()->OnPassphraseRequired(
         KeyDerivationParams::CreateForPbkdf2(), sync_pb::EncryptedData());
@@ -1392,8 +1398,8 @@
   ON_CALL(mock_sync_service_observer, OnStateChanged)
       .WillByDefault(Invoke([this, &met_configuring_data_type_manager](
                                 SyncService* service) {
-        // TODO(crbug.com/1425026): verify that there are no errors during
-        // backend initialization.
+        EXPECT_NE(service->GetDownloadStatusFor(syncer::BOOKMARKS),
+                  SyncService::ModelTypeDownloadStatus::kError);
         if (!data_type_manager()) {
           return;
         }
@@ -1407,6 +1413,7 @@
 
   // Observers must be added after initialization has been started.
   InitializeForNthSync(false);
+  ASSERT_THAT(engine(), IsNull());
 
   // GetDownloadStatusFor() must be called only after Initialize(), see
   // SyncServiceImpl::Initialize().
@@ -1415,9 +1422,11 @@
 
   service()->AddObserver(&mock_sync_service_observer);
   base::RunLoop().RunUntilIdle();
+  SetInvalidationsEnabled();
 
   EXPECT_TRUE(met_configuring_data_type_manager);
-  // TODO(crbug.com/1425026): add check for up-to-date status.
+  EXPECT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
+            SyncService::ModelTypeDownloadStatus::kUpToDate);
   service()->RemoveObserver(&mock_sync_service_observer);
 }
 
@@ -1445,16 +1454,45 @@
   SignIn();
   CreateService();
   InitializeForNthSync();
+  SetInvalidationsEnabled();
 
-  SyncStatus status;
+  SyncStatus status = engine()->GetDetailedStatus();
   status.invalidated_data_types.Put(BOOKMARKS);
   engine()->SetDetailedStatus(status);
 
   EXPECT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
             SyncService::ModelTypeDownloadStatus::kWaitingForUpdates);
-  // TODO(crbug.com/1425026): uncomment once up-to-date status is supported.
-  // EXPECT_EQ(service()->GetDownloadStatusFor(syncer::PASSWORDS),
-  //           SyncService::ModelTypeDownloadStatus::kUpToDate);
+  EXPECT_EQ(service()->GetDownloadStatusFor(syncer::DEVICE_INFO),
+            SyncService::ModelTypeDownloadStatus::kUpToDate);
+}
+
+TEST_F(SyncServiceImplTest, ShouldWaitForInitializedInvalidations) {
+  SignIn();
+  CreateService();
+  InitializeForNthSync();
+  ASSERT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
+            SyncService::ModelTypeDownloadStatus::kWaitingForUpdates);
+
+  SetInvalidationsEnabled();
+  EXPECT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
+            SyncService::ModelTypeDownloadStatus::kUpToDate);
+}
+
+TEST_F(SyncServiceImplTest, ShouldWaitForPollRequest) {
+  SignIn();
+  CreateService();
+  InitializeForNthSync();
+  SetInvalidationsEnabled();
+  ASSERT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
+            SyncService::ModelTypeDownloadStatus::kUpToDate);
+
+  engine()->SetPollIntervalElapsed(true);
+  EXPECT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
+            SyncService::ModelTypeDownloadStatus::kWaitingForUpdates);
+
+  engine()->SetPollIntervalElapsed(false);
+  EXPECT_EQ(service()->GetDownloadStatusFor(syncer::BOOKMARKS),
+            SyncService::ModelTypeDownloadStatus::kUpToDate);
 }
 
 }  // namespace
diff --git a/components/sync/test/fake_sync_engine.cc b/components/sync/test/fake_sync_engine.cc
index bd05182..d0dd41d 100644
--- a/components/sync/test/fake_sync_engine.cc
+++ b/components/sync/test/fake_sync_engine.cc
@@ -35,6 +35,10 @@
   host_->OnEngineInitialized(success, is_first_time_sync_configure_);
 }
 
+void FakeSyncEngine::SetPollIntervalElapsed(bool elapsed) {
+  is_next_poll_time_in_the_past_ = elapsed;
+}
+
 void FakeSyncEngine::SetDetailedStatus(const SyncStatus& status) {
   sync_status_ = status;
 }
@@ -138,6 +142,10 @@
 
 void FakeSyncEngine::SetInvalidationsForSessionsEnabled(bool enabled) {}
 
+bool FakeSyncEngine::IsNextPollTimeInThePast() const {
+  return is_next_poll_time_in_the_past_;
+}
+
 void FakeSyncEngine::GetNigoriNodeForDebugging(AllNodesCallback callback) {}
 
 }  // namespace syncer
diff --git a/components/sync/test/fake_sync_engine.h b/components/sync/test/fake_sync_engine.h
index 335017a..44c802c 100644
--- a/components/sync/test/fake_sync_engine.h
+++ b/components/sync/test/fake_sync_engine.h
@@ -43,6 +43,8 @@
     return started_handling_invalidations_;
   }
 
+  void SetPollIntervalElapsed(bool elapsed);
+
   // Manual completion of Initialize(), required if auto-completion was disabled
   // in the constructor.
   void TriggerInitializationCompletion(bool success);
@@ -107,6 +109,7 @@
   void OnCookieJarChanged(bool account_mismatch,
                           base::OnceClosure callback) override;
   void SetInvalidationsForSessionsEnabled(bool enabled) override;
+  bool IsNextPollTimeInThePast() const override;
   void GetNigoriNodeForDebugging(AllNodesCallback callback) override;
 
  private:
@@ -119,6 +122,7 @@
   SyncStatus sync_status_;
   CoreAccountId authenticated_account_id_;
   bool started_handling_invalidations_ = false;
+  bool is_next_poll_time_in_the_past_ = false;
 };
 
 }  // namespace syncer
diff --git a/components/sync/test/fake_sync_manager.cc b/components/sync/test/fake_sync_manager.cc
index 988c444a..4b0c6de 100644
--- a/components/sync/test/fake_sync_manager.cc
+++ b/components/sync/test/fake_sync_manager.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/run_loop.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 #include "components/sync/engine/engine_components_factory.h"
 #include "components/sync/engine/net/http_post_provider_factory.h"
 #include "components/sync/test/fake_model_type_connector.h"
@@ -69,6 +70,13 @@
                                 base::Unretained(this), status));
 }
 
+void FakeSyncManager::NotifySyncCycleCompleted(
+    const SyncCycleSnapshot& snapshot) {
+  sync_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&FakeSyncManager::DoNotifySyncCycleCompleted,
+                                base::Unretained(this), snapshot));
+}
+
 void FakeSyncManager::DoNotifySyncStatusChanged(const SyncStatus& status) {
   DCHECK(sync_task_runner_->RunsTasksInCurrentSequence());
   for (Observer& observer : observers_) {
@@ -76,6 +84,14 @@
   }
 }
 
+void FakeSyncManager::DoNotifySyncCycleCompleted(
+    const SyncCycleSnapshot& snapshot) {
+  DCHECK(sync_task_runner_->RunsTasksInCurrentSequence());
+  for (Observer& observer : observers_) {
+    observer.OnSyncCycleCompleted(snapshot);
+  }
+}
+
 void FakeSyncManager::Init(InitArgs* args) {
   cache_guid_ = args->cache_guid;
   birthday_ = args->birthday;
diff --git a/components/sync/test/fake_sync_manager.h b/components/sync/test/fake_sync_manager.h
index e01ef9c9..d6ad7d9 100644
--- a/components/sync/test/fake_sync_manager.h
+++ b/components/sync/test/fake_sync_manager.h
@@ -25,6 +25,7 @@
 namespace syncer {
 
 class FakeSyncEncryptionHandler;
+class SyncCycleSnapshot;
 
 class FakeSyncManager : public SyncManager {
  public:
@@ -71,6 +72,9 @@
   // Notifies all observers about the changed |status|.
   void NotifySyncStatusChanged(const SyncStatus& status);
 
+  // Notifies |observers_| about sync cycle completion.
+  void NotifySyncCycleCompleted(const SyncCycleSnapshot& snapshot);
+
   // SyncManager implementation.
   // Note: we treat whatever message loop this is called from as the sync
   // loop for purposes of callbacks.
@@ -109,6 +113,7 @@
 
  private:
   void DoNotifySyncStatusChanged(const SyncStatus& status);
+  void DoNotifySyncCycleCompleted(const SyncCycleSnapshot& snapshot);
 
   scoped_refptr<base::SequencedTaskRunner> sync_task_runner_;
 
diff --git a/components/sync/test/mock_sync_engine.h b/components/sync/test/mock_sync_engine.h
index 10df7b56..9b22b67 100644
--- a/components/sync/test/mock_sync_engine.h
+++ b/components/sync/test/mock_sync_engine.h
@@ -77,6 +77,7 @@
   MOCK_METHOD(void, DisableProtocolEventForwarding, (), (override));
   MOCK_METHOD(void, OnCookieJarChanged, (bool, base::OnceClosure), (override));
   MOCK_METHOD(void, SetInvalidationsForSessionsEnabled, (bool), (override));
+  MOCK_METHOD(bool, IsNextPollTimeInThePast, (), (const override));
   MOCK_METHOD(void, GetNigoriNodeForDebugging, (AllNodesCallback), (override));
 };
 
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index 0159f7b..b414828 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -418,14 +418,13 @@
   // Accepted user types: USER_TYPE_REGULAR, USER_TYPE_GUEST, USER_TYPE_CHILD.
   virtual bool IsUserAllowed(const User& user) const = 0;
 
-  // Returns false if `account_id` is a device owner.
+  // Explicitly non-ephemeral accounts are Owner account (on consumer-owned
+  // devices) and Stub accounts (used in tests).
   //
-  // Returns true if trusted device policies have successfully been retrieved
-  // and `account_id` is ephemeral by policies.
+  // Explicitly ephemeral accounts are Guest and Managed Guest sessions.
   //
-  // NOTE: this function does explicitly-ephemeral accounts like MGS separately.
-  // This function gives an answer whether `account_id` is ephemeral by policies
-  // except when `account_id` is a device owner.
+  // In all other cases the ephemeral status of account depends on set of
+  // policies.
   virtual bool IsEphemeralAccountId(const AccountId& account_id) const = 0;
 
   // Returns "Local State" PrefService instance.
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index 7713fd6..57c3b29 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -779,6 +779,16 @@
     return false;
   }
 
+  // Even though kiosk accounts might be ephemeral, non-cryptohome data
+  // of kiosk accounts should be non-ephemeral.
+  if (const User* user = FindUser(account_id);
+      user && (user->GetType() == USER_TYPE_PUBLIC_ACCOUNT ||
+               user->GetType() == USER_TYPE_KIOSK_APP ||
+               user->GetType() == USER_TYPE_ARC_KIOSK_APP ||
+               user->GetType() == USER_TYPE_WEB_KIOSK_APP)) {
+    return false;
+  }
+
   // Data belonging to the currently logged-in user is ephemeral when:
   // a) The user logged into a regular gaia account while the ephemeral users
   //    policy was enabled.
diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 916ff3c..3324177f 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -18,7 +18,7 @@
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gl/trace_util.h"
 
diff --git a/components/viz/service/display_embedder/image_context_impl.cc b/components/viz/service/display_embedder/image_context_impl.cc
index 809e2816..31ddc81 100644
--- a/components/viz/service/display_embedder/image_context_impl.cc
+++ b/components/viz/service/display_embedder/image_context_impl.cc
@@ -13,7 +13,7 @@
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkCanvas.h"
diff --git a/components/viz/service/display_embedder/skia_output_device_dawn.cc b/components/viz/service/display_embedder/skia_output_device_dawn.cc
index 4f866b3b..16f43c63 100644
--- a/components/viz/service/display_embedder/skia_output_device_dawn.cc
+++ b/components/viz/service/display_embedder/skia_output_device_dawn.cc
@@ -11,7 +11,7 @@
 #include "base/time/time.h"
 #include "components/viz/common/gpu/dawn_context_provider.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "third_party/skia/include/gpu/graphite/BackendTexture.h"
 #include "third_party/skia/include/gpu/graphite/Surface.h"
 #include "ui/gfx/presentation_feedback.h"
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 4fdcbd6..4bbe1dd 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -38,7 +38,7 @@
 #include "gpu/command_buffer/common/sync_token.h"
 #include "gpu/command_buffer/service/scheduler.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/single_task_sequence.h"
 #include "gpu/command_buffer/service/skia_utils.h"
diff --git a/components/viz/test/DEPS b/components/viz/test/DEPS
index f02429a..5d0b5b4a53 100644
--- a/components/viz/test/DEPS
+++ b/components/viz/test/DEPS
@@ -12,7 +12,7 @@
   "+gpu/command_buffer/client/shared_image_interface.h",
   "+gpu/command_buffer/client/shared_memory_limits.h",
   "+gpu/command_buffer/common",
-  "+gpu/command_buffer/service/shared_image/shared_image_format_utils.h",
+  "+gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h",
   "+gpu/config/gpu_feature_info.h",
   "+gpu/config/skia_limits.h",
   "+gpu/GLES2",
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index cb35d23..fac4dc44 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -22,7 +22,7 @@
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/common/swap_buffers_complete_params.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "third_party/khronos/GLES2/gl2ext.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPixelRef.h"
diff --git a/components/web_app_resources/web_app_error_page.html b/components/web_app_resources/web_app_error_page.html
index 4d213063..070d8c7 100644
--- a/components/web_app_resources/web_app_error_page.html
+++ b/components/web_app_resources/web_app_error_page.html
@@ -15,7 +15,7 @@
       </div>
     </div>
     <div id="errorContainer">
-      <svg id="offlineIcon" xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M818 1000 703 885H248q-88 0-148-59T40 679q0-80 50.5-134T217 479q2-14 6.5-31.5T236 416L70 250l42-42 749 749-43 43ZM248 825h397L285 465q-11 15-14.5 34t-3.5 37h-19q-62 0-105 39.5t-43 101q0 61.5 43 105T248 825Zm216-181Zm390 210-47-47q25-17 39-38t14-50q0-43-31-73.5T755 615h-67v-81q0-88-61-147.5T478.473 327Q450 327 417.5 336T358 365l-42-42q36-29 77.5-42.5T478 267q111 0 190.5 79T748 536v21q72-1 122 45t50 117q0 35-16.5 73.5T854 854ZM583 586Z">
+      <svg id="offlineIcon" xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M824.218-40.173 707.641-156.87H248q-93.067 0-157.034-62.967T27-375.87q0-81.695 51.63-139.652 51.631-57.957 129.327-70.522.87-11.174 4.239-28.108 3.37-16.935 10.804-28.674L57.565-808.261l47.087-45.957L871.74-87.13l-47.522 46.957ZM248.246-236.087h382.058L291.218-575.173q-10.435 12.739-13.55 29.733-3.114 16.995-3.114 32.788H248q-59.053 0-100.417 37.89-41.365 37.89-41.365 96.741t41.365 100.392q41.364 41.542 100.663 41.542Zm211.232-170.826ZM863.044-191.26l-60.566-61.131q24.435-17 37.87-36.438 13.434-19.437 13.434-47.273 0-39.76-29.394-68.655-29.395-28.895-69.388-28.895h-73.218v-87.218q0-85.046-59.117-142.914-59.116-57.868-143.972-57.868-28.748 0-59.525 9.283-30.777 9.282-56.081 27.587l-56.131-56.131q36-30.131 81.174-45.044 45.174-14.913 90.051-14.913 112.806 0 195.269 79.565 82.463 79.565 86.985 192.261v21q73.131 4.087 122.848 52.368 49.718 48.281 49.718 120.999 0 37.764-18.196 79.373t-51.761 64.044ZM583.565-468.87Z">
       <span id="default-web-app-msg" class="message">$i18n{web_app_error_page_message}</span>
     </div>
     <script src="web_app_error_page.js"></script>
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java
index bb3ce8e7..03c8d5f 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorFactory.java
@@ -32,13 +32,6 @@
         }
 
         WebAuthenticationDelegate delegate = new WebAuthenticationDelegate();
-        @WebAuthenticationDelegate.Support
-        int supportLevel = delegate.getSupportLevel(webContents);
-        if (supportLevel == WebAuthenticationDelegate.Support.NONE) {
-            return null;
-        }
-
-        return new AuthenticatorImpl(
-                delegate.getIntentSender(webContents), mRenderFrameHost, supportLevel);
+        return new AuthenticatorImpl(delegate.getIntentSender(webContents), mRenderFrameHost);
     }
 }
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
index c12f56d..a01ca0b 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/AuthenticatorImpl.java
@@ -39,7 +39,6 @@
     public static final int GMSCORE_MIN_VERSION_GET_MATCHING_CRED_IDS = 223300000;
     private final WebAuthenticationDelegate.IntentSender mIntentSender;
     private final RenderFrameHost mRenderFrameHost;
-    private final @WebAuthenticationDelegate.Support int mSupportLevel;
 
     /** Ensures only one request is processed at a time. */
     private boolean mIsOperationPending;
@@ -74,13 +73,10 @@
      * @param renderFrameHost The host of the frame that has invoked the API.
      * @param intentSender If present then an interface that will be used to start {@link Intent}s
      *         from Play Services.
-     * @param supportLevel Whether this code should use the privileged or non-privileged Play
-     *         Services API. (Note that a value of `NONE` is not allowed.)
      */
-    public AuthenticatorImpl(WebAuthenticationDelegate.IntentSender intentSender,
-            RenderFrameHost renderFrameHost, @WebAuthenticationDelegate.Support int supportLevel) {
+    public AuthenticatorImpl(
+            WebAuthenticationDelegate.IntentSender intentSender, RenderFrameHost renderFrameHost) {
         assert renderFrameHost != null;
-        assert supportLevel != WebAuthenticationDelegate.Support.NONE;
 
         if (intentSender != null) {
             mIntentSender = intentSender;
@@ -89,7 +85,6 @@
         }
 
         mRenderFrameHost = renderFrameHost;
-        mSupportLevel = supportLevel;
         mOrigin = mRenderFrameHost.getLastCommittedOrigin();
 
         mGmsCorePackageVersion = PackageUtils.getPackageVersion(GMSCORE_PACKAGE_NAME);
@@ -104,7 +99,7 @@
             return sFido2CredentialRequestOverrideForTesting;
         }
 
-        return new Fido2CredentialRequest(mIntentSender, mSupportLevel);
+        return new Fido2CredentialRequest(mIntentSender);
     }
 
     /**
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java
index 66d44e1..29bd0f4 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2Api.java
@@ -166,7 +166,7 @@
      * @throws NoSuchAlgorithmException when options requests an impossible-to-satisfy public-key
      *         algorithm.
      */
-    public static void appendMakeCredentialOptionsToParcel(
+    private static void appendMakeCredentialOptionsToParcel(
             PublicKeyCredentialCreationOptions options, Parcel parcel)
             throws NoSuchAlgorithmException {
         final int a = writeHeader(OBJECT_MAGIC, parcel);
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCall.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCall.java
index 79ea02a0..1ba5b474 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCall.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCall.java
@@ -27,8 +27,6 @@
 import com.google.android.gms.tasks.Task;
 import com.google.android.gms.tasks.TaskCompletionSource;
 
-import org.chromium.content_public.browser.WebAuthenticationDelegate;
-
 import java.util.List;
 
 /**
@@ -57,7 +55,7 @@
  * <p>
  * Here's an example:
  * <pre>
- * Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext(), false);
+ * Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext());
  * Parcel args = call.start();
  * Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult(call);
  * args.writeStrongBinder(result);
@@ -84,18 +82,6 @@
 
     private static final String TAG = "Fido2ApiCall";
 
-    private static final String APP_DESCRIPTOR =
-            "com.google.android.gms.fido.fido2.internal.regular.IFido2AppService";
-    private static final String APP_CALLBACK_DESCRIPTOR =
-            "com.google.android.gms.fido.fido2.internal.regular.IFido2AppCallbacks";
-    private static final String APP_START_SERVICE_ACTION =
-            "com.google.android.gms.fido.fido2.regular.START";
-    private static final int APP_API_ID = 148;
-    private static final Api.ClientKey<FidoClient> APP_CLIENT_KEY = new Api.ClientKey<>();
-    private static final Api<ApiOptions.NoOptions> APP_API = new Api<>("Fido.FIDO2_API",
-            new FidoClient.Builder(APP_DESCRIPTOR, APP_START_SERVICE_ACTION, APP_API_ID),
-            APP_CLIENT_KEY);
-
     private static final String BROWSER_DESCRIPTOR =
             "com.google.android.gms.fido.fido2.internal.privileged.IFido2PrivilegedService";
     private static final String BROWSER_CALLBACK_DESCRIPTOR =
@@ -110,27 +96,18 @@
                             BROWSER_DESCRIPTOR, BROWSER_START_SERVICE_ACTION, BROWSER_API_ID),
                     BROWSER_CLIENT_KEY);
 
-    final boolean mAppMode;
-
     /**
      * Construct an instance.
      *
      * @param context the Android {@link Context} for the current process.
-     * @param supportLevel Whether this code should use the privileged or non-privileged Play
-     *         Services API. (Note that a value of `NONE` is not allowed.)
      */
-    public Fido2ApiCall(Context context, @WebAuthenticationDelegate.Support int supportLevel) {
-        super(context,
-                supportLevel == WebAuthenticationDelegate.Support.APP ? APP_API : BROWSER_API,
-                ApiOptions.NO_OPTIONS, new ApiExceptionMapper());
-
-        assert supportLevel != WebAuthenticationDelegate.Support.NONE;
-        mAppMode = supportLevel == WebAuthenticationDelegate.Support.APP;
+    public Fido2ApiCall(Context context) {
+        super(context, BROWSER_API, ApiOptions.NO_OPTIONS, new ApiExceptionMapper());
     }
 
     public Parcel start() {
         Parcel p = Parcel.obtain();
-        p.writeInterfaceToken(mAppMode ? APP_DESCRIPTOR : BROWSER_DESCRIPTOR);
+        p.writeInterfaceToken(BROWSER_DESCRIPTOR);
         return p;
     }
 
@@ -239,13 +216,8 @@
 
     public static final class PendingIntentResult
             extends Binder implements Callback<PendingIntent> {
-        private final boolean mAppMode;
         private TaskCompletionSource<PendingIntent> mCompletionSource;
 
-        public PendingIntentResult(Fido2ApiCall call) {
-            mAppMode = call.mAppMode;
-        }
-
         @Override
         public void setCompletionSource(TaskCompletionSource<PendingIntent> cs) {
             mCompletionSource = cs;
@@ -255,8 +227,7 @@
         public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
             switch (code) {
                 case IBinder.FIRST_CALL_TRANSACTION + 0:
-                    data.enforceInterface(
-                            mAppMode ? APP_CALLBACK_DESCRIPTOR : BROWSER_CALLBACK_DESCRIPTOR);
+                    data.enforceInterface(BROWSER_CALLBACK_DESCRIPTOR);
 
                     Status status = null;
                     if (data.readInt() != 0) {
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCallHelper.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCallHelper.java
index fe20d30a..ef72b29 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCallHelper.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2ApiCallHelper.java
@@ -38,10 +38,10 @@
         return sInstance;
     }
 
-    public void invokeFido2GetCredentials(String relyingPartyId, int supportLevel,
+    public void invokeFido2GetCredentials(String relyingPartyId,
             OnSuccessListener<List<WebAuthnCredentialDetails>> successCallback,
             OnFailureListener failureCallback) {
-        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext(), supportLevel);
+        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext());
         Parcel args = call.start();
         Fido2ApiCall.WebAuthnCredentialDetailsListResult result =
                 new Fido2ApiCall.WebAuthnCredentialDetailsListResult();
@@ -54,4 +54,4 @@
         task.addOnSuccessListener(successCallback);
         task.addOnFailureListener(failureCallback);
     }
-}
\ No newline at end of file
+}
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
index 76151c8b..b4362d93 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
@@ -85,7 +85,6 @@
     private static Boolean sIsCredManEnabled;
 
     private final WebAuthenticationDelegate.IntentSender mIntentSender;
-    private final @WebAuthenticationDelegate.Support int mSupportLevel;
     private GetAssertionResponseCallback mGetAssertionCallback;
     private MakeCredentialResponseCallback mMakeCredentialCallback;
     private FidoErrorResponseCallback mErrorCallback;
@@ -124,12 +123,8 @@
      * @param supportLevel Whether this code should use the privileged or non-privileged Play
      *         Services API. (Note that a value of `NONE` is not allowed.)
      */
-    public Fido2CredentialRequest(WebAuthenticationDelegate.IntentSender intentSender,
-            @WebAuthenticationDelegate.Support int supportLevel) {
-        assert supportLevel != WebAuthenticationDelegate.Support.NONE;
-
+    public Fido2CredentialRequest(WebAuthenticationDelegate.IntentSender intentSender) {
         mIntentSender = intentSender;
-        mSupportLevel = supportLevel;
     }
 
     private void returnErrorAndResetCallback(int error) {
@@ -190,19 +185,15 @@
             return;
         }
 
-        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext(), mSupportLevel);
+        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext());
         Parcel args = call.start();
-        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult(call);
+        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult();
         args.writeStrongBinder(result);
         args.writeInt(1); // This indicates that the following options are present.
 
         try {
-            if (mSupportLevel == WebAuthenticationDelegate.Support.BROWSER) {
-                Fido2Api.appendBrowserMakeCredentialOptionsToParcel(options,
-                        Uri.parse(convertOriginToString(origin)), /* clientDataHash= */ null, args);
-            } else {
-                Fido2Api.appendMakeCredentialOptionsToParcel(options, args);
-            }
+            Fido2Api.appendBrowserMakeCredentialOptionsToParcel(options,
+                    Uri.parse(convertOriginToString(origin)), /* clientDataHash= */ null, args);
         } catch (NoSuchAlgorithmException e) {
             returnErrorAndResetCallback(AuthenticatorStatus.ALGORITHM_UNSUPPORTED);
             return;
@@ -293,7 +284,6 @@
             final byte[] finalClientDataHash = clientDataHash;
             mConditionalUiState = ConditionalUiState.WAITING_FOR_CREDENTIAL_LIST;
             Fido2ApiCallHelper.getInstance().invokeFido2GetCredentials(options.relyingPartyId,
-                    mSupportLevel,
                     (credentials)
                             -> onWebAuthnCredentialDetailsListReceived(
                                     options, callerOriginString, finalClientDataHash, credentials),
@@ -345,7 +335,7 @@
             return;
         }
 
-        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext(), mSupportLevel);
+        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext());
         Fido2ApiCall.BooleanResult result = new Fido2ApiCall.BooleanResult();
         Parcel args = call.start();
         args.writeStrongBinder(result);
@@ -377,7 +367,7 @@
             return;
         }
 
-        Fido2ApiCallHelper.getInstance().invokeFido2GetCredentials(relyingPartyId, mSupportLevel,
+        Fido2ApiCallHelper.getInstance().invokeFido2GetCredentials(relyingPartyId,
                 (credentials)
                         -> onGetMatchingCredentialIdsListReceived(credentials, allowCredentialIds,
                                 requireThirdPartyPayment, callback),
@@ -523,19 +513,14 @@
             mConditionalUiState = ConditionalUiState.REQUEST_SENT_TO_PLATFORM;
         }
 
-        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext(), mSupportLevel);
+        Fido2ApiCall call = new Fido2ApiCall(ContextUtils.getApplicationContext());
         Parcel args = call.start();
-        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult(call);
+        Fido2ApiCall.PendingIntentResult result = new Fido2ApiCall.PendingIntentResult();
         args.writeStrongBinder(result);
         args.writeInt(1); // This indicates that the following options are present.
 
-        if (mSupportLevel == WebAuthenticationDelegate.Support.BROWSER) {
-            Fido2Api.appendBrowserGetAssertionOptionsToParcel(options,
-                    Uri.parse(callerOriginString), clientDataHash, /*tunnelId=*/null, args);
-        } else {
-            Fido2Api.appendGetAssertionOptionsToParcel(options, /*tunnelId=*/null, args);
-        }
-
+        Fido2Api.appendBrowserGetAssertionOptionsToParcel(
+                options, Uri.parse(callerOriginString), clientDataHash, /*tunnelId=*/null, args);
         Task<PendingIntent> task = call.run(
                 Fido2ApiCall.METHOD_BROWSER_SIGN, Fido2ApiCall.TRANSACTION_SIGN, args, result);
         task.addOnSuccessListener(this::onGotPendingIntent);
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/InternalAuthenticator.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/InternalAuthenticator.java
index 43498771..e655543 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/InternalAuthenticator.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/InternalAuthenticator.java
@@ -32,8 +32,7 @@
     private InternalAuthenticator(long nativeInternalAuthenticatorAndroid,
             WebAuthenticationDelegate.IntentSender intentSender, RenderFrameHost renderFrameHost) {
         mNativeInternalAuthenticatorAndroid = nativeInternalAuthenticatorAndroid;
-        mAuthenticator = new AuthenticatorImpl(
-                intentSender, renderFrameHost, WebAuthenticationDelegate.Support.BROWSER);
+        mAuthenticator = new AuthenticatorImpl(intentSender, renderFrameHost);
     }
 
     @VisibleForTesting
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/MockFido2CredentialRequest.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/MockFido2CredentialRequest.java
index 16dfb66..fb51edfc 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/MockFido2CredentialRequest.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/MockFido2CredentialRequest.java
@@ -9,13 +9,12 @@
 import org.chromium.blink.mojom.PublicKeyCredentialCreationOptions;
 import org.chromium.blink.mojom.PublicKeyCredentialRequestOptions;
 import org.chromium.content_public.browser.RenderFrameHost;
-import org.chromium.content_public.browser.WebAuthenticationDelegate;
 import org.chromium.url.Origin;
 
 /** A mock Fido2CredentialRequest that returns NOT_IMPLEMENTED for all calls. */
 public class MockFido2CredentialRequest extends Fido2CredentialRequest {
     public MockFido2CredentialRequest() {
-        super(null, WebAuthenticationDelegate.Support.BROWSER);
+        super(null);
     }
 
     @Override
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index d6dd4947..9f56131c 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -3436,6 +3436,12 @@
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        NullObjectOnHypertextOffsetComputation) {
+  if (!base::FeatureList::IsEnabled(blink::features::kMutationEvents)) {
+    // TODO(crbug.com/1446498) Remove this test (and the .html file) when
+    // MutationEvents are disabled for good. This is just a crash test related
+    // to `DOMNodeInserted`.
+    return;
+  }
   RunRegressionTest(
       FILE_PATH_LITERAL("null-object-on-hypertext-offset-computation.html"));
 }
diff --git a/content/browser/attribution_reporting/attribution_debug_report.cc b/content/browser/attribution_reporting/attribution_debug_report.cc
index 7efdc48..00befdb1 100644
--- a/content/browser/attribution_reporting/attribution_debug_report.cc
+++ b/content/browser/attribution_reporting/attribution_debug_report.cc
@@ -41,6 +41,9 @@
   kSourceStorageLimit,
   kSourceSuccess,
   kSourceUnknownError,
+  // TODO(tquintanilla): Add interop test for `kSourceDestinationRateLimit`
+  // case.
+  kSourceDestinationRateLimit,
   kTriggerNoMatchingSource,
   kTriggerAttributionsPerSourceDestinationLimit,
   kTriggerNoMatchingFilterData,
@@ -72,15 +75,21 @@
     case StorableSource::Result::kProhibitedByBrowserPolicy:
       return absl::nullopt;
     case StorableSource::Result::kSuccess:
-    // `kSourceSuccess` is sent for unattributed reporting origin limit to
-    // mitigate the security concerns on reporting this error. Because
-    // `kExcessiveReportingOrigins` is thrown based on information across
-    // reporting origins, reporting on it would violate the same-origin policy.
+    // `kSourceSuccess` is sent for unattributed reporting origin limit and max
+    // unique destinations per source site to mitigate the security concerns on
+    // reporting these errors. Because `kDestinationGlobalLimitReached` and
+    // `kExcessiveReportingOrigins` are thrown based on information across
+    // reporting origins, reporting on them would violate the same-origin
+    // policy.
     case StorableSource::Result::kExcessiveReportingOrigins:
+    case StorableSource::Result::kDestinationGlobalLimitReached:
       return DataTypeIfCookieSet(DebugDataType::kSourceSuccess,
                                  is_debug_cookie_set);
     case StorableSource::Result::kInsufficientUniqueDestinationCapacity:
       return DebugDataType::kSourceDestinationLimit;
+    case StorableSource::Result::kDestinationReportingLimitReached:
+    case StorableSource::Result::kDestinationBothLimitsReached:
+      return DebugDataType::kSourceDestinationRateLimit;
     case StorableSource::Result::kSuccessNoised:
       return DataTypeIfCookieSet(DebugDataType::kSourceNoised,
                                  is_debug_cookie_set);
@@ -200,6 +209,8 @@
       return "source-storage-limit";
     case DebugDataType::kSourceSuccess:
       return "source-success";
+    case DebugDataType::kSourceDestinationRateLimit:
+      return "source-destination-rate-limit";
     case DebugDataType::kSourceUnknownError:
       return "source-unknown-error";
     case DebugDataType::kTriggerNoMatchingSource:
@@ -279,6 +290,10 @@
     case DebugDataType::kSourceStorageLimit:
       SetLimit(data_body, result.max_sources_per_origin);
       break;
+    case DebugDataType::kSourceDestinationRateLimit:
+      SetLimit(data_body,
+               result.max_destinations_per_rate_limit_window_reporting_origin);
+      break;
     case DebugDataType::kSourceNoised:
     case DebugDataType::kSourceSuccess:
     case DebugDataType::kSourceUnknownError:
@@ -373,6 +388,7 @@
     case DebugDataType::kSourceStorageLimit:
     case DebugDataType::kSourceSuccess:
     case DebugDataType::kSourceUnknownError:
+    case DebugDataType::kSourceDestinationRateLimit:
       NOTREACHED();
       return base::Value::Dict();
   }
diff --git a/content/browser/attribution_reporting/attribution_debug_report_unittest.cc b/content/browser/attribution_reporting/attribution_debug_report_unittest.cc
index 7248e6f1..4e76a32 100644
--- a/content/browser/attribution_reporting/attribution_debug_report_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_debug_report_unittest.cc
@@ -115,6 +115,7 @@
     StorableSource::Result result;
     absl::optional<int> max_destinations_per_source_site_reporting_site;
     absl::optional<int> max_sources_per_origin;
+    absl::optional<int> max_destinations_per_rate_limit_window_reporting_origin;
     absl::optional<uint64_t> debug_key;
     const char* expected_report_body_without_cookie;
     const char* expected_report_body_with_cookie;
@@ -122,6 +123,7 @@
       {StorableSource::Result::kSuccess,
        /*max_destinations_per_source_site_reporting_site=*/absl::nullopt,
        /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/absl::nullopt,
        /*expected_report_body_without_cookie=*/nullptr,
        R"json([{
@@ -135,6 +137,7 @@
       {StorableSource::Result::kInternalError,
        /*max_destinations_per_source_site_reporting_site=*/absl::nullopt,
        /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/456,
        /*expected_report_body_without_cookie=*/nullptr,
        /*expected_report_body_with_cookie=*/
@@ -150,6 +153,7 @@
       {StorableSource::Result::kInsufficientSourceCapacity,
        /*max_destinations_per_source_site_reporting_site=*/absl::nullopt,
        /*max_sources_per_origin=*/10,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/absl::nullopt,
        /*expected_report_body_without_cookie=*/nullptr,
        /*expected_report_body_with_cookie=*/
@@ -165,12 +169,14 @@
       {StorableSource::Result::kProhibitedByBrowserPolicy,
        /*max_destinations_per_source_site_reporting_site=*/absl::nullopt,
        /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/absl::nullopt,
        /*expected_report_body_without_cookie=*/nullptr,
        /*expected_report_body_with_cookie=*/nullptr},
       {StorableSource::Result::kInsufficientUniqueDestinationCapacity,
        /*max_destinations_per_source_site_reporting_site=*/3,
        /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/absl::nullopt,
        /*expected_report_body_without_cookie=*/
        R"json([{
@@ -195,6 +201,7 @@
       {StorableSource::Result::kSuccessNoised,
        /*max_destinations_per_source_site_reporting_site=*/absl::nullopt,
        /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/absl::nullopt,
        /*expected_report_body_without_cookie=*/nullptr,
        /*expected_report_body_with_cookie=*/
@@ -209,6 +216,7 @@
       {StorableSource::Result::kExcessiveReportingOrigins,
        /*max_destinations_per_source_site_reporting_site=*/absl::nullopt,
        /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
        /*debug_key=*/789,
        /*expected_report_body_without_cookie=*/nullptr,
        /*expected_report_body_with_cookie=*/
@@ -221,6 +229,72 @@
          },
          "type": "source-success"
        }])json"},
+      {StorableSource::Result::kDestinationGlobalLimitReached,
+       /*max_destinations_per_source_site_reporting_origin=*/absl::nullopt,
+       /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/absl::nullopt,
+       /*debug_key=*/789,
+       /*expected_report_body_without_cookie=*/nullptr,
+       /*expected_report_body_with_cookie=*/
+       R"json([{
+         "body": {
+           "attribution_destination": "https://conversion.test",
+           "source_debug_key": "789",
+           "source_event_id": "123",
+           "source_site": "https://impression.test"
+         },
+         "type": "source-success"
+       }])json"},
+      {StorableSource::Result::kDestinationReportingLimitReached,
+       /*max_destinations_per_source_site_reporting_origin=*/absl::nullopt,
+       /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/50,
+       /*debug_key=*/absl::nullopt,
+       /*expected_report_body_without_cookie=*/
+       R"json([{
+         "body": {
+           "attribution_destination": "https://conversion.test",
+           "limit": "50",
+           "source_event_id": "123",
+           "source_site": "https://impression.test"
+         },
+         "type": "source-destination-rate-limit"
+       }])json",
+       /*expected_report_body_with_cookie=*/
+       R"json([{
+         "body": {
+           "attribution_destination": "https://conversion.test",
+           "limit": "50",
+           "source_event_id": "123",
+           "source_site": "https://impression.test"
+         },
+         "type": "source-destination-rate-limit"
+       }])json"},
+      {StorableSource::Result::kDestinationBothLimitsReached,
+       /*max_destinations_per_source_site_reporting_origin=*/absl::nullopt,
+       /*max_sources_per_origin=*/absl::nullopt,
+       /*max_sources_per_reporting_origin=*/50,
+       /*debug_key=*/absl::nullopt,
+       /*expected_report_body_without_cookie=*/
+       R"json([{
+         "body": {
+           "attribution_destination": "https://conversion.test",
+           "limit": "50",
+           "source_event_id": "123",
+           "source_site": "https://impression.test"
+         },
+         "type": "source-destination-rate-limit"
+       }])json",
+       /*expected_report_body_with_cookie=*/
+       R"json([{
+         "body": {
+           "attribution_destination": "https://conversion.test",
+           "limit": "50",
+           "source_event_id": "123",
+           "source_site": "https://impression.test"
+         },
+         "type": "source-destination-rate-limit"
+       }])json"},
   };
 
   for (bool is_debug_cookie_set : {false, true}) {
@@ -236,7 +310,9 @@
                   test_case.result,
                   /*min_fake_report_time=*/absl::nullopt,
                   test_case.max_destinations_per_source_site_reporting_site,
-                  test_case.max_sources_per_origin));
+                  test_case.max_sources_per_origin,
+                  test_case
+                      .max_destinations_per_rate_limit_window_reporting_origin));
       const char* expected_report_body =
           is_debug_cookie_set ? test_case.expected_report_body_with_cookie
                               : test_case.expected_report_body_without_cookie;
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc
index ac1cd23..20c9c0f 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -58,6 +58,7 @@
 #include "content/browser/attribution_reporting/attribution_storage_sql.h"
 #include "content/browser/attribution_reporting/attribution_trigger.h"
 #include "content/browser/attribution_reporting/create_report_result.h"
+#include "content/browser/attribution_reporting/destination_throttler.h"
 #include "content/browser/attribution_reporting/os_registration.h"
 #include "content/browser/attribution_reporting/send_result.h"
 #include "content/browser/attribution_reporting/storable_source.h"
@@ -184,9 +185,9 @@
 
 void RecordStoreSourceStatus(StoreSourceResult result) {
   static_assert(StorableSource::Result::kMaxValue ==
-                    StorableSource::Result::kSuccessNoised,
-                "Bump version of Conversions.SourceStoredStatus2 histogram.");
-  base::UmaHistogramEnumeration("Conversions.SourceStoredStatus2",
+                    StorableSource::Result::kDestinationBothLimitsReached,
+                "Bump version of Conversions.SourceStoredStatus3 histogram.");
+  base::UmaHistogramEnumeration("Conversions.SourceStoredStatus3",
                                 result.status);
 }
 
@@ -362,6 +363,20 @@
       AttributionNoiseMode::kDefault, AttributionDelayMode::kDefault);
 }
 
+StorableSource::Result ThrottleResultToStorableSourceResult(
+    DestinationThrottler::Result r) {
+  switch (r) {
+    case DestinationThrottler::Result::kAllowed:
+      return StorableSource::Result::kSuccess;
+    case DestinationThrottler::Result::kHitGlobalLimit:
+      return StorableSource::Result::kDestinationGlobalLimitReached;
+    case DestinationThrottler::Result::kHitReportingLimit:
+      return StorableSource::Result::kDestinationReportingLimitReached;
+    case DestinationThrottler::Result::kHitBothLimits:
+      return StorableSource::Result::kDestinationBothLimitsReached;
+  }
+}
+
 bool IsOperationAllowed(
     StoragePartitionImpl* storage_partition,
     ContentBrowserClient::AttributionReportingOperation operation,
@@ -576,32 +591,9 @@
     return;
   }
 
-  // TODO(csharrison): Consider enforcing this limit after checking metrics.
-  DestinationThrottler::Result throttle_result = throttler_.UpdateAndGetResult(
-      source.registration().destination_set,
-      net::SchemefulSite(source.common_info().source_origin()),
-      net::SchemefulSite(source.common_info().reporting_origin()));
-  base::UmaHistogramEnumeration("Conversions.DestinationThrottlerResult",
-                                throttle_result);
-
   MaybeEnqueueEvent(std::move(source));
 }
 
-void AttributionManagerImpl::StoreSource(StorableSource source,
-                                         bool is_debug_cookie_set) {
-  absl::optional<uint64_t> cleared_debug_key;
-  if (!is_debug_cookie_set) {
-    cleared_debug_key =
-        std::exchange(source.registration().debug_key, absl::nullopt);
-  }
-
-  attribution_storage_.AsyncCall(&AttributionStorage::StoreSource)
-      .WithArgs(source)
-      .Then(base::BindOnce(&AttributionManagerImpl::OnSourceStored,
-                           weak_factory_.GetWeakPtr(), std::move(source),
-                           cleared_debug_key, is_debug_cookie_set));
-}
-
 void AttributionManagerImpl::RecordPendingAggregatableReportsTimings() {
   const base::Time now = base::Time::Now();
 
@@ -742,7 +734,8 @@
 
   absl::visit(base::Overloaded{
                   [&](StorableSource& source) {
-                    StoreSource(std::move(source), is_debug_cookie_set);
+                    CheckDestinationThrottlerAndStoreSource(
+                        std::move(source), is_debug_cookie_set);
                   },
                   [&](AttributionTrigger& trigger) {
                     StoreTrigger(std::move(trigger), is_debug_cookie_set);
@@ -753,6 +746,39 @@
   pending_events_.pop_front();
 }
 
+void AttributionManagerImpl::CheckDestinationThrottlerAndStoreSource(
+    StorableSource source,
+    bool is_debug_cookie_set) {
+  DestinationThrottler::Result throttle_result = throttler_.UpdateAndGetResult(
+      source.registration().destination_set,
+      net::SchemefulSite(source.common_info().source_origin()),
+      net::SchemefulSite(source.common_info().reporting_origin()));
+  base::UmaHistogramEnumeration("Conversions.DestinationThrottlerResult",
+                                throttle_result);
+
+  absl::optional<uint64_t> cleared_debug_key;
+  if (!is_debug_cookie_set) {
+    cleared_debug_key =
+        std::exchange(source.registration().debug_key, absl::nullopt);
+  }
+
+  if (StorableSource::Result result =
+          ThrottleResultToStorableSourceResult(throttle_result);
+      result == StorableSource::Result::kSuccess) {
+    attribution_storage_.AsyncCall(&AttributionStorage::StoreSource)
+        .WithArgs(source)
+        .Then(base::BindOnce(&AttributionManagerImpl::OnSourceStored,
+                             weak_factory_.GetWeakPtr(), std::move(source),
+                             cleared_debug_key, is_debug_cookie_set));
+  } else {
+    StoreSourceResult store_result(result);
+    store_result.max_destinations_per_rate_limit_window_reporting_origin =
+        throttler_.GetMaxPerReportingSite();
+    OnSourceStored(source, cleared_debug_key, is_debug_cookie_set,
+                   StoreSourceResult(result));
+  }
+}
+
 void AttributionManagerImpl::AddPendingAggregatableReportTiming(
     const AttributionReport& report) {
   // The maximum number of pending reports that should be considered. Past this
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.h b/content/browser/attribution_reporting/attribution_manager_impl.h
index fe279a32..48ce2916 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.h
+++ b/content/browser/attribution_reporting/attribution_manager_impl.h
@@ -176,7 +176,6 @@
   void MaybeEnqueueEvent(SourceOrTrigger);
   void ProcessEvents();
   void ProcessNextEvent(bool is_debug_cookie_set);
-  void StoreSource(StorableSource source, bool is_debug_cookie_set);
   void StoreTrigger(AttributionTrigger trigger, bool is_debug_cookie_set);
 
   void GetReportsToSend();
@@ -242,6 +241,9 @@
                         bool is_debug_key_allowed,
                         bool success);
 
+  void CheckDestinationThrottlerAndStoreSource(StorableSource source,
+                                               bool is_debug_cookie_set);
+
   DestinationThrottler throttler_;
 
   // Never null.
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index f47714f7..e8eb487 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -1384,7 +1384,7 @@
   base::HistogramTester histograms;
   attribution_manager_->HandleSource(SourceBuilder().Build(), kFrameId);
   task_environment_.RunUntilIdle();
-  histograms.ExpectUniqueSample("Conversions.SourceStoredStatus2",
+  histograms.ExpectUniqueSample("Conversions.SourceStoredStatus3",
                                 StorableSource::Result::kSuccess, 1);
 }
 
@@ -1580,7 +1580,7 @@
   EXPECT_THAT(StoredSources(), IsEmpty());
 
   histograms.ExpectUniqueSample(
-      "Conversions.SourceStoredStatus2",
+      "Conversions.SourceStoredStatus3",
       StorableSource::Result::kProhibitedByBrowserPolicy, 1);
 }
 
@@ -2246,6 +2246,112 @@
 }
 
 TEST_F(AttributionManagerImplTest,
+       HandleSource_DestinationThrottleReportingLimitReached) {
+  base::HistogramTester histograms;
+
+  // Current reporting limit for Destination Throttle
+  int max_per_reporting_source_site = 50;
+
+  for (int i = 0; i < max_per_reporting_source_site; i++) {
+    attribution_manager_->HandleSource(
+        SourceBuilder()
+            .SetDestinationSites({net::SchemefulSite::Deserialize(
+                "https://d" + base::NumberToString(i) + ".test")})
+            .Build(),
+        kFrameId);
+  }
+  EXPECT_THAT(StoredSources(), SizeIs(max_per_reporting_source_site));
+
+  // Should fail due to limit
+  attribution_manager_->HandleSource(SourceBuilder().Build(), kFrameId);
+  EXPECT_THAT(StoredSources(), SizeIs(max_per_reporting_source_site));
+  histograms.ExpectBucketCount("Conversions.SourceStoredStatus3",
+                               StorableSource::Result::kSuccess, 50);
+  histograms.ExpectBucketCount(
+      "Conversions.SourceStoredStatus3",
+      StorableSource::Result::kDestinationReportingLimitReached, 1);
+}
+
+TEST_F(AttributionManagerImplTest,
+       HandleSource_DestinationThrottleGlobalLimitReached) {
+  base::HistogramTester histograms;
+
+  // Current global limit for Destination Throttle
+  int max_global_source_site = 200;
+  for (int i = 0; i < max_global_source_site; i++) {
+    attribution_manager_->HandleSource(
+        SourceBuilder()
+            .SetReportingOrigin(*SuitableOrigin::Deserialize(
+                "https://r" + base::NumberToString(i) + ".test"))
+            .SetDestinationSites({net::SchemefulSite::Deserialize(
+                "https://d" + base::NumberToString(i) + ".test")})
+            .Build(),
+        kFrameId);
+  }
+  EXPECT_THAT(StoredSources(), SizeIs(max_global_source_site));
+
+  // Should fail due to limit
+  attribution_manager_->HandleSource(SourceBuilder().Build(), kFrameId);
+  EXPECT_THAT(StoredSources(), SizeIs(max_global_source_site));
+  histograms.ExpectBucketCount("Conversions.SourceStoredStatus3",
+                               StorableSource::Result::kSuccess, 200);
+  histograms.ExpectBucketCount(
+      "Conversions.SourceStoredStatus3",
+      StorableSource::Result::kDestinationGlobalLimitReached, 1);
+}
+
+TEST_F(AttributionManagerImplTest,
+       HandleSource_DestinationThrottleBothLimitsReached) {
+  base::HistogramTester histograms;
+
+  int max_global_source_site = 200;
+  for (int i = 0; i < max_global_source_site; i += 4) {
+    attribution_manager_->HandleSource(
+        SourceBuilder()
+            .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r1.test"))
+            .SetDestinationSites({net::SchemefulSite::Deserialize(
+                "https://d" + base::NumberToString(i) + ".test")})
+            .Build(),
+        kFrameId);
+    attribution_manager_->HandleSource(
+        SourceBuilder()
+            .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r2.test"))
+            .SetDestinationSites({net::SchemefulSite::Deserialize(
+                "https://d" + base::NumberToString(i + 1) + ".test")})
+            .Build(),
+        kFrameId);
+    attribution_manager_->HandleSource(
+        SourceBuilder()
+            .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r3.test"))
+            .SetDestinationSites({net::SchemefulSite::Deserialize(
+                "https://d" + base::NumberToString(i + 2) + ".test")})
+            .Build(),
+        kFrameId);
+    attribution_manager_->HandleSource(
+        SourceBuilder()
+            .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r4.test"))
+            .SetDestinationSites({net::SchemefulSite::Deserialize(
+                "https://d" + base::NumberToString(i + 3) + ".test")})
+            .Build(),
+        kFrameId);
+  }
+  EXPECT_THAT(StoredSources(), SizeIs(max_global_source_site));
+
+  // Should fail due to limit
+  attribution_manager_->HandleSource(
+      SourceBuilder()
+          .SetReportingOrigin(*SuitableOrigin::Deserialize("https://r1.test"))
+          .Build(),
+      kFrameId);
+  EXPECT_THAT(StoredSources(), SizeIs(max_global_source_site));
+  histograms.ExpectBucketCount("Conversions.SourceStoredStatus3",
+                               StorableSource::Result::kSuccess, 200);
+  histograms.ExpectBucketCount(
+      "Conversions.SourceStoredStatus3",
+      StorableSource::Result::kDestinationBothLimitsReached, 1);
+}
+
+TEST_F(AttributionManagerImplTest,
        AggregateReportAssemblySucceeded_ReportSent) {
   base::HistogramTester histograms;
 
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender.cc b/content/browser/attribution_reporting/attribution_report_network_sender.cc
index 3aa5d47..7c9df968c 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender.cc
+++ b/content/browser/attribution_reporting/attribution_report_network_sender.cc
@@ -25,6 +25,7 @@
 #include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "url/gurl.h"
 
 namespace content {
@@ -61,7 +62,8 @@
   net::HttpRequestHeaders headers;
   report.PopulateAdditionalHeaders(headers);
 
-  SendReport(std::move(url), body, std::move(headers),
+  url::Origin origin(report.GetReportingOrigin());
+  SendReport(std::move(url), std::move(origin), body, std::move(headers),
              base::BindOnce(&AttributionReportNetworkSender::OnReportSent,
                             base::Unretained(this), std::move(report),
                             is_debug_report, std::move(sent_callback)));
@@ -71,15 +73,18 @@
     AttributionDebugReport report,
     DebugReportSentCallback callback) {
   GURL url = report.report_url();
+  // TODO(csharrison): Avoid reparsing the origin from the URL.
+  url::Origin origin = url::Origin::Create(url);
   std::string body = SerializeAttributionJson(report.ReportBody());
   SendReport(
-      std::move(url), body, net::HttpRequestHeaders(),
+      std::move(url), std::move(origin), body, net::HttpRequestHeaders(),
       base::BindOnce(&AttributionReportNetworkSender::OnVerboseDebugReportSent,
                      base::Unretained(this),
                      base::BindOnce(std::move(callback), std::move(report))));
 }
 
 void AttributionReportNetworkSender::SendReport(GURL url,
+                                                url::Origin origin,
                                                 const std::string& body,
                                                 net::HttpRequestHeaders headers,
                                                 UrlLoaderCallback callback) {
@@ -88,6 +93,8 @@
   resource_request->headers = std::move(headers);
   resource_request->method = net::HttpRequestHeaders::kPostMethod;
   resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  resource_request->mode = network::mojom::RequestMode::kSameOrigin;
+  resource_request->request_initiator = origin;
   resource_request->load_flags =
       net::LOAD_DISABLE_CACHE | net::LOAD_BYPASS_CACHE;
   resource_request->trusted_params = network::ResourceRequest::TrustedParams();
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender.h b/content/browser/attribution_reporting/attribution_report_network_sender.h
index 87544edf..02edc90 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender.h
+++ b/content/browser/attribution_reporting/attribution_report_network_sender.h
@@ -13,6 +13,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "content/browser/attribution_reporting/attribution_report_sender.h"
 #include "content/common/content_export.h"
+#include "url/origin.h"
 
 class GURL;
 
@@ -63,6 +64,7 @@
                               scoped_refptr<net::HttpResponseHeaders>)>;
 
   void SendReport(GURL url,
+                  url::Origin origin,
                   const std::string& body,
                   net::HttpRequestHeaders headers,
                   UrlLoaderCallback callback);
diff --git a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
index 54099b6..0ccde99 100644
--- a/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_report_network_sender_unittest.cc
@@ -123,6 +123,17 @@
   EXPECT_TRUE(load_flags & net::LOAD_DISABLE_CACHE);
 }
 
+TEST_F(AttributionReportNetworkSenderTest, SameSite) {
+  auto report = DefaultEventLevelReport();
+  network_sender_->SendReport(report, /*is_debug_report=*/false,
+                              base::DoNothing());
+  EXPECT_EQ(test_url_loader_factory_.GetPendingRequest(0)->request.mode,
+            network::mojom::RequestMode::kSameOrigin);
+  EXPECT_EQ(
+      test_url_loader_factory_.GetPendingRequest(0)->request.request_initiator,
+      report.GetReportingOrigin());
+}
+
 TEST_F(AttributionReportNetworkSenderTest, Isolation) {
   auto report = DefaultEventLevelReport();
   network_sender_->SendReport(report, /*is_debug_report=*/false,
diff --git a/content/browser/attribution_reporting/destination_throttler.h b/content/browser/attribution_reporting/destination_throttler.h
index 17ff55e1..918a54a 100644
--- a/content/browser/attribution_reporting/destination_throttler.h
+++ b/content/browser/attribution_reporting/destination_throttler.h
@@ -25,6 +25,8 @@
 class CONTENT_EXPORT DestinationThrottler {
  public:
   struct Policy {
+    // TODO(tquintanilla): Move these parameters to `AttributionConfig` to align
+    // with other parameters.
     int max_total = 200;
     int max_per_reporting_site = 50;
     base::TimeDelta rate_limit_window = base::Minutes(1);
@@ -59,6 +61,8 @@
       const net::SchemefulSite& source_site,
       const net::SchemefulSite& reporting_site);
 
+  int GetMaxPerReportingSite() const { return policy_.max_per_reporting_site; }
+
  private:
   void CleanUpOldEntries();
 
diff --git a/content/browser/attribution_reporting/store_source_result.cc b/content/browser/attribution_reporting/store_source_result.cc
index 4a095b3..a48968f 100644
--- a/content/browser/attribution_reporting/store_source_result.cc
+++ b/content/browser/attribution_reporting/store_source_result.cc
@@ -15,18 +15,26 @@
     attribution_reporting::mojom::StoreSourceResult status,
     absl::optional<base::Time> min_fake_report_time,
     absl::optional<int> max_destinations_per_source_site_reporting_site,
-    absl::optional<int> max_sources_per_origin)
+    absl::optional<int> max_sources_per_origin,
+    absl::optional<int> max_destinations_per_rate_limit_window_reporting_origin)
     : status(status),
       min_fake_report_time(min_fake_report_time),
       max_destinations_per_source_site_reporting_site(
           max_destinations_per_source_site_reporting_site),
-      max_sources_per_origin(max_sources_per_origin) {
+      max_sources_per_origin(max_sources_per_origin),
+      max_destinations_per_rate_limit_window_reporting_origin(
+          max_destinations_per_rate_limit_window_reporting_origin) {
   DCHECK(!max_destinations_per_source_site_reporting_site.has_value() ||
          status == attribution_reporting::mojom::StoreSourceResult::
                        kInsufficientUniqueDestinationCapacity);
   DCHECK(!max_sources_per_origin.has_value() ||
          status == attribution_reporting::mojom::StoreSourceResult::
                        kInsufficientSourceCapacity);
+  DCHECK(!max_destinations_per_rate_limit_window_reporting_origin.has_value() ||
+         status == attribution_reporting::mojom::StoreSourceResult::
+                       kDestinationReportingLimitReached ||
+         status == attribution_reporting::mojom::StoreSourceResult::
+                       kDestinationBothLimitsReached);
 }
 
 StoreSourceResult::~StoreSourceResult() = default;
diff --git a/content/browser/attribution_reporting/store_source_result.h b/content/browser/attribution_reporting/store_source_result.h
index ebe0516..abb10a6b 100644
--- a/content/browser/attribution_reporting/store_source_result.h
+++ b/content/browser/attribution_reporting/store_source_result.h
@@ -18,7 +18,10 @@
       absl::optional<base::Time> min_fake_report_time = absl::nullopt,
       absl::optional<int> max_destinations_per_source_site_reporting_site =
           absl::nullopt,
-      absl::optional<int> max_sources_per_origin = absl::nullopt);
+      absl::optional<int> max_sources_per_origin = absl::nullopt,
+      absl::optional<int>
+          max_destinations_per_rate_limit_window_reporting_origin =
+              absl::nullopt);
 
   ~StoreSourceResult();
 
@@ -41,6 +44,12 @@
   // Only populated in case of
   // `attribution_reporting::mojom::StoreSourceResult::kInsufficientSourceCapacity`.
   absl::optional<int> max_sources_per_origin;
+
+  // Populated in the cases of either
+  // `attribution_reporting::mojom::StoreSourceResult::kDestinationReportingLimitReached`
+  // or
+  // `attribution_reporting::mojom::StoreSourceResult::kDestinationBothLimitsReached`
+  absl::optional<int> max_destinations_per_rate_limit_window_reporting_origin;
 };
 
 }  // namespace content
diff --git a/content/browser/attribution_reporting/store_source_result.mojom b/content/browser/attribution_reporting/store_source_result.mojom
index fd45155..dc0c7283 100644
--- a/content/browser/attribution_reporting/store_source_result.mojom
+++ b/content/browser/attribution_reporting/store_source_result.mojom
@@ -16,4 +16,7 @@
   kExcessiveReportingOrigins = 4,
   kProhibitedByBrowserPolicy = 5,
   kSuccessNoised = 6,
+  kDestinationReportingLimitReached = 7,
+  kDestinationGlobalLimitReached = 8,
+  kDestinationBothLimitsReached = 9,
 };
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index f987900..f2efbcd8 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -1890,13 +1890,12 @@
   DispatchToAgents(ftn, &protocol::FedCmHandler::WillShowDialog, intercept);
 }
 
-void OnFedCmAccountsDialogShown(RenderFrameHost* render_frame_host,
-                                bool auto_reauthn) {
+void OnFedCmAccountsDialogShown(RenderFrameHost* render_frame_host) {
   FrameTreeNode* ftn = FrameTreeNode::From(render_frame_host);
   if (!ftn) {
     return;
   }
-  DispatchToAgents(ftn, &protocol::FedCmHandler::OnDialogShown, auto_reauthn);
+  DispatchToAgents(ftn, &protocol::FedCmHandler::OnDialogShown);
 }
 
 }  // namespace devtools_instrumentation
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index 57947de2..e36b1e9 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -404,8 +404,7 @@
                           bool* intercept,
                           bool* disable_delay);
 void WillShowFedCmDialog(RenderFrameHost* render_frame_host, bool* intercept);
-void OnFedCmAccountsDialogShown(RenderFrameHost* render_frame_host,
-                                bool auto_reauthn);
+void OnFedCmAccountsDialogShown(RenderFrameHost* render_frame_host);
 
 }  // namespace devtools_instrumentation
 
diff --git a/content/browser/devtools/protocol/fedcm_handler.cc b/content/browser/devtools/protocol/fedcm_handler.cc
index 5e9d37fc..f425853 100644
--- a/content/browser/devtools/protocol/fedcm_handler.cc
+++ b/content/browser/devtools/protocol/fedcm_handler.cc
@@ -36,8 +36,18 @@
 }
 
 DispatchResponse FedCmHandler::Enable(Maybe<bool> in_disableRejectionDelay) {
+  auto* auth_request = GetFederatedAuthRequest();
+  bool was_enabled = enabled_;
   enabled_ = true;
   disable_delay_ = in_disableRejectionDelay.fromMaybe(false);
+
+  // OnDialogShown should have been called previously if was_enabled is true.
+  // This could happen if FedCmHandler::Enable was called to enable/disable the
+  // rejection delay.
+  if (!was_enabled && auth_request && auth_request->GetDialogController()) {
+    OnDialogShown();
+  }
+
   return DispatchResponse::Success();
 }
 
@@ -46,16 +56,15 @@
   return DispatchResponse::Success();
 }
 
-void FedCmHandler::OnDialogShown(bool auto_reauthn) {
-  static int next_dialog_id_ = 0;
-
-  dialog_id_ = base::NumberToString(next_dialog_id_++);
-
+void FedCmHandler::OnDialogShown() {
   DCHECK(frontend_);
   if (!enabled_) {
     return;
   }
 
+  static int next_dialog_id_ = 0;
+  dialog_id_ = base::NumberToString(next_dialog_id_++);
+
   auto* auth_request = GetFederatedAuthRequest();
   const auto* idp_data = GetIdentityProviderData(auth_request);
   DCHECK(idp_data);
@@ -114,7 +123,7 @@
   IdentityRequestDialogController* dialog = auth_request->GetDialogController();
   CHECK(dialog);
 
-  FedCm::DialogType dialog_type = auto_reauthn
+  FedCm::DialogType dialog_type = auth_request->IsAutoReauthn()
                                       ? FedCm::DialogTypeEnum::AutoReauthn
                                       : FedCm::DialogTypeEnum::AccountChooser;
   Maybe<String> maybe_subtitle;
diff --git a/content/browser/devtools/protocol/fedcm_handler.h b/content/browser/devtools/protocol/fedcm_handler.h
index e4784fa..a0ff4dc 100644
--- a/content/browser/devtools/protocol/fedcm_handler.h
+++ b/content/browser/devtools/protocol/fedcm_handler.h
@@ -42,7 +42,7 @@
       *intercept = true;
     }
   }
-  void OnDialogShown(bool auto_reauthn);
+  void OnDialogShown();
 
  private:
   // DevToolsDomainHandler:
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index e941131..cd4bbfa90 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -1245,7 +1245,8 @@
           it.second->GetDangerType() !=
               download::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT &&
           it.second->GetDangerType() !=
-              download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING) {
+              download::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING &&
+          !download->IsInsecure()) {
         ++count;
       }
     }
diff --git a/content/browser/download/download_manager_impl_unittest.cc b/content/browser/download/download_manager_impl_unittest.cc
index d8c6b4a..e01b6a8 100644
--- a/content/browser/download/download_manager_impl_unittest.cc
+++ b/content/browser/download/download_manager_impl_unittest.cc
@@ -902,6 +902,8 @@
 TEST_F(DownloadManagerTest, BlockingShutdownCount) {
   download::MockDownloadItemImpl& item(AddItemToManager());
 
+  EXPECT_CALL(item, IsInsecure()).WillRepeatedly(Return(false));
+
   EXPECT_CALL(item, GetState())
       .WillRepeatedly(Return(download::DownloadItem::COMPLETE));
   EXPECT_EQ(download_manager_->BlockingShutdownCount(), 0);
@@ -926,6 +928,10 @@
     EXPECT_CALL(item, GetDangerType()).WillRepeatedly(Return(danger_type));
     EXPECT_EQ(download_manager_->BlockingShutdownCount(), 0);
   }
+
+  SCOPED_TRACE(testing::Message() << "Failed for insecure" << std::endl);
+  EXPECT_CALL(item, IsInsecure()).WillRepeatedly(Return(true));
+  EXPECT_EQ(download_manager_->BlockingShutdownCount(), 0);
 }
 
 class DownloadManagerWithExpirationTest : public DownloadManagerTest {
diff --git a/content/browser/fenced_frame/fenced_frame_reporter.cc b/content/browser/fenced_frame/fenced_frame_reporter.cc
index 033a1188..12e5dbf3 100644
--- a/content/browser/fenced_frame/fenced_frame_reporter.cc
+++ b/content/browser/fenced_frame/fenced_frame_reporter.cc
@@ -532,7 +532,7 @@
          main_frame_origin_.value().scheme() == url::kHttpsScheme);
   bool bound = private_aggregation_manager_->BindNewReceiver(
       winner_origin_.value(), main_frame_origin_.value(),
-      PrivateAggregationBudgetKey::Api::kFledge,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
       /*context_id=*/absl::nullopt,
       private_aggregation_host_.BindNewPipeAndPassReceiver());
   // FLEDGE's worklets should all be trustworthy, including `winner_origin_`, so
diff --git a/content/browser/indexed_db/database_impl.h b/content/browser/indexed_db/database_impl.h
index 5b3ac6a..062d2f21 100644
--- a/content/browser/indexed_db/database_impl.h
+++ b/content/browser/indexed_db/database_impl.h
@@ -140,10 +140,7 @@
 
   // This raw pointer is safe because all DatabaseImpl instances are owned by
   // an IndexedDBDispatcherHost.
-  // This dangling raw_ptr occurred in:
-  // browser_tests: AccessContextAuditBrowserTest.MultipleAccesses
-  // https://ci.chromium.org/ui/p/chromium/builders/try/win-rel/172869/test-results?q=ExactID%3Aninja%3A%2F%2Fchrome%2Ftest%3Abrowser_tests%2FAccessContextAuditBrowserTest.MultipleAccesses+VHash%3Abdbee181b3e0309b
-  raw_ptr<IndexedDBDispatcherHost, FlakyDanglingUntriaged> dispatcher_host_;
+  raw_ptr<IndexedDBDispatcherHost> dispatcher_host_;
   scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
   std::unique_ptr<IndexedDBConnection> connection_;
   const storage::BucketInfo bucket_info_;
diff --git a/content/browser/interest_group/ad_auction_service_impl_unittest.cc b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
index ad0a3de..66dee5c 100644
--- a/content/browser/interest_group/ad_auction_service_impl_unittest.cc
+++ b/content/browser/interest_group/ad_auction_service_impl_unittest.cc
@@ -6832,7 +6832,8 @@
         EXPECT_EQ(request.payload_contents().contributions[1].bucket, 3);
         EXPECT_EQ(request.payload_contents().contributions[1].value, 4);
         EXPECT_EQ(request.shared_info().reporting_origin, kOriginA);
-        EXPECT_EQ(budget_key.api(), PrivateAggregationBudgetKey::Api::kFledge);
+        EXPECT_EQ(budget_key.api(),
+                  PrivateAggregationBudgetKey::Api::kProtectedAudience);
         EXPECT_EQ(budget_key.origin(), kOriginA);
         run_loop.Quit();
       }));
diff --git a/content/browser/interest_group/interest_group_auction_reporter.cc b/content/browser/interest_group/interest_group_auction_reporter.cc
index 0fc8334..a6f5509 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.cc
+++ b/content/browser/interest_group/interest_group_auction_reporter.cc
@@ -244,7 +244,7 @@
     mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
     if (!private_aggregation_manager->BindNewReceiver(
             origin, main_frame_origin,
-            PrivateAggregationBudgetKey::Api::kFledge,
+            PrivateAggregationBudgetKey::Api::kProtectedAudience,
             /*context_id=*/absl::nullopt,
             remote.BindNewPipeAndPassReceiver())) {
       continue;
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index b3e4ffb..1f4a0616 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -13078,7 +13078,7 @@
             EXPECT_EQ(request.payload_contents().contributions[0].value, 5);
             EXPECT_EQ(request.shared_info().reporting_origin, test_origin);
             EXPECT_EQ(budget_key.api(),
-                      PrivateAggregationBudgetKey::Api::kFledge);
+                      PrivateAggregationBudgetKey::Api::kProtectedAudience);
             EXPECT_EQ(budget_key.origin(), test_origin);
             run_loop.Quit();
           }));
diff --git a/content/browser/interest_group/test_interest_group_private_aggregation_manager.cc b/content/browser/interest_group/test_interest_group_private_aggregation_manager.cc
index d7a9e0a..598d2ee 100644
--- a/content/browser/interest_group/test_interest_group_private_aggregation_manager.cc
+++ b/content/browser/interest_group/test_interest_group_private_aggregation_manager.cc
@@ -44,7 +44,8 @@
     mojo::PendingReceiver<blink::mojom::PrivateAggregationHost>
         pending_receiver) {
   EXPECT_EQ(expected_top_frame_origin_, top_frame_origin);
-  EXPECT_EQ(PrivateAggregationBudgetKey::Api::kFledge, api_for_budgeting);
+  EXPECT_EQ(PrivateAggregationBudgetKey::Api::kProtectedAudience,
+            api_for_budgeting);
   EXPECT_FALSE(context_id.has_value());
 
   receiver_set_.Add(this, std::move(pending_receiver), worklet_origin);
diff --git a/content/browser/interest_group/test_interest_group_private_aggregation_manager.h b/content/browser/interest_group/test_interest_group_private_aggregation_manager.h
index d0a0f80b..f6e9cd2 100644
--- a/content/browser/interest_group/test_interest_group_private_aggregation_manager.h
+++ b/content/browser/interest_group/test_interest_group_private_aggregation_manager.h
@@ -25,7 +25,7 @@
 namespace content {
 
 // An implementation of PrivateAggregationManager used for interest group tests
-// that tracks PrivateAggregationBudgetKey::Api::kFledge reports.
+// that tracks PrivateAggregationBudgetKey::Api::kProtectedAudience reports.
 class TestInterestGroupPrivateAggregationManager
     : public PrivateAggregationManager,
       public blink::mojom::PrivateAggregationHost {
diff --git a/content/browser/preloading/prefetch/prefetch_document_manager.h b/content/browser/preloading/prefetch/prefetch_document_manager.h
index 4e941a1..8d538f2 100644
--- a/content/browser/preloading/prefetch/prefetch_document_manager.h
+++ b/content/browser/preloading/prefetch/prefetch_document_manager.h
@@ -9,6 +9,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/containers/circular_deque.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/preloading/prefetch/no_vary_search_helper.h"
 #include "content/browser/preloading/prefetch/prefetch_type.h"
@@ -142,7 +143,8 @@
   size_t number_eager_prefetches_completed_{0};
   // A list of non-eager prefetch requests (from this page) that have completed
   // (oldest to newest).
-  std::deque<base::WeakPtr<PrefetchContainer>> completed_non_eager_prefetches_;
+  base::circular_deque<base::WeakPtr<PrefetchContainer>>
+      completed_non_eager_prefetches_;
 
   // Metrics related to the prefetches requested by this page load.
   PrefetchReferringPageMetrics referring_page_metrics_;
diff --git a/content/browser/private_aggregation/private_aggregation_budget_key.h b/content/browser/private_aggregation/private_aggregation_budget_key.h
index 28b84ba..85a79cb 100644
--- a/content/browser/private_aggregation/private_aggregation_budget_key.h
+++ b/content/browser/private_aggregation/private_aggregation_budget_key.h
@@ -20,7 +20,7 @@
 // `PrivateAggregationBudgeter::kBudgetScopeDuration`.
 class CONTENT_EXPORT PrivateAggregationBudgetKey {
  public:
-  enum class Api { kFledge, kSharedStorage };
+  enum class Api { kProtectedAudience, kSharedStorage };
 
   // Represents a period of time for which budget usage is recorded. This
   // interval includes the `start_time()` instant but excludes the end time
diff --git a/content/browser/private_aggregation/private_aggregation_budget_key_unittest.cc b/content/browser/private_aggregation/private_aggregation_budget_key_unittest.cc
index 37543611..014b1cd 100644
--- a/content/browser/private_aggregation/private_aggregation_budget_key_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_budget_key_unittest.cc
@@ -28,14 +28,16 @@
   const url::Origin example_origin =
       url::Origin::Create(GURL(kExampleOriginUrl));
 
-  absl::optional<PrivateAggregationBudgetKey> fledge_key =
+  absl::optional<PrivateAggregationBudgetKey> protected_audience_key =
       PrivateAggregationBudgetKey::Create(
           example_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
-  ASSERT_TRUE(fledge_key.has_value());
-  EXPECT_EQ(fledge_key->origin(), example_origin);
-  EXPECT_EQ(fledge_key->time_window().start_time(), kExampleHourBoundary);
-  EXPECT_EQ(fledge_key->api(), PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
+  ASSERT_TRUE(protected_audience_key.has_value());
+  EXPECT_EQ(protected_audience_key->origin(), example_origin);
+  EXPECT_EQ(protected_audience_key->time_window().start_time(),
+            kExampleHourBoundary);
+  EXPECT_EQ(protected_audience_key->api(),
+            PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   absl::optional<PrivateAggregationBudgetKey> shared_storage_key =
       PrivateAggregationBudgetKey::Create(
@@ -56,14 +58,14 @@
   absl::optional<PrivateAggregationBudgetKey> example_key =
       PrivateAggregationBudgetKey::Create(
           example_origin, /*api_invocation_time=*/kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   ASSERT_TRUE(example_key.has_value());
   EXPECT_EQ(example_key->time_window().start_time(), kExampleHourBoundary);
 
   absl::optional<PrivateAggregationBudgetKey> on_the_hour =
       PrivateAggregationBudgetKey::Create(
           example_origin, /*api_invocation_time=*/kExampleHourBoundary,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   ASSERT_TRUE(on_the_hour.has_value());
   EXPECT_EQ(on_the_hour->time_window().start_time(), kExampleHourBoundary);
 
@@ -71,7 +73,7 @@
       PrivateAggregationBudgetKey::Create(
           example_origin,
           /*api_invocation_time=*/kExampleHourBoundary + base::Microseconds(1),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   ASSERT_TRUE(just_after_the_hour.has_value());
   EXPECT_EQ(just_after_the_hour->time_window().start_time(),
             kExampleHourBoundary);
@@ -80,7 +82,7 @@
       PrivateAggregationBudgetKey::Create(
           example_origin,
           /*api_invocation_time=*/kExampleHourBoundary - base::Microseconds(1),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   ASSERT_TRUE(just_before_the_hour.has_value());
   EXPECT_EQ(just_before_the_hour->time_window().start_time(),
             kExampleHourBoundary - base::Hours(1));
@@ -125,14 +127,14 @@
   absl::optional<PrivateAggregationBudgetKey> opaque_origin_budget_key =
       PrivateAggregationBudgetKey::Create(
           url::Origin(), base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   EXPECT_FALSE(opaque_origin_budget_key.has_value());
 
   absl::optional<PrivateAggregationBudgetKey> insecure_origin_budget_key =
       PrivateAggregationBudgetKey::Create(
           url::Origin::Create(GURL("http://origin.example")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   EXPECT_FALSE(insecure_origin_budget_key.has_value());
 }
 
diff --git a/content/browser/private_aggregation/private_aggregation_budgeter.cc b/content/browser/private_aggregation/private_aggregation_budgeter.cc
index 9b8951f..1f60161 100644
--- a/content/browser/private_aggregation/private_aggregation_budgeter.cc
+++ b/content/browser/private_aggregation/private_aggregation_budgeter.cc
@@ -119,8 +119,8 @@
 GetHourlyBudgets(PrivateAggregationBudgetKey::Api api,
                  proto::PrivateAggregationBudgets& budgets) {
   switch (api) {
-    case PrivateAggregationBudgetKey::Api::kFledge:
-      return budgets.mutable_fledge_budgets();
+    case PrivateAggregationBudgetKey::Api::kProtectedAudience:
+      return budgets.mutable_protected_audience_budgets();
     case PrivateAggregationBudgetKey::Api::kSharedStorage:
       return budgets.mutable_shared_storage_budgets();
   }
@@ -441,7 +441,7 @@
     storage_->budgets_data()->TryGetData(origin_key, &budgets);
 
     static constexpr PrivateAggregationBudgetKey::Api kAllApis[] = {
-        PrivateAggregationBudgetKey::Api::kFledge,
+        PrivateAggregationBudgetKey::Api::kProtectedAudience,
         PrivateAggregationBudgetKey::Api::kSharedStorage};
 
     for (PrivateAggregationBudgetKey::Api api : kAllApis) {
diff --git a/content/browser/private_aggregation/private_aggregation_budgeter_unittest.cc b/content/browser/private_aggregation/private_aggregation_budgeter_unittest.cc
index 99a614a..3fcacf32 100644
--- a/content/browser/private_aggregation/private_aggregation_budgeter_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_budgeter_unittest.cc
@@ -84,8 +84,9 @@
 
     google::protobuf::RepeatedPtrField<proto::PrivateAggregationBudgetPerHour>*
         hourly_budgets =
-            budget_key.api() == PrivateAggregationBudgetKey::Api::kFledge
-                ? budgets.mutable_fledge_budgets()
+            budget_key.api() ==
+                    PrivateAggregationBudgetKey::Api::kProtectedAudience
+                ? budgets.mutable_protected_audience_budgets()
                 : budgets.mutable_shared_storage_budgets();
 
     proto::PrivateAggregationBudgetPerHour* new_budget = hourly_budgets->Add();
@@ -190,7 +191,7 @@
     return PrivateAggregationBudgetKey::CreateForTesting(
         /*origin=*/url::Origin::Create(GURL("https://a.example/")),
         /*api_invocation_time=*/kExampleTime,
-        /*api-*/ PrivateAggregationBudgetKey::Api::kFledge);
+        /*api-*/ PrivateAggregationBudgetKey::Api::kProtectedAudience);
   }
 
   base::FilePath db_path() const {
@@ -260,7 +261,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   budgeter()->ConsumeBudget(
       blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
       example_key,
@@ -298,7 +299,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
   budgeter()->ConsumeBudget(
       blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
       example_key,
@@ -335,7 +336,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   // Budget can be increased to below max
   budgeter()->ConsumeBudget(
@@ -385,7 +386,7 @@
     example_keys.push_back(PrivateAggregationBudgetKey::CreateForTesting(
         url::Origin::Create(GURL("https://a.example")),
         reference_time + i * base::Hours(1),
-        PrivateAggregationBudgetKey::Api::kFledge));
+        PrivateAggregationBudgetKey::Api::kProtectedAudience));
   }
 
   // Consuming this budget 24 times in a day would not exceed the daily budget,
@@ -437,11 +438,11 @@
 
   CreateAndInitializeBudgeterThenWait();
 
-  PrivateAggregationBudgetKey fledge_key =
+  PrivateAggregationBudgetKey protected_audience_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey shared_storage_key =
       PrivateAggregationBudgetKey::CreateForTesting(
@@ -451,7 +452,7 @@
 
   budgeter()->ConsumeBudget(
       blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
-      fledge_key,
+      protected_audience_key,
       base::BindLambdaForTesting(
           [&num_queries_processed](RequestResult request) {
             EXPECT_EQ(request, RequestResult::kApproved);
@@ -482,13 +483,13 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey key_b =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://b.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   budgeter()->ConsumeBudget(
       blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(), key_a,
@@ -520,7 +521,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   // Request will be rejected if budget non-positive
   budgeter()->ConsumeBudget(
@@ -654,7 +655,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   // Queries should be processed in the order they are received.
   int num_queries_processed = 0;
@@ -731,7 +732,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   // Queries should be processed in the order they are received.
   int num_queries_processed = 0;
@@ -780,7 +781,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   int num_queries_succeeded = 0;
 
@@ -830,7 +831,7 @@
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           base::Time::FromJavaTime(1652984901234),
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   int num_consume_queries_succeeded = 0;
 
@@ -904,7 +905,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -953,13 +954,13 @@
   PrivateAggregationBudgetKey example_key_1 =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey example_key_2 =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           kExampleTime + PrivateAggregationBudgetKey::TimeWindow::kDuration,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   EXPECT_NE(example_key_1.time_window().start_time(),
             example_key_2.time_window().start_time());
@@ -1024,19 +1025,19 @@
   PrivateAggregationBudgetKey key_to_clear =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey key_after =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           kExampleTime + PrivateAggregationBudgetKey::TimeWindow::kDuration,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey key_before =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")),
           kExampleTime - PrivateAggregationBudgetKey::TimeWindow::kDuration,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   EXPECT_LT(key_to_clear.time_window().start_time(),
             key_after.time_window().start_time());
@@ -1106,10 +1107,10 @@
 
   CreateAndInitializeBudgeterThenWait();
 
-  PrivateAggregationBudgetKey fledge_key =
+  PrivateAggregationBudgetKey protected_audience_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey shared_storage_key =
       PrivateAggregationBudgetKey::CreateForTesting(
@@ -1131,11 +1132,11 @@
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
-      fledge_key, expect_approved);
+      protected_audience_key, expect_approved);
 
   // Maximum budget has been used so this should fail.
   budgeter()->ConsumeBudget(
-      /*budget=*/1, fledge_key, expect_insufficient_budget);
+      /*budget=*/1, protected_audience_key, expect_insufficient_budget);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -1158,7 +1159,7 @@
   // After clearing, we can use the full budget again
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
-      fledge_key, expect_approved);
+      protected_audience_key, expect_approved);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -1178,7 +1179,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -1227,7 +1228,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -1276,7 +1277,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -1327,11 +1328,13 @@
 
   PrivateAggregationBudgetKey example_key_a =
       PrivateAggregationBudgetKey::CreateForTesting(
-          kOriginA, kExampleTime, PrivateAggregationBudgetKey::Api::kFledge);
+          kOriginA, kExampleTime,
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey example_key_b =
       PrivateAggregationBudgetKey::CreateForTesting(
-          kOriginB, kExampleTime, PrivateAggregationBudgetKey::Api::kFledge);
+          kOriginB, kExampleTime,
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   base::RepeatingCallback<void(RequestResult)> expect_approved =
       base::BindLambdaForTesting(
@@ -1396,11 +1399,13 @@
 
   PrivateAggregationBudgetKey example_key_a =
       PrivateAggregationBudgetKey::CreateForTesting(
-          kOriginA, kExampleTime, PrivateAggregationBudgetKey::Api::kFledge);
+          kOriginA, kExampleTime,
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   PrivateAggregationBudgetKey example_key_b =
       PrivateAggregationBudgetKey::CreateForTesting(
-          kOriginB, kExampleTime, PrivateAggregationBudgetKey::Api::kFledge);
+          kOriginB, kExampleTime,
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   base::RepeatingCallback<void(RequestResult)> expect_approved =
       base::BindLambdaForTesting(
@@ -1465,7 +1470,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::CreateForTesting(
           url::Origin::Create(GURL("https://a.example/")), kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge);
+          PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
   budgeter()->ConsumeBudget(
       /*budget=*/blink::features::kPrivateAggregationApiMaxBudgetPerScope.Get(),
@@ -1507,7 +1512,7 @@
         PrivateAggregationBudgetKey::CreateForTesting(
             url::Origin::Create(GURL("https://a.example/")),
             base::Time::FromJavaTime(1652984901234),
-            PrivateAggregationBudgetKey::Api::kFledge);
+            PrivateAggregationBudgetKey::Api::kProtectedAudience);
 
     // Budget can be increased to below max
     budgeter()->ConsumeBudget(
diff --git a/content/browser/private_aggregation/private_aggregation_host_unittest.cc b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
index 830623a..6bc3ea1 100644
--- a/content/browser/private_aggregation/private_aggregation_host_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_host_unittest.cc
@@ -83,15 +83,16 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
   absl::optional<AggregatableReportRequest> validated_request;
-  EXPECT_CALL(mock_callback_,
-              Run(_, Property(&PrivateAggregationBudgetKey::api,
-                              PrivateAggregationBudgetKey::Api::kFledge)))
+  EXPECT_CALL(
+      mock_callback_,
+      Run(_, Property(&PrivateAggregationBudgetKey::api,
+                      PrivateAggregationBudgetKey::Api::kProtectedAudience)))
       .WillOnce(MoveArg<0>(&validated_request));
 
   std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
@@ -133,8 +134,9 @@
               AggregatableReportSharedInfo::DebugMode::kDisabled,
               /*additional_fields=*/base::Value::Dict(),
               /*api_version=*/"0.1",
-              /*api_identifier=*/"fledge"),
-          /*reporting_path=*/"/.well-known/private-aggregation/report-fledge");
+              /*api_identifier=*/"protected-audience"),
+          /*reporting_path=*/
+          "/.well-known/private-aggregation/report-protected-audience");
   ASSERT_TRUE(expected_request);
 
   EXPECT_TRUE(aggregation_service::ReportRequestsEqual(
@@ -154,7 +156,7 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   const PrivateAggregationBudgetKey::Api apis[] = {
-      PrivateAggregationBudgetKey::Api::kFledge,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
       PrivateAggregationBudgetKey::Api::kSharedStorage};
 
   std::vector<mojo::Remote<blink::mojom::PrivateAggregationHost>> remotes{
@@ -187,11 +189,12 @@
   }
 
   EXPECT_EQ(validated_requests[0]->reporting_path(),
-            "/.well-known/private-aggregation/report-fledge");
+            "/.well-known/private-aggregation/report-protected-audience");
   EXPECT_EQ(validated_requests[1]->reporting_path(),
             "/.well-known/private-aggregation/report-shared-storage");
 
-  EXPECT_EQ(validated_requests[0]->shared_info().api_identifier, "fledge");
+  EXPECT_EQ(validated_requests[0]->shared_info().api_identifier,
+            "protected-audience");
   EXPECT_EQ(validated_requests[1]->shared_info().api_identifier,
             "shared-storage");
 
@@ -217,10 +220,10 @@
       /*debug_key=*/blink::mojom::DebugKey::New(/*value=*/1234u)));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
   std::vector<absl::optional<AggregatableReportRequest>> validated_requests{
       /*n=*/3};
@@ -277,14 +280,14 @@
   std::vector<mojo::Remote<blink::mojom::PrivateAggregationHost>> remotes(
       /*n=*/4);
 
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOriginA, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remotes[0].BindNewPipeAndPassReceiver()));
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOriginB, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remotes[1].BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOriginA, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remotes[0].BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOriginB, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remotes[1].BindNewPipeAndPassReceiver()));
   EXPECT_TRUE(host_->BindNewReceiver(
       kExampleOriginA, kMainFrameOrigin,
       PrivateAggregationBudgetKey::Api::kSharedStorage,
@@ -295,9 +298,10 @@
       /*context_id=*/absl::nullopt, remotes[3].BindNewPipeAndPassReceiver()));
 
   // Use the bucket as a sentinel to ensure that calls were routed correctly.
-  EXPECT_CALL(mock_callback_,
-              Run(_, Property(&PrivateAggregationBudgetKey::api,
-                              PrivateAggregationBudgetKey::Api::kFledge)))
+  EXPECT_CALL(
+      mock_callback_,
+      Run(_, Property(&PrivateAggregationBudgetKey::api,
+                      PrivateAggregationBudgetKey::Api::kProtectedAudience)))
       .WillOnce(
           Invoke([&kExampleOriginB](AggregatableReportRequest request,
                                     PrivateAggregationBudgetKey budget_key) {
@@ -364,16 +368,16 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote_1;
-  EXPECT_FALSE(host_->BindNewReceiver(kInsecureOrigin, kMainFrameOrigin,
-                                      PrivateAggregationBudgetKey::Api::kFledge,
-                                      /*context_id=*/absl::nullopt,
-                                      remote_1.BindNewPipeAndPassReceiver()));
+  EXPECT_FALSE(host_->BindNewReceiver(
+      kInsecureOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote_1.BindNewPipeAndPassReceiver()));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote_2;
-  EXPECT_FALSE(host_->BindNewReceiver(kOpaqueOrigin, kMainFrameOrigin,
-                                      PrivateAggregationBudgetKey::Api::kFledge,
-                                      /*context_id=*/absl::nullopt,
-                                      remote_2.BindNewPipeAndPassReceiver()));
+  EXPECT_FALSE(host_->BindNewReceiver(
+      kOpaqueOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote_2.BindNewPipeAndPassReceiver()));
 
   // Attempt to send a message to an unconnected remote. The request should
   // not be processed.
@@ -409,10 +413,10 @@
       "this_is_an_example_of_a_context_id_that_is_too_long_to_be_allowed";
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_FALSE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                      PrivateAggregationBudgetKey::Api::kFledge,
-                                      kTooLongContextId,
-                                      remote.BindNewPipeAndPassReceiver()));
+  EXPECT_FALSE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience, kTooLongContextId,
+      remote.BindNewPipeAndPassReceiver()));
 
   // Attempt to send a message to an unconnected remote. The request should
   // not be processed.
@@ -441,10 +445,10 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
   // Negative values are invalid
   std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
@@ -497,10 +501,10 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
   std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
       too_many_contributions;
   for (int i = 0; i < PrivateAggregationHost::kMaxNumberOfContributions + 1;
@@ -543,10 +547,10 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
   // If the API is enabled, the call should succeed.
   EXPECT_CALL(browser_client,
@@ -583,10 +587,10 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
   // If the API is enabled, the call should succeed.
   EXPECT_CALL(browser_client,
@@ -621,10 +625,10 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     "example_context_id",
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      "example_context_id", remote.BindNewPipeAndPassReceiver()));
 
   absl::optional<AggregatableReportRequest> validated_request;
   EXPECT_CALL(mock_callback_, Run).WillOnce(MoveArg<0>(&validated_request));
@@ -706,8 +710,8 @@
     mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
     EXPECT_TRUE(host_->BindNewReceiver(
         kExampleOrigin, kMainFrameOrigin,
-        PrivateAggregationBudgetKey::Api::kFledge, "example_context_id",
-        remote.BindNewPipeAndPassReceiver()));
+        PrivateAggregationBudgetKey::Api::kProtectedAudience,
+        "example_context_id", remote.BindNewPipeAndPassReceiver()));
 
     remote->SetDebugModeDetailsOnNullReport(std::move(debug_mode_details_arg));
 
@@ -720,8 +724,8 @@
     mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
     EXPECT_TRUE(host_->BindNewReceiver(
         kExampleOrigin, kMainFrameOrigin,
-        PrivateAggregationBudgetKey::Api::kFledge, "example_context_id",
-        remote.BindNewPipeAndPassReceiver()));
+        PrivateAggregationBudgetKey::Api::kProtectedAudience,
+        "example_context_id", remote.BindNewPipeAndPassReceiver()));
 
     // While it is expected that SetDebugModeDetailsOnNullReport() be called, a
     // null report should still be sent if it isn't.
@@ -770,7 +774,7 @@
     mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
     EXPECT_TRUE(host_->BindNewReceiver(
         kExampleOrigin, kMainFrameOrigin,
-        PrivateAggregationBudgetKey::Api::kFledge,
+        PrivateAggregationBudgetKey::Api::kProtectedAudience,
         /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
     EXPECT_TRUE(remote.is_connected());
@@ -782,7 +786,7 @@
     mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
     EXPECT_TRUE(host_->BindNewReceiver(
         kExampleOrigin, kMainFrameOrigin,
-        PrivateAggregationBudgetKey::Api::kFledge,
+        PrivateAggregationBudgetKey::Api::kProtectedAudience,
         /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
     // Setting the debug details has no effect.
@@ -804,10 +808,10 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     "example_context_id",
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      "example_context_id", remote.BindNewPipeAndPassReceiver()));
 
   absl::optional<AggregatableReportRequest> validated_request;
   EXPECT_CALL(mock_callback_, Run).WillOnce(MoveArg<0>(&validated_request));
@@ -850,15 +854,16 @@
       url::Origin::Create(GURL("https://main_frame.com"));
 
   mojo::Remote<blink::mojom::PrivateAggregationHost> remote;
-  EXPECT_TRUE(host_->BindNewReceiver(kExampleOrigin, kMainFrameOrigin,
-                                     PrivateAggregationBudgetKey::Api::kFledge,
-                                     /*context_id=*/absl::nullopt,
-                                     remote.BindNewPipeAndPassReceiver()));
+  EXPECT_TRUE(host_->BindNewReceiver(
+      kExampleOrigin, kMainFrameOrigin,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt, remote.BindNewPipeAndPassReceiver()));
 
   absl::optional<AggregatableReportRequest> validated_request;
-  EXPECT_CALL(mock_callback_,
-              Run(_, Property(&PrivateAggregationBudgetKey::api,
-                              PrivateAggregationBudgetKey::Api::kFledge)))
+  EXPECT_CALL(
+      mock_callback_,
+      Run(_, Property(&PrivateAggregationBudgetKey::api,
+                      PrivateAggregationBudgetKey::Api::kProtectedAudience)))
       .WillOnce(MoveArg<0>(&validated_request));
 
   std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
diff --git a/content/browser/private_aggregation/private_aggregation_manager_impl_unittest.cc b/content/browser/private_aggregation/private_aggregation_manager_impl_unittest.cc
index 9e0e3af..8265eda3 100644
--- a/content/browser/private_aggregation/private_aggregation_manager_impl_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_manager_impl_unittest.cc
@@ -107,7 +107,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::Create(
           example_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
 
   AggregatableReportRequest expected_request =
@@ -166,7 +166,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::Create(
           example_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
 
   AggregatableReportRequest example_request =
@@ -230,7 +230,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::Create(
           example_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
 
   AggregatableReportRequest expected_request =
@@ -279,7 +279,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::Create(
           example_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
 
   AggregatableReportRequest example_request =
@@ -322,7 +322,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::Create(
           example_request.shared_info().reporting_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
 
   absl::optional<AggregatableReportRequest> standard_request =
@@ -333,7 +333,7 @@
       AggregatableReportRequest::Create(
           example_request.payload_contents(), std::move(shared_info),
           /*reporting_path=*/
-          "/.well-known/private-aggregation/debug/report-fledge");
+          "/.well-known/private-aggregation/debug/report-protected-audience");
   ASSERT_TRUE(standard_request.has_value());
   ASSERT_TRUE(expected_debug_request.has_value());
 
@@ -386,10 +386,10 @@
           /*reporting_path=*/"/example-reporting-path");
   ASSERT_TRUE(standard_request.has_value());
 
-  PrivateAggregationBudgetKey fledge_key =
+  PrivateAggregationBudgetKey protected_audience_key =
       PrivateAggregationBudgetKey::Create(
           example_request.shared_info().reporting_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
   PrivateAggregationBudgetKey shared_storage_key =
       PrivateAggregationBudgetKey::Create(
@@ -401,7 +401,7 @@
   {
     testing::InSequence seq;
 
-    EXPECT_CALL(*budgeter_, ConsumeBudget(_, fledge_key, _))
+    EXPECT_CALL(*budgeter_, ConsumeBudget(_, protected_audience_key, _))
         .WillOnce(
             Invoke([](int, const PrivateAggregationBudgetKey&,
                       base::OnceCallback<void(
@@ -414,7 +414,8 @@
           EXPECT_EQ(report_request.shared_info().reporting_origin,
                     example_request.shared_info().reporting_origin);
           EXPECT_EQ(report_request.reporting_path(),
-                    "/.well-known/private-aggregation/debug/report-fledge");
+                    "/.well-known/private-aggregation/debug/"
+                    "report-protected-audience");
         }));
     // Still triggers the standard (non-debug) report.
     EXPECT_CALL(*aggregation_service_, ScheduleReport);
@@ -443,7 +444,7 @@
 
   manager_.OnReportRequestReceivedFromHost(
       aggregation_service::CloneReportRequest(standard_request.value()),
-      fledge_key);
+      protected_audience_key);
   checkpoint.Call(1);
   manager_.OnReportRequestReceivedFromHost(
       aggregation_service::CloneReportRequest(standard_request.value()),
@@ -467,7 +468,7 @@
   PrivateAggregationBudgetKey example_key =
       PrivateAggregationBudgetKey::Create(
           example_request.shared_info().reporting_origin, kExampleTime,
-          PrivateAggregationBudgetKey::Api::kFledge)
+          PrivateAggregationBudgetKey::Api::kProtectedAudience)
           .value();
 
   absl::optional<AggregatableReportRequest> standard_request =
@@ -506,13 +507,15 @@
   const url::Origin example_main_frame_origin =
       url::Origin::Create(GURL(kExampleMainFrameUrl));
 
-  EXPECT_CALL(*host_, BindNewReceiver(example_origin, example_main_frame_origin,
-                                      PrivateAggregationBudgetKey::Api::kFledge,
-                                      testing::Eq(absl::nullopt), _))
+  EXPECT_CALL(*host_, BindNewReceiver(
+                          example_origin, example_main_frame_origin,
+                          PrivateAggregationBudgetKey::Api::kProtectedAudience,
+                          testing::Eq(absl::nullopt), _))
       .WillOnce(Return(true));
   EXPECT_TRUE(manager_.BindNewReceiver(
       example_origin, example_main_frame_origin,
-      PrivateAggregationBudgetKey::Api::kFledge, /*context_id=*/absl::nullopt,
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      /*context_id=*/absl::nullopt,
       mojo::PendingReceiver<blink::mojom::PrivateAggregationHost>()));
 
   EXPECT_CALL(*host_,
@@ -526,13 +529,15 @@
       /*context_id=*/absl::nullopt,
       mojo::PendingReceiver<blink::mojom::PrivateAggregationHost>()));
 
-  EXPECT_CALL(*host_, BindNewReceiver(example_origin, example_main_frame_origin,
-                                      PrivateAggregationBudgetKey::Api::kFledge,
-                                      testing::Eq("example_context_id"), _))
+  EXPECT_CALL(*host_, BindNewReceiver(
+                          example_origin, example_main_frame_origin,
+                          PrivateAggregationBudgetKey::Api::kProtectedAudience,
+                          testing::Eq("example_context_id"), _))
       .WillOnce(Return(true));
   EXPECT_TRUE(manager_.BindNewReceiver(
       example_origin, example_main_frame_origin,
-      PrivateAggregationBudgetKey::Api::kFledge, "example_context_id",
+      PrivateAggregationBudgetKey::Api::kProtectedAudience,
+      "example_context_id",
       mojo::PendingReceiver<blink::mojom::PrivateAggregationHost>()));
 }
 
diff --git a/content/browser/private_aggregation/private_aggregation_report_golden_unittest.cc b/content/browser/private_aggregation/private_aggregation_report_golden_unittest.cc
index 51939ce..f56d077 100644
--- a/content/browser/private_aggregation/private_aggregation_report_golden_unittest.cc
+++ b/content/browser/private_aggregation/private_aggregation_report_golden_unittest.cc
@@ -344,13 +344,13 @@
            /*debug_key=*/blink::mojom::DebugKey::New(/*value=*/123u)),
        .contributions = {blink::mojom::AggregatableReportHistogramContribution(
            /*bucket=*/1, /*value=*/2)},
-       .api_identifier = PrivateAggregationBudgetKey::Api::kFledge,
+       .api_identifier = PrivateAggregationBudgetKey::Api::kProtectedAudience,
        .report_file = "report_1.json",
        .cleartext_payloads_file = "report_1_cleartext_payloads.json"},
       {.debug_details = blink::mojom::DebugModeDetails::New(),
        .contributions = {blink::mojom::AggregatableReportHistogramContribution(
            /*bucket==*/1, /*value=*/2)},
-       .api_identifier = PrivateAggregationBudgetKey::Api::kFledge,
+       .api_identifier = PrivateAggregationBudgetKey::Api::kProtectedAudience,
        .report_file = "report_2.json",
        .cleartext_payloads_file = "report_2_cleartext_payloads.json"},
       {.debug_details = blink::mojom::DebugModeDetails::New(
@@ -376,13 +376,13 @@
            /*debug_key=*/blink::mojom::DebugKey::New(/*value=*/123u)),
        .contributions = {blink::mojom::AggregatableReportHistogramContribution(
            /*bucket==*/1, /*value=*/2)},
-       .api_identifier = PrivateAggregationBudgetKey::Api::kFledge,
+       .api_identifier = PrivateAggregationBudgetKey::Api::kProtectedAudience,
        .report_file = "report_5.json",
        .cleartext_payloads_file = "report_5_cleartext_payloads.json"},
       {.debug_details = blink::mojom::DebugModeDetails::New(),
        .contributions = {blink::mojom::AggregatableReportHistogramContribution(
            /*bucket==*/1, /*value=*/2)},
-       .api_identifier = PrivateAggregationBudgetKey::Api::kFledge,
+       .api_identifier = PrivateAggregationBudgetKey::Api::kProtectedAudience,
        .report_file = "report_6.json",
        .cleartext_payloads_file = "report_6_cleartext_payloads.json"},
   };
diff --git a/content/browser/private_aggregation/private_aggregation_utils.cc b/content/browser/private_aggregation/private_aggregation_utils.cc
index 6b39f26..a8409514d 100644
--- a/content/browser/private_aggregation/private_aggregation_utils.cc
+++ b/content/browser/private_aggregation/private_aggregation_utils.cc
@@ -18,14 +18,15 @@
   static constexpr char kSharedReportingPathPrefix[] =
       "/.well-known/private-aggregation/";
   static constexpr char kDebugReportingPathInfix[] = "debug/";
-  static constexpr char kFledgeReportingPathSuffix[] = "report-fledge";
+  static constexpr char kProtectedAudienceReportingPathSuffix[] =
+      "report-protected-audience";
   static constexpr char kSharedStorageReportingPathSuffix[] =
       "report-shared-storage";
 
   base::StringPiece api_suffix;
   switch (api) {
-    case PrivateAggregationBudgetKey::Api::kFledge:
-      api_suffix = kFledgeReportingPathSuffix;
+    case PrivateAggregationBudgetKey::Api::kProtectedAudience:
+      api_suffix = kProtectedAudienceReportingPathSuffix;
       break;
     case PrivateAggregationBudgetKey::Api::kSharedStorage:
       api_suffix = kSharedStorageReportingPathSuffix;
@@ -39,8 +40,8 @@
 
 std::string GetApiIdentifier(PrivateAggregationBudgetKey::Api api) {
   switch (api) {
-    case PrivateAggregationBudgetKey::Api::kFledge:
-      return "fledge";
+    case PrivateAggregationBudgetKey::Api::kProtectedAudience:
+      return "protected-audience";
     case PrivateAggregationBudgetKey::Api::kSharedStorage:
       return "shared-storage";
   }
diff --git a/content/browser/private_aggregation/proto/private_aggregation_budgets.proto b/content/browser/private_aggregation/proto/private_aggregation_budgets.proto
index 0f035b9..d445fc4 100644
--- a/content/browser/private_aggregation/proto/private_aggregation_budgets.proto
+++ b/content/browser/private_aggregation/proto/private_aggregation_budgets.proto
@@ -28,6 +28,6 @@
   // a window is not present, the budget used is inferred to be zero. For each
   // API (separately), the sum of budget_used should never be greater than
   // PrivateAggregationBudgeter::kMaxDailyBudget.
-  repeated PrivateAggregationBudgetPerHour fledge_budgets = 1;
+  repeated PrivateAggregationBudgetPerHour protected_audience_budgets = 1;
   repeated PrivateAggregationBudgetPerHour shared_storage_budgets = 2;
 }
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index ce02660..ad258ab 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -4936,9 +4936,8 @@
   // there is no client security state for top-level navigations, which mainly
   // means that Private Network Access checks are skipped for such requests.
   //
-  // TODO(https://crbug.com/1170335): Pass the client security state of the
-  // navigation initiator to this navigation request somehow and use that
-  // instead.
+  // TODO(https://crbug.com/1291252): Use the client security state of the
+  // navigation initiator instead.
   //
   // TODO(https://crbug.com/1129326): Figure out the UX story for main-frame
   // navigations, then revisit the exception made in that case.
diff --git a/content/browser/renderer_host/private_network_access_browsertest.cc b/content/browser/renderer_host/private_network_access_browsertest.cc
index 682fb02..9e33382 100644
--- a/content/browser/renderer_host/private_network_access_browsertest.cc
+++ b/content/browser/renderer_host/private_network_access_browsertest.cc
@@ -2943,7 +2943,7 @@
       child_frame->BuildClientSecurityState();
   ASSERT_FALSE(security_state.is_null());
 
-  // TODO(https://crbug.com/1170335): Expect `kAllow` here once inheritance is
+  // TODO(https://crbug.com/1291252): Expect `kAllow` here once inheritance is
   // properly implemented.
   EXPECT_EQ(security_state->local_network_request_policy,
             network::mojom::LocalNetworkRequestPolicy::kBlock);
@@ -2961,7 +2961,7 @@
       child_frame->BuildClientSecurityState();
   ASSERT_FALSE(security_state.is_null());
 
-  // TODO(https://crbug.com/1170335): Expect `kAllow` here once inheritance is
+  // TODO(https://crbug.com/1291252): Expect `kAllow` here once inheritance is
   // properly implemented.
   EXPECT_EQ(security_state->local_network_request_policy,
             network::mojom::LocalNetworkRequestPolicy::kBlock);
@@ -3949,7 +3949,7 @@
 // against the parent document's address space. This is incorrect, as the
 // initiator of the navigation is not always the parent document.
 //
-// TODO(https://crbug.com/1170335): Revisit this when the initiator's address
+// TODO(https://crbug.com/1291252): Revisit this when the initiator's address
 // space is used instead.
 //
 // Top-level navigations are never blocked.
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 89fefd9b..802f6b0 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -365,18 +365,17 @@
 }
 
 void RenderFrameProxyHost::UpdateOpener() {
-  // Another frame in this proxy's SiteInstance may reach the new opener by
+  // Another frame in this proxy's SiteInstanceGroup may reach the new opener by
   // first reaching this proxy and then referencing its window.opener.  Ensure
   // the new opener's proxy exists in this case. If this is already a proxy for
   // a frame in another BrowsingInstance in the same CoopRelatedGroup, we should
   // not add extra proxies, as these are not discoverable via window.opener
   // because property access is restricted.
-  bool is_coop_rp_proxy = frame_tree_node_->current_frame_host()
-                              ->GetSiteInstance()
-                              ->IsCoopRelatedSiteInstance(GetSiteInstance()) &&
-                          !frame_tree_node_->current_frame_host()
-                               ->GetSiteInstance()
-                               ->IsRelatedSiteInstance(GetSiteInstance());
+  SiteInstanceGroup* current_group =
+      frame_tree_node_->current_frame_host()->GetSiteInstance()->group();
+  bool is_coop_rp_proxy =
+      current_group->IsCoopRelatedSiteInstanceGroup(site_instance_group()) &&
+      !current_group->IsRelatedSiteInstanceGroup(site_instance_group());
   if (is_coop_rp_proxy) {
     return;
   }
diff --git a/content/browser/webauth/web_authentication_delegate_android.cc b/content/browser/webauth/web_authentication_delegate_android.cc
index 77b9570f..410fc0dd 100644
--- a/content/browser/webauth/web_authentication_delegate_android.cc
+++ b/content/browser/webauth/web_authentication_delegate_android.cc
@@ -32,16 +32,3 @@
              static_cast<intptr_t>(delegatePtr))
       ->GetIntentSender(web_contents);
 }
-
-static int JNI_WebAuthenticationDelegate_GetSupportLevel(
-    JNIEnv* env,
-    jlong delegatePtr,
-    const base::android::JavaParamRef<jobject>& java_web_contents) {
-  content::WebContents* const web_contents =
-      content::WebContents::FromJavaWebContents(java_web_contents);
-
-  static_assert(sizeof(delegatePtr) >= sizeof(intptr_t));
-  return reinterpret_cast<content::WebAuthenticationDelegate*>(
-             static_cast<intptr_t>(delegatePtr))
-      ->GetSupportLevel(web_contents);
-}
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index a68e0e7..16c9ccf 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -676,7 +676,7 @@
                                  /*should_delay_callback=*/true);
         return;
       }
-      // TODO(crbug.com/1383384): Handle auto_reauthn for multi IDP.
+      // TODO(crbug.com/1383384): Handle auto_reauthn_ for multi IDP.
       if (ShouldFailBeforeFetchingAccounts(
               idp_ptr->get_federated()->config_url)) {
         CompleteRequestWithError(
@@ -1125,12 +1125,12 @@
   // RenderFrameHost should be in the primary page (ex not in the BFCache).
   DCHECK(render_frame_host().GetPage().IsPrimary());
 
-  // TODO(crbug.com/1383384): Handle auto_reauthn for multi IDP.
+  // TODO(crbug.com/1383384): Handle auto_reauthn_ for multi IDP.
   bool auto_reauthn_enabled =
       mediation_requirement_ != MediationRequirement::kRequired &&
       IsFedCmAutoReauthnEnabled();
 
-  bool auto_reauthn = auto_reauthn_enabled;
+  auto_reauthn_ = auto_reauthn_enabled;
   bool is_auto_reauthn_setting_enabled = false;
   bool is_auto_reauthn_embargoed = false;
   absl::optional<base::TimeDelta> time_from_embargo;
@@ -1141,7 +1141,6 @@
   if (auto_reauthn_enabled) {
     is_auto_reauthn_setting_enabled =
         auto_reauthn_permission_delegate_->IsAutoReauthnSettingEnabled();
-    auto_reauthn &= is_auto_reauthn_setting_enabled;
     is_auto_reauthn_embargoed =
         auto_reauthn_permission_delegate_->IsAutoReauthnEmbargoed(
             GetEmbeddingOrigin());
@@ -1157,18 +1156,18 @@
           "Auto re-authn was previously triggered less than 10 minutes ago. "
           "Only one auto re-authn request can be made every 10 minutes.");
     }
-    auto_reauthn &= !is_auto_reauthn_embargoed;
     requires_user_mediation = RequiresUserMediation();
-    auto_reauthn &= !requires_user_mediation;
     // Auto signs in returning users if they have a single returning account and
     // are signing in.
     has_single_returning_account =
         GetSingleReturningAccount(&auto_reauthn_idp, &auto_reauthn_account);
-    auto_reauthn &= has_single_returning_account;
+    auto_reauthn_ &= !requires_user_mediation &&
+                     is_auto_reauthn_setting_enabled &&
+                     !is_auto_reauthn_embargoed && has_single_returning_account;
     if (!has_single_returning_account &&
         mediation_requirement_ == MediationRequirement::kSilent) {
       fedcm_metrics_->RecordAutoReauthnMetrics(
-          has_single_returning_account, auto_reauthn_account, auto_reauthn,
+          has_single_returning_account, auto_reauthn_account, auto_reauthn_,
           !is_auto_reauthn_setting_enabled, is_auto_reauthn_embargoed,
           time_from_embargo, requires_user_mediation);
 
@@ -1190,13 +1189,13 @@
           /*should_delay_callback=*/false);
       return;
     }
-  }
 
-  if (auto_reauthn) {
-    IdentityRequestAccount account{*auto_reauthn_account};
-    IdentityProviderData idp{*auto_reauthn_idp};
-    idp.accounts = {account};
-    idp_data_for_display_ = {idp};
+    if (auto_reauthn_) {
+      IdentityRequestAccount account{*auto_reauthn_account};
+      IdentityProviderData idp{*auto_reauthn_idp};
+      idp.accounts = {account};
+      idp_data_for_display_ = {idp};
+    }
   }
 
   // TODO(crbug.com/1408520): opt-out affordance is not included in the origin
@@ -1230,18 +1229,17 @@
       WebContents::FromRenderFrameHost(&render_frame_host()),
       GetTopFrameOriginForDisplay(GetEmbeddingOrigin()), iframe_for_display,
       idp_data_for_display_,
-      auto_reauthn ? SignInMode::kAuto : SignInMode::kExplicit,
+      auto_reauthn_ ? SignInMode::kAuto : SignInMode::kExplicit,
       show_auto_reauthn_checkbox,
       base::BindOnce(&FederatedAuthRequestImpl::OnAccountSelected,
-                     weak_ptr_factory_.GetWeakPtr(), auto_reauthn),
+                     weak_ptr_factory_.GetWeakPtr()),
       base::BindOnce(&FederatedAuthRequestImpl::OnDialogDismissed,
                      weak_ptr_factory_.GetWeakPtr()));
-  devtools_instrumentation::OnFedCmAccountsDialogShown(&render_frame_host(),
-                                                       auto_reauthn);
+  devtools_instrumentation::OnFedCmAccountsDialogShown(&render_frame_host());
 
   if (auto_reauthn_enabled) {
     fedcm_metrics_->RecordAutoReauthnMetrics(
-        has_single_returning_account, auto_reauthn_account, auto_reauthn,
+        has_single_returning_account, auto_reauthn_account, auto_reauthn_,
         !is_auto_reauthn_setting_enabled, is_auto_reauthn_embargoed,
         time_from_embargo, requires_user_mediation);
   }
@@ -1482,8 +1480,7 @@
   });
 }
 
-void FederatedAuthRequestImpl::OnAccountSelected(bool auto_reauthn,
-                                                 const GURL& idp_config_url,
+void FederatedAuthRequestImpl::OnAccountSelected(const GURL& idp_config_url,
                                                  const std::string& account_id,
                                                  bool is_sign_in) {
   DCHECK(!account_id.empty());
@@ -1503,7 +1500,7 @@
     return;
   }
 
-  if (auto_reauthn) {
+  if (auto_reauthn_) {
     // Embargo auto re-authn to mitigate a deadloop where an auto
     // re-authenticated user gets auto re-authenticated again soon after logging
     // out of the active session.
@@ -2024,7 +2021,7 @@
     const IdentityRequestAccount& account) {
   bool is_sign_in =
       account.login_state == IdentityRequestAccount::LoginState::kSignIn;
-  OnAccountSelected(/*auto_reauthn=*/false, config_url, account.id, is_sign_in);
+  OnAccountSelected(config_url, account.id, is_sign_in);
 }
 
 void FederatedAuthRequestImpl::DismissAccountsDialogForDevtools(
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 5bb8cc07..31d69f89 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -141,6 +141,8 @@
     return idp_data_for_display_;
   }
 
+  bool IsAutoReauthn() { return auto_reauthn_; }
+
   void AcceptAccountsDialogForDevtools(const GURL& config_url,
                                        const IdentityRequestAccount& account);
   void DismissAccountsDialogForDevtools(bool should_embargo);
@@ -221,8 +223,7 @@
       std::unique_ptr<IdentityProviderInfo> idp_info,
       IdpNetworkRequestManager::FetchStatus status,
       IdpNetworkRequestManager::AccountList accounts);
-  void OnAccountSelected(bool auto_reauthn,
-                         const GURL& idp_config_url,
+  void OnAccountSelected(const GURL& idp_config_url,
                          const std::string& account_id,
                          bool is_sign_in);
   void OnDismissFailureDialog(
@@ -393,6 +394,8 @@
   // the navigator.credentials.get call.
   std::vector<GURL> idp_order_;
 
+  // Auto re-authentication.
+  bool auto_reauthn_{false};
   MediationRequirement mediation_requirement_;
 
   std::unique_ptr<MDocProvider> mdoc_provider_;
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebAuthenticationDelegate.java b/content/public/android/java/src/org/chromium/content_public/browser/WebAuthenticationDelegate.java
index 9803cee8..67581c7 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebAuthenticationDelegate.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebAuthenticationDelegate.java
@@ -8,15 +8,11 @@
 import android.content.Intent;
 import android.util.Pair;
 
-import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
 import org.chromium.base.annotations.NativeMethods;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Reflects the Android parts of the C++ class <code>WebAuthenticationDelegate</code>.
  * <p>
@@ -57,39 +53,9 @@
         return WebAuthenticationDelegateJni.get().getIntentSender(mNativeDelegate, webContents);
     }
 
-    /**
-     * Enumerates the different level of WebAuthn support in an Android app.
-     */
-    @IntDef({Support.NONE, Support.APP, Support.BROWSER})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Support {
-        /**
-         * WebAuthn is disabled. The <code>PublicKeyCredential</code> will be missing in Javascript
-         * so that sites can detect that there's no WebAuthn support.
-         */
-        int NONE = 0;
-        /**
-         * The unprivileged Play Services API will be used. This requires that the app be listed in
-         * the Asset Links for the RP ID that it is trying to assert / register for.
-         */
-        int APP = 1;
-        /**
-         * The privileged Play Services API will be used which allows any RP ID.
-         */
-        int BROWSER = 2;
-    }
-
-    /**
-     * @return the current WebAuthn support level for the given {@link WebContents}.
-     */
-    public @Support int getSupportLevel(WebContents webContents) {
-        return WebAuthenticationDelegateJni.get().getSupportLevel(mNativeDelegate, webContents);
-    }
-
     @NativeMethods
     interface Natives {
         long getNativeDelegate();
         IntentSender getIntentSender(long delegatePtr, WebContents webContents);
-        int getSupportLevel(long delegatePtr, WebContents webContents);
     }
 }
diff --git a/content/public/browser/authenticator_request_client_delegate.cc b/content/public/browser/authenticator_request_client_delegate.cc
index a4ac206e..aef7babe 100644
--- a/content/public/browser/authenticator_request_client_delegate.cc
+++ b/content/public/browser/authenticator_request_client_delegate.cc
@@ -121,10 +121,6 @@
 WebAuthenticationDelegate::GetIntentSender(WebContents* web_contents) {
   return nullptr;
 }
-
-int WebAuthenticationDelegate::GetSupportLevel(WebContents* web_contents) {
-  return 2 /* browser-like support */;
-}
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index ec69693..c9e001c 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -169,19 +169,6 @@
   // WebAuthenticationDelegate.java. See the comments in that file for details.
   virtual base::android::ScopedJavaLocalRef<jobject> GetIntentSender(
       WebContents* web_contents);
-
-  // GetSupportLevel returns one of:
-  //   0 -> No WebAuthn support for this `WebContents`.
-  //   1 -> WebAuthn should be implemented like an app.
-  //   2 -> WebAuthn should be implemented like a browser.
-  //
-  // The difference between app and browser is meaningful on Android because
-  // there is a different, privileged interface for browsers.
-  //
-  // The return value is an `int` rather than an enum because it's bounced
-  // access JNI boundaries multiple times and so it's only converted to an
-  // enum at the very end.
-  virtual int GetSupportLevel(WebContents* web_contents);
 #endif
 };
 
diff --git a/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2 b/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2
index 42c4926..e100ba4c 100644
--- a/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2
+++ b/content/shell/android/browsertests_apk/AndroidManifest.xml.jinja2
@@ -23,7 +23,8 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
     <application android:name="ContentBrowserTestsApplication"
-            android:label="ContentBrowserTests">
+            android:label="ContentBrowserTests"
+            android:memtagMode="sync">
         <activity android:name="ContentBrowserTestsActivity"
                   android:launchMode="singleTask"
                   android:theme="@android:style/Theme.Holo.Light.NoActionBar"
diff --git a/content/test/data/private_aggregation/aggregatable_report_goldens/README.md b/content/test/data/private_aggregation/aggregatable_report_goldens/README.md
index b882729..847c86c 100644
--- a/content/test/data/private_aggregation/aggregatable_report_goldens/README.md
+++ b/content/test/data/private_aggregation/aggregatable_report_goldens/README.md
@@ -33,9 +33,10 @@
 | "`0.1`" | [link](https://github.com/patcg-individual-drafts/private-aggregation-api) | [link](https://chromium.googlesource.com/chromium/src/+/57a65e032513965829e3ed1c1cd20b39d63d2224/content/browser/aggregation_service/payload_encryption.md) | n/a (initial release).
 
 ## Golden Report Descriptions
-1. Debug report, 1 contribution, FLEDGE API.
-2. Non-debug report, 1 contribution, FLEDGE API.
+
+1. Debug report, 1 contribution, Protected Audience API.
+2. Non-debug report, 1 contribution, Protected Audience API.
 3. Debug report, 2 contributions, Shared Storage API.
 4. Non-debug report, 2 contributions, Shared Storage API.
-5. Debug report, key with extreme value, FLEDGE API.
-6. Non-debug report, key with extreme value, FLEDGE API.
+5. Debug report, key with extreme value, Protected Audience API.
+6. Non-debug report, key with extreme value, Protected Audience API.
diff --git a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_1.json b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_1.json
index bf39ac0..1f2ab8a 100644
--- a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_1.json
+++ b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_1.json
@@ -1,9 +1,11 @@
 {
-    "aggregation_service_payloads": [ {
+  "aggregation_service_payloads": [
+    {
       "debug_cleartext_payload": "omRkYXRhgaJldmFsdWVEAAAAAmZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAWlvcGVyYXRpb25paGlzdG9ncmFt",
       "key_id": "example_id",
-      "payload": "kcFA9AlwpR+WlUL4w6SvIIFtouGNaYmILbMzJH2wRi5x1A8s0ok1ne8Hqmz7AcBYMQoIO216FlCdq6/mIZU2xraDdRvEKp5IQH1iRGQ8pHyqPlQrmwlvMcVOXhOFThcIm4ATeJ5HrsHs785k30uz"
-    } ],
-    "debug_key": "123",
-    "shared_info": "{\"api\":\"fledge\",\"debug_mode\":\"enabled\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
+      "payload": "CshrMlKt5qi4LljJMFTO8L5vfsduXqdoIwwByyjETxwfeefmN5kJQ/ohJ5onjKYEO/RkrJtlMTWuUS1Argbhv9HzRRysKoEnwwW5oDnRi/Mc5QKD5BsKHir0v1awW91x0hEuPk5cNx9Qqv3t2HmU"
+    }
+  ],
+  "debug_key": "123",
+  "shared_info": "{\"api\":\"protected-audience\",\"debug_mode\":\"enabled\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
 }
diff --git a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_2.json b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_2.json
index d2b0afd0d..a0664c3ad 100644
--- a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_2.json
+++ b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_2.json
@@ -1,7 +1,9 @@
 {
-    "aggregation_service_payloads": [ {
+  "aggregation_service_payloads": [
+    {
       "key_id": "example_id",
-      "payload": "VpAiKW5LqtNgWx8yDZhIzUes2v78bFqQiw5WLcaG9ggZ7Ge+uF41uOy3m4tmO8H/aUoH59VZngk7030FJdVS4xfwZQ/18Y3rTx9oe/vf83QI0LNVa9/y3nt+omXT9xA5pEwcZtSEy48U69huzXwl"
-    } ],
-    "shared_info": "{\"api\":\"fledge\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
+      "payload": "80hdtgdoBc0ZtAX0ASe/SY50cPImwXjAFLA34f5uAyt9xQksAq1zOgneWeof4mzCCL5BABhCpnTXYBRE90q1FKdI9xUIKj2oXqWJ9mm3PRTM6IajiF2Tpq8XGARpN7i48Bcz0xpYcS/9elUoYJie"
+    }
+  ],
+  "shared_info": "{\"api\":\"protected-audience\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
 }
\ No newline at end of file
diff --git a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_5.json b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_5.json
index 13c7155..04de674a 100644
--- a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_5.json
+++ b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_5.json
@@ -1,9 +1,11 @@
 {
-    "aggregation_service_payloads": [ {
-       "debug_cleartext_payload": "omRkYXRhgaJldmFsdWVEAAAAAmZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAWlvcGVyYXRpb25paGlzdG9ncmFt",
-       "key_id": "example_id",
-       "payload": "b00u1M3jTVc3XcZKfoAQa+67NxaxVx7vkl1DhEJUKFP2f4f0Hy2mo5SXh2txWTxkUoTBqkbm2knRnTjHMl+wC1NW92QbjkCQ9ydHBd0uWyP9hMkoN4yFr3/oP8dbK7GlbmFG1pBGZBFnQbrwU7G2"
-    } ],
-    "debug_key": "123",
-    "shared_info": "{\"api\":\"fledge\",\"debug_mode\":\"enabled\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
+  "aggregation_service_payloads": [
+    {
+      "debug_cleartext_payload": "omRkYXRhgaJldmFsdWVEAAAAAmZidWNrZXRQAAAAAAAAAAAAAAAAAAAAAWlvcGVyYXRpb25paGlzdG9ncmFt",
+      "key_id": "example_id",
+      "payload": "sJx01QvqJkpGVbphrC+OucTkOrO4k7iZXXd+RCWwpADK1HwyvucIPJjERqQJzR+uIeWXNMl/pViPKIM8W09ecs0Hw9POeeIVY4U/fNCWpFDl5BBr47eBOnNPeuG/eQNHtzS/RhKbCHBLm6QontSv"
+    }
+  ],
+  "debug_key": "123",
+  "shared_info": "{\"api\":\"protected-audience\",\"debug_mode\":\"enabled\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
 }
diff --git a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_6.json b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_6.json
index 07762f7..2514d6d5 100644
--- a/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_6.json
+++ b/content/test/data/private_aggregation/aggregatable_report_goldens/latest/report_6.json
@@ -1,7 +1,9 @@
 {
-    "aggregation_service_payloads": [ {
-       "key_id": "example_id",
-       "payload": "IxviV/VY/k2Gl8jbZmyuJiW0ujqXSyLHpqkTZ7Wp+UafGxxA8kFsb18me6EXfcLRzltE6Z/TT34t6ruuYYKW12VEpUN2PAmhBcexa3C0036Xn7JWR98JG32Y4sOMVh/rG/2mcxrYxJuIQa9kqIYh"
-    } ],
-    "shared_info": "{\"api\":\"fledge\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
+  "aggregation_service_payloads": [
+    {
+      "key_id": "example_id",
+      "payload": "vc3z+BWEMwcQ+DYITl0b4a1u+pZwk9nKlWM9lDRjEH4wJB/kOecjWq0buTgh7I3RrTQ2nt3EX7qDdT2Pn9y5nffPLPj5VfrQmhQReXEaXgJ9iuIi/OeENgt2JxDom98V5IYzpQVTx+cer2+3jEPn"
+    }
+  ],
+  "shared_info": "{\"api\":\"protected-audience\",\"report_id\":\"21abd97f-73e8-4b88-9389-a9fee6abda5e\",\"reporting_origin\":\"https://report.test\",\"scheduled_report_time\":\"1234486400\",\"version\":\"0.1\"}"
 }
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index abb1263..a31cd81 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -605,7 +605,7 @@
 crbug.com/1288590 [ android android-pixel-6 ] conformance/misc/shader-precision-format.html [ Failure ]
 crbug.com/1288603 [ android android-pixel-6 passthrough angle-opengles ] conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Failure ]
 crbug.com/1357064 [ android android-pixel-6 no-passthrough ] conformance/rendering/blending.html [ Failure ]
-
+crbug.com/1448093 [ android android-pixel-6 passthrough angle-opengles ] conformance/glsl/bugs/in-parameter-passed-as-inout-argument-and-global.html [ Failure ]
 
 ## Misc failures ##
 
diff --git a/device/fido/cable/fido_ble_transaction.h b/device/fido/cable/fido_ble_transaction.h
index 72a0249..3c9e20e 100644
--- a/device/fido/cable/fido_ble_transaction.h
+++ b/device/fido/cable/fido_ble_transaction.h
@@ -10,7 +10,7 @@
 
 #include "base/component_export.h"
 #include "base/containers/queue.h"
-#include "base/memory/raw_ptr_exclusion.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "device/fido/cable/fido_ble_frames.h"
@@ -50,9 +50,7 @@
   void StopTimeout();
   void OnError(absl::optional<FidoBleFrame> response_frame);
 
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #union
-  RAW_PTR_EXCLUSION FidoBleConnection* connection_;
+  raw_ptr<FidoBleConnection> connection_;
   uint16_t control_point_length_;
 
   absl::optional<FidoBleFrame> request_frame_;
diff --git a/docs/clang_format.md b/docs/clang_format.md
index 3245a81..e00123e 100644
--- a/docs/clang_format.md
+++ b/docs/clang_format.md
@@ -27,7 +27,7 @@
 Many developers find it useful to integrate the clang-format tool with their
 editor of choice. As a convenience, the scripts for this are also available in
 your checkout of Chrome under
-[src/buildtools/clang_format/script/](https://source.chromium.org/chromium/chromium/src/+/HEAD:buildtools/clang_format/script/).
+[src/third_party/clang_format/script/](https://source.chromium.org/chromium/chromium/src/+/HEAD:third_party/clang_format/script/).
 
 If you use an editor integration, you should try to make sure that you're using
 the version of clang-format that comes with your checkout. That way, you'll
diff --git a/docs/dangling_ptr_guide.md b/docs/dangling_ptr_guide.md
index dc0bcfa..40244679 100644
--- a/docs/dangling_ptr_guide.md
+++ b/docs/dangling_ptr_guide.md
@@ -73,6 +73,15 @@
 appear last in the class, since the unowned members often refer to resources
 owned by the owning members or the class itself.
 
+One sub-category of destruction order issues is related to `KeyedService`s which
+need to correctly
+[declare their dependencies](https://source.chromium.org/chromium/chromium/src/+/main:components/keyed_service/core/keyed_service_base_factory.h;l=60-62;drc=8ba1bad80dc22235693a0dd41fe55c0fd2dbdabd)
+and
+[are expected to drop references](https://source.chromium.org/chromium/chromium/src/+/main:components/keyed_service/core/keyed_service.h;l=12-13;drc=8ba1bad80dc22235693a0dd41fe55c0fd2dbdabd)
+to their dependencies in their
+[`Shutdown`](https://source.chromium.org/chromium/chromium/src/+/main:components/keyed_service/core/keyed_service.h;l=36-39;drc=8ba1bad80dc22235693a0dd41fe55c0fd2dbdabd) method
+(i.e. before their destructor runs).
+
 #### Observer callback
 
 This represents ~4% of the dangling pointers.
diff --git a/docs/emacs.md b/docs/emacs.md
index 34efba8..c13220a 100644
--- a/docs/emacs.md
+++ b/docs/emacs.md
@@ -160,7 +160,7 @@
 which adds c-mode formatting. Then add to your .emacs:
 
 ```el
-(load "/<path/to/chromium>/src/buildtools/clang_format/script/clang-format.el")
+(load "/<path/to/chromium>/src/third_party/clang_format/script/clang-format.el")
 (add-hook 'c-mode-common-hook
     (function (lambda () (local-set-key (kbd "TAB") 'clang-format-region))))
 ```
diff --git a/docs/sublime_ide.md b/docs/sublime_ide.md
index e8ec1d9..518c470 100644
--- a/docs/sublime_ide.md
+++ b/docs/sublime_ide.md
@@ -234,7 +234,7 @@
 
     ```shell
     cd /path/to/chromium/src
-    cp buildtools/clang_format/script/clang-format-sublime.py ~/.config/sublime-text-3/Packages/User/
+    cp third_party/clang_format/script/clang-format-sublime.py ~/.config/sublime-text-3/Packages/User/
     ```
 
 1. This installs a plugin that defines the command "clang\_format". You can add
diff --git a/extensions/browser/extension_registrar.cc b/extensions/browser/extension_registrar.cc
index 4d5416f0..5d176458 100644
--- a/extensions/browser/extension_registrar.cc
+++ b/extensions/browser/extension_registrar.cc
@@ -54,6 +54,12 @@
 
 ExtensionRegistrar::~ExtensionRegistrar() = default;
 
+void ExtensionRegistrar::Shutdown() {
+  // Setting to `nullptr`, because this raw pointer may become dangling once
+  // the `ExtensionSystem` keyed service is destroyed.
+  extension_system_ = nullptr;
+}
+
 void ExtensionRegistrar::AddExtension(
     scoped_refptr<const Extension> extension) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/extensions/browser/extension_registrar.h b/extensions/browser/extension_registrar.h
index 554505d..463d9a5 100644
--- a/extensions/browser/extension_registrar.h
+++ b/extensions/browser/extension_registrar.h
@@ -97,6 +97,9 @@
 
   ~ExtensionRegistrar() override;
 
+  // Called when the associated Profile is going to be destroyed.
+  void Shutdown();
+
   // Adds the extension to the ExtensionRegistry. The extension will be added to
   // the enabled, disabled, blocklisted or blocked set. If the extension is
   // added as enabled, it will be activated.
@@ -186,7 +189,7 @@
   const raw_ptr<Delegate> delegate_;
 
   // Keyed services we depend on. Cached here for repeated access.
-  const raw_ptr<ExtensionSystem, DanglingUntriaged> extension_system_;
+  raw_ptr<ExtensionSystem> extension_system_;
   const raw_ptr<ExtensionPrefs> extension_prefs_;
   const raw_ptr<ExtensionRegistry> registry_;
   const raw_ptr<RendererStartupHelper> renderer_helper_;
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index 3179aae..ee830de 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -254,8 +254,8 @@
     "shared_image/shared_image_backing_factory.h",
     "shared_image/shared_image_factory.cc",
     "shared_image/shared_image_factory.h",
-    "shared_image/shared_image_format_utils.cc",
-    "shared_image/shared_image_format_utils.h",
+    "shared_image/shared_image_format_service_utils.cc",
+    "shared_image/shared_image_format_service_utils.h",
     "shared_image/shared_image_manager.cc",
     "shared_image/shared_image_manager.h",
     "shared_image/shared_image_representation.cc",
@@ -491,7 +491,7 @@
       "shared_image/iosurface_image_backing.mm",
       "shared_image/iosurface_image_backing_factory.h",
       "shared_image/iosurface_image_backing_factory.mm",
-      "shared_image/shared_image_format_utils_mac.mm",
+      "shared_image/shared_image_format_service_utils_mac.mm",
     ]
 
     if (skia_use_metal) {
diff --git a/gpu/command_buffer/service/ahardwarebuffer_utils.cc b/gpu/command_buffer/service/ahardwarebuffer_utils.cc
index fd0985d..d65070e 100644
--- a/gpu/command_buffer/service/ahardwarebuffer_utils.cc
+++ b/gpu/command_buffer/service/ahardwarebuffer_utils.cc
@@ -11,7 +11,7 @@
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/command_buffer/service/gl_utils.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/vulkan/vulkan_image.h"
 #include "ui/gfx/color_space.h"
diff --git a/gpu/command_buffer/service/copy_shared_image_helper.cc b/gpu/command_buffer/service/copy_shared_image_helper.cc
index ba470258..67d82534 100644
--- a/gpu/command_buffer/service/copy_shared_image_helper.cc
+++ b/gpu/command_buffer/service/copy_shared_image_helper.cc
@@ -13,7 +13,7 @@
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/config/gpu_finch_features.h"
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_textures.cc
index 13e7514..412987dd 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_textures.cc
@@ -6,7 +6,7 @@
 
 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
 #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/test_image_backing.h"
 
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index ff6fed42..bf124128 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -57,7 +57,7 @@
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/wrapped_sk_image_backing_factory.h"
 #include "gpu/command_buffer/service/skia_utils.h"
diff --git a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc
index 41046145..336d146 100644
--- a/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/ahardwarebuffer_image_backing_factory.cc
@@ -35,7 +35,7 @@
 #include "gpu/command_buffer/service/shared_image/gl_texture_android_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/gl_texture_passthrough_android_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/skia_vk_android_image_representation.h"
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
index 660314c5..fb8e5bc6 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
@@ -9,7 +9,7 @@
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_image/gl_texture_image_backing_helper.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory.cc
index a06ac73d..110cc2e 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory.cc
@@ -11,7 +11,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "ui/gl/gl_surface_egl.h"
 
diff --git a/gpu/command_buffer/service/shared_image/compound_image_backing.cc b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
index 9daf4c2..40076dd9 100644
--- a/gpu/command_buffer/service/shared_image/compound_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/compound_image_backing.cc
@@ -19,7 +19,7 @@
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/shared_memory_image_backing.h"
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index 1bc8557..d8c8777 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -14,7 +14,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
 #include "gpu/command_buffer/service/shared_image/d3d_image_representation.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 #include "third_party/libyuv/include/libyuv/planar_functions.h"
 #include "ui/gl/egl_util.h"
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
index 648d542..a15951c 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory.cc
@@ -13,7 +13,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
 #include "gpu/command_buffer/service/shared_image/d3d_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gl/direct_composition_support.h"
diff --git a/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc b/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
index 2ce845c..5fc1a08 100644
--- a/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/dawn_egl_image_representation.cc
@@ -5,7 +5,7 @@
 #include "gpu/command_buffer/service/shared_image/dawn_egl_image_representation.h"
 
 #include "build/build_config.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 
 #include <dawn/native/OpenGLBackend.h>
diff --git a/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc b/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
index 1285a7a..91a128d 100644
--- a/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.cc
@@ -10,7 +10,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "third_party/angle/include/EGL/egl.h"
 #include "third_party/angle/include/EGL/eglext.h"
 #include "third_party/angle/include/EGL/eglext_angle.h"
diff --git a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
index c6c7506..7d3d4e5 100644
--- a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.cc
@@ -20,7 +20,7 @@
 #include "gpu/command_buffer/service/shared_image/d3d_image_backing_factory.h"
 #include "gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 #include "third_party/skia/include/core/SkAlphaType.h"
diff --git a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc
index a4c29bce..3d6d870 100644
--- a/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_representation.cc
@@ -9,7 +9,7 @@
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
diff --git a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
index 2f019fc..aaa3884 100644
--- a/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/egl_image_backing_factory_unittest.cc
@@ -18,7 +18,7 @@
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/test_utils.h"
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
index 1f13993f..3a903a2 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
@@ -17,7 +17,7 @@
 #include "gpu/command_buffer/service/shared_image/external_vk_image_overlay_representation.h"
 #include "gpu/command_buffer/service/shared_image/external_vk_image_skia_representation.h"
 #include "gpu/command_buffer/service/shared_image/gl_texture_holder.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/ipc/common/vulkan_ycbcr_info.h"
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc
index 58f8e7a..faa9794 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory.cc
@@ -9,7 +9,7 @@
 #include "components/viz/common/resources/shared_image_format.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_image/external_vk_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/vulkan/vulkan_command_buffer.h"
 #include "gpu/vulkan/vulkan_command_pool.h"
diff --git a/gpu/command_buffer/service/shared_image/gl_common_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/gl_common_image_backing_factory.cc
index 419ee03..600d75dc 100644
--- a/gpu/command_buffer/service/shared_image/gl_common_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/gl_common_image_backing_factory.cc
@@ -10,7 +10,7 @@
 #include "components/viz/common/resources/shared_image_format.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/service/service_utils.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/config/gpu_preferences.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/size.h"
diff --git a/gpu/command_buffer/service/shared_image/gl_ozone_image_representation.cc b/gpu/command_buffer/service/shared_image/gl_ozone_image_representation.cc
index 6f27f13..1caf040 100644
--- a/gpu/command_buffer/service/shared_image/gl_ozone_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/gl_ozone_image_representation.cc
@@ -13,7 +13,7 @@
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/shared_image/ozone_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/texture_manager.h"
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_holder.h b/gpu/command_buffer/service/shared_image/gl_texture_holder.h
index 7a9a46b..0979c89 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_holder.h
+++ b/gpu/command_buffer/service/shared_image/gl_texture_holder.h
@@ -6,7 +6,7 @@
 #define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_GL_TEXTURE_HOLDER_H_
 
 #include "gpu/command_buffer/service/shared_image/gl_common_image_backing_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "ui/gl/progress_reporter.h"
 
 namespace gpu {
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
index 556a839..26170c3b 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing.cc
@@ -17,7 +17,7 @@
 #include "gpu/command_buffer/service/shared_image/gl_texture_image_backing_helper.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "third_party/skia/include/core/SkPromiseImageTexture.h"
diff --git a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
index dfc057c..a013f40 100644
--- a/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory_unittest.cc
@@ -18,7 +18,7 @@
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
index 06cc519..9d1fac6 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing.mm
@@ -14,7 +14,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/skia_graphite_dawn_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm
index 1b87ace..4883800 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory.mm
@@ -14,7 +14,7 @@
 #include "gpu/command_buffer/service/shared_image/iosurface_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
diff --git a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
index 3e98ce9..7bb605e5 100644
--- a/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/iosurface_image_backing_factory_unittest.cc
@@ -13,7 +13,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_test_base.h"
diff --git a/gpu/command_buffer/service/shared_image/ozone_image_backing.cc b/gpu/command_buffer/service/shared_image/ozone_image_backing.cc
index 24fe149d..da62ba6 100644
--- a/gpu/command_buffer/service/shared_image/ozone_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/ozone_image_backing.cc
@@ -18,7 +18,7 @@
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/gl_ozone_image_representation.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/skia_gl_image_representation.h"
diff --git a/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
index 9be4a66..5235882 100644
--- a/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/ozone_image_backing_factory.cc
@@ -17,7 +17,7 @@
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shared_image/ozone_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_memory_region_wrapper.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gfx/native_pixmap.h"
diff --git a/gpu/command_buffer/service/shared_image/pbuffer_image_backing.cc b/gpu/command_buffer/service/shared_image/pbuffer_image_backing.cc
index 901d530..37981660 100644
--- a/gpu/command_buffer/service/shared_image/pbuffer_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/pbuffer_image_backing.cc
@@ -8,7 +8,7 @@
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkPromiseImageTexture.h"
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.cc b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
index 11f4ca4..b5d18a7 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
@@ -23,7 +23,7 @@
 #include "gpu/command_buffer/service/shared_image/gl_texture_image_backing_factory.h"
 #include "gpu/command_buffer/service/shared_image/raw_draw_image_backing_factory.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_image/shared_memory_image_backing_factory.h"
diff --git a/gpu/command_buffer/service/shared_image/shared_image_format_utils.cc b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
similarity index 99%
rename from gpu/command_buffer/service/shared_image/shared_image_format_utils.cc
rename to gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
index 46eb8c2..3be75bd 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_format_utils.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
diff --git a/gpu/command_buffer/service/shared_image/shared_image_format_utils.h b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h
similarity index 98%
rename from gpu/command_buffer/service/shared_image/shared_image_format_utils.h
rename to gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h
index f50ea5c..e210b03e 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_format_utils.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_FORMAT_UTILS_H_
-#define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_FORMAT_UTILS_H_
+#ifndef GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_FORMAT_SERVICE_UTILS_H_
+#define GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_FORMAT_SERVICE_UTILS_H_
 
 #include "build/build_config.h"
 #include "components/viz/common/resources/shared_image_format.h"
@@ -122,4 +122,4 @@
 
 }  // namespace gpu
 
-#endif  // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_FORMAT_UTILS_H_
+#endif  // GPU_COMMAND_BUFFER_SERVICE_SHARED_IMAGE_SHARED_IMAGE_FORMAT_SERVICE_UTILS_H_
diff --git a/gpu/command_buffer/service/shared_image/shared_image_format_utils_mac.mm b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils_mac.mm
similarity index 97%
rename from gpu/command_buffer/service/shared_image/shared_image_format_utils_mac.mm
rename to gpu/command_buffer/service/shared_image/shared_image_format_service_utils_mac.mm
index eb62bd2..b20c70016 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_format_utils_mac.mm
+++ b/gpu/command_buffer/service/shared_image/shared_image_format_service_utils_mac.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 
 #include <Metal/MTLPixelFormat.h>
 
@@ -38,8 +38,9 @@
   }
 
   // Does not support external sampler.
-  if (format.PrefersExternalSampler())
+  if (format.PrefersExternalSampler()) {
     return static_cast<unsigned int>(MTLPixelFormatInvalid);
+  }
 
   // For multiplanar formats without external sampler, Metal formats are per
   // plane.
@@ -69,4 +70,4 @@
   return static_cast<unsigned int>(mtl_pixel_format);
 }
 
-}  // namespace gpu
\ No newline at end of file
+}  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/shared_image_representation.cc b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
index f36225e5..9de216c 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_representation.cc
@@ -9,7 +9,7 @@
 #include "build/build_config.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImage.h"
diff --git a/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc b/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc
index e76733e..af8b065 100644
--- a/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/shared_memory_image_backing.cc
@@ -13,7 +13,7 @@
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/shared_memory_region_wrapper.h"
diff --git a/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc b/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
index 616b918..efc6e254 100644
--- a/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image/skia_gl_image_representation.cc
@@ -8,7 +8,7 @@
 #include "base/memory/ptr_util.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
diff --git a/gpu/command_buffer/service/shared_image/test_image_backing.cc b/gpu/command_buffer/service/shared_image/test_image_backing.cc
index 22d6cca..52a02bee 100644
--- a/gpu/command_buffer/service/shared_image/test_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/test_image_backing.cc
@@ -6,7 +6,7 @@
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "skia/ext/legacy_display_globals.h"
 #include "third_party/skia/include/core/SkPromiseImageTexture.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
diff --git a/gpu/command_buffer/service/shared_image/wrapped_graphite_texture_backing.cc b/gpu/command_buffer/service/shared_image/wrapped_graphite_texture_backing.cc
index 43218b8..fdc31791 100644
--- a/gpu/command_buffer/service/shared_image/wrapped_graphite_texture_backing.cc
+++ b/gpu/command_buffer/service/shared_image/wrapped_graphite_texture_backing.cc
@@ -9,7 +9,7 @@
 #include "base/logging.h"
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkSurface.h"
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index 39146fc..59b0c274 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -38,7 +38,7 @@
 #include "gpu/command_buffer/service/isolation_key_provider.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
 #include "gpu/command_buffer/service/skia_utils.h"
diff --git a/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc b/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc
index f2bae7c..05266c0 100644
--- a/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc
+++ b/gpu/command_buffer/tests/webgpu_mailbox_unittest.cc
@@ -8,7 +8,7 @@
 #include "gpu/command_buffer/client/webgpu_implementation.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
-#include "gpu/command_buffer/service/shared_image/shared_image_format_utils.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
 #include "gpu/command_buffer/service/webgpu_decoder.h"
 #include "gpu/command_buffer/tests/webgpu_test.h"
 #include "gpu/config/gpu_test_config.h"
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index fda4814..07d41bb 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -551,12 +551,12 @@
           =1 {Bookmark saved to "{title}". It is only saved to this device.}
           other {Bookmarks saved to "{title}". It is only saved to this device.}}
       </message>
-      <message name="IDS_IOS_BOOKMARK_PAGE_SAVED_INTO_ACCOUNT" desc="Presented in a snackbar when bookmarks are created in an account. Specifies in which folder the bookmark was added. ">
+      <message name="IDS_IOS_BOOKMARK_PAGE_SAVED_INTO_ACCOUNT" desc="This confirmation message is intended to help users understand that a bookmark has been saved in their Google Account. The confirmation message appears at the bottom of the page after users choose to bookmark a page. The tone should be informative.">
           {count, plural,
           =1 {Bookmark saved in your Google Account, {email}}
           other {Bookmarks saved in your Google Account, {email}}}
       </message>
-      <message name="IDS_IOS_BOOKMARK_PAGE_SAVED_INTO_ACCOUNT_FOLDER" desc="Presented in a snackbar when bookmarks are created in the folder named `TITLE` in an account with `EMAIL`.">
+      <message name="IDS_IOS_BOOKMARK_PAGE_SAVED_INTO_ACCOUNT_FOLDER" desc="This confirmation message is intended to help users understand that a bookmark has been saved to a particular folder in their Google Account. The confirmation message appears at the bottom of the page after users choose to bookmark a page. The tone should be informative.">
          {count, plural,
          =1 {Bookmark saved to "{title}" in your account, {email}}
          other {Bookmarks saved to "{title}" in your account, {email}}}
diff --git a/ios/chrome/browser/bookmarks/account_bookmark_model_factory.cc b/ios/chrome/browser/bookmarks/account_bookmark_model_factory.cc
index 9a55a22f..db00dd7 100644
--- a/ios/chrome/browser/bookmarks/account_bookmark_model_factory.cc
+++ b/ios/chrome/browser/bookmarks/account_bookmark_model_factory.cc
@@ -42,7 +42,8 @@
           browser_state, /*managed_bookmark_service=*/nullptr,
           ios::AccountBookmarkSyncServiceFactory::GetForBrowserState(
               browser_state),
-          ios::BookmarkUndoServiceFactory::GetForBrowserState(browser_state))));
+          ios::BookmarkUndoServiceFactory::GetForBrowserState(browser_state),
+          bookmarks::StorageType::kAccount)));
   bookmark_model->Load(browser_state->GetStatePath(),
                        bookmarks::StorageType::kAccount);
   ios::BookmarkUndoServiceFactory::GetForBrowserState(browser_state)
diff --git a/ios/chrome/browser/bookmarks/bookmark_client_impl.cc b/ios/chrome/browser/bookmarks/bookmark_client_impl.cc
index 03c0857..d3ab416 100644
--- a/ios/chrome/browser/bookmarks/bookmark_client_impl.cc
+++ b/ios/chrome/browser/bookmarks/bookmark_client_impl.cc
@@ -24,11 +24,13 @@
     ChromeBrowserState* browser_state,
     bookmarks::ManagedBookmarkService* managed_bookmark_service,
     sync_bookmarks::BookmarkSyncService* bookmark_sync_service,
-    BookmarkUndoService* bookmark_undo_service)
+    BookmarkUndoService* bookmark_undo_service,
+    bookmarks::StorageType storage_type_for_uma)
     : browser_state_(browser_state),
       managed_bookmark_service_(managed_bookmark_service),
       bookmark_sync_service_(bookmark_sync_service),
-      bookmark_undo_service_(bookmark_undo_service) {}
+      bookmark_undo_service_(bookmark_undo_service),
+      storage_type_for_uma_(storage_type_for_uma) {}
 
 BookmarkClientImpl::~BookmarkClientImpl() {}
 
@@ -86,6 +88,19 @@
   return bookmarks::LoadManagedNodeCallback();
 }
 
+bookmarks::metrics::StorageStateForUma
+BookmarkClientImpl::GetStorageStateForUma() {
+  switch (storage_type_for_uma_) {
+    case bookmarks::StorageType::kAccount:
+      return bookmarks::metrics::StorageStateForUma::kAccount;
+    case bookmarks::StorageType::kLocalOrSyncable:
+      return bookmark_sync_service_->IsTrackingMetadata()
+                 ? bookmarks::metrics::StorageStateForUma::kSyncEnabled
+                 : bookmarks::metrics::StorageStateForUma::kLocalOnly;
+  }
+  NOTREACHED_NORETURN();
+}
+
 bool BookmarkClientImpl::CanSetPermanentNodeTitle(
     const bookmarks::BookmarkNode* permanent_node) {
   if (managed_bookmark_service_)
diff --git a/ios/chrome/browser/bookmarks/bookmark_client_impl.h b/ios/chrome/browser/bookmarks/bookmark_client_impl.h
index 3232f1b..6b2cfac 100644
--- a/ios/chrome/browser/bookmarks/bookmark_client_impl.h
+++ b/ios/chrome/browser/bookmarks/bookmark_client_impl.h
@@ -12,6 +12,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/task/deferred_sequenced_task_runner.h"
 #include "components/bookmarks/browser/bookmark_client.h"
+#include "components/bookmarks/common/storage_type.h"
 
 class BookmarkUndoService;
 class ChromeBrowserState;
@@ -32,7 +33,8 @@
       ChromeBrowserState* browser_state,
       bookmarks::ManagedBookmarkService* managed_bookmark_service,
       sync_bookmarks::BookmarkSyncService* bookmark_sync_service,
-      BookmarkUndoService* bookmark_undo_service);
+      BookmarkUndoService* bookmark_undo_service,
+      bookmarks::StorageType storage_type_for_uma);
 
   BookmarkClientImpl(const BookmarkClientImpl&) = delete;
   BookmarkClientImpl& operator=(const BookmarkClientImpl&) = delete;
@@ -50,6 +52,7 @@
   bool IsPermanentNodeVisibleWhenEmpty(
       bookmarks::BookmarkNode::Type type) override;
   bookmarks::LoadManagedNodeCallback GetLoadManagedNodeCallback() override;
+  bookmarks::metrics::StorageStateForUma GetStorageStateForUma() override;
   bool CanSetPermanentNodeTitle(
       const bookmarks::BookmarkNode* permanent_node) override;
   bool CanSyncNode(const bookmarks::BookmarkNode* node) override;
@@ -80,6 +83,8 @@
   // Pointer to BookmarkUndoService, responsible for making operations undoable.
   const raw_ptr<BookmarkUndoService> bookmark_undo_service_;
 
+  const bookmarks::StorageType storage_type_for_uma_;
+
   raw_ptr<bookmarks::BookmarkModel> model_ = nullptr;
 };
 
diff --git a/ios/chrome/browser/bookmarks/local_or_syncable_bookmark_model_factory.cc b/ios/chrome/browser/bookmarks/local_or_syncable_bookmark_model_factory.cc
index 8557b786..45178b0a 100644
--- a/ios/chrome/browser/bookmarks/local_or_syncable_bookmark_model_factory.cc
+++ b/ios/chrome/browser/bookmarks/local_or_syncable_bookmark_model_factory.cc
@@ -37,7 +37,8 @@
       ManagedBookmarkServiceFactory::GetForBrowserState(browser_state),
       ios::LocalOrSyncableBookmarkSyncServiceFactory::GetForBrowserState(
           browser_state),
-      ios::BookmarkUndoServiceFactory::GetForBrowserState(browser_state)));
+      ios::BookmarkUndoServiceFactory::GetForBrowserState(browser_state),
+      bookmarks::StorageType::kLocalOrSyncable));
   bookmark_model->Load(browser_state->GetStatePath(),
                        bookmarks::StorageType::kLocalOrSyncable);
   ios::BookmarkUndoServiceFactory::GetForBrowserState(browser_state)
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index d49b82d..454c55c 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -471,11 +471,38 @@
      std::size(kEnablePinnedTabsOverflow), nullptr},
 };
 
-const FeatureEntry::FeatureParam kAutofillBrandingIOSMonotone[] = {
-    {autofill::features::kAutofillBrandingIOSParam, "true"}};
+const FeatureEntry::FeatureParam
+    kAutofillBrandingIOSDismissWhenInteractedNoAnimation[] = {
+        {autofill::features::kAutofillBrandingIOSParamFrequencyTypePhone,
+         autofill::features::
+             kAutofillBrandingIOSParamFrequencyTypeDismissWhenInteracted},
+        {autofill::features::kAutofillBrandingIOSParamFrequencyTypeTablet,
+         autofill::features::
+             kAutofillBrandingIOSParamFrequencyTypeDismissWhenInteracted}};
+const FeatureEntry::FeatureParam kAutofillBrandingIOSAlwaysShowAndDismiss[] = {
+    {autofill::features::kAutofillBrandingIOSParamFrequencyTypePhone,
+     autofill::features::
+         kAutofillBrandingIOSParamFrequencyTypeAlwaysShowAndDismiss},
+    {autofill::features::kAutofillBrandingIOSParamFrequencyTypeTablet,
+     autofill::features::kAutofillBrandingIOSParamFrequencyTypeAlways}};
+const FeatureEntry::FeatureParam
+    kAutofillBrandingIOSDismissWhenInteractedWithAnimation[] = {
+        {autofill::features::kAutofillBrandingIOSParamFrequencyTypePhone,
+         autofill::features::
+             kAutofillBrandingIOSParamFrequencyTypeDismissWhenInteracted},
+        {autofill::features::kAutofillBrandingIOSParamFrequencyTypeTablet,
+         autofill::features::kAutofillBrandingIOSParamFrequencyTypeAlways}};
 const FeatureEntry::FeatureVariation kAutofillBrandingIOSVariations[] = {
-    {"(Monotone)", kAutofillBrandingIOSMonotone,
-     std::size(kAutofillBrandingIOSMonotone), nullptr}};
+    {"(will not show again after user interacts with keyboard accessories)",
+     kAutofillBrandingIOSDismissWhenInteractedNoAnimation,
+     std::size(kAutofillBrandingIOSDismissWhenInteractedNoAnimation), nullptr},
+    {"(shows and fades to the left every time)",
+     kAutofillBrandingIOSAlwaysShowAndDismiss,
+     std::size(kAutofillBrandingIOSAlwaysShowAndDismiss), nullptr},
+    {"(fades to the left after user interacts with keyboard accessories)",
+     kAutofillBrandingIOSDismissWhenInteractedWithAnimation,
+     std::size(kAutofillBrandingIOSDismissWhenInteractedWithAnimation),
+     nullptr}};
 
 const FeatureEntry::FeatureParam kIOSNewPostRestoreExperienceMinimal[] = {
     {post_restore_signin::features::kIOSNewPostRestoreExperienceParam, "true"}};
@@ -575,6 +602,11 @@
 const FeatureEntry::FeatureParam
     kCredentialProviderExtensionPromoOnLoginWithAutofill[] = {
         {kCredentialProviderExtensionPromoOnLoginWithAutofillParam, "true"}};
+const FeatureEntry::FeatureParam
+    kCredentialProviderExtensionPromoOnAllTriggers[] = {
+        {kCredentialProviderExtensionPromoOnLoginWithAutofillParam, "true"},
+        {kCredentialProviderExtensionPromoOnPasswordCopiedParam, "true"},
+        {kCredentialProviderExtensionPromoOnPasswordSavedParam, "true"}};
 
 const FeatureEntry::FeatureVariation
     kCredentialProviderExtensionPromoVariations[] = {
@@ -587,6 +619,8 @@
          kCredentialProviderExtensionPromoOnLoginWithAutofill,
          std::size(kCredentialProviderExtensionPromoOnLoginWithAutofill),
          nullptr},
+        {"On all triggers", kCredentialProviderExtensionPromoOnAllTriggers,
+         std::size(kCredentialProviderExtensionPromoOnAllTriggers), nullptr},
 };
 
 const FeatureEntry::FeatureParam kIOSEditMenuPartialTranslateNoIncognito[] = {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index ffc35bdb..01c262c 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -33,8 +33,9 @@
 
 const char kAutofillBrandingIOSName[] = "Autofill Branding on iOS";
 const char kAutofillBrandingIOSDescription[] =
-    "Adds the Chrome logo in the form input suggestions bar. Full color by "
-    "default.";
+    "Adds the Chrome logo in the form input suggestions bar. If select "
+    "\"Enabled\", the branding logo shows twice, and would not be "
+    "dismissed with any animation.";
 
 const char kAutofillCreditCardUploadName[] =
     "Offers uploading Autofilled credit cards";
diff --git a/ios/chrome/browser/ntp/BUILD.gn b/ios/chrome/browser/ntp/BUILD.gn
index a0d8533f..382e5eb 100644
--- a/ios/chrome/browser/ntp/BUILD.gn
+++ b/ios/chrome/browser/ntp/BUILD.gn
@@ -71,6 +71,7 @@
   deps = [
     ":features",
     ":set_up_list_item_type",
+    ":set_up_list_metrics",
     ":set_up_list_prefs",
     "//base",
     "//components/password_manager/core/browser",
@@ -86,6 +87,18 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
+source_set("set_up_list_metrics") {
+  sources = [
+    "set_up_list_metrics.h",
+    "set_up_list_metrics.mm",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+  deps = [
+    ":set_up_list_item_type",
+    "//base",
+  ]
+}
+
 source_set("set_up_list_prefs") {
   sources = [
     "set_up_list_prefs.h",
@@ -94,6 +107,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
     ":set_up_list_item_type",
+    ":set_up_list_metrics",
     "//base",
     "//components/pref_registry",
     "//components/prefs",
diff --git a/ios/chrome/browser/ntp/set_up_list.mm b/ios/chrome/browser/ntp/set_up_list.mm
index 78656fa..5c27858 100644
--- a/ios/chrome/browser/ntp/set_up_list.mm
+++ b/ios/chrome/browser/ntp/set_up_list.mm
@@ -11,6 +11,7 @@
 #import "ios/chrome/browser/ntp/set_up_list_delegate.h"
 #import "ios/chrome/browser/ntp/set_up_list_item.h"
 #import "ios/chrome/browser/ntp/set_up_list_item_type.h"
+#import "ios/chrome/browser/ntp/set_up_list_metrics.h"
 #import "ios/chrome/browser/ntp/set_up_list_prefs.h"
 #import "ios/chrome/browser/signin/authentication_service.h"
 
@@ -53,6 +54,9 @@
       new_state = complete ? SetUpListItemState::kCompleteNotInList
                            : SetUpListItemState::kNotComplete;
       set_up_list_prefs::SetItemState(local_state, type, new_state);
+      if (complete) {
+        set_up_list_metrics::RecordItemCompleted(type);
+      }
       return [[SetUpListItem alloc] initWithType:type complete:complete];
     case SetUpListItemState::kCompleteInList:
       // Display in list this time, but remove from list next time.
diff --git a/ios/chrome/browser/ntp/set_up_list_item_type.h b/ios/chrome/browser/ntp/set_up_list_item_type.h
index 79218e6..d558add 100644
--- a/ios/chrome/browser/ntp/set_up_list_item_type.h
+++ b/ios/chrome/browser/ntp/set_up_list_item_type.h
@@ -5,7 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_NTP_SET_UP_LIST_ITEM_TYPE_H_
 #define IOS_CHROME_BROWSER_NTP_SET_UP_LIST_ITEM_TYPE_H_
 
-// The possible types of items for the Set Up List.
+// The possible types of items for the Set Up List. This enum must match the
+// UMA histogram enum IOSSetUpListItemType.
 enum class SetUpListItemType {
   kSignInSync = 1,
   kDefaultBrowser = 2,
diff --git a/ios/chrome/browser/ntp/set_up_list_metrics.h b/ios/chrome/browser/ntp/set_up_list_metrics.h
new file mode 100644
index 0000000..f6bbe99
--- /dev/null
+++ b/ios/chrome/browser/ntp/set_up_list_metrics.h
@@ -0,0 +1,26 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_NTP_SET_UP_LIST_METRICS_H_
+#define IOS_CHROME_BROWSER_NTP_SET_UP_LIST_METRICS_H_
+
+enum class SetUpListItemType;
+
+namespace set_up_list_metrics {
+
+// Records that the Set Up List was displayed.
+void RecordDisplayed();
+
+// Records that the Set Up List item of the given `type` was displayed.
+void RecordItemDisplayed(SetUpListItemType type);
+
+// Records that a Set Up List item was selected.
+void RecordItemSelected(SetUpListItemType type);
+
+// Records that a Set Up List item was completed.
+void RecordItemCompleted(SetUpListItemType type);
+
+}  // namespace set_up_list_metrics
+
+#endif  // IOS_CHROME_BROWSER_NTP_SET_UP_LIST_METRICS_H_
diff --git a/ios/chrome/browser/ntp/set_up_list_metrics.mm b/ios/chrome/browser/ntp/set_up_list_metrics.mm
new file mode 100644
index 0000000..b4399b1
--- /dev/null
+++ b/ios/chrome/browser/ntp/set_up_list_metrics.mm
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ntp/set_up_list_metrics.h"
+
+#import "base/metrics/histogram_functions.h"
+#import "ios/chrome/browser/ntp/set_up_list_item_type.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace set_up_list_metrics {
+
+void RecordDisplayed() {
+  base::UmaHistogramBoolean("IOS.SetUpList.Displayed", true);
+}
+
+void RecordItemDisplayed(SetUpListItemType type) {
+  base::UmaHistogramEnumeration("IOS.SetUpList.ItemDisplayed", type);
+}
+
+void RecordItemSelected(SetUpListItemType type) {
+  base::UmaHistogramEnumeration("IOS.SetUpList.ItemSelected", type);
+}
+
+void RecordItemCompleted(SetUpListItemType type) {
+  base::UmaHistogramEnumeration("IOS.SetUpList.ItemCompleted", type);
+}
+
+}  // namespace set_up_list_metrics
diff --git a/ios/chrome/browser/ntp/set_up_list_prefs.mm b/ios/chrome/browser/ntp/set_up_list_prefs.mm
index bcb59f3..49b4df1c 100644
--- a/ios/chrome/browser/ntp/set_up_list_prefs.mm
+++ b/ios/chrome/browser/ntp/set_up_list_prefs.mm
@@ -7,6 +7,7 @@
 #import "components/prefs/pref_registry_simple.h"
 #import "components/prefs/pref_service.h"
 #import "ios/chrome/browser/ntp/set_up_list_item_type.h"
+#import "ios/chrome/browser/ntp/set_up_list_metrics.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -57,11 +58,17 @@
 
 void MarkItemComplete(PrefService* prefs, SetUpListItemType type) {
   // If it is already completed and already removed from list, skip setting.
-  if (GetItemState(prefs, type) == SetUpListItemState::kCompleteNotInList) {
-    return;
+  switch (GetItemState(prefs, type)) {
+    case SetUpListItemState::kUnknown:
+    case SetUpListItemState::kNotComplete:
+      set_up_list_metrics::RecordItemCompleted(type);
+      SetItemState(prefs, type, SetUpListItemState::kCompleteInList);
+      break;
+    case SetUpListItemState::kCompleteInList:
+    case SetUpListItemState::kCompleteNotInList:
+      // Already complete, so there is nothing to do.
+      break;
   }
-
-  SetItemState(prefs, type, SetUpListItemState::kCompleteInList);
 }
 
 bool IsSetUpListDisabled(PrefService* prefs) {
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index a669288a..f06d5a0 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -257,7 +257,10 @@
     "features.cc",
     "features.h",
   ]
-  public_deps = [ "//base" ]
+  public_deps = [
+    "//base",
+    "//ui/base",
+  ]
 }
 
 source_set("unit_tests") {
diff --git a/ios/chrome/browser/ui/autofill/features.cc b/ios/chrome/browser/ui/autofill/features.cc
index 4f1f4d55..3ae58bc 100644
--- a/ios/chrome/browser/ui/autofill/features.cc
+++ b/ios/chrome/browser/ui/autofill/features.cc
@@ -4,22 +4,74 @@
 
 #import "ios/chrome/browser/ui/autofill/features.h"
 #import "base/metrics/field_trial_params.h"
+#import "ui/base/device_form_factor.h"
 
 namespace autofill::features {
 
 BASE_FEATURE(kAutofillBrandingIOS,
              "AutofillBrandingIOS",
              base::FEATURE_DISABLED_BY_DEFAULT);
-const char kAutofillBrandingIOSParam[] = "ios-autofill-branding-monotones";
 
-AutofillBrandingType GetAutofillBrandingType() {
+const char kAutofillBrandingIOSParamFrequencyTypeTwice[] = "twice";
+const char kAutofillBrandingIOSParamFrequencyTypeUntilInteracted[] =
+    "until-interacted";
+const char kAutofillBrandingIOSParamFrequencyTypeDismissWhenInteracted[] =
+    "dismiss-when-interacted";
+const char kAutofillBrandingIOSParamFrequencyTypeAlwaysShowAndDismiss[] =
+    "always-show-and-dismiss";
+const char kAutofillBrandingIOSParamFrequencyTypeAlways[] = "always";
+
+const char kAutofillBrandingIOSParamFrequencyTypePhone[] =
+    "ios-autofill-branding-frequency-type-phone";
+const char kAutofillBrandingIOSParamFrequencyTypeTablet[] =
+    "ios-autofill-branding-frequency-type-tablet";
+
+constexpr base::FeatureParam<AutofillBrandingFrequencyType>::Option
+    kAutofillBrandingFrequencyTypeOptions[] = {
+        {AutofillBrandingFrequencyType::kTwice,
+         kAutofillBrandingIOSParamFrequencyTypeTwice},
+        {AutofillBrandingFrequencyType::kUntilInteracted,
+         kAutofillBrandingIOSParamFrequencyTypeUntilInteracted},
+        {AutofillBrandingFrequencyType::kDismissWhenInteracted,
+         kAutofillBrandingIOSParamFrequencyTypeDismissWhenInteracted},
+        {AutofillBrandingFrequencyType::kAlwaysShowAndDismiss,
+         kAutofillBrandingIOSParamFrequencyTypeAlwaysShowAndDismiss},
+        {AutofillBrandingFrequencyType::kAlways,
+         kAutofillBrandingIOSParamFrequencyTypeAlways},
+};
+
+constexpr base::FeatureParam<AutofillBrandingFrequencyType>
+    kAutofillBrandingFrequencyPhone{
+        &kAutofillBrandingIOS, kAutofillBrandingIOSParamFrequencyTypePhone,
+        /*default=*/AutofillBrandingFrequencyType::kTwice,
+        &kAutofillBrandingFrequencyTypeOptions};
+constexpr base::FeatureParam<AutofillBrandingFrequencyType>
+    kAutofillBrandingFrequencyTablet{
+        &kAutofillBrandingIOS, kAutofillBrandingIOSParamFrequencyTypeTablet,
+        /*default=*/AutofillBrandingFrequencyType::kTwice,
+        &kAutofillBrandingFrequencyTypeOptions};
+
+AutofillBrandingFrequencyType GetAutofillBrandingFrequencyType() {
   if (base::FeatureList::IsEnabled(kAutofillBrandingIOS)) {
-    return base::GetFieldTrialParamByFeatureAsBool(
-               kAutofillBrandingIOS, kAutofillBrandingIOSParam, false)
-               ? AutofillBrandingType::kMonotone
-               : AutofillBrandingType::kFullColor;
+    return ui::GetDeviceFormFactor() ==
+                   ui::DeviceFormFactor::DEVICE_FORM_FACTOR_PHONE
+               ? kAutofillBrandingFrequencyPhone.Get()
+               : kAutofillBrandingFrequencyTablet.Get();
   }
-  return AutofillBrandingType::kDisabled;
+  return AutofillBrandingFrequencyType::kNever;
+}
+
+bool ShouldAutofillBrandingDismissWithAnimation() {
+  switch (GetAutofillBrandingFrequencyType()) {
+    case AutofillBrandingFrequencyType::kDismissWhenInteracted:
+    case AutofillBrandingFrequencyType::kAlwaysShowAndDismiss:
+      return true;
+    case AutofillBrandingFrequencyType::kNever:
+    case AutofillBrandingFrequencyType::kTwice:
+    case AutofillBrandingFrequencyType::kUntilInteracted:
+    case AutofillBrandingFrequencyType::kAlways:
+      return false;
+  }
 }
 
 }  // namespace autofill::features
diff --git a/ios/chrome/browser/ui/autofill/features.h b/ios/chrome/browser/ui/autofill/features.h
index 286f71a..be55990 100644
--- a/ios/chrome/browser/ui/autofill/features.h
+++ b/ios/chrome/browser/ui/autofill/features.h
@@ -9,24 +9,50 @@
 
 namespace autofill::features {
 
-// Feature flag and variatns to add the Chrome logo inide form input accessory
-// bar.
+// Feature flag to add the Chrome logo inide form input accessory bar.
 BASE_DECLARE_FEATURE(kAutofillBrandingIOS);
-extern const char kAutofillBrandingIOSParam[];
 
-// Autofill branding options.
-enum class AutofillBrandingType {
-  // Autofill branding enabled with full color Chrome logo.
-  kFullColor = 0,
-  // Autofill branding enabled with monotone Chrome logo.
-  kMonotone,
-  // Autofill branding not enabled.
-  kDisabled,
+// Available values for autofill branding frequency type key.
+extern const char kAutofillBrandingIOSParamFrequencyTypeTwice[];
+extern const char kAutofillBrandingIOSParamFrequencyTypeUntilInteracted[];
+extern const char kAutofillBrandingIOSParamFrequencyTypeDismissWhenInteracted[];
+extern const char kAutofillBrandingIOSParamFrequencyTypeAlwaysShowAndDismiss[];
+extern const char kAutofillBrandingIOSParamFrequencyTypeAlways[];
+
+// Variation param key that specifies the frequency type of the autofill
+// branding. Default value is `kAutofillBrandingIOSParamFrequencyTypeTwice`.
+extern const char kAutofillBrandingIOSParamFrequencyTypePhone[];
+extern const char kAutofillBrandingIOSParamFrequencyTypeTablet[];
+
+// Number of times autofill branding should be shown.
+enum class AutofillBrandingFrequencyType {
+  // Autofill branding should never be shown.
+  kNever = 0,
+  // Autofill branding should be shown for two times.
+  kTwice,
+  // Autofill branding should be shown until the user interacts with
+  // keyboard accessory items. The branding would stay on the keyboard
+  // accessories view until keyboard dismissal, but would not show again on
+  // keyboard reappearance.
+  kUntilInteracted,
+  // Autofill branding should be shown until the user interacts with
+  // keyboard accessory items. The branding would be dismissed with animation
+  // upon user interaction.
+  kDismissWhenInteracted,
+  // Autofill branding should always be show and be dismissed with animation
+  // immediately afterwards.
+  kAlwaysShowAndDismiss,
+  // Autofill branding should always be visible.
+  kAlways,
 };
 
-// Returns the current AutofillBrandingType according to the feature flag and
-// experiment "AutofillBrandingIOS".
-AutofillBrandingType GetAutofillBrandingType();
+// Returns the current AutofillBrandingFrequencyType according to the feature
+// flag and experiment "AutofillBrandingIOS".
+AutofillBrandingFrequencyType GetAutofillBrandingFrequencyType();
+
+// Returns whether the autofill branding should be dismissed by animating to the
+// leading edge of the device.
+bool ShouldAutofillBrandingDismissWithAnimation();
 
 }  // namespace autofill::features
 
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm
index 874497e..2201dbb 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/branding_view_controller.mm
@@ -49,19 +49,6 @@
 #pragma mark - Life Cycle
 
 - (void)loadView {
-  NSString* logoName;
-  switch (autofill::features::GetAutofillBrandingType()) {
-    case autofill::features::AutofillBrandingType::kFullColor:
-      logoName = @"fullcolor_branding_icon";
-      break;
-    case autofill::features::AutofillBrandingType::kMonotone:
-      logoName = @"monotone_branding_icon";
-      break;
-    case autofill::features::AutofillBrandingType::kDisabled:
-      NOTREACHED();
-      break;
-  }
-
   UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
   if (IsUIButtonConfigurationEnabled()) {
     UIButtonConfiguration* buttonConfiguration =
@@ -78,7 +65,7 @@
   button.isAccessibilityElement = NO;  // Prevents VoiceOver users from tap.
   button.translatesAutoresizingMaskIntoConstraints = NO;
   self.view = button;
-  [self configureBrandingWithImageName:logoName];
+  [self configureBrandingWithImageName:@"fullcolor_branding_icon"];
 
   // Adds keyboard popup listener to show animation when keyboard is fully
   // settled.
@@ -89,22 +76,6 @@
            object:nil];
 }
 
-#pragma mark - UITraitEnvironment
-
-// UIImages with rendering mode UIImageRenderingModeAlwaysOriginal do not
-// automatically respond when the user interface mode changes. To fix this, the
-// view controller should get notified on these changes and update the image
-// accordingly. Similar issue and fix as crbug.com/998090.
-- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
-  [super traitCollectionDidChange:previousTraitCollection];
-  if (self.traitCollection.userInterfaceStyle !=
-          previousTraitCollection.userInterfaceStyle &&
-      autofill::features::GetAutofillBrandingType() ==
-          autofill::features::AutofillBrandingType::kMonotone) {
-    [self configureBrandingWithImageName:@"monotone_branding_icon"];
-  }
-}
-
 #pragma mark - Accessors
 
 - (BOOL)shouldAnimate {
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm
index 47437f6..2c7e4d9 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_view_controller.mm
@@ -145,8 +145,8 @@
 #pragma mark - Getter
 
 - (BOOL)isBrandingVisible {
-  if (autofill::features::GetAutofillBrandingType() ==
-      autofill::features::AutofillBrandingType::kDisabled) {
+  if (autofill::features::GetAutofillBrandingFrequencyType() ==
+      autofill::features::AutofillBrandingFrequencyType::kNever) {
     return NO;
   }
   return !(self.manualFillAccessoryViewController.allButtonsHidden &&
diff --git a/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_view_controller.mm b/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_view_controller.mm
index e16b775..5d993b42 100644
--- a/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_view_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/home/bookmarks_home_view_controller.mm
@@ -410,7 +410,8 @@
 
   self.searchTerm = @"";
 
-  if (_profileBookmarkModel->loaded()) {
+  if (bookmark_utils_ios::AreAllAvailableBookmarkModelsLoaded(
+          _profileBookmarkModel.get(), _accountBookmarkModel.get())) {
     [self loadBookmarkViews];
   } else {
     [self showLoadingSpinnerBackground];
@@ -425,7 +426,8 @@
   self.navigationController.interactivePopGestureRecognizer.delegate = self;
 
   // Hide the toolbar if we're displaying the root node.
-  if (_profileBookmarkModel->loaded() &&
+  if (bookmark_utils_ios::AreAllAvailableBookmarkModelsLoaded(
+          _profileBookmarkModel.get(), _accountBookmarkModel.get()) &&
       (![self isDisplayingBookmarkRoot] ||
        self.mediator.currentlyShowingSearchResults)) {
     self.navigationController.toolbarHidden = NO;
@@ -529,7 +531,8 @@
 
   [self editExternalBookmarkIfSet];
 
-  DCHECK(_profileBookmarkModel->loaded());
+  DCHECK(bookmark_utils_ios::AreAllAvailableBookmarkModelsLoaded(
+      _profileBookmarkModel.get(), _accountBookmarkModel.get()));
   DCHECK([self isViewLoaded]);
 }
 
diff --git a/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm b/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm
index 2c38364..4f2f82fb 100644
--- a/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm
+++ b/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm
@@ -173,9 +173,6 @@
     }
   }
 
-  if (reason == ActiveWebStateChangeReason::Inserted) {
-    [self didInsertActiveWebState:newWebState];
-  }
   if (oldWebState) {
     [self.consumer prepareForNewTabAnimation];
   }
@@ -194,6 +191,14 @@
       [_ntpCoordinator didNavigateToNTPInWebState:newWebState];
     }
 
+    if (reason == ActiveWebStateChangeReason::Inserted) {
+      // This starts the new tab animation. It is important for the
+      // NTPCoordinator to know about the new web state
+      // (via the call to `-didNavigateToNTPInWebState:` above) before this is
+      // called.
+      [self didInsertActiveWebState:newWebState];
+    }
+
     [self.consumer webStateSelected];
   }
 }
@@ -254,9 +259,6 @@
   }
   NewTabPageTabHelper* NTPHelper =
       NewTabPageTabHelper::FromWebState(newWebState);
-  if (NTPHelper->IsActive()) {
-    [_ntpCoordinator start];
-  }
   BOOL inBackground =
       (NTPHelper && NTPHelper->ShouldShowStartSurface()) || !animated;
   if (inBackground) {
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 2fa1015..3580ec8 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -154,6 +154,8 @@
     "//components/favicon_base",
     "//components/ntp_tiles",
     "//ios/chrome/browser/metrics:metrics_internal",
+    "//ios/chrome/browser/ntp:set_up_list_item_type",
+    "//ios/chrome/browser/ntp:set_up_list_metrics",
     "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/content_suggestions/cells:constants",
     "//ios/chrome/browser/ui/favicon",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 20a6e00f..892174f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -366,6 +366,7 @@
 #pragma mark - SetUpListViewDelegate
 
 - (void)didSelectSetUpListItem:(SetUpListItemType)type {
+  [self.contentSuggestionsMetricsRecorder recordSetUpListItemSelected:type];
   switch (type) {
     case SetUpListItemType::kSignInSync:
       [self showSignIn];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index 35d7fcd49..40d2f78 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -303,7 +303,13 @@
   }
   if ([self shouldShowSetUpList]) {
     self.setUpList.delegate = self;
-    [self.consumer showSetUpListWithItems:[self setUpListItems]];
+    NSArray<SetUpListItemViewData*>* items = [self setUpListItems];
+    [self.consumer showSetUpListWithItems:items];
+    [self.contentSuggestionsMetricsRecorder recordSetUpListShown];
+    for (SetUpListItemViewData* item in items) {
+      [self.contentSuggestionsMetricsRecorder
+          recordSetUpListItemShown:item.type];
+    }
   }
   // Show Shortcuts if the Tile Ablation is not enabled and either:
   // 1) Magic Stack is enabled (always show shortcuts in Magic Stack).
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h
index 88b8b13..726d566 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h
@@ -16,6 +16,7 @@
 typedef NS_ENUM(NSInteger, NTPCollectionShortcutType);
 
 @class ContentSuggestionsMostVisitedItem;
+enum class SetUpListItemType;
 
 // Metrics recorder for the content suggestions.
 @interface ContentSuggestionsMetricsRecorder : NSObject
@@ -47,6 +48,15 @@
 // Logs a most visited tile being removed.
 - (void)recordMostVisitedTileRemoved;
 
+// Logs the Set Up List being shown.
+- (void)recordSetUpListShown;
+
+// Logs a Set Up List item being shown.
+- (void)recordSetUpListItemShown:(SetUpListItemType)type;
+
+// Logs a Set Up List item being selected.
+- (void)recordSetUpListItemSelected:(SetUpListItemType)type;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm
index 6c5b066e..6d5946d 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.mm
@@ -12,6 +12,8 @@
 #import "components/ntp_tiles/metrics.h"
 #import "components/ntp_tiles/ntp_tile_impression.h"
 #import "components/ntp_tiles/tile_visual_type.h"
+#import "ios/chrome/browser/ntp/set_up_list_item_type.h"
+#import "ios/chrome/browser/ntp/set_up_list_metrics.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
@@ -92,6 +94,18 @@
   base::RecordAction(base::UserMetricsAction(kMostVisitedUrlBlacklistedAction));
 }
 
+- (void)recordSetUpListShown {
+  set_up_list_metrics::RecordDisplayed();
+}
+
+- (void)recordSetUpListItemShown:(SetUpListItemType)type {
+  set_up_list_metrics::RecordItemDisplayed(type);
+}
+
+- (void)recordSetUpListItemSelected:(SetUpListItemType)type {
+  set_up_list_metrics::RecordItemSelected(type);
+}
+
 #pragma mark - Private
 
 // Returns the visual type of a favicon for metrics logging.
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn
index 6d7f495f..8b2070b4 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/BUILD.gn
@@ -78,6 +78,7 @@
     ":set_up_list",
     "//base",
     "//base/test:test_support",
+    "//ios/chrome/browser/default_browser:utils",
     "//ios/chrome/browser/ntp:set_up_list",
     "//ios/chrome/browser/ntp:set_up_list_item_type",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.mm
index 9710829..f8615e2 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.mm
@@ -5,6 +5,9 @@
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.h"
 
 #import "base/ios/block_types.h"
+#import "base/metrics/histogram_functions.h"
+#import "base/metrics/user_metrics.h"
+#import "base/metrics/user_metrics_action.h"
 #import "components/feature_engagement/public/tracker.h"
 #import "ios/chrome/browser/default_browser/utils.h"
 #import "ios/chrome/browser/feature_engagement/tracker_factory.h"
@@ -20,6 +23,10 @@
 #error "This file requires ARC support."
 #endif
 
+using base::RecordAction;
+using base::UmaHistogramEnumeration;
+using base::UserMetricsAction;
+
 @implementation SetUpListDefaultBrowserPromoCoordinator {
   // The view controller that displays the default browser promo.
   DefaultBrowserScreenViewController* _viewController;
@@ -44,7 +51,7 @@
 #pragma mark - ChromeCoordinator
 
 - (void)start {
-  // TODO(crbug.com/1435073): Implement Set Up List metrics.
+  RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Appear"));
   [self recordDefaultBrowserPromoShown];
   _viewController = [[DefaultBrowserScreenViewController alloc] init];
   _viewController.delegate = self;
@@ -73,7 +80,9 @@
 #pragma mark - PromoStyleViewControllerDelegate
 
 - (void)didTapPrimaryActionButton {
-  // TODO(crbug.com/1435073): Implement Set Up List metrics.
+  RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Accepted"));
+  [self logDefaultBrowserFullscreenPromoHistogramForAction:
+            IOSDefaultBrowserFullscreenPromoAction::kActionButton];
   [_application openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]
                 options:{}
       completionHandler:nil];
@@ -82,7 +91,9 @@
 }
 
 - (void)didTapSecondaryActionButton {
-  // TODO(crbug.com/1435073): Implement Set Up List metrics.
+  RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Dismiss"));
+  [self logDefaultBrowserFullscreenPromoHistogramForAction:
+            IOSDefaultBrowserFullscreenPromoAction::kCancel];
   _markItemComplete = YES;
   [self.delegate setUpListDefaultBrowserPromoDidFinish:NO];
 }
@@ -91,10 +102,19 @@
 
 - (void)presentationControllerDidDismiss:
     (UIPresentationController*)presentationController {
-  // TODO(crbug.com/1435073): Implement Set Up List metrics.
+  RecordAction(UserMetricsAction("IOS.DefaultBrowserPromo.SetUpList.Dismiss"));
+  [self logDefaultBrowserFullscreenPromoHistogramForAction:
+            IOSDefaultBrowserFullscreenPromoAction::kCancel];
   [self.delegate setUpListDefaultBrowserPromoDidFinish:NO];
 }
 
+#pragma mark - Metrics Helpers
+
+- (void)logDefaultBrowserFullscreenPromoHistogramForAction:
+    (IOSDefaultBrowserFullscreenPromoAction)action {
+  UmaHistogramEnumeration("IOS.DefaultBrowserPromo.SetUpList.Action", action);
+}
+
 #pragma mark - Private
 
 // Records that a default browser promo has been shown.
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm
index d7d75ab..473406942 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator_unittest.mm
@@ -7,7 +7,10 @@
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator.h"
 
 #import "base/test/ios/wait_util.h"
+#import "base/test/metrics/histogram_tester.h"
+#import "base/test/metrics/user_action_tester.h"
 #import "base/test/task_environment.h"
+#import "ios/chrome/browser/default_browser/utils.h"
 #import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
 #import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_default_browser_promo_coordinator_delegate.h"
@@ -55,6 +58,8 @@
 // Test that touching the primary button calls the correct delegate method
 // and opens the settings.
 TEST_F(SetUpListDefaultBrowserPromoCoordinatorTest, PrimaryButton) {
+  base::HistogramTester histogram_tester;
+  base::UserActionTester user_action_tester;
   [coordinator_ start];
 
   OCMExpect([delegate_ setUpListDefaultBrowserPromoDidFinish:YES]);
@@ -67,10 +72,20 @@
 
   [coordinator_ stop];
   task_environment_.RunUntilIdle();
+
+  histogram_tester.ExpectUniqueSample(
+      "IOS.DefaultBrowserPromo.SetUpList.Action",
+      IOSDefaultBrowserFullscreenPromoAction::kActionButton, 1);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "IOS.DefaultBrowserPromo.SetUpList.Appear"));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "IOS.DefaultBrowserPromo.SetUpList.Accepted"));
 }
 
 // Test that touching the secondary button calls the correct delegate method.
 TEST_F(SetUpListDefaultBrowserPromoCoordinatorTest, SecondaryButton) {
+  base::HistogramTester histogram_tester;
+  base::UserActionTester user_action_tester;
   [coordinator_ start];
 
   OCMExpect([delegate_ setUpListDefaultBrowserPromoDidFinish:NO]);
@@ -79,10 +94,19 @@
 
   [coordinator_ stop];
   task_environment_.RunUntilIdle();
+  histogram_tester.ExpectUniqueSample(
+      "IOS.DefaultBrowserPromo.SetUpList.Action",
+      IOSDefaultBrowserFullscreenPromoAction::kCancel, 1);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "IOS.DefaultBrowserPromo.SetUpList.Appear"));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "IOS.DefaultBrowserPromo.SetUpList.Dismiss"));
 }
 
 // Test that touching the secondary button calls the correct delegate method.
 TEST_F(SetUpListDefaultBrowserPromoCoordinatorTest, SwipeToDismiss) {
+  base::HistogramTester histogram_tester;
+  base::UserActionTester user_action_tester;
   [coordinator_ start];
 
   OCMExpect([delegate_ setUpListDefaultBrowserPromoDidFinish:NO]);
@@ -93,4 +117,11 @@
 
   [coordinator_ stop];
   task_environment_.RunUntilIdle();
+  histogram_tester.ExpectUniqueSample(
+      "IOS.DefaultBrowserPromo.SetUpList.Action",
+      IOSDefaultBrowserFullscreenPromoAction::kCancel, 1);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "IOS.DefaultBrowserPromo.SetUpList.Appear"));
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "IOS.DefaultBrowserPromo.SetUpList.Dismiss"));
 }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index 6d0915a..c15ef0b3 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -462,7 +462,6 @@
 }
 
 - (void)locationBarDidBecomeFirstResponder {
-  [self.headerViewController locationBarBecomesFirstResponder];
   self.NTPViewController.omniboxFocused = YES;
 }
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_consumer.h b/ios/chrome/browser/ui/ntp/new_tab_page_header_consumer.h
index e31823b8..d836c9e 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_consumer.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_consumer.h
@@ -18,9 +18,6 @@
 // Exposes view and methods to drive the doodle.
 - (void)setLogoVendor:(id<LogoVendor>)logoVendor;
 
-// Tell location bar has taken focus.
-- (void)locationBarBecomesFirstResponder;
-
 // Sets whether voice search is currently enabled.
 - (void)setVoiceSearchIsEnabled:(BOOL)voiceSearchIsEnabled;
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm
index bbd913ac..f35af340 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm
@@ -662,14 +662,6 @@
   _logoVendor.doodleObserver = self;
 }
 
-- (void)locationBarBecomesFirstResponder {
-  if (!self.isShowing) {
-    return;
-  }
-
-  [self.commandHandler fakeboxTapped];
-}
-
 - (void)setVoiceSearchIsEnabled:(BOOL)voiceSearchIsEnabled {
   if (_voiceSearchIsEnabled == voiceSearchIsEnabled) {
     return;
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index 61bcbe1..0ca0ea1 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -12,6 +12,7 @@
   ]
   deps = [
     ":common",
+    ":password_constants",
     ":password_ui",
     "//components/keyed_service/core",
     "//components/password_manager/core/common:features",
@@ -289,6 +290,7 @@
     "//components/strings",
     "//components/sync/service",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/credential_provider_promo:features",
     "//ios/chrome/browser/metrics:eg_test_support+eg2",
     "//ios/chrome/browser/policy:eg_test_support+eg2",
     "//ios/chrome/browser/shared/ui/util",
@@ -299,6 +301,7 @@
     "//ios/chrome/browser/ui/settings/password/password_details:password_details_table_view_constants",
     "//ios/chrome/browser/ui/settings/password/password_settings:password_settings_constants",
     "//ios/chrome/browser/ui/settings/password/passwords_in_other_apps:eg_test_support+eg2",
+    "//ios/chrome/common/ui/confirmation_alert:constants",
     "//ios/chrome/common/ui/reauthentication",
     "//ios/chrome/common/ui/table_view:cells_constants",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
diff --git a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
index d9b7a74..a43fc1b 100644
--- a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
@@ -19,6 +19,7 @@
 #import "components/sync/base/features.h"
 #import "components/sync/base/sync_prefs.h"
 #import "components/sync/base/user_selectable_type.h"
+#import "ios/chrome/browser/credential_provider_promo/features.h"
 #import "ios/chrome/browser/metrics/metrics_app_interface.h"
 #import "ios/chrome/browser/policy/policy_earl_grey_utils.h"
 #import "ios/chrome/browser/signin/fake_system_identity.h"
@@ -30,8 +31,10 @@
 #import "ios/chrome/browser/ui/settings/password/passwords_in_other_apps/passwords_in_other_apps_app_interface.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
 #import "ios/chrome/browser/ui/settings/settings_root_table_constants.h"
+#import "ios/chrome/common/ui/confirmation_alert/constants.h"
 #import "ios/chrome/common/ui/reauthentication/reauthentication_protocol.h"
 #import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
+#import "ios/chrome/grit/ios_google_chrome_strings.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -431,6 +434,75 @@
   return SettingToolbarEditDoneButton();
 }
 
+// Returns the matcher for the Credential Provider Promo's subtitle.
+id<GREYMatcher> SubtitleMatcher() {
+  return grey_accessibilityID(
+      kConfirmationAlertSubtitleAccessibilityIdentifier);
+}
+
+// Returns the matcher for the Credential Provider Promo's primary action
+// button.
+id<GREYMatcher> PrimaryActionButtonMatcher() {
+  return grey_accessibilityID(
+      kConfirmationAlertPrimaryActionAccessibilityIdentifier);
+}
+
+// Returns the matcher for the Credential Provider Promo's secondary action
+// button.
+id<GREYMatcher> SecondaryActionButtonMatcher() {
+  return grey_accessibilityID(
+      kConfirmationAlertSecondaryActionAccessibilityIdentifier);
+}
+
+// Returns the matcher for the Credential Provider Promo's tertiary action
+// button.
+id<GREYMatcher> TertiaryActionButtonMatcher() {
+  return grey_accessibilityID(
+      kConfirmationAlertTertiaryActionAccessibilityIdentifier);
+}
+
+// Checks that the Credential Provider Promo subtitle text and buttons are
+// interactable.
+void CheckThatCredentialPromoElementsAreInteractable() {
+  [[EarlGrey selectElementWithMatcher:SubtitleMatcher()]
+      assertWithMatcher:grey_interactable()];
+  [[EarlGrey selectElementWithMatcher:PrimaryActionButtonMatcher()]
+      assertWithMatcher:grey_interactable()];
+  [[EarlGrey selectElementWithMatcher:SecondaryActionButtonMatcher()]
+      assertWithMatcher:grey_interactable()];
+  [[EarlGrey selectElementWithMatcher:TertiaryActionButtonMatcher()]
+      assertWithMatcher:grey_interactable()];
+}
+
+// Checks that the Credential Provider Promo appears and dismisses it.
+void CheckForAndDismissCredentialProviderPromo() {
+  // Check that the inital promo is visible.
+  id<GREYMatcher> initialTitleLabelMatcher = grey_text(
+      l10n_util::GetNSString(IDS_IOS_CREDENTIAL_PROVIDER_PROMO_INITIAL_TITLE));
+  [[EarlGrey selectElementWithMatcher:initialTitleLabelMatcher]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  CheckThatCredentialPromoElementsAreInteractable();
+
+  // Tap the primary action button to display the `learn more` promo.
+  [[EarlGrey
+      selectElementWithMatcher:grey_allOf(PrimaryActionButtonMatcher(),
+                                          grey_sufficientlyVisible(), nil)]
+      performAction:grey_tap()];
+
+  // Check that the `learn more` promo is visible.
+  id<GREYMatcher> learnMoreTitleLabelMatcher = grey_text(l10n_util::GetNSString(
+      IDS_IOS_CREDENTIAL_PROVIDER_PROMO_LEARN_MORE_TITLE));
+  [[EarlGrey selectElementWithMatcher:learnMoreTitleLabelMatcher]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  CheckThatCredentialPromoElementsAreInteractable();
+
+  // Tap the secondary action button to dismiss the promo.
+  [[EarlGrey
+      selectElementWithMatcher:grey_allOf(SecondaryActionButtonMatcher(),
+                                          grey_sufficientlyVisible(), nil)]
+      performAction:grey_tap()];
+}
+
 }  // namespace
 
 // Various tests for the main Password Manager UI.
@@ -584,6 +656,16 @@
         password_manager::features::kIOSPasswordCheckup);
   }
 
+  if ([self isRunningTest:@selector(testCopyPasswordMenuItem)] ||
+      [self isRunningTest:@selector(testCopyPasswordToast)]) {
+    config.additional_args.push_back(
+        std::string("--enable-features=CredentialProviderExtensionPromo:enable_"
+                    "promo_on_password_copied/true"));
+    // Without relaunch, the credential provider promo meets its impression
+    // limit and does not display in subsequent runs.
+    config.relaunch_policy = ForceRelaunchByCleanShutdown;
+  }
+
   return config;
 }
 
@@ -644,6 +726,9 @@
   [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
       performAction:grey_tap()];
 
+  // Dismiss the credential provider extension promo.
+  CheckForAndDismissCredentialProviderPromo();
+
   // Check the snackbar in case of failed reauthentication.
   [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                     ReauthenticationResult::kFailure];
@@ -1293,6 +1378,9 @@
   [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
       performAction:grey_tap()];
 
+  // Dismiss the credential provider extension promo.
+  CheckForAndDismissCredentialProviderPromo();
+
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
diff --git a/ios/chrome/browser/ui/settings/password/passwords_consumer.h b/ios/chrome/browser/ui/settings/password/passwords_consumer.h
index 3b06308..aa8ec43 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_consumer.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_consumer.h
@@ -10,35 +10,13 @@
 #import <memory>
 #import <vector>
 
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
+
 namespace password_manager {
 struct CredentialUIEntry;
 class AffiliatedGroup;
 }  // namespace password_manager
 
-// Enum with all possible UI states of password check.
-typedef NS_ENUM(NSInteger, PasswordCheckUIState) {
-  // When no insecure passwords were detected.
-  PasswordCheckStateSafe,
-  // When user has unmuted compromised passwords.
-  PasswordCheckStateUnmutedCompromisedPasswords,
-  // When user has reused passwords.
-  PasswordCheckStateReusedPasswords,
-  // When user has weak passwords.
-  PasswordCheckStateWeakPasswords,
-  // When user has dismissed warnings.
-  PasswordCheckStateDismissedWarnings,
-  // When check was not perfect and state is unclear.
-  PasswordCheckStateDefault,
-  // When password check is running.
-  PasswordCheckStateRunning,
-  // When user has no passwords and check can't be performed.
-  PasswordCheckStateDisabled,
-  // When password check failed due to network issues, quota limit or others.
-  PasswordCheckStateError,
-  // When password check failed due to user being signed out.
-  PasswordCheckStateSignedOut,
-};
-
 // Consumer for the Passwords Screen.
 @protocol PasswordsConsumer <NSObject>
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
index e2221de..67eb426 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
@@ -20,6 +20,7 @@
 #import "ios/chrome/browser/sync/sync_setup_service.h"
 #import "ios/chrome/browser/ui/settings/password/account_storage_utils.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_consumer.h"
+#import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
 #import "ios/chrome/browser/ui/settings/password/saved_passwords_presenter_observer.h"
 #import "ios/chrome/browser/ui/settings/utils/password_auto_fill_status_manager.h"
 #import "ios/chrome/common/string_util.h"
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h b/ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h
index 4105108f..1e36df7 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h
@@ -41,4 +41,29 @@
   SectionIdentifierAddPasswordButton,
 };
 
+// Enum with all possible UI states for the Password Manager's Password Checkup
+// cell.
+typedef NS_ENUM(NSInteger, PasswordCheckUIState) {
+  // When no insecure passwords were detected.
+  PasswordCheckStateSafe,
+  // When user has unmuted compromised passwords.
+  PasswordCheckStateUnmutedCompromisedPasswords,
+  // When user has reused passwords.
+  PasswordCheckStateReusedPasswords,
+  // When user has weak passwords.
+  PasswordCheckStateWeakPasswords,
+  // When user has dismissed warnings.
+  PasswordCheckStateDismissedWarnings,
+  // When check was not perfect and state is unclear.
+  PasswordCheckStateDefault,
+  // When password check is running.
+  PasswordCheckStateRunning,
+  // When user has no passwords and check can't be performed.
+  PasswordCheckStateDisabled,
+  // When password check failed due to network issues, quota limit or others.
+  PasswordCheckStateError,
+  // When password check failed due to user being signed out.
+  PasswordCheckStateSignedOut,
+};
+
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PASSWORD_PASSWORDS_TABLE_VIEW_CONSTANTS_H_
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index a79b50f..4ab9581 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -97,7 +97,7 @@
 }
 
 bool IsFullscreenAPIEnabled() {
-  if (@available(iOS 16.0, *)) {
+  if (@available(iOS 16.4, *)) {
     return base::FeatureList::IsEnabled(kEnableFullscreenAPI);
   }
   return false;
diff --git a/ios/web/public/session/proto/proto_util.cc b/ios/web/public/session/proto/proto_util.cc
index 11f1f5ee..e0b13ea 100644
--- a/ios/web/public/session/proto/proto_util.cc
+++ b/ios/web/public/session/proto/proto_util.cc
@@ -4,10 +4,10 @@
 
 #include "ios/web/public/session/proto/proto_util.h"
 
-#import <ostream>
-#import <type_traits>
+#include <ostream>
+#include <type_traits>
 
-#import "base/notreached.h"
+#include "base/notreached.h"
 
 namespace web {
 
diff --git a/ios/web/session/BUILD.gn b/ios/web/session/BUILD.gn
index 1cc7fd3..af72b0a8 100644
--- a/ios/web/session/BUILD.gn
+++ b/ios/web/session/BUILD.gn
@@ -19,6 +19,8 @@
     "crw_session_certificate_policy_cache_storage.mm",
     "crw_session_storage.mm",
     "crw_session_user_data.mm",
+    "session_certificate.cc",
+    "session_certificate.h",
     "session_certificate_policy_cache_impl.h",
     "session_certificate_policy_cache_impl.mm",
     "session_certificate_policy_cache_storage_builder.h",
diff --git a/ios/web/session/session_certificate.cc b/ios/web/session/session_certificate.cc
new file mode 100644
index 0000000..1d5280f
--- /dev/null
+++ b/ios/web/session/session_certificate.cc
@@ -0,0 +1,125 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/session/session_certificate.h"
+
+#include "ios/web/public/session/proto/session.pb.h"
+#include "ios/web/session/hash_util.h"
+#include "net/cert/x509_util.h"
+
+// Break if CertStatus values changed, as they are persisted on disk and thus
+// must be consistent.
+static_assert(net::CERT_STATUS_ALL_ERRORS == 0xFF00FFFF,
+              "The value of CERT_STATUS_ALL_ERRORS changed!");
+static_assert(net::CERT_STATUS_COMMON_NAME_INVALID == 1 << 0,
+              "The value of CERT_STATUS_COMMON_NAME_INVALID changed!");
+static_assert(net::CERT_STATUS_DATE_INVALID == 1 << 1,
+              "The value of CERT_STATUS_DATE_INVALID changed!");
+static_assert(net::CERT_STATUS_AUTHORITY_INVALID == 1 << 2,
+              "The value of CERT_STATUS_AUTHORITY_INVALID changed!");
+static_assert(net::CERT_STATUS_NO_REVOCATION_MECHANISM == 1 << 4,
+              "The value of CERT_STATUS_NO_REVOCATION_MECHANISM changed!");
+static_assert(net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION == 1 << 5,
+              "The value of CERT_STATUS_UNABLE_TO_CHECK_REVOCATION changed!");
+static_assert(net::CERT_STATUS_REVOKED == 1 << 6,
+              "The value of CERT_STATUS_REVOKED changed!");
+static_assert(net::CERT_STATUS_INVALID == 1 << 7,
+              "The value of CERT_STATUS_INVALID changed!");
+static_assert(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM == 1 << 8,
+              "The value of CERT_STATUS_WEAK_SIGNATURE_ALGORITHM changed!");
+static_assert(net::CERT_STATUS_NON_UNIQUE_NAME == 1 << 10,
+              "The value of CERT_STATUS_NON_UNIQUE_NAME changed!");
+static_assert(net::CERT_STATUS_WEAK_KEY == 1 << 11,
+              "The value of CERT_STATUS_WEAK_KEY changed!");
+static_assert(net::CERT_STATUS_IS_EV == 1 << 16,
+              "The value of CERT_STATUS_IS_EV changed!");
+static_assert(net::CERT_STATUS_REV_CHECKING_ENABLED == 1 << 17,
+              "The value of CERT_STATUS_REV_CHECKING_ENABLED changed!");
+
+namespace web {
+namespace {
+
+// Extracts the leaf certificate in the chain from `certificate`.
+scoped_refptr<net::X509Certificate> ExtractLeafCertificate(
+    const scoped_refptr<net::X509Certificate>& certificate) {
+  // Nothing to do if `certificate` is already a leaf certificate.
+  if (certificate->intermediate_buffers().empty()) {
+    return certificate;
+  }
+
+  scoped_refptr<net::X509Certificate> leaf_certificate =
+      net::X509Certificate::CreateFromBuffer(
+          bssl::UpRef(certificate->cert_buffer()), {});
+  CHECK(leaf_certificate);
+  CHECK(leaf_certificate->intermediate_buffers().empty());
+  return leaf_certificate;
+}
+
+}  // namespace
+
+// Store user decisions with the leaf cert, ignoring any intermediates.
+// This is because WKWebView returns the verified certificate chain in
+// `-webView:didReceiveAuthenticationChallenge:completionHandler:` but
+// `-webView:didFailProvisionalNavigation:withError:` only receive the
+// server-supplied chain.
+SessionCertificate::SessionCertificate(
+    const scoped_refptr<net::X509Certificate>& certificate,
+    const std::string& host,
+    net::CertStatus status)
+    : certificate_(ExtractLeafCertificate(certificate)),
+      host_(host),
+      status_(status) {}
+
+SessionCertificate::SessionCertificate(const proto::CertificateStorage& storage)
+    : host_(storage.host()), status_(storage.status()) {
+  const std::string& cert_string = storage.certificate();
+  certificate_ = net::X509Certificate::CreateFromBytes(
+      base::make_span(reinterpret_cast<const uint8_t*>(cert_string.data()),
+                      cert_string.size()));
+}
+
+SessionCertificate::SessionCertificate(SessionCertificate&&) = default;
+SessionCertificate::SessionCertificate(const SessionCertificate&) = default;
+
+SessionCertificate& SessionCertificate::operator=(SessionCertificate&&) =
+    default;
+SessionCertificate& SessionCertificate::operator=(const SessionCertificate&) =
+    default;
+
+SessionCertificate::~SessionCertificate() = default;
+
+void SessionCertificate::SerializeToProto(
+    proto::CertificateStorage& storage) const {
+  const base::StringPiece cert_string =
+      net::x509_util::CryptoBufferAsStringPiece(certificate_->cert_buffer());
+
+  storage.set_certificate(cert_string.data(), cert_string.size());
+  storage.set_host(host_);
+  storage.set_status(status_);
+}
+
+bool operator==(const SessionCertificate& lhs, const SessionCertificate& rhs) {
+  if (lhs.status() != rhs.status()) {
+    return false;
+  }
+
+  if (lhs.host() != rhs.host()) {
+    return false;
+  }
+
+  return net::x509_util::CryptoBufferEqual(lhs.certificate()->cert_buffer(),
+                                           rhs.certificate()->cert_buffer());
+}
+
+bool operator!=(const SessionCertificate& lhs, const SessionCertificate& rhs) {
+  return !(lhs == rhs);
+}
+
+size_t SessionCertificateHasher::operator()(
+    const SessionCertificate& value) const {
+  return session::ComputeHash(value.certificate(), value.host(),
+                              value.status());
+}
+
+}  // namespace web
diff --git a/ios/web/session/session_certificate.h b/ios/web/session/session_certificate.h
new file mode 100644
index 0000000..1047dbc
--- /dev/null
+++ b/ios/web/session/session_certificate.h
@@ -0,0 +1,74 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_SESSION_SESSION_CERTIFICATE_H_
+#define IOS_WEB_SESSION_SESSION_CERTIFICATE_H_
+
+#include <string>
+#include <unordered_set>
+
+#include "base/memory/scoped_refptr.h"
+#include "net/base/hash_value.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/x509_certificate.h"
+
+namespace web {
+namespace proto {
+class CertificateStorage;
+}  // namespace proto
+
+// Represents an allowed certificate for a specific host as stored in the
+// SessionCertificatePolicyCache.
+class SessionCertificate {
+ public:
+  // Creates a SessionCertificate representing the leaf certificate
+  // `certificate` delivered by `host` with `status`.
+  SessionCertificate(const scoped_refptr<net::X509Certificate>& certificate,
+                     const std::string& host,
+                     net::CertStatus status);
+
+  // Creates a SessionCertificate from serialized representation.
+  explicit SessionCertificate(const proto::CertificateStorage& storage);
+
+  SessionCertificate(SessionCertificate&&);
+  SessionCertificate(const SessionCertificate&);
+
+  SessionCertificate& operator=(SessionCertificate&&);
+  SessionCertificate& operator=(const SessionCertificate&);
+
+  ~SessionCertificate();
+
+  // Serializes the SessionCertificate into `storage`.
+  void SerializeToProto(proto::CertificateStorage& storage) const;
+
+  // Returns the `host`, `status` and `certificate` respectively.
+  const std::string& host() const { return host_; }
+  net::CertStatus status() const { return status_; }
+  const scoped_refptr<net::X509Certificate>& certificate() const {
+    return certificate_;
+  }
+
+ private:
+  scoped_refptr<net::X509Certificate> certificate_;
+  std::string host_;
+  net::CertStatus status_;
+};
+
+// Equality and inequality operator for SessionCertificate.
+bool operator==(const SessionCertificate& lhs, const SessionCertificate& rhs);
+bool operator!=(const SessionCertificate& lhs, const SessionCertificate& rhs);
+
+// Hash operator.
+struct SessionCertificateHasher {
+  size_t operator()(const SessionCertificate& value) const;
+};
+
+// Unordered set of SessionCertificate using SessionCertificateHasher as the
+// hashing functor.
+using SessionCertificateSet =
+    std::unordered_set<SessionCertificate, SessionCertificateHasher>;
+
+}  // namespace web
+
+#endif  // IOS_WEB_SESSION_SESSION_CERTIFICATE_H_
diff --git a/ios/web/session/session_certificate_policy_cache_impl.h b/ios/web/session/session_certificate_policy_cache_impl.h
index 16796cf..44b5be1 100644
--- a/ios/web/session/session_certificate_policy_cache_impl.h
+++ b/ios/web/session/session_certificate_policy_cache_impl.h
@@ -8,6 +8,7 @@
 #import <Foundation/Foundation.h>
 
 #include "ios/web/public/session/session_certificate_policy_cache.h"
+#include "ios/web/session/session_certificate.h"
 
 @class CRWSessionCertificateStorage;
 
@@ -37,7 +38,7 @@
 
  private:
   // Represents the allowed certificates.
-  NSMutableSet<CRWSessionCertificateStorage*>* allowed_certs_;
+  SessionCertificateSet allowed_certs_;
 };
 
 }  // namespace web
diff --git a/ios/web/session/session_certificate_policy_cache_impl.mm b/ios/web/session/session_certificate_policy_cache_impl.mm
index 9ead6f97..6c792d2 100644
--- a/ios/web/session/session_certificate_policy_cache_impl.mm
+++ b/ios/web/session/session_certificate_policy_cache_impl.mm
@@ -5,86 +5,48 @@
 #import "ios/web/session/session_certificate_policy_cache_impl.h"
 
 #import "base/functional/bind.h"
-#import "ios/web/public/browser_state.h"
 #import "ios/web/public/security/certificate_policy_cache.h"
 #import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
 #import "ios/web/public/thread/web_task_traits.h"
 #import "ios/web/public/thread/web_thread.h"
-#import "net/cert/x509_util.h"
-#import "net/cert/x509_util_apple.h"
+#import "ios/web/session/session_certificate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-// Break if CertStatus values changed, as they are persisted on disk and thus
-// must be consistent.
-static_assert(net::CERT_STATUS_ALL_ERRORS == 0xFF00FFFF,
-              "The value of CERT_STATUS_ALL_ERRORS changed!");
-static_assert(net::CERT_STATUS_COMMON_NAME_INVALID == 1 << 0,
-              "The value of CERT_STATUS_COMMON_NAME_INVALID changed!");
-static_assert(net::CERT_STATUS_DATE_INVALID == 1 << 1,
-              "The value of CERT_STATUS_DATE_INVALID changed!");
-static_assert(net::CERT_STATUS_AUTHORITY_INVALID == 1 << 2,
-              "The value of CERT_STATUS_AUTHORITY_INVALID changed!");
-static_assert(net::CERT_STATUS_NO_REVOCATION_MECHANISM == 1 << 4,
-              "The value of CERT_STATUS_NO_REVOCATION_MECHANISM changed!");
-static_assert(net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION == 1 << 5,
-              "The value of CERT_STATUS_UNABLE_TO_CHECK_REVOCATION changed!");
-static_assert(net::CERT_STATUS_REVOKED == 1 << 6,
-              "The value of CERT_STATUS_REVOKED changed!");
-static_assert(net::CERT_STATUS_INVALID == 1 << 7,
-              "The value of CERT_STATUS_INVALID changed!");
-static_assert(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM == 1 << 8,
-              "The value of CERT_STATUS_WEAK_SIGNATURE_ALGORITHM changed!");
-static_assert(net::CERT_STATUS_NON_UNIQUE_NAME == 1 << 10,
-              "The value of CERT_STATUS_NON_UNIQUE_NAME changed!");
-static_assert(net::CERT_STATUS_WEAK_KEY == 1 << 11,
-              "The value of CERT_STATUS_WEAK_KEY changed!");
-static_assert(net::CERT_STATUS_IS_EV == 1 << 16,
-              "The value of CERT_STATUS_IS_EV changed!");
-static_assert(net::CERT_STATUS_REV_CHECKING_ENABLED == 1 << 17,
-              "The value of CERT_STATUS_REV_CHECKING_ENABLED changed!");
-
 namespace web {
 namespace {
 
-// Returns the leaf certificate in the certificate chain from `certificate`.
-scoped_refptr<net::X509Certificate> ExtractLeafCertificate(
-    const scoped_refptr<net::X509Certificate>& certificate) {
-  // Nothing to do if `certificate` is already a leaf certificate.
-  if (certificate->intermediate_buffers().empty()) {
-    return certificate;
-  }
+// Registers `certificate` with `cache`.
+void RegisterCertificate(scoped_refptr<CertificatePolicyCache> cache,
+                         SessionCertificate certificate) {
+  cache->AllowCertForHost(certificate.certificate().get(), certificate.host(),
+                          certificate.status());
+}
 
-  scoped_refptr<net::X509Certificate> leaf_certificate =
-      net::X509Certificate::CreateFromBuffer(
-          bssl::UpRef(certificate->cert_buffer()), {});
-  DCHECK(leaf_certificate);
-  DCHECK(leaf_certificate->intermediate_buffers().empty());
-  return leaf_certificate;
+// Registers `certificates` with `cache`.
+void RegisterCertificates(scoped_refptr<CertificatePolicyCache> cache,
+                          SessionCertificateSet certificates) {
+  for (const SessionCertificate& certificate : certificates) {
+    cache->AllowCertForHost(certificate.certificate().get(), certificate.host(),
+                            certificate.status());
+  }
 }
 
 }  // anonymous namespace
 
 SessionCertificatePolicyCacheImpl::SessionCertificatePolicyCacheImpl(
     BrowserState* browser_state)
-    : SessionCertificatePolicyCache(browser_state),
-      allowed_certs_([[NSMutableSet alloc] init]) {}
+    : SessionCertificatePolicyCache(browser_state) {}
 
 SessionCertificatePolicyCacheImpl::~SessionCertificatePolicyCacheImpl() {}
 
 void SessionCertificatePolicyCacheImpl::UpdateCertificatePolicyCache() const {
   DCHECK_CURRENTLY_ON(WebThread::UI);
-  NSSet* allowed_certs = [NSSet setWithSet:allowed_certs_];
-  const scoped_refptr<CertificatePolicyCache> cache =
-      GetCertificatePolicyCache();
   GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(^{
-        for (CRWSessionCertificateStorage* cert in allowed_certs) {
-          cache->AllowCertForHost(cert.certificate, cert.host, cert.status);
-        }
-      }));
+      FROM_HERE, base::BindOnce(&RegisterCertificates,
+                                GetCertificatePolicyCache(), allowed_certs_));
 }
 
 void SessionCertificatePolicyCacheImpl::RegisterAllowedCertificate(
@@ -92,34 +54,35 @@
     const std::string& host,
     net::CertStatus status) {
   DCHECK_CURRENTLY_ON(WebThread::UI);
-  // Store user decisions with the leaf cert, ignoring any intermediates.
-  // This is because WKWebView returns the verified certificate chain in
-  // `webView:didReceiveAuthenticationChallenge:completionHandler:`,
-  // but the server-supplied chain in
-  // `webView:didFailProvisionalNavigation:withError:`.
-  scoped_refptr<net::X509Certificate> leaf_certificate =
-      ExtractLeafCertificate(certificate);
-
-  [allowed_certs_ addObject:[[CRWSessionCertificateStorage alloc]
-                                initWithCertificate:leaf_certificate
-                                               host:host
-                                             status:status]];
-  const scoped_refptr<CertificatePolicyCache> cache =
-      GetCertificatePolicyCache();
+  SessionCertificate allowed_cert(certificate, host, status);
+  allowed_certs_.insert(allowed_cert);
   GetIOThreadTaskRunner({})->PostTask(
-      FROM_HERE,
-      base::BindOnce(&CertificatePolicyCache::AllowCertForHost, cache,
-                     base::RetainedRef(leaf_certificate.get()), host, status));
+      FROM_HERE, base::BindOnce(&RegisterCertificate,
+                                GetCertificatePolicyCache(), allowed_cert));
 }
 
 void SessionCertificatePolicyCacheImpl::SetAllowedCerts(
     NSSet<CRWSessionCertificateStorage*>* allowed_certs) {
-  allowed_certs_ = [allowed_certs mutableCopy];
+  DCHECK(allowed_certs_.empty());
+  for (CRWSessionCertificateStorage* cert_storage in allowed_certs) {
+    allowed_certs_.insert(SessionCertificate(
+        cert_storage.certificate, cert_storage.host, cert_storage.status));
+  }
 }
 
 NSSet<CRWSessionCertificateStorage*>*
 SessionCertificatePolicyCacheImpl::GetAllowedCerts() const {
-  return allowed_certs_;
+  NSMutableSet<CRWSessionCertificateStorage*>* allowed_certs =
+      [[NSMutableSet alloc] initWithCapacity:allowed_certs_.size()];
+
+  for (const SessionCertificate& cert : allowed_certs_) {
+    [allowed_certs addObject:[[CRWSessionCertificateStorage alloc]
+                                 initWithCertificate:cert.certificate()
+                                                host:cert.host()
+                                              status:cert.status()]];
+  }
+
+  return allowed_certs;
 }
 
 }  // namespace web
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
index e6bcfa7..1d7b9af 100644
--- a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm
@@ -123,7 +123,7 @@
 
 #if defined(__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0
   if (@available(iOS 16.0, *)) {
-    if (base::FeatureList::IsEnabled(features::kEnableFullscreenAPI)) {
+    if (web::features::IsFullscreenAPIEnabled()) {
       [[configuration_ preferences] setElementFullscreenEnabled:YES];
     }
   }
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc
index 7ebcf30e..c9aefa7e 100644
--- a/ipc/ipc_channel_mojo_unittest.cc
+++ b/ipc/ipc_channel_mojo_unittest.cc
@@ -19,7 +19,6 @@
 #include "base/location.h"
 #include "base/memory/platform_shared_memory_region.h"
 #include "base/memory/raw_ptr.h"
-#include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "base/memory/unsafe_shared_memory_region.h"
@@ -1155,9 +1154,7 @@
     receiver_.Bind(std::move(receiver));
   }
 
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #constexpr-ctor-field-initializer
-  RAW_PTR_EXCLUSION IPC::Sender* sync_sender_ = nullptr;
+  raw_ptr<IPC::Sender> sync_sender_ = nullptr;
   int32_t next_expected_value_ = 0;
   int32_t response_value_ = 0;
   base::OnceClosure quit_closure_;
@@ -1298,12 +1295,8 @@
 
   bool use_sync_sender_ = false;
   mojo::AssociatedReceiver<IPC::mojom::SimpleTestClient> receiver_{this};
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #constexpr-ctor-field-initializer
-  RAW_PTR_EXCLUSION IPC::Sender* sync_sender_ = nullptr;
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #constexpr-ctor-field-initializer
-  RAW_PTR_EXCLUSION IPC::mojom::SimpleTestDriver* driver_ = nullptr;
+  raw_ptr<IPC::Sender> sync_sender_ = nullptr;
+  raw_ptr<IPC::mojom::SimpleTestDriver> driver_ = nullptr;
   std::unique_ptr<base::RunLoop> run_loop_;
 };
 
diff --git a/ipc/ipc_channel_proxy_unittest.cc b/ipc/ipc_channel_proxy_unittest.cc
index d85866b..57e515b 100644
--- a/ipc/ipc_channel_proxy_unittest.cc
+++ b/ipc/ipc_channel_proxy_unittest.cc
@@ -2,11 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "build/build_config.h"
 
 #include <stddef.h>
 #include <stdint.h>
+
 #include <memory>
 
 #include "base/message_loop/message_pump_type.h"
@@ -133,9 +135,7 @@
   RAW_PTR_EXCLUSION base::RunLoop* run_loop_ = nullptr;
 
  private:
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #constexpr-ctor-field-initializer
-  RAW_PTR_EXCLUSION IPC::Channel* channel_ = nullptr;
+  raw_ptr<IPC::Channel> channel_ = nullptr;
 };
 
 class MessageCountFilter : public IPC::MessageFilter {
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 042eafb..2c6482f 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1270,20 +1270,14 @@
 // lacros-chrome through the crosapi.
 const base::Feature MEDIA_EXPORT kExposeOutOfProcessVideoDecodingToLacros{
     "ExposeOutOfProcessVideoDecodingToLacros",
-    base::FEATURE_ENABLED_BY_DEFAULT};
+    base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 // Spawn utility processes to perform hardware decode acceleration instead of
 // using the GPU process.
 const base::Feature MEDIA_EXPORT kUseOutOfProcessVideoDecoding{
-  "UseOutOfProcessVideoDecoding",
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-      base::FEATURE_ENABLED_BY_DEFAULT
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-#endif
-};
+    "UseOutOfProcessVideoDecoding", base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index b0140b4c..8a5416d1 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -2453,16 +2453,21 @@
     return nullptr;
   }
 
+  if (GetImplementationType() == VAImplementation::kIntelIHD &&
+      pixmap->GetBufferFormatModifier() ==
+          gfx::NativePixmapHandle::kNoModifier) {
+    // Buffers imported into the iHD libva driver should always have a valid
+    // modifier.
+    return nullptr;
+  }
+
   // TODO(b/233894465): use the DRM_PRIME_2 API with the Mesa Gallium driver
   // when AMD supports it.
   // TODO(b/233924862): use the DRM_PRIME_2 API with protected content.
   // TODO(b/233929647): use the DRM_PRIME_2 API with the i965 driver.
-  // TODO(b/236746283): remove the kNoModifier check once the modifier is
-  // plumbed for JPEG decoding and encoding.
   const bool use_drm_prime_2 =
       GetImplementationType() == VAImplementation::kIntelIHD &&
-      !protected_content &&
-      pixmap->GetBufferFormatModifier() != gfx::NativePixmapHandle::kNoModifier;
+      !protected_content;
 
   union {
     VADRMPRIMESurfaceDescriptor descriptor;
diff --git a/mojo/core/invitation_unittest.cc b/mojo/core/invitation_unittest.cc
index 0c180c27..1f6e5d09 100644
--- a/mojo/core/invitation_unittest.cc
+++ b/mojo/core/invitation_unittest.cc
@@ -34,6 +34,7 @@
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/system/invitation.h"
+#include "mojo/public/cpp/system/message_pipe.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -83,6 +84,14 @@
       uintptr_t error_handler_context,
       base::StringPiece isolated_invitation_name);
 
+  static void WaitForProcessToTerminate(base::Process& process) {
+    int wait_result = -1;
+    base::WaitForMultiprocessTestChildExit(
+        process, TestTimeouts::action_timeout(), &wait_result);
+    EXPECT_EQ(0, wait_result);
+    process.Close();
+  }
+
  private:
   base::test::TaskEnvironment task_environment_;
 };
@@ -315,6 +324,11 @@
   base::CommandLine& command_line =
       custom_command_line ? *custom_command_line : default_command_line;
 
+  // If this is called from a test child process launching yet another child
+  // process, ensure this switch isn't set so that `SpawnMultiprocessTestChild`
+  // sets it properly.
+  command_line.RemoveSwitch(switches::kTestChildProcess);
+
   base::LaunchOptions default_launch_options;
   base::LaunchOptions& launch_options =
       custom_launch_options ? *custom_launch_options : default_launch_options;
@@ -435,6 +449,15 @@
              MojoAcceptInvitation(&transport_endpoint, &options, &invitation));
     return invitation;
   }
+
+  static MojoHandle ExtractPipeFromInvitation(MojoHandle invitation) {
+    MojoHandle pipe = MOJO_HANDLE_INVALID;
+    const uint32_t kPipeName = 0;
+    EXPECT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(
+                                  invitation, &kPipeName, 4, nullptr, &pipe));
+    EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
+    return pipe;
+  }
 };
 
 #define DEFINE_TEST_CLIENT(name)             \
@@ -465,21 +488,12 @@
   EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 DEFINE_TEST_CLIENT(SendInvitationClient) {
-  MojoHandle primordial_pipe;
   MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
-  const uint32_t pipe_name = 0;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
-                                                 nullptr, &primordial_pipe));
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
+  MojoHandle primordial_pipe = ExtractPipeFromInvitation(invitation);
 
   WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
   ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
@@ -507,11 +521,7 @@
   ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipes[0]));
   ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipes[1]));
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 DEFINE_TEST_CLIENT(SendInvitationMultiplePipesClient) {
@@ -628,22 +638,14 @@
 
   EXPECT_TRUE(process_state.disconnected());
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe));
 }
 
 DEFINE_TEST_CLIENT(ProcessErrorsClient) {
-  MojoHandle pipe;
   MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
-  const uint32_t pipe_name = 0;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(
-                                invitation, &pipe_name, 4, nullptr, &pipe));
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
+  MojoHandle pipe = ExtractPipeFromInvitation(invitation);
 
   // Send a message. Contents are irrelevant, the test process is just going to
   // flag it as a bad.
@@ -702,20 +704,12 @@
   EXPECT_EQ(kTestMessage4, ReadMessage(new_pipe.get().value()));
   WriteMessage(new_pipe.get().value(), kDisconnectMessage);
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 DEFINE_TEST_CLIENT(ReinvitationClient) {
-  MojoHandle pipe;
   MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
-  const uint32_t pipe_name = 0;
-  ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(
-                                invitation, &pipe_name, 4, nullptr, &pipe));
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
+  MojoHandle pipe = ExtractPipeFromInvitation(invitation);
   EXPECT_EQ(kTestMessage1, ReadMessage(pipe));
   WriteMessage(pipe, kTestMessage2);
 
@@ -750,22 +744,13 @@
   EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 DEFINE_TEST_CLIENT(SendIsolatedInvitationClient) {
-  MojoHandle primordial_pipe;
   MojoHandle invitation =
       AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED);
-  const uint32_t pipe_name = 0;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
-                                                 nullptr, &primordial_pipe));
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
+  MojoHandle primordial_pipe = ExtractPipeFromInvitation(invitation);
 
   WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
   ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
@@ -820,22 +805,13 @@
   EXPECT_EQ(kTestMessage3, ReadMessage(new_pipe));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(new_pipe));
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 DEFINE_TEST_CLIENT(SendMultipleIsolatedInvitationsClient) {
-  MojoHandle primordial_pipe;
   MojoHandle invitation =
       AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED);
-  const uint32_t pipe_name = 0;
-  ASSERT_EQ(MOJO_RESULT_OK,
-            MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
-                                                 nullptr, &primordial_pipe));
-  ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
+  MojoHandle primordial_pipe = ExtractPipeFromInvitation(invitation);
 
   WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
   ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
@@ -848,6 +824,7 @@
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
 
   primordial_pipe = MOJO_HANDLE_INVALID;
+  const uint32_t pipe_name = 0;
   ASSERT_EQ(MOJO_RESULT_OK,
             MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
                                                  nullptr, &primordial_pipe));
@@ -920,11 +897,7 @@
             WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 TEST_F(MAYBE_InvitationTest,
@@ -938,17 +911,77 @@
             WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
   EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
 
-  int wait_result = -1;
-  base::WaitForMultiprocessTestChildExit(
-      child_process, TestTimeouts::action_timeout(), &wait_result);
-  child_process.Close();
-  EXPECT_EQ(0, wait_result);
+  WaitForProcessToTerminate(child_process);
 }
 
 DEFINE_TEST_CLIENT(BrokenTransportClient) {
   // No-op. Exit immediately without accepting any invitation.
 }
 
+TEST_F(MAYBE_InvitationTest, NonBrokerToNonBroker) {
+  // Tests a non-broker inviting another non-broker to join the network.
+  MojoHandle host;
+  base::Process host_process = LaunchChildTestClient(
+      "NonBrokerToNonBrokerHost", &host, 1, MOJO_SEND_INVITATION_FLAG_NONE);
+
+  // Send a pipe to the host, which it will forward to its launched client.
+  MessagePipe pipe;
+  MojoHandle client = pipe.handle0.release().value();
+  MojoHandle pipe_for_client = pipe.handle1.release().value();
+  WriteMessageWithHandles(host, "aaa", &pipe_for_client, 1);
+
+  // If the host can successfully invite the client, the client will receive
+  // this message and we'll eventually receive a message back from it.
+  WriteMessage(client, "bbb");
+  EXPECT_EQ("ccc", ReadMessage(client));
+
+  // Signal to the host that it's OK to terminate, then wait for it ack.
+  WriteMessage(host, "bye");
+  WaitForProcessToTerminate(host_process);
+  MojoClose(host);
+  MojoClose(client);
+}
+
+DEFINE_TEST_CLIENT(NonBrokerToNonBrokerHost) {
+  MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
+  MojoHandle test = ExtractPipeFromInvitation(invitation);
+
+  MojoHandle pipe_for_client;
+  EXPECT_EQ("aaa", ReadMessageWithHandles(test, &pipe_for_client, 1));
+
+  MojoHandle client;
+  base::Process client_process =
+      LaunchChildTestClient("NonBrokerToNonBrokerClient", &client, 1,
+                            MOJO_SEND_INVITATION_FLAG_SHARE_BROKER);
+
+  // Forward the pipe from the test to the client, then wait. We're done
+  // whenever the client acks. The success of the test is determined by
+  // the outcome of interactions between the test and the client process.
+  WriteMessageWithHandles(client, "ddd", &pipe_for_client, 1);
+
+  // Wait for a signal from the test to let us know we can terminate.
+  EXPECT_EQ("bye", ReadMessage(test));
+  WriteMessage(client, "bye");
+  WaitForProcessToTerminate(client_process);
+
+  MojoClose(client);
+  MojoClose(test);
+}
+
+DEFINE_TEST_CLIENT(NonBrokerToNonBrokerClient) {
+  MojoHandle invitation =
+      AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_INHERIT_BROKER);
+  MojoHandle host = ExtractPipeFromInvitation(invitation);
+
+  MojoHandle pipe_to_test;
+  EXPECT_EQ("ddd", ReadMessageWithHandles(host, &pipe_to_test, 1));
+
+  EXPECT_EQ("bbb", ReadMessage(pipe_to_test));
+  WriteMessage(pipe_to_test, "ccc");
+
+  EXPECT_EQ("bye", ReadMessage(host));
+}
+
 }  // namespace
 }  // namespace core
 }  // namespace mojo
diff --git a/mojo/core/ipcz_driver/invitation.cc b/mojo/core/ipcz_driver/invitation.cc
index e4ff56b..5f32e0c 100644
--- a/mojo/core/ipcz_driver/invitation.cc
+++ b/mojo/core/ipcz_driver/invitation.cc
@@ -13,6 +13,7 @@
 #include "mojo/core/ipcz_driver/transport.h"
 #include "mojo/core/platform_handle_utils.h"
 #include "mojo/core/scoped_ipcz_handle.h"
+#include "mojo/public/c/system/invitation.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/platform/platform_channel_server_endpoint.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
@@ -223,16 +224,24 @@
     }
   }
 
+  const bool share_broker =
+      options && (options->flags & MOJO_SEND_INVITATION_FLAG_SHARE_BROKER);
   const bool is_isolated =
       options && (options->flags & MOJO_SEND_INVITATION_FLAG_ISOLATED) != 0;
   const IpczNodeOptions& config = GetIpczNodeOptions();
+  if (share_broker && (is_isolated || config.is_broker)) {
+    return MOJO_RESULT_INVALID_ARGUMENT;
+  }
+
   IpczConnectNodeFlags flags = 0;
   if (!config.is_broker) {
-    // TODO: Support non-broker to non-broker connection. Requires new flags for
-    // MojoSendInvitation and MojoAcceptInvitation, because ipcz requires
-    // explicit opt-in from both sides of the connection in order for broker
-    // inheritance to be allowed.
-    flags |= IPCZ_CONNECT_NODE_TO_BROKER;
+    if (share_broker) {
+      flags |= IPCZ_CONNECT_NODE_SHARE_BROKER;
+    } else {
+      // If we're a non-broker not sharing our broker, we have to assume the
+      // target is itself a broker.
+      flags |= IPCZ_CONNECT_NODE_TO_BROKER;
+    }
     if (!config.use_local_shared_memory_allocation) {
       flags |= IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE;
     }
@@ -326,6 +335,7 @@
   // normal process termination.
   bool leak_transport = false;
   bool is_isolated = false;
+  bool inherit_broker = false;
   if (options) {
     if (options->struct_size < sizeof(*options)) {
       return MOJO_RESULT_INVALID_ARGUMENT;
@@ -333,6 +343,8 @@
     leak_transport =
         (options->flags & MOJO_ACCEPT_INVITATION_FLAG_LEAK_TRANSPORT_ENDPOINT);
     is_isolated = (options->flags & MOJO_ACCEPT_INVITATION_FLAG_ISOLATED);
+    inherit_broker =
+        (options->flags & MOJO_ACCEPT_INVITATION_FLAG_INHERIT_BROKER);
   }
 
   auto invitation = base::MakeRefCounted<Invitation>();
@@ -341,16 +353,22 @@
   if (is_isolated) {
     // Nodes using isolated invitations are required by MojoIpcz to both be
     // brokers.
-    CHECK(config.is_broker);
+    CHECK(config.is_broker && !inherit_broker);
   } else {
     CHECK(!config.is_broker);
   }
 
-  IpczConnectNodeFlags flags = IPCZ_CONNECT_NODE_TO_BROKER;
+  IpczConnectNodeFlags flags = IPCZ_NO_FLAGS;
   if (!config.use_local_shared_memory_allocation) {
     flags |= IPCZ_CONNECT_NODE_TO_ALLOCATION_DELEGATE;
   }
 
+  if (inherit_broker) {
+    flags |= IPCZ_CONNECT_NODE_INHERIT_BROKER;
+  } else {
+    flags |= IPCZ_CONNECT_NODE_TO_BROKER;
+  }
+
   const bool is_elevated =
       options && (options->flags & MOJO_ACCEPT_INVITATION_FLAG_ELEVATED) != 0;
 #if !BUILDFLAG(IS_WIN)
diff --git a/mojo/public/c/system/invitation.h b/mojo/public/c/system/invitation.h
index 6ec5cd8..6634416e 100644
--- a/mojo/public/c/system/invitation.h
+++ b/mojo/public/c/system/invitation.h
@@ -211,6 +211,12 @@
 // implicitly trusted more than others.
 #define MOJO_SEND_INVITATION_FLAG_ELEVATED ((MojoSendInvitationFlags)4)
 
+// Indicates that the invitation is being sent from a non-broker to another
+// non-broker and that the sender is referring its broker to the recipient.
+// The recipient must except this and accept the invitation with
+// MOJO_ACCEPT_INVITATION_FLAG_INHERIT_BROKER.
+#define MOJO_SEND_INVITATION_FLAG_SHARE_BROKER ((MojoSendInvitationFlags)8)
+
 // Options passed to |MojoSendInvitation()|.
 struct MOJO_ALIGNAS(8) MojoSendInvitationOptions {
   // The size of this structure, used for versioning.
@@ -257,6 +263,12 @@
 // MOJO_SEND_INVITATION_FLAG_ELEVATED.
 #define MOJO_ACCEPT_INVITATION_FLAG_ELEVATED ((MojoAcceptInvitationFlags)4)
 
+// This invitation is being accepted by a non-broker who expects the sender to
+// refer its own broker to the caller. Invitations accepted with this flag must
+// have been sent with MOJO_SEND_INVITATION_FLAG_SHARE_BROKER.
+#define MOJO_ACCEPT_INVITATION_FLAG_INHERIT_BROKER \
+  ((MojoAcceptInvitationFlags)8)
+
 // Options passed to |MojoAcceptInvitation()|.
 struct MOJO_ALIGNAS(8) MojoAcceptInvitationOptions {
   // The size of this structure, used for versioning.
diff --git a/net/socket/ssl_client_socket.cc b/net/socket/ssl_client_socket.cc
index 4dd73b5c..f305e1a 100644
--- a/net/socket/ssl_client_socket.cc
+++ b/net/socket/ssl_client_socket.cc
@@ -131,11 +131,10 @@
 }
 
 void SSLClientContext::OnSSLContextConfigChanged() {
-  // TODO(davidben): Should we flush |ssl_client_session_cache_| here? We flush
-  // the socket pools, but not the session cache. While BoringSSL-based servers
-  // never change version or cipher negotiation based on client-offered
-  // sessions, other servers do.
   config_ = ssl_config_service_->GetSSLContextConfig();
+  if (ssl_client_session_cache_) {
+    ssl_client_session_cache_->Flush();
+  }
   NotifySSLConfigChanged(SSLConfigChangeType::kSSLConfigChanged);
 }
 
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 8fd32cd..d8d487c6 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -3002,6 +3002,22 @@
   ASSERT_THAT(rv, IsOk());
   ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
   EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, ssl_info.handshake_type);
+
+  // Pick up the ticket again and confirm resumption works.
+  EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk());
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
+  EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, ssl_info.handshake_type);
+  sock_.reset();
+
+  // Updating the context-wide configuration should flush the session cache.
+  SSLContextConfig config;
+  config.disabled_cipher_suites = {1234};
+  ssl_config_service_->UpdateSSLConfigAndNotify(config);
+  ASSERT_TRUE(CreateAndConnectSSLClientSocket(ssl_config, &rv));
+  ASSERT_THAT(rv, IsOk());
+  ASSERT_TRUE(sock_->GetSSLInfo(&ssl_info));
 }
 
 namespace {
diff --git a/testing/buildbot/filters/linux-lacros.browser_tests.filter b/testing/buildbot/filters/linux-lacros.browser_tests.filter
index 6a8ad6c..5a8cf08 100644
--- a/testing/buildbot/filters/linux-lacros.browser_tests.filter
+++ b/testing/buildbot/filters/linux-lacros.browser_tests.filter
@@ -1,6 +1,4 @@
 # TODO(crbug.com/1111979) Enable all tests on lacros.
--AccessContextAuditBrowserTest.CheckSessionOnly
--AccessContextAuditBrowserTest.RemoveRecords
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideo/*
 -All/WebRtcScreenCaptureBrowserTestWithPicker.ScreenCaptureVideoAndAudio/*
 -BrowserViewTest.GetAccessibleTabModalDialogTitle
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 221a997..bbfca2d4 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -4280,6 +4280,25 @@
             ]
         }
     ],
+    "DesktopOmniboxGrouping": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_NON_ZPS_V2",
+                    "enable_features": [
+                        "OmniboxGroupingFrameworkForNonZPS"
+                    ]
+                }
+            ]
+        }
+    ],
     "DesktopOmniboxPerf": [
         {
             "platforms": [
@@ -6841,6 +6860,17 @@
             ],
             "experiments": [
                 {
+                    "name": "EnabledOnAllTriggers",
+                    "params": {
+                        "enable_promo_on_login_with_autofill": "true",
+                        "enable_promo_on_password_copied": "true",
+                        "enable_promo_on_password_saved": "true"
+                    },
+                    "enable_features": [
+                        "CredentialProviderExtensionPromo"
+                    ]
+                },
+                {
                     "name": "EnabledOnPasswordSaved",
                     "params": {
                         "enable_promo_on_password_saved": "true"
@@ -11711,6 +11741,25 @@
             ]
         }
     ],
+    "SafeBrowsingNestedArchives": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SafeBrowsingArchiveImprovements",
+                        "SafeBrowsingStrictDownloadtimeout"
+                    ]
+                }
+            ]
+        }
+    ],
     "SafeBrowsingOnUIThread": [
         {
             "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 242aa82..f7c5eb3 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -73,6 +73,7 @@
 /chromevox/third_party/sre/src
 /chromite
 /crossbench
+/clang_format/script
 /cld_2/src
 /cld_3/src
 /colorama/src
diff --git a/third_party/androidx_javascriptengine/BUILD.gn b/third_party/androidx_javascriptengine/BUILD.gn
index 204eead7..8ca725be76 100644
--- a/third_party/androidx_javascriptengine/BUILD.gn
+++ b/third_party/androidx_javascriptengine/BUILD.gn
@@ -11,13 +11,13 @@
 }
 
 android_aidl("js_sandbox_aidl") {
-  import_include = [ "src/main/aidl" ]
+  import_include = [ "src/main/stableAidl" ]
   sources = [
-    "src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl",
-    "src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolate.aidl",
-    "src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateCallback.aidl",
-    "src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateSyncCallback.aidl",
-    "src/main/aidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxService.aidl",
+    "src/main/stableAidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxConsoleCallback.aidl",
+    "src/main/stableAidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolate.aidl",
+    "src/main/stableAidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateCallback.aidl",
+    "src/main/stableAidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxIsolateSyncCallback.aidl",
+    "src/main/stableAidl/org/chromium/android_webview/js_sandbox/common/IJsSandboxService.aidl",
   ]
 }
 
@@ -68,6 +68,7 @@
     ":javascriptengine_java",
     "//third_party/android_deps:com_google_guava_listenablefuture_java",
     "//third_party/android_deps:guava_android_java",
+    "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_test_core_java",
     "//third_party/androidx:androidx_test_ext_junit_java",
     "//third_party/androidx:androidx_test_runner_java",
diff --git a/third_party/blink/public/common/permissions_policy/permissions_policy.h b/third_party/blink/public/common/permissions_policy/permissions_policy.h
index 9d6e4fe..3e938b7 100644
--- a/third_party/blink/public/common/permissions_policy/permissions_policy.h
+++ b/third_party/blink/public/common/permissions_policy/permissions_policy.h
@@ -291,13 +291,16 @@
       mojom::PermissionsPolicyFeature feature,
       const url::Origin& origin) const;
 
+  // Returns the inherited policy of the given feature for this document.
   bool InheritedValueForFeature(
       const PermissionsPolicy* parent_policy,
       std::pair<mojom::PermissionsPolicyFeature,
                 PermissionsPolicyFeatureDefault> feature,
       const ParsedPermissionsPolicy& container_policy) const;
 
-  // Returns the value of the given feature on the given origin.
+  // If the feature is in the declared policy, returns whether the given origin
+  // exists in its declared allowlist; otherwise, returns the value from
+  // inherited policy.
   bool GetFeatureValueForOrigin(mojom::PermissionsPolicyFeature feature,
                                 const url::Origin& origin) const;
 
@@ -319,6 +322,7 @@
   // parent frame.
   PermissionsPolicyFeatureState inherited_policies_;
 
+  // The map of features to their default enable state.
   const raw_ref<const PermissionsPolicyFeatureList> feature_list_;
 };
 
diff --git a/third_party/blink/public/common/permissions_policy/permissions_policy_features.h b/third_party/blink/public/common/permissions_policy/permissions_policy_features.h
index 7a702cb..957b1de 100644
--- a/third_party/blink/public/common/permissions_policy/permissions_policy_features.h
+++ b/third_party/blink/public/common/permissions_policy/permissions_policy_features.h
@@ -13,12 +13,16 @@
 namespace blink {
 
 // The PermissionsPolicyFeatureDefault enum defines the default enable state for
-// a feature when the feature is not declared in iframe 'allow' attribute.
+// a feature. For a top-level frame, this is the default enable state; for an
+// iframe, this is the default enable state unless the iframe has an 'allow'
+// attribute.
+//
 // See |PermissionsPolicy::InheritedValueForFeature| for usage.
 //
 // The 2 possibilities map directly to Permissions Policy Allowlist semantics.
 //
-// The default values for each feature are set in GetDefaultFeatureList.
+// The default values for each feature are set in
+// GetPermissionsPolicyFeatureList.
 enum class PermissionsPolicyFeatureDefault {
   // Equivalent to ["self"]. If this default policy is in effect for a frame,
   // then the feature will be enabled for that frame, and any same-origin
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index ec54c5c..5c63e99 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -715,6 +715,11 @@
                         key='Measure',
                         values=ext_attrs.value_of(
                             'LegacyWindowAlias_Measure')))
+            if 'LegacyWindowAlias_MeasureAs' in ext_attrs:
+                extended_attributes.append(
+                    ExtendedAttribute(key='MeasureAs',
+                                      values=ext_attrs.value_of(
+                                          'LegacyWindowAlias_MeasureAs')))
             if 'LegacyWindowAlias_RuntimeEnabled' in ext_attrs:
                 feature_name = ext_attrs.value_of(
                     'LegacyWindowAlias_RuntimeEnabled')
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h
index 1df4d69..264493bc 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.h
+++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -93,13 +93,8 @@
       const absl::optional<TimelineOffset>& range_start,
       const absl::optional<TimelineOffset>& range_end,
       const Timing& timing) {
-    // TODO(crbug.com/1441013): Support range start and end for scroll-timelines
-    // that are not view timelines.
-    return CalculateIntrinsicIterationDuration(
-        GetTimelineRange(),
-        IsViewTimeline() ? range_start : absl::optional<TimelineOffset>(),
-        IsViewTimeline() ? range_end : absl::optional<TimelineOffset>(),
-        timing);
+    return CalculateIntrinsicIterationDuration(GetTimelineRange(), range_start,
+                                               range_end, timing);
   }
 
   // See class TimelineRange.
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index cd49949d..a930265b 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -219,23 +219,14 @@
     // animation's start time. Need to compute a new value for
     // inherited_time_.
     double relative_offset;
-    if (timeline->IsViewTimeline()) {
-      TimelineRange timeline_range = timeline->GetTimelineRange();
-      // TODO(kevers): Support animation-range for a non-view scroll-timeline.
-      if (playback_rate_ >= 0) {
-        relative_offset =
-            range_start ? timeline_range.ToFractionalOffset(range_start.value())
-                        : 0;
-      } else {
-        relative_offset =
-            range_end ? timeline_range.ToFractionalOffset(range_end.value())
-                      : 1;
-      }
+    TimelineRange timeline_range = timeline->GetTimelineRange();
+    if (playback_rate_ >= 0) {
+      relative_offset =
+          range_start ? timeline_range.ToFractionalOffset(range_start.value())
+                      : 0;
     } else {
-      // A non-view scroll-timeline has its start time at 0 or end time.
-      // TODO(kevers): Update once non-view scroll-timeline support animation
-      // ranges.
-      relative_offset = playback_rate_ >= 0 ? 0 : 1;
+      relative_offset =
+          range_end ? timeline_range.ToFractionalOffset(range_end.value()) : 1;
     }
     if (timeline->CurrentTime()) {
       AnimationTimeDelta pending_start_time =
diff --git a/third_party/blink/renderer/core/animation/scroll_snapshot_timeline.cc b/third_party/blink/renderer/core/animation/scroll_snapshot_timeline.cc
index 00dd2bc..55d62ff 100644
--- a/third_party/blink/renderer/core/animation/scroll_snapshot_timeline.cc
+++ b/third_party/blink/renderer/core/animation/scroll_snapshot_timeline.cc
@@ -114,8 +114,18 @@
 
 TimelineRange ScrollSnapshotTimeline::GetTimelineRange() const {
   absl::optional<ScrollOffsets> scroll_offsets = GetResolvedScrollOffsets();
-  return scroll_offsets.has_value() ? TimelineRange(scroll_offsets.value())
-                                    : TimelineRange();
+
+  if (!scroll_offsets.has_value()) {
+    return TimelineRange();
+  }
+
+  absl::optional<ScrollOffsets> view_offsets = GetResolvedViewOffsets();
+  // The subject_size is always zero for ScrollTimelines.
+  double subject_size = view_offsets.has_value()
+                            ? (view_offsets->end - view_offsets->start)
+                            : 0.0f;
+
+  return TimelineRange(scroll_offsets.value(), subject_size);
 }
 
 void ScrollSnapshotTimeline::ServiceAnimations(TimingUpdateReason reason) {
diff --git a/third_party/blink/renderer/core/animation/view_timeline.cc b/third_party/blink/renderer/core/animation/view_timeline.cc
index d84e07d8..780e5fa 100644
--- a/third_party/blink/renderer/core/animation/view_timeline.cc
+++ b/third_party/blink/renderer/core/animation/view_timeline.cc
@@ -237,18 +237,6 @@
                                                              axis,
                                                              inset)) {}
 
-TimelineRange ViewTimeline::GetTimelineRange() const {
-  absl::optional<ScrollOffsets> scroll_offsets = GetResolvedScrollOffsets();
-  absl::optional<ScrollOffsets> view_offsets = GetResolvedViewOffsets();
-
-  if (!scroll_offsets.has_value() || !view_offsets.has_value()) {
-    return TimelineRange();
-  }
-
-  double subject_size = view_offsets->end - view_offsets->start;
-  return TimelineRange(scroll_offsets.value(), subject_size);
-}
-
 void ViewTimeline::CalculateOffsets(PaintLayerScrollableArea* scrollable_area,
                                     ScrollOrientation physical_orientation,
                                     TimelineState* state) const {
diff --git a/third_party/blink/renderer/core/animation/view_timeline.h b/third_party/blink/renderer/core/animation/view_timeline.h
index 1c8ec5d..bc8f7ada 100644
--- a/third_party/blink/renderer/core/animation/view_timeline.h
+++ b/third_party/blink/renderer/core/animation/view_timeline.h
@@ -42,8 +42,6 @@
 
   CSSNumericValue* getCurrentTime(const String& rangeName) override;
 
-  TimelineRange GetTimelineRange() const override;
-
   // IDL API implementation.
   Element* subject() const;
 
diff --git a/third_party/blink/renderer/core/dom/mutation_observer.idl b/third_party/blink/renderer/core/dom/mutation_observer.idl
index b0402dc..aeae1a0f 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer.idl
+++ b/third_party/blink/renderer/core/dom/mutation_observer.idl
@@ -37,7 +37,7 @@
     ActiveScriptWrappable,
     Exposed=Window,
     LegacyWindowAlias=WebKitMutationObserver,
-    LegacyWindowAlias_Measure=PrefixedMutationObserverConstructor
+    LegacyWindowAlias_MeasureAs=PrefixedMutationObserverConstructor
 ] interface MutationObserver {
     [CallWith=ScriptState] constructor(MutationCallback callback);
     [RaisesException] void observe(Node target, optional MutationObserverInit options = {});
diff --git a/third_party/blink/renderer/core/dom/range_test.cc b/third_party/blink/renderer/core/dom/range_test.cc
index a54d52f..61187955 100644
--- a/third_party/blink/renderer/core/dom/range_test.cc
+++ b/third_party/blink/renderer/core/dom/range_test.cc
@@ -40,6 +40,11 @@
 class RangeTest : public EditingTestBase {};
 
 TEST_F(RangeTest, extractContentsWithDOMMutationEvent) {
+  if (!RuntimeEnabledFeatures::MutationEventsEnabled()) {
+    // TODO(crbug.com/1446498) Remove this test when MutationEvents are disabled
+    // for good. This is just a test of `DOMSubtreeModified` and ranges.
+    return;
+  }
   GetDocument().body()->setInnerHTML("<span><b>abc</b>def</span>");
   GetDocument().GetSettings()->SetScriptEnabled(true);
   Element* const script_element =
diff --git a/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc b/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc
index a36d790..e1e5ab09 100644
--- a/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc
+++ b/third_party/blink/renderer/core/editing/ime/stylus_writing_gesture.cc
@@ -338,9 +338,10 @@
   // Delete the text between offsets and set cursor.
   InputMethodController& input_method_controller =
       frame->GetInputMethodController();
-  input_method_controller.ReplaceText("", gesture_range.value());
   input_method_controller.SetEditableSelectionOffsets(
-      PlainTextRange(gesture_range->Start(), gesture_range->Start()));
+      PlainTextRange(gesture_range->End(), gesture_range->End()));
+  input_method_controller.DeleteSurroundingText(
+      gesture_range->End() - gesture_range->Start(), 0);
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.idl b/third_party/blink/renderer/core/html/forms/html_option_element.idl
index d0a475f..8877371 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.idl
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.idl
@@ -22,15 +22,15 @@
 
 [
     Exposed=Window,
-    HTMLConstructor,
     NamedConstructor=Option(optional DOMString data = "",
                             optional DOMString value,
                             optional boolean defaultSelected = false,
                             optional boolean selected = false),
     NamedConstructor_CallWith=Document,
-    NamedConstructor_RaisesException,
-    RaisesException=Constructor
+    NamedConstructor_RaisesException
 ] interface HTMLOptionElement : HTMLElement {
+    [HTMLConstructor] constructor();
+
     [CEReactions, Reflect] attribute boolean disabled;
     readonly attribute HTMLFormElement? form;
     [CEReactions] attribute DOMString label;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 1d78927..4abd631 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -6348,6 +6348,42 @@
   return PhysicalOffset();
 }
 
+namespace {
+
+template <typename Function>
+void ForEachAnchorQueryOnContainer(const LayoutBox& box, Function func) {
+  const LayoutObject* container = box.Container();
+  if (container->IsLayoutBlock()) {
+    for (const NGPhysicalBoxFragment& fragment :
+         To<LayoutBlock>(container)->PhysicalFragments()) {
+      if (const NGPhysicalAnchorQuery* anchor_query = fragment.AnchorQuery()) {
+        func(*anchor_query);
+      }
+    }
+    return;
+  }
+
+  // Now the container is a relatively positioned inline.
+  CHECK(container->IsLayoutInline());
+  CHECK(container->IsRelPositioned());
+  const LayoutInline* inline_container = To<LayoutInline>(container);
+  if (!inline_container->HasInlineFragments()) {
+    return;
+  }
+  NGInlineCursor cursor;
+  cursor.MoveTo(*container);
+  for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
+    if (const NGPhysicalBoxFragment* fragment =
+            cursor.Current().BoxFragment()) {
+      if (const NGPhysicalAnchorQuery* anchor_query = fragment->AnchorQuery()) {
+        func(*anchor_query);
+      }
+    }
+  }
+}
+
+}  // namespace
+
 const LayoutObject* LayoutBox::FindTargetAnchor(
     const ScopedCSSName& anchor_name) const {
   if (!IsOutOfFlowPositioned()) {
@@ -6355,36 +6391,17 @@
   }
 
   // Go through the already built NGPhysicalAnchorQuery to avoid tree traversal.
-  // OOF elements are positioned against |css_container|, which can be different
-  // from |container_block| if |css_container| is a relatively-positioned
-  // inline. However, NGPhysicalAnchorQuery is always hosted by block-level
-  // objects and contains everything in the block. So when they are different,
-  // we need to double check if the result is in |css_container|.
-  const LayoutObject* css_container = Container();
-  const LayoutBlock* containing_block = css_container->IsLayoutBlock()
-                                            ? To<LayoutBlock>(css_container)
-                                            : css_container->ContainingBlock();
   const LayoutObject* anchor = nullptr;
-  for (const NGPhysicalBoxFragment& fragment :
-       containing_block->PhysicalFragments()) {
-    if (const NGPhysicalAnchorQuery* anchor_query = fragment.AnchorQuery()) {
-      if (const LayoutObject* current =
-              anchor_query->AnchorLayoutObject(*this, &anchor_name)) {
-        if (!css_container->IsLayoutBlock() &&
-            !current->IsDescendantOf(css_container)) {
-          // TODO(crbug.com/1446442): This is wrong. We need to preserve
-          // relatively positioned anchor references, and then go through all
-          // the name-conflicting references to see if there's any in
-          // |css_container|.
-          continue;
-        }
-        if (!anchor ||
-            (anchor != current && anchor->IsBeforeInPreOrder(*current))) {
-          anchor = current;
-        }
+  auto search_for_anchor = [&](const NGPhysicalAnchorQuery& anchor_query) {
+    if (const LayoutObject* current =
+            anchor_query.AnchorLayoutObject(*this, &anchor_name)) {
+      if (!anchor ||
+          (anchor != current && anchor->IsBeforeInPreOrder(*current))) {
+        anchor = current;
       }
     }
-  }
+  };
+  ForEachAnchorQueryOnContainer(*this, search_for_anchor);
   return anchor;
 }
 
@@ -6401,28 +6418,14 @@
     return nullptr;
   }
   // Go through the already built NGPhysicalAnchorQuery to avoid tree traversal.
-  // OOF elements are positioned against |css_container|, which can be different
-  // from |container_block| if |css_container| is a relatively-positioned
-  // inline. However, NGPhysicalAnchorQuery is always hosted by block-level
-  // objects and contains everything in the block. So when they are different,
-  // we need to double check if the result is in |css_container|.
-  const LayoutObject* css_container = Container();
-  const LayoutBlock* containing_block = css_container->IsLayoutBlock()
-                                            ? To<LayoutBlock>(css_container)
-                                            : css_container->ContainingBlock();
-  if (!css_container->IsLayoutBlock() &&
-      !anchor_layout_object->IsDescendantOf(css_container)) {
-    return nullptr;
-  }
-  for (const NGPhysicalBoxFragment& fragment :
-       containing_block->PhysicalFragments()) {
-    if (const NGPhysicalAnchorQuery* anchor_query = fragment.AnchorQuery()) {
-      if (anchor_query->AnchorReference(*this, anchor_layout_object)) {
-        return anchor_layout_object;
-      }
+  bool is_acceptable_anchor = false;
+  auto validate_anchor = [&](const NGPhysicalAnchorQuery& anchor_query) {
+    if (anchor_query.AnchorLayoutObject(*this, anchor_layout_object)) {
+      is_acceptable_anchor = true;
     }
-  }
-  return nullptr;
+  };
+  ForEachAnchorQueryOnContainer(*this, validate_anchor);
+  return is_acceptable_anchor ? anchor_layout_object : nullptr;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/layout_box_test.cc b/third_party/blink/renderer/core/layout/layout_box_test.cc
index 625625c..af3067e0 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -1964,7 +1964,7 @@
       <span style="position: relative">
         <span id="anchor" style="anchor-name: --a">anchor</span>
         <div id="target" anchor="not-implicit-anchor"
-             style="position: absolute"></div>
+             style="position: absolute; top: anchor(--a top)"></div>
       </span>
       some text
     </div>
@@ -1984,15 +1984,15 @@
     <div>
       <span style="position: relative">
         <span id="anchor1" style="anchor-name: --a">anchor</span>
-        <div id="target1" style="position: absolute"></div>
+        <div id="target1" style="position: absolute;top: anchor(--a top)"></div>
       </span>
       <span style="position: relative">
         <span id="anchor2" style="anchor-name: --a">anchor</span>
-        <div id="target2" style="position: absolute"></div>
+        <div id="target2" style="position: absolute;top: anchor(--a top)"></div>
       </span>
       <span style="position: relative">
         <span id="anchor3" style="anchor-name: --a">anchor</span>
-        <div id="target3" style="position: absolute"></div>
+        <div id="target3" style="position: absolute;top: anchor(--a top)"></div>
       </span>
     </div>
   )HTML");
@@ -2002,13 +2002,13 @@
 
   const LayoutBox* target1 =
       To<LayoutBox>(GetLayoutObjectByElementId("target1"));
-  // TODO(crbug.com/1446442): Should get #anchor1.
-  EXPECT_EQ(nullptr, target1->FindTargetAnchor(anchor_name));
+  EXPECT_EQ(GetLayoutObjectByElementId("anchor1"),
+            target1->FindTargetAnchor(anchor_name));
 
   const LayoutBox* target2 =
       To<LayoutBox>(GetLayoutObjectByElementId("target2"));
-  // TODO(crbug.com/1446442): Should get #anchor2.
-  EXPECT_EQ(nullptr, target2->FindTargetAnchor(anchor_name));
+  EXPECT_EQ(GetLayoutObjectByElementId("anchor2"),
+            target2->FindTargetAnchor(anchor_name));
 
   const LayoutBox* target3 =
       To<LayoutBox>(GetLayoutObjectByElementId("target3"));
diff --git a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
index d032d2f..e102b2b 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -106,10 +106,20 @@
                                ? PartitionOptions::BackupRefPtr::kEnabled
                                : PartitionOptions::BackupRefPtr::kDisabled;
 
+  const bool enable_memory_tagging = base::allocator::PartitionAllocSupport::
+      ShouldEnableMemoryTaggingInRendererProcess();
+  const auto memory_tagging =
+      enable_memory_tagging
+          ? partition_alloc::PartitionOptions::MemoryTagging::kEnabled
+          : partition_alloc::PartitionOptions::MemoryTagging::kDisabled;
+  // No need to call ChangeMemoryTaggingModeForAllThreadsPerProcess() as it will
+  // be handled in ReconfigureAfterFeatureListInit().
+
   return PartitionOptions{
       .quarantine = PartitionOptions::Quarantine::kAllowed,
       .cookie = PartitionOptions::Cookie::kAllowed,
       .backup_ref_ptr = brp_setting,
+      .memory_tagging = memory_tagging,
   };
 }
 
@@ -211,6 +221,8 @@
       // so we'll use the default Pool).
       .use_configurable_pool =
           partition_alloc::PartitionOptions::UseConfigurablePool::kIfAvailable,
+      .memory_tagging =
+          partition_alloc::PartitionOptions::MemoryTagging::kDisabled,
   });
 
   array_buffer_root_ = array_buffer_allocator->root();
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index cc5f8a7b..25ce4b6 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5768,11 +5768,6 @@
 # Sheriff 2022-07-12
 crbug.com/1343674 editing/deleting/460938.html [ Failure Pass ]
 
-# Disable test to land a MSE change. Spec change (MSE PR#306) is still pending,
-# and once landed, this line can be removed.
-crbug.com/878133 external/wpt/media-source/idlharness.window.html [ Failure Pass ]
-
-
 # Sheriff 2022-07-15
 crbug.com/1344652 [ Mac ] external/wpt/webmessaging/with-ports/020.html [ Failure Pass ]
 crbug.com/1344771 virtual/force-defer-script/external/wpt/html/semantics/scripting-1/the-script-element/defer-script/document-write.html [ Crash Failure Pass Timeout ]
@@ -6720,7 +6715,6 @@
 [ Debug Mac12 ] media/controls/doubletap-to-jump-backwards-at-start.html [ Failure ]
 [ Debug Mac12 ] media/video-source-error-no-candidate.html [ Failure ]
 [ Debug Mac12 ] media/video-source.html [ Failure ]
-[ Debug Mac12 ] media/video-source-none-supported.html [ Failure ]
 [ Debug Mac12 ] virtual/attribution-reporting-debug-mode/wpt_internal/attribution-reporting/event-level-trigger-filter-data.sub.https.html?input=not_filters_no_matching [ Failure ]
 [ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/can-load-api.https.html [ Failure ]
 [ Debug Mac12 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-allow-all.https.html [ Failure ]
@@ -6842,3 +6836,5 @@
 crbug.com/1446723 http/tests/security/cross-origin-OffscreenCanvas2D-createPattern.html [ Failure Pass ]
 crbug.com/1448043 virtual/compositor-threaded-percent-based-scrolling-dsf-2/virtual/percent-based-scrolling/max-percent-delta-pinch-zoom.html [ Pass Timeout ]
 crbug.com/1448024 external/wpt/scroll-animations/scroll-timelines/scroll-timeline-invalidation.html [ Failure Pass ]
+crbug.com/1447101 media/video-source-none-supported.html [ Failure Pass Timeout ]
+crbug.com/1430215 external/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html [ Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/attribution-reporting/resources/reports.py b/third_party/blink/web_tests/external/wpt/attribution-reporting/resources/reports.py
index 9682a6dd..2511dd67 100644
--- a/third_party/blink/web_tests/external/wpt/attribution-reporting/resources/reports.py
+++ b/third_party/blink/web_tests/external/wpt/attribution-reporting/resources/reports.py
@@ -1,6 +1,7 @@
 """Methods for the report-event-attribution and report-aggregate-attribution endpoints"""
 import json
 from typing import List, Optional, Tuple, TypedDict
+import urllib.parse
 
 from wptserve.request import Request
 from wptserve.stash import Stash
@@ -8,14 +9,15 @@
 
 # Key used to access the reports in the stash.
 REPORTS = "4691a2d7fca5430fb0f33b1bd8a9d388"
+REDIRECT = "9250f93f-2c05-4aae-83b9-2817b0e18b4e"
 
 CLEAR_STASH = isomorphic_encode("clear_stash")
+CONFIG_REDIRECT = isomorphic_encode("redirect_to")
 
 Header = Tuple[str, str]
 Status = Tuple[int, str]
 Response = Tuple[Status, List[Header], str]
 
-
 def decode_headers(headers: dict) -> dict:
   """Decodes the headers from wptserve.
 
@@ -35,6 +37,20 @@
   return "%s://%s" % (request.url_parts.scheme,
                       request.url_parts.netloc)
 
+def configure_redirect(request, origin) -> None:
+  with request.server.stash.lock:
+      request.server.stash.put(REDIRECT, origin)
+      return None
+
+def get_report_redirect_url(request):
+  with request.server.stash.lock:
+      origin = request.server.stash.take(REDIRECT)
+      if origin is None:
+         return None
+      origin_parts = urllib.parse.urlsplit(origin)
+      parts = request.url_parts
+      new_parts = origin_parts._replace(path=bytes(parts.path, 'utf-8'))
+      return urllib.parse.urlunsplit(new_parts)
 
 def handle_post_report(request: Request, headers: List[Header]) -> Response:
   """Handles POST request for reports.
@@ -48,6 +64,22 @@
         "code": 200,
         "message": "Stash successfully cleared.",
     })
+
+  redirect_origin = request.GET.get(CONFIG_REDIRECT)
+  if redirect_origin:
+    configure_redirect(request, redirect_origin)
+    return (200, "OK"), headers, json.dumps({
+        "code": 200,
+        "message": "Redirect successfully configured.",
+    })
+
+  redirect_url = get_report_redirect_url(request)
+  if redirect_url is not None:
+    headers.append(("Location", redirect_url))
+    return (308, "Permanent Redirect"), headers, json.dumps({
+        "code": 308
+    })
+
   store_report(
       request.server.stash, get_request_origin(request), {
           "body": request.body.decode("utf-8"),
@@ -87,6 +119,7 @@
 def clear_stash(stash: Stash) -> None:
   "Clears the stash."
   stash.take(REPORTS)
+  stash.take(REDIRECT)
   return None
 
 def take_reports(stash: Stash, origin: str) -> List[str]:
diff --git a/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-004.html b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-004.html
new file mode 100644
index 0000000..ce66a27
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-anchor-position/anchor-scroll-004.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests anchor-scroll with relatively positioned inline containers</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/#scroll">
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/test-common.js"></script>
+
+<style>
+body {
+  margin: 0;
+}
+
+.cb {
+  position: relative;
+  font: 20px/1 Ahem, monospace;
+}
+
+.scroller {
+  display: inline-block;
+  overflow-x: scroll;
+  width: 160px;
+  white-space: nowrap;
+}
+
+.anchor {
+  anchor-name: --a;
+  color: orange;
+}
+
+.target {
+  position: absolute;
+  anchor-scroll: --a;
+  top: anchor(--a bottom);
+  left: anchor(--a left);
+  color: lime;
+}
+</style>
+
+<div>
+  <span class="cb">
+    <span class="scroller" id="scroller1">
+      before
+      <span class="anchor" id="anchor1">anchor</span>
+      after
+    </span>
+    <span class="target" id="target1">target</span>
+  </span>
+
+  <br>
+  <br>
+
+  <span class="cb">
+    <span class="scroller" id="scroller2">
+      before
+      <span class="anchor" id="anchor2">anchor</span>
+      after
+    </span>
+    <span class="target" id="target2">target</span>
+  </span>
+</div>
+
+<script>
+promise_test(async () => {
+  await waitUntilNextAnimationFrame();
+  await waitUntilNextAnimationFrame();
+
+  assert_equals(target1.getBoundingClientRect().left, 140);
+  assert_equals(target2.getBoundingClientRect().left, 140);
+}, 'Initial position of the targets');
+
+promise_test(async () => {
+  scroller1.scrollLeft = 20;
+  await waitUntilNextAnimationFrame();
+  await waitUntilNextAnimationFrame();
+
+  assert_equals(target1.getBoundingClientRect().left, 120);
+}, '#target1 should scroll with #anchor1');
+
+promise_test(async () => {
+  scroller2.scrollLeft = 40;
+  await waitUntilNextAnimationFrame();
+  await waitUntilNextAnimationFrame();
+
+  assert_equals(target2.getBoundingClientRect().left, 100);
+}, '#target2 should scroll with #anchor2');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt
index 46a6a5b..8793bf69 100644
--- a/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/media-source/idlharness.window-expected.txt
@@ -17,6 +17,7 @@
 PASS MediaSource interface: existence and properties of interface prototype object
 PASS MediaSource interface: existence and properties of interface prototype object's "constructor" property
 PASS MediaSource interface: existence and properties of interface prototype object's @@unscopables property
+PASS MediaSource interface: member handle
 PASS MediaSource interface: attribute sourceBuffers
 PASS MediaSource interface: attribute activeSourceBuffers
 PASS MediaSource interface: attribute readyState
@@ -30,10 +31,10 @@
 PASS MediaSource interface: operation endOfStream(optional EndOfStreamError)
 PASS MediaSource interface: operation setLiveSeekableRange(double, double)
 PASS MediaSource interface: operation clearLiveSeekableRange()
-PASS MediaSource interface: operation getHandle()
 PASS MediaSource interface: operation isTypeSupported(DOMString)
 PASS MediaSource must be primary interface of mediaSource
 PASS Stringification of mediaSource
+PASS MediaSource interface: mediaSource must not have property "handle"
 PASS MediaSource interface: mediaSource must inherit property "sourceBuffers" with the proper type
 PASS MediaSource interface: mediaSource must inherit property "activeSourceBuffers" with the proper type
 PASS MediaSource interface: mediaSource must inherit property "readyState" with the proper type
@@ -51,7 +52,6 @@
 PASS MediaSource interface: mediaSource must inherit property "setLiveSeekableRange(double, double)" with the proper type
 PASS MediaSource interface: calling setLiveSeekableRange(double, double) on mediaSource with too few arguments must throw TypeError
 PASS MediaSource interface: mediaSource must inherit property "clearLiveSeekableRange()" with the proper type
-PASS MediaSource interface: mediaSource must inherit property "getHandle()" with the proper type
 PASS MediaSource interface: mediaSource must inherit property "isTypeSupported(DOMString)" with the proper type
 PASS MediaSource interface: calling isTypeSupported(DOMString) on mediaSource with too few arguments must throw TypeError
 PASS MediaSourceHandle interface: existence and properties of interface object
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-range-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-range-animation.html
new file mode 100644
index 0000000..df087da
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-range-animation.html
@@ -0,0 +1,182 @@
+<!DOCTYPE html>
+<title>Scroll timelines and animation attachment ranges</title>
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range">
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#animation-range">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
+<style>
+  @keyframes anim {
+    from { z-index: 0; background-color: skyblue;}
+    to { z-index: 100; background-color: coral; }
+  }
+  #scroller {
+    border:  10px solid lightgray;
+    overflow-y: scroll;
+    width: 200px;
+    height: 200px;
+  }
+  #scroller > div {
+    margin: 800px 0px;
+    width: 100px;
+    height: 100px;
+  }
+  #target {
+    font-size: 10px;
+    background-color: green;
+    z-index: -1;
+  }
+</style>
+<main id=main>
+</main>
+
+<template id=template_without_scope>
+  <div id=scroller class=timeline>
+    <div id=target></div>
+  </div>
+</template>
+
+<template id=template_with_scope>
+  <div id=scope>
+    <div id=target></div>
+    <div id=scroller class=timeline>
+      <div></div>
+    </div>
+  </div>
+</template>
+
+<script>
+  setup(assert_implements_animation_timeline);
+
+  function inflate(t, template) {
+    t.add_cleanup(() => main.replaceChildren());
+    main.append(template.content.cloneNode(true));
+  }
+  async function scrollTop(e, value) {
+    e.scrollTop = value;
+    await waitForNextFrame();
+  }
+  async function waitForAnimationReady(target) {
+    await waitForNextFrame();
+    await Promise.all(target.getAnimations().map(x => x.ready));
+  }
+  async function assertValueAt(scroller, target, args) {
+    await waitForAnimationReady(target);
+    await scrollTop(scroller, args.scrollTop);
+    assert_equals(getComputedStyle(target).zIndex, args.expected.toString());
+  }
+  function test_animation_range(options, template, desc_suffix) {
+    if (template === undefined)
+      template = template_without_scope;
+    if (desc_suffix === undefined)
+      desc_suffix = '';
+
+    promise_test(async (t) => {
+      inflate(t, template);
+      let scroller = main.querySelector('#scroller');
+      let target = main.querySelector('#target');
+      let timeline = main.querySelector('.timeline');
+      let scope = main.querySelector('#scope');
+      let maxScroll = scroller.scrollHeight - scroller.clientHeight;
+
+      if (scope != null) {
+        scope.style.timelineScope = '--t1';
+      }
+
+      timeline.style.scrollTimeline = '--t1';
+      target.style.animation = 'anim auto linear';
+      target.style.animationTimeline = '--t1';
+      target.style.animationRangeStart = options.rangeStart;
+      target.style.animationRangeEnd = options.rangeEnd;
+
+      // Accommodates floating point precision errors at the endpoints.
+      target.style.animationFillMode = 'both';
+
+      // 0%
+      await assertValueAt(scroller, target,
+          { scrollTop: options.startOffset, expected: 0 });
+      // 50%
+      await assertValueAt(scroller, target,
+          { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 });
+      // 100%
+      await assertValueAt(scroller, target,
+          { scrollTop: options.endOffset, expected: 100 });
+
+      // Test before/after phases (need to clear the fill mode for that).
+      target.style.animationFillMode = 'initial';
+      let before_scroll = options.startOffset - 10;
+      if (before_scroll >= 0) {
+        await assertValueAt(scroller, target,
+            { scrollTop: options.startOffset - 10, expected: -1 });
+      }
+      let after_scroll = options.startOffset + 10;
+      if (after_scroll <= scroller.maxmum) {
+        await assertValueAt(scroller, target,
+            { scrollTop: options.endOffset + 10, expected: -1 });
+      }
+      // Check 50% again without fill mode.
+      await assertValueAt(scroller, target,
+          { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 });
+
+    }, `Animation with ranges [${options.rangeStart}, ${options.rangeEnd}] ${desc_suffix}`.trim());
+  }
+
+  test_animation_range({
+    rangeStart: 'initial',
+    rangeEnd: 'initial',
+    startOffset: 0,
+    endOffset: 1500
+  });
+
+  test_animation_range({
+    rangeStart: '0%',
+    rangeEnd: '100%',
+    startOffset: 0,
+    endOffset: 1500
+  });
+
+  test_animation_range({
+    rangeStart: '10%',
+    rangeEnd: '100%',
+    startOffset: 150,
+    endOffset: 1500
+  });
+
+  test_animation_range({
+    rangeStart: '0%',
+    rangeEnd: '50%',
+    startOffset: 0,
+    endOffset: 750
+  });
+
+  test_animation_range({
+    rangeStart: '10%',
+    rangeEnd: '50%',
+    startOffset: 150,
+    endOffset: 750
+  });
+
+  test_animation_range({
+    rangeStart: '150px',
+    rangeEnd: '75em',
+    startOffset: 150,
+    endOffset: 750
+  });
+
+  test_animation_range({
+    rangeStart: 'calc(1% + 135px)',
+    rangeEnd: 'calc(70em + 50px)',
+    startOffset: 150,
+    endOffset: 750
+  });
+
+  // Test animation-range via timeline-scope.
+  test_animation_range({
+    rangeStart: 'calc(1% + 135px)',
+    rangeEnd: 'calc(70em + 50px)',
+    startOffset: 150,
+    endOffset: 750
+  }, template_with_scope, '(scoped)');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-animation.html
index bebee9f..9e5993b 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-animation.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-animation.html
@@ -1,6 +1,7 @@
 <!DOCTYPE html>
-<title>Animations using named timeline ranges</title>
+<title>View timelines and animation attachment ranges</title>
 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range">
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#animation-range">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
@@ -16,22 +17,35 @@
     width: 200px;
     height: 200px;
   }
-  #target {
+  #scroller > div {
     margin: 800px 0px;
     width: 100px;
     height: 100px;
-    z-index: -1;
-    background-color: green;
+  }
+  #target {
     font-size: 10px;
+    background-color: green;
+    z-index: -1;
   }
 </style>
 <main id=main>
 </main>
-<template>
+
+<template id=template_without_scope>
   <div id=scroller>
-    <div id=target></div>
+    <div id=target class=timeline></div>
   </div>
 </template>
+
+<template id=template_with_scope>
+  <div id=scope>
+    <div id=target></div>
+    <div id=scroller>
+      <div class=timeline></div>
+    </div>
+  </div>
+</template>
+
 <script>
   setup(assert_implements_animation_timeline);
 
@@ -52,17 +66,24 @@
     await scrollTop(scroller, args.scrollTop);
     assert_equals(getComputedStyle(target).zIndex, args.expected.toString());
   }
-  function test_animation_delay(options) {
+  function test_animation_range(options, template, desc_suffix) {
+    if (template === undefined)
+      template = template_without_scope;
+    if (desc_suffix === undefined)
+      desc_suffix = '';
+
     promise_test(async (t) => {
-      inflate(t, document.querySelector('template'));
+      inflate(t, template);
       let scroller = main.querySelector('#scroller');
       let target = main.querySelector('#target');
+      let timeline = main.querySelector('.timeline');
+      let scope = main.querySelector('#scope');
 
-      target.style.viewTimeline = '--t1 block';
-      // TODO(crbug.com/1375998): Create the timeline in a separate frame to
-      // work around a bug.
-      await waitForNextFrame();
+      if (scope != null) {
+        scope.style.timelineScope = '--t1';
+      }
 
+      timeline.style.viewTimeline = '--t1';
       target.style.animation = 'anim auto linear';
       target.style.animationTimeline = '--t1';
       target.style.animationRangeStart = options.rangeStart;
@@ -91,24 +112,24 @@
       await assertValueAt(scroller, target,
           { scrollTop: (options.startOffset + options.endOffset) / 2, expected: 50 });
 
-    }, `Animation with ranges [${options.rangeStart}, ${options.rangeEnd}]`);
+    }, `Animation with ranges [${options.rangeStart}, ${options.rangeEnd}] ${desc_suffix}`.trim());
   }
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'initial',
     rangeEnd: 'initial',
     startOffset: 600,
     endOffset: 900
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'cover 0%',
     rangeEnd: 'cover 100%',
     startOffset: 600,
     endOffset: 900
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'contain 0%',
     rangeEnd: 'contain 100%',
     startOffset: 700,
@@ -116,88 +137,96 @@
   });
 
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'entry 0%',
     rangeEnd: 'entry 100%',
     startOffset: 600,
     endOffset: 700
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'exit 0%',
     rangeEnd: 'exit 100%',
     startOffset: 800,
     endOffset: 900
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'contain -50%',
     rangeEnd: 'entry 200%',
     startOffset: 650,
     endOffset: 800
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'entry 0%',
     rangeEnd: 'exit 100%',
     startOffset: 600,
     endOffset: 900
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'cover 20px',
     rangeEnd: 'cover 100px',
     startOffset: 620,
     endOffset: 700
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'contain 20px',
     rangeEnd: 'contain 100px',
     startOffset: 720,
     endOffset: 800
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'entry 20px',
     rangeEnd: 'entry 100px',
     startOffset: 620,
     endOffset: 700
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'entry-crossing 20px',
     rangeEnd: 'entry-crossing 100px',
     startOffset: 620,
     endOffset: 700
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'exit 20px',
     rangeEnd: 'exit 80px',
     startOffset: 820,
     endOffset: 880
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'exit-crossing 20px',
     rangeEnd: 'exit-crossing 80px',
     startOffset: 820,
     endOffset: 880
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'contain 20px',
     rangeEnd: 'contain calc(100px - 10%)',
     startOffset: 720,
     endOffset: 790
   });
 
-  test_animation_delay({
+  test_animation_range({
     rangeStart: 'exit 2em',
     rangeEnd: 'exit 8em',
     startOffset: 820,
     endOffset: 880
   });
 
+  // Test animation-range via timeline-scope.
+  test_animation_range({
+    rangeStart: 'exit 2em',
+    rangeEnd: 'exit 8em',
+    startOffset: 820,
+    endOffset: 880
+  }, template_with_scope, '(scoped)');
+
 </script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-animation-frame-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-animation-frame-expected.txt
deleted file mode 100644
index 114f42d..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-animation-frame-expected.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-Tests the Timeline events for Animation Frame feature
-
-RequestAnimationFrame Properties:
-{
-    data : {
-        frame : <string>
-        id : <number>
-        stackTrace : <object>
-    }
-    endTime : <number>
-    frameId : <string>
-    stackTrace : <object>
-    startTime : <number>
-    type : "RequestAnimationFrame"
-}
-Text details for RequestAnimationFrame: test://evaluations/0/timeline-animation-frame.js:14:34
-FireAnimationFrame Properties:
-{
-    data : {
-        frame : <string>
-        id : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "FireAnimationFrame"
-}
-Text details for FireAnimationFrame: test://evaluations/0/timeline-animation-frame.js:14:34
-CancelAnimationFrame Properties:
-{
-    data : {
-        frame : <string>
-        id : <number>
-        stackTrace : <object>
-    }
-    endTime : <number>
-    frameId : <string>
-    stackTrace : <object>
-    startTime : <number>
-    type : "CancelAnimationFrame"
-}
-Text details for CancelAnimationFrame: test://evaluations/0/timeline-animation-frame.js:17:22
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-animation-frame.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-animation-frame.js
deleted file mode 100644
index 98de0ee3..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-misc/timeline-animation-frame.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(`Tests the Timeline events for Animation Frame feature\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
-  await TestRunner.showPanel('timeline');
-  await TestRunner.evaluateInPagePromise(`
-      function performActions()
-      {
-          var callback;
-          var promise = new Promise((fulfill) => callback = fulfill);
-          var requestId = window.requestAnimationFrame(animationFrameCallback);
-          function animationFrameCallback()
-          {
-              window.cancelAnimationFrame(requestId);
-              if (callback)
-                  callback();
-          }
-          return promise;
-      }
-  `);
-
-  await PerformanceTestRunner.invokeAsyncWithTimeline('performActions');
-
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('RequestAnimationFrame');
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('FireAnimationFrame');
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('CancelAnimationFrame');
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt
deleted file mode 100644
index d3f02a1..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Tests the Timeline UI API for network requests.
-
-URL: timeline-network-resource.js
-Duration: string
-Request Method: GET
-Priority: Low
-Mime Type: string
-Encoded Data: string
-Decoded Body: 223 B
-Initiator: timeline-network-resource-details.js:19:25
-URL: anImage.png
-Duration: string
-Request Method: GET
-Priority: Low
-Mime Type: string
-Encoded Data: string
-Decoded Body: 13.3 kB
-Preview: File size:13.3 kBCurrent source:http://127.0.0.1:8000/devtools/tracing/resources/anImage.png
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js
deleted file mode 100644
index 883bcb3b..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(`Tests the Timeline UI API for network requests.\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
-  await TestRunner.loadLegacyModule('components');
-  await TestRunner.showPanel('timeline');
-  await TestRunner.evaluateInPagePromise(`
-      function performActions()
-      {
-          var image = new Image();
-          var imagePromise = new Promise((fulfill) => image.onload = fulfill);
-          image.src = "../resources/anImage.png";
-
-          var script = document.createElement("script");
-          script.src = "../resources/timeline-network-resource.js";
-          document.body.appendChild(script);
-          var scriptPromise = new Promise((fulfill) => window.timelineNetworkResourceEvaluated = fulfill);
-
-          return Promise.all([imagePromise, scriptPromise]);
-      }
-  `);
-
-  await TestRunner.NetworkAgent.setCacheDisabled(true);
-  await PerformanceTestRunner.invokeAsyncWithTimeline('performActions');
-
-  var model = PerformanceTestRunner.timelineModel();
-  var linkifier = new Components.Linkifier();
-
-  for (var request of model.networkRequests()) {
-    var element = await Timeline.TimelineUIUtils.buildNetworkRequestDetails(request, model, linkifier);
-    printElement(element);
-  }
-  TestRunner.completeTest();
-
-  function printElement(element) {
-    var rows = element.querySelectorAll('.timeline-details-view-row');
-    for (var i = 0; i < rows.length; ++i) {
-      var title = TestRunner.deepTextContent(rows[i].firstChild);
-      var value = TestRunner.deepTextContent(rows[i].lastChild);
-      if (title === 'Duration' || title === 'Mime Type' || title === 'Encoded Data')
-        value = typeof value;
-      if (/^file:\/\//.test(value))
-        value = /[^/]*$/.exec(value)[0];
-      if (!title && !value)
-        continue;
-      TestRunner.addResult(title + ': ' + value);
-    }
-  }
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-time/timeline-timer-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-time/timeline-timer-expected.txt
deleted file mode 100644
index bd9530e..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-time/timeline-timer-expected.txt
+++ /dev/null
@@ -1,138 +0,0 @@
-Tests the Timeline events for Timers
-
-TimerInstall Properties:
-{
-    data : {
-        frame : <string>
-        singleShot : true
-        stackTrace : <object>
-        timeout : 10
-        timerId : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    stackTrace : <object>
-    startTime : <number>
-    type : "TimerInstall"
-}
-Text details for TimerInstall: test://evaluations/0/timeline-timer.js:14:26
-TimerInstall Properties:
-{
-    data : {
-        frame : <string>
-        singleShot : false
-        stackTrace : <object>
-        timeout : 20
-        timerId : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    stackTrace : <object>
-    startTime : <number>
-    type : "TimerInstall"
-}
-Text details for TimerInstall: test://evaluations/0/timeline-timer.js:15:26
-TimerFire Properties:
-{
-    data : {
-        frame : <string>
-        timerId : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "TimerFire"
-}
-Text details for TimerFire: test://evaluations/0/timeline-timer.js:14:26
-TimerFire Properties:
-{
-    data : {
-        frame : <string>
-        timerId : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "TimerFire"
-}
-Text details for TimerFire: test://evaluations/0/timeline-timer.js:15:26
-TimerFire Properties:
-{
-    data : {
-        frame : <string>
-        timerId : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "TimerFire"
-}
-Text details for TimerFire: test://evaluations/0/timeline-timer.js:15:26
-TimerRemove Properties:
-{
-    data : {
-        frame : <string>
-        stackTrace : <object>
-        timerId : <number>
-    }
-    endTime : <number>
-    frameId : <string>
-    stackTrace : <object>
-    startTime : <number>
-    type : "TimerRemove"
-}
-Text details for TimerRemove: test://evaluations/0/timeline-timer.js:22:15
-FunctionCall Properties:
-{
-    data : {
-        columnNumber : 36
-        frame : <string>
-        functionName : "intervalTimerWork"
-        lineNumber : 17
-        scriptId : <string>
-        url : test://evaluations/0/timeline-timer.js
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "FunctionCall"
-}
-FunctionCall Properties:
-{
-    data : {
-        columnNumber : 36
-        frame : <string>
-        functionName : "intervalTimerWork"
-        lineNumber : 17
-        scriptId : <string>
-        url : test://evaluations/0/timeline-timer.js
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "FunctionCall"
-}
-EvaluateScript Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "EvaluateScript"
-}
-Text details for EvaluateScript: undefined
-EvaluateScript Properties:
-{
-    data : {
-        columnNumber : 0
-        frame : <string>
-        lineNumber : 0
-        url : 
-    }
-    endTime : <number>
-    frameId : <string>
-    startTime : <number>
-    type : "EvaluateScript"
-}
-Text details for EvaluateScript: undefined
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-time/timeline-timer.js b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-time/timeline-timer.js
deleted file mode 100644
index 5f3926cd..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-time/timeline-timer.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-(async function() {
-  TestRunner.addResult(`Tests the Timeline events for Timers\n`);
-  await TestRunner.loadLegacyModule('timeline'); await TestRunner.loadTestModule('performance_test_runner');
-  await TestRunner.showPanel('timeline');
-  await TestRunner.evaluateInPagePromise(`
-      function performActions()
-      {
-          var callback;
-          var promise = new Promise((fulfill) => callback = fulfill);
-          var timerOne = setTimeout("1 + 1", 10);
-          var timerTwo = setInterval(intervalTimerWork, 20);
-          var iteration = 0;
-
-          function intervalTimerWork()
-          {
-              if (++iteration < 2)
-                  return;
-              clearInterval(timerTwo);
-              callback();
-          }
-          return promise;
-      }
-  `);
-
-  UI.panels.timeline.disableCaptureJSProfileSetting.set(true);
-  await PerformanceTestRunner.invokeAsyncWithTimeline('performActions');
-
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('TimerInstall');
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('TimerFire');
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('TimerRemove');
-  PerformanceTestRunner.printTimelineRecords('FunctionCall');
-  await PerformanceTestRunner.printTimelineRecordsWithDetails('EvaluateScript');
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-after-dialog-expected.txt
similarity index 90%
copy from third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt
copy to third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-after-dialog-expected.txt
index f85583d5..4e6de70b 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-after-dialog-expected.txt
@@ -1,4 +1,4 @@
-Check that the dialogShown event works after enabling the FedCm domain
+Check that the dialogShown event works after triggering the FedCm dialog then enabling the FedCm domain
 msg.params: {
     accounts : [
         [0] : {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-after-dialog.js
similarity index 78%
copy from third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js
copy to third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-after-dialog.js
index c092708..696509a 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-after-dialog.js
@@ -1,15 +1,18 @@
 (async function(testRunner) {
   const {page, session, dp} =
       await testRunner.startBlank(
-          "Check that the dialogShown event works after enabling the " +
-          "FedCm domain");
+          "Check that the dialogShown event works after triggering the " +
+          "FedCm dialog then enabling the FedCm domain");
 
   await page.navigate(
       "https://devtools.test:8443/inspector-protocol/fedcm/resources/dialog-shown-event.https.html");
 
+  // Trigger FedCM dialog
+  const dialogPromise = session.evaluateAsync("triggerDialog()");
+
+  // Enable FedCM domain
   await dp.FedCm.enable({disableRejectionDelay: true});
 
-  const dialogPromise = session.evaluateAsync("triggerDialog()");
   let msg = await dp.FedCm.onceDialogShown();
   if (msg.error) {
     testRunner.log(msg.error);
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-before-dialog-expected.txt
similarity index 96%
rename from third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt
rename to third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-before-dialog-expected.txt
index f85583d5..27eb581 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-before-dialog-expected.txt
@@ -1,4 +1,4 @@
-Check that the dialogShown event works after enabling the FedCm domain
+Check that the dialogShown event works after enabling the FedCm domain then triggering the FedCm dialog
 msg.params: {
     accounts : [
         [0] : {
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-before-dialog.js
similarity index 87%
rename from third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js
rename to third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-before-dialog.js
index c092708..9c873513 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/fedcm-dialog-event-enable-before-dialog.js
@@ -2,14 +2,17 @@
   const {page, session, dp} =
       await testRunner.startBlank(
           "Check that the dialogShown event works after enabling the " +
-          "FedCm domain");
+          "FedCm domain then triggering the FedCm dialog");
 
   await page.navigate(
       "https://devtools.test:8443/inspector-protocol/fedcm/resources/dialog-shown-event.https.html");
 
+  // Enable FedCM domain
   await dp.FedCm.enable({disableRejectionDelay: true});
 
+  // Trigger FedCM dialog
   const dialogPromise = session.evaluateAsync("triggerDialog()");
+
   let msg = await dp.FedCm.onceDialogShown();
   if (msg.error) {
     testRunner.log(msg.error);
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/resources/dialog-shown-event.https.html b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/resources/dialog-shown-event.https.html
index ea02bce..da608de 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/resources/dialog-shown-event.https.html
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fedcm/resources/dialog-shown-event.https.html
@@ -11,7 +11,8 @@
           clientId: '123',
           nonce: '2',
         }]
-      }
+      },
+      mediation: 'required',
     });
     return result.token;
   } catch (error) {
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/borders/border-image-repeat-round-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/borders/border-image-repeat-round-expected.png
new file mode 100644
index 0000000..c6dcaf8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/fast/borders/border-image-repeat-round-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/color-profile-border-image-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/color-profile-border-image-expected.png
new file mode 100644
index 0000000..8db1433
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/exotic-color-space/images/color-profile-border-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/gpu-rasterization/images/color-profile-border-image-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
new file mode 100644
index 0000000..cda8a9e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/gpu-rasterization/images/color-profile-border-image-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/cors-report.sub.https.html b/third_party/blink/web_tests/wpt_internal/attribution-reporting/cors-report.sub.https.html
new file mode 100644
index 0000000..ce98f44
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/cors-report.sub.https.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name=timeout content=long>
+<meta name=variant content="">
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script>
+const reportingOrigin = 'https://{{host}}:{{ports[https][0]}}';
+const redirectOrigin = 'https://{{host}}:{{ports[https][1]}}';
+attribution_reporting_promise_test(async t => {
+  await redirectReportsTo(redirectOrigin);
+  registerAttributionSrcByImg(createRedirectChain([
+    {
+      cookie: 'foo=bar;Secure;HttpOnly;Path=/',
+      reportingOrigin,
+      source: {
+        destination: 'https://{{host}}',
+        source_event_id: generateSourceEventId(),
+      },
+    },
+    {
+      reportingOrigin,
+      trigger: {event_trigger_data: [{trigger_data: '2'}]},
+    },
+  ]));
+  const payload = await pollAttributionReports(eventLevelReportsUrl, redirectOrigin, 500 /*ms*/);
+  assert_equals(payload, null);
+}, 'Ensure that cross-origin redirects are disallowed.');
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
index 510c2a4..66b524e 100644
--- a/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
+++ b/third_party/blink/web_tests/wpt_internal/attribution-reporting/resources/helpers.js
@@ -76,6 +76,13 @@
   return fetch(url, options);
 };
 
+const redirectReportsTo = origin => {
+  return Promise.all([
+      fetch(`${eventLevelReportsUrl}?redirect_to=${origin}`, {method: 'POST'}),
+      fetch(`${aggregatableReportsUrl}?redirect_to=${origin}`, {method: 'POST'})
+    ]);
+};
+
 const getFetchParams = (origin, cookie) => {
   let credentials;
   const headers = [];
@@ -307,10 +314,12 @@
 
 /**
  * Method that polls a particular URL for reports. Once reports
- * are received, returns the payload as promise.
+ * are received, returns the payload as promise. Returns null if the
+ * timeout is reached before a report is available.
  */
-const pollAttributionReports = async (url, origin = location.origin) => {
-  while (true) {
+const pollAttributionReports = async (url, origin = location.origin, timeout = 60 * 1000 /*ms*/) => {
+  let startTime = performance.now();
+  while (performance.now() - startTime < timeout) {
     const resp = await fetch(new URL(url, origin));
     const payload = await resp.json();
     if (payload.reports.length > 0) {
@@ -318,6 +327,7 @@
     }
     await delay(/*ms=*/ 100);
   }
+  return null;
 };
 
 // Verbose debug reporting must have been enabled on the source registration for this to work.
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 2057a0c..30a34f3 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-13-0-142-g562f34819
-Revision: 562f34819229080abb05b4e7ae7e9e47fc84c6eb
+Version: VER-2-13-0-144-g80a507a6b
+Revision: 80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef
 CPEPrefix: cpe:/a:freetype:freetype:2.12.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/tensorflow_models/README.chromium b/third_party/tensorflow_models/README.chromium
index 6ee229b..6030615e 100644
--- a/third_party/tensorflow_models/README.chromium
+++ b/third_party/tensorflow_models/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Models
 Short Name: tensorflow_models
 URL: https://github.com/tensorflow/models
-Version: a2e1e19eea5e7b926dafcc7e10c4e4e475d42f51
-Date: 2022/11/09
+Version: 02bbd23f37fc85bbc4ad0540a08c383dddc9b501
+Date: 2023/05/23
 License: Apache 2.0
 License File: src/LICENSE
 Security Critical: Yes
@@ -13,7 +13,11 @@
 implementations of state-of-the-art (SOTA) models and modeling solutions for
 TensorFlow users.
 
-Note: There are two shim directories, "libutf" and "icu4c", used to redirect
+Important: Only a small selection of the upstream files are actually committed
+here. See update.sh in this directory.
+
+Note:
+There are two shim directories, "libutf" and "icu4c", used to redirect
 includes for those directories to their Chromium versions which have slightly
 different names than those used here.
 
diff --git a/tools/cast3p/cast_core.version b/tools/cast3p/cast_core.version
index 9bcbf7f0..ef565141 100644
--- a/tools/cast3p/cast_core.version
+++ b/tools/cast3p/cast_core.version
@@ -1 +1 @@
-cast_20230512_0201_RC00
+cast_20230321_1054_RC09
diff --git a/tools/checklicenses/checklicenses.py b/tools/checklicenses/checklicenses.py
index adf5ea5..ec7d0c5 100755
--- a/tools/checklicenses/checklicenses.py
+++ b/tools/checklicenses/checklicenses.py
@@ -209,10 +209,15 @@
         'UNKNOWN',
     ],
 
+    # TODO(crbug.com/1447924): remove buildtools path
     # http://crbug.com/333508
     'buildtools/clang_format/script': [
         'UNKNOWN',
     ],
+    # http://crbug.com/333508
+    'third_party/clang_format/script': [
+        'UNKNOWN',
+    ],
     'third_party/devscripts': [
         'GPL (v2 or later)',
     ],
diff --git a/tools/clang/plugins/RawPtrManualPathsToIgnore.h b/tools/clang/plugins/RawPtrManualPathsToIgnore.h
index e89beda..5406d9cc 100644
--- a/tools/clang/plugins/RawPtrManualPathsToIgnore.h
+++ b/tools/clang/plugins/RawPtrManualPathsToIgnore.h
@@ -121,7 +121,9 @@
 //      grep -v third_party | \
 //      grep -v '^$' | \
 //      sort | uniq > ~/scratch/git-paths
+    // TODO(crbug.com/1447924): remove buildtools path
     "buildtools/clang_format/script/",
+    "third_party/clang_format/script/",
     "chrome/app/theme/default_100_percent/google_chrome/",
     "chrome/app/theme/default_200_percent/google_chrome/",
     "chrome/app/theme/google_chrome/",
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py
index bed534ad..bdcbed1 100755
--- a/tools/clang/scripts/build.py
+++ b/tools/clang/scripts/build.py
@@ -828,6 +828,10 @@
     rpmalloc_dir = DownloadRPMalloc()
     base_cmake_args.append('-DLLVM_INTEGRATED_CRT_ALLOC=' + rpmalloc_dir)
 
+    # Set a sysroot to make the build more hermetic.
+    base_cmake_args.append('-DLLVM_WINSYSROOT=' +
+                           os.path.dirname(os.path.dirname(GetWinSDKDir())))
+
   # Statically link libxml2 to make lld-link not require mt.exe on Windows,
   # and to make sure lld-link output on other platforms is identical to
   # lld-link on Windows (for cross-builds).
@@ -1103,15 +1107,18 @@
             'LLVM_INCLUDE_TESTS=OFF',
         ]))
   elif sys.platform == 'win32':
+    sysroot = os.path.dirname(os.path.dirname(GetWinSDKDir()))
     runtimes_triples_args.append(
         ('i386-pc-windows-msvc',
          compiler_rt_cmake_flags(sanitizers=False, profile=True) + [
              'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
+             'LLVM_WINSYSROOT=' + sysroot,
          ]))
     runtimes_triples_args.append(
         ('x86_64-pc-windows-msvc',
          compiler_rt_cmake_flags(sanitizers=True, profile=True) + [
              'LLVM_ENABLE_PER_TARGET_RUNTIME_DIR=OFF',
+             'LLVM_WINSYSROOT=' + sysroot,
          ]))
   elif sys.platform == 'darwin':
     compiler_rt_args = [
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index c615f9a..91ccd8c 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -36,7 +36,7 @@
 # Reverting problematic clang rolls is safe, though.
 # This is the output of `git describe` and is usable as a commit-ish.
 CLANG_REVISION = 'llvmorg-17-init-10134-g3da83fba'
-CLANG_SUB_REVISION = 1
+CLANG_SUB_REVISION = 2
 
 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION)
 RELEASE_VERSION = '17'
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 09eea4f1..a24085ca3a3 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -11712,6 +11712,32 @@
   </description>
 </action>
 
+<action name="IOS.DefaultBrowserPromo.SetUpList.Accepted">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <description>
+    The user accepted a Set Up List default browser promo modal, and was taken
+    to the Settings app. iOS only.
+  </description>
+</action>
+
+<action name="IOS.DefaultBrowserPromo.SetUpList.Appear">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <description>
+    The user triggered the SetUpList default browser promo and it appeared, iOS
+    only.
+  </description>
+</action>
+
+<action name="IOS.DefaultBrowserPromo.SetUpList.Dismiss">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <description>
+    The user dismissed a Set Up List default browser promo modal. iOS only.
+  </description>
+</action>
+
 <action name="IOS.DefaultBrowserPromo.TailoredFullscreen.Accepted">
   <owner>rkgibson@google.com</owner>
   <owner>djean@chromium.org</owner>
@@ -37583,6 +37609,15 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action-suffix separator="." ordering="suffix">
+  <suffix name="AccountStorage" label="Bookmark added in account storage."/>
+  <suffix name="LocalStorage"
+      label="Bookmark added in local storage which is not being synced."/>
+  <suffix name="LocalStorageSyncing"
+      label="Bookmark added in local storage which is being synced."/>
+  <affected-action name="Bookmarks.Added"/>
+</action-suffix>
+
 <action-suffix separator="_" ordering="suffix">
   <suffix name="AdaptiveButtonInTopToolbarCustomization_AddToBookmarks"
       label="For AdaptiveButtonInTopToolbarCustomization add to bookmarks
diff --git a/tools/metrics/actions/extract_actions.py b/tools/metrics/actions/extract_actions.py
index 111bfa2..b943c77 100755
--- a/tools/metrics/actions/extract_actions.py
+++ b/tools/metrics/actions/extract_actions.py
@@ -108,6 +108,7 @@
     'devtools_ui_bindings.cc',  # see AddDevToolsActions()
     'sharing_hub_bubble_controller.cc',  # share targets
     'sharing_hub_sub_menu_model.cc',  # share targets
+    'bookmark_metrics.cc',  # see AddBookmarkUsageActions()
 )
 
 # The path to the root of the repository.
@@ -204,6 +205,19 @@
   actions.add('BookmarkManager_NavigateTo_Search')
   actions.add('BookmarkManager_NavigateTo_SubFolder')
 
+
+def AddBookmarkUsageActions(actions):
+  """Add actions that are sent by the PDF plugin.
+
+  Arguments
+    actions: set of actions to add to.
+  """
+  actions.add('Bookmarks.Added')
+  actions.add('Bookmarks.Added.AccountStorage')
+  actions.add('Bookmarks.Added.LocalStorage')
+  actions.add('Bookmarks.Added.LocalStorageSyncing')
+
+
 def AddChromeOSActions(actions):
   """Add actions reported by non-Chrome processes in Chrome OS.
 
@@ -741,6 +755,7 @@
   AddLiteralActions(actions)
   AddAutomaticResetBannerActions(actions)
   AddBookmarkManagerActions(actions)
+  AddBookmarkUsageActions(actions)
   AddChromeOSActions(actions)
   AddExtensionActions(actions)
   AddHistoryPageActions(actions)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a40b016..cac00af 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19890,6 +19890,9 @@
   <int value="4" label="Excessive reporting orgins"/>
   <int value="5" label="Prohibited by browser policy"/>
   <int value="6" label="Success but noise applied"/>
+  <int value="7" label="Excessive destinations (per-reporting rate-limit)"/>
+  <int value="8" label="Excessive destinations (global rate-limit)"/>
+  <int value="9" label="Excessive destinations (both rate-limits)"/>
 </enum>
 
 <enum name="ConversionStorageSqlInitStatus">
@@ -54557,6 +54560,13 @@
   <int value="6" label="Migrated Multi Scenes to Single Scene"/>
 </enum>
 
+<enum name="IOSSetUpListItemType">
+  <int value="1" label="SignIn Sync"/>
+  <int value="2" label="Default Browser"/>
+  <int value="3" label="Autofill"/>
+  <int value="4" label="Follow"/>
+</enum>
+
 <enum name="IOSShareAction">
   <int value="0" label="Unknown"/>
   <int value="1" label="Cancel"/>
@@ -60209,6 +60219,7 @@
   <int value="-1342039126" label="AshNewSystemMenu:enabled"/>
   <int value="-1341685799" label="AutofillAssistantDirectActions:enabled"/>
   <int value="-1341092934" label="enable-accelerated-overflow-scroll"/>
+  <int value="-1340674276" label="Windows11MicaTitlebar:enabled"/>
   <int value="-1340518055"
       label="AssistantNonPersonalizedVoiceSearch:disabled"/>
   <int value="-1340460869" label="ShutdownSupportForKeepalive:enabled"/>
@@ -62591,6 +62602,7 @@
   <int value="-79327236" label="ModeSpecificPowerButton:enabled"/>
   <int value="-79015588"
       label="ExperimentalAccessibilityDictationMoreCommands:enabled"/>
+  <int value="-78721378" label="Windows11MicaTitlebar:disabled"/>
   <int value="-78308597" label="DesktopPartialTranslate:enabled"/>
   <int value="-78035185" label="custom_summary"/>
   <int value="-77872983" label="BookmarkAppsMac:disabled"/>
@@ -89393,6 +89405,13 @@
   <int value="3" label="Very Loose"/>
 </enum>
 
+<enum name="ReadAnythingScrollEvent">
+  <int value="0" label="Side panel selected"/>
+  <int value="1" label="Main panel selected"/>
+  <int value="2" label="Side panel scrolled"/>
+  <int value="3" label="Main panel scrolled"/>
+</enum>
+
 <enum name="ReadAnythingSettingsChange">
   <int value="0" label="Font change"/>
   <int value="1" label="Font size change"/>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index 9251476..9964b1b 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -1814,6 +1814,13 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.ReadAnything.ScrollEvent"
+    enum="ReadAnythingScrollEvent" expires_after="2023-11-30">
+  <owner>abigailbklein@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>Records when a scroll happens.</summary>
+</histogram>
+
 <histogram name="Accessibility.ReadAnything.SettingsChange"
     enum="ReadAnythingSettingsChange" expires_after="2023-11-30">
   <owner>abigailbklein@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml
index d2a1329..32fc13a 100644
--- a/tools/metrics/histograms/metadata/browser/histograms.xml
+++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -766,7 +766,7 @@
 </histogram>
 
 <histogram name="BrowserRenderProcessHost.LabeledInTaskManager"
-    enum="BooleanLabeledRendererTask" expires_after="2023-06-04">
+    enum="BooleanLabeledRendererTask" expires_after="2023-12-04">
   <owner>creis@chromium.org</owner>
   <owner>avi@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 7f230a047..a572df4 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -930,6 +930,40 @@
 </histogram>
 
 <histogram
+    name="History.Clusters.Backend.GetMostRecentClustersForUI.GetMostRecentPersistedClustersTimeHorizon{Source}"
+    units="hours" expires_after="2023-10-01">
+  <owner>sophiechang@chromium.org</owner>
+  <owner>chrome-journeys@google.com</owner>
+  <component>UI&gt;Browser&gt;Journeys</component>
+  <summary>
+    On navigation, context clusters are created, grown, and persisted.
+
+    On startup and every 1 hour afterwards, these context clusters have their
+    triggability calculated. Context clusters older than 2 hours are not updated
+    are not re-updated after the initial triggability is calculated. However, if
+    there are unclustered visits (due to visits from before the user had context
+    clustering enabled or synced visits without a cluster ID), they will also be
+    context clustered with their triggerability calculated as well.
+
+    When a user visits the Journeys WebUI, persisted clusters (both those with
+    and without triggerability calculated) are fetched. Clusters are then
+    augmented with appropriate metadata needed for displaying on the Journeys
+    WebUI (e.g. ranking, deduping, labeling).
+
+    When the keyword cache used to match omnibox inputs and show journey chips
+    or suggestions in the omnibox needs updating, context clusters (both those
+    with and without triggerability calculated) are fetched.
+
+    This histogram records the number of hours that the persisted clusters
+    returned covers.
+
+    Recorded for every batch of fetched clusters; can be recorded more than once
+    per load of {Source}.
+  </summary>
+  <token key="Source" variants="ClusteringRequestSource"/>
+</histogram>
+
+<histogram
     name="History.Clusters.Backend.GetMostRecentClustersForUI.{Segment}{Source}"
     units="ms" expires_after="2023-10-01">
   <owner>sophiechang@chromium.org</owner>
@@ -2160,7 +2194,7 @@
 </histogram>
 
 <histogram name="History.ForeignVisitsLegacy" units="visits"
-    expires_after="2023-07-01">
+    expires_after="2023-12-01">
   <owner>tommycli@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -2174,7 +2208,7 @@
 </histogram>
 
 <histogram name="History.ForeignVisitsNotRemapped" units="visits"
-    expires_after="2023-07-01">
+    expires_after="2023-12-01">
   <owner>tommycli@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -2188,7 +2222,7 @@
 </histogram>
 
 <histogram name="History.ForeignVisitsRemappableFrom" units="visits"
-    expires_after="2023-07-01">
+    expires_after="2023-12-01">
   <owner>tommycli@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -2202,7 +2236,7 @@
 </histogram>
 
 <histogram name="History.ForeignVisitsRemappableOpener" units="visits"
-    expires_after="2023-07-01">
+    expires_after="2023-12-01">
   <owner>tommycli@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -2216,7 +2250,7 @@
 </histogram>
 
 <histogram name="History.ForeignVisitsTotal" units="visits"
-    expires_after="2023-07-01">
+    expires_after="2023-12-01">
   <owner>tommycli@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index c2327db..19aecd1 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -590,6 +590,17 @@
   </token>
 </histogram>
 
+<histogram name="IOS.DefaultBrowserPromo.SetUpList.Action"
+    enum="IOSDefaultBrowserFullscreenPromoAction" expires_after="2023-12-01">
+  <owner>sebsg@chromium.org</owner>
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>
+    The action taken by the user in response to the Set Up List default browser
+    promo.
+  </summary>
+</histogram>
+
 <histogram name="IOS.DefaultBrowserPromo.TailoredFullscreen.{Action}"
     enum="IOSDefaultBrowserTailoredPromoType" expires_after="2023-12-01">
   <owner>sebsg@chromium.org</owner>
@@ -1838,7 +1849,7 @@
 </histogram>
 
 <histogram name="IOS.PushNotification.APNSDeviceRegistration"
-    enum="BooleanSuccess" expires_after="2023-07-01">
+    enum="BooleanSuccess" expires_after="2024-05-24">
   <owner>ajuma@google.com</owner>
   <owner>danieltwhite@google.com</owner>
   <summary>
@@ -1851,7 +1862,7 @@
 </histogram>
 
 <histogram name="IOS.PushNotification.ChimeDeviceRegistration"
-    enum="BooleanSuccess" expires_after="2023-07-01">
+    enum="BooleanSuccess" expires_after="2024-05-24">
   <owner>ajuma@google.com</owner>
   <owner>danieltwhite@google.com</owner>
   <summary>
@@ -1875,7 +1886,7 @@
 </histogram>
 
 <histogram name="IOS.PushNotification.EnabledPermisisons"
-    enum="PushNotificationPermissionPromptStatus" expires_after="2023-12-12">
+    enum="PushNotificationPermissionPromptStatus" expires_after="2024-05-24">
   <owner>ajuma@google.com</owner>
   <owner>danieltwhite@google.com</owner>
   <summary>
@@ -1886,7 +1897,7 @@
 </histogram>
 
 <histogram name="IOS.PushNotification.IncomingNotificationProcessingTime"
-    units="ms" expires_after="2023-07-01">
+    units="ms" expires_after="2024-05-24">
   <owner>ajuma@google.com</owner>
   <owner>danieltwhite@google.com</owner>
   <summary>
@@ -2192,6 +2203,46 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.SetUpList.Displayed" enum="Boolean"
+    expires_after="2023-12-01">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>
+    This histogram counts the number of times the Set Up List is shown on the
+    NTP.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SetUpList.ItemCompleted" enum="IOSSetUpListItemType"
+    expires_after="2023-12-01">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>
+    This histogram counts the number of times Set Up List items are completed.
+    It is recorded when the Set Up List item is marked complete in prefs, or
+    when Set Up List is loaded and the code detects that an item was completed
+    since the last time Set Up List was displayed.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SetUpList.ItemDisplayed" enum="IOSSetUpListItemType"
+    expires_after="2023-12-01">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>
+    This histogram counts the number of times Set Up List items are displayed.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SetUpList.ItemSelected" enum="IOSSetUpListItemType"
+    expires_after="2023-12-01">
+  <owner>scottyoder@google.com</owner>
+  <owner>bling-get-set-up@google.com</owner>
+  <summary>
+    This histogram counts the number of times Set Up List items are selected.
+  </summary>
+</histogram>
+
 <histogram name="IOS.ShareExtension.ReceivedEntriesCount" units="files"
     expires_after="2023-10-04">
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 7987670..edef374 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -2118,6 +2118,21 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.PartitionAlloc.PartitionRoot.ExtrasSize" units="bytes"
+    expires_after="2024-05-22">
+  <owner>bartekn@chromium.org</owner>
+  <owner>keishi@chromium.org</owner>
+  <summary>
+    Size of the extras configured for PartitionAlloc's `PartitionRoot` struct.
+    This is specific to the main partition, which powers the bulk of
+    PartitionAlloc-Everywhere. Recorded once for each process during allocator
+    shim configuration.
+
+    Extras are optional. Extras can include the PartitionAlloc cookie (16B) and
+    the MiraclePtr ref-count (up to 16B).
+  </summary>
+</histogram>
+
 <histogram name="Memory.PartitionAlloc.PeriodicPurge" units="microseconds"
     expires_after="2023-11-12">
   <owner>lizeb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index b6ba130..9e269d4 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -4084,10 +4084,10 @@
   </summary>
 </histogram>
 
-<histogram name="Conversions.SourceStoredStatus2"
+<histogram name="Conversions.SourceStoredStatus3"
     enum="ConversionStorageSourceStatus" expires_after="2023-10-08">
+  <owner>tquintanilla@chromium.org</owner>
   <owner>anthonygarant@chromium.org</owner>
-  <owner>linnan@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
   <summary>
     Measures how often sources are stored successfully or rejected and why.
@@ -10494,7 +10494,7 @@
 </histogram>
 
 <histogram name="RenderTextHarfBuzz.GetFallbackFontTime" units="ms"
-    expires_after="2023-06-04">
+    expires_after="2024-06-04">
   <owner>ccameron@chromium.org</owner>
   <owner>etienneb@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 485d8d1..88f68f0 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -6906,7 +6906,7 @@
   </metric>
   <metric name="TextSearchCount">
     <summary>
-      The number of times text search box was used.
+      The number of times text search box was used, clamped to a max of 10.
     </summary>
   </metric>
   <metric name="VQ.ClickPosition">
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 80413a3..c23b2e2 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,24 +5,24 @@
             "full_remote_path": "perfetto-luci-artifacts/v34.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "37ea2c7c1864fde9d14936ce3fb2c66e6c319528",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6b2ac6940bfdfb2c5565a3ae8775a30afa90ef86/trace_processor_shell.exe"
+            "hash": "3918d79c0de038900b2c30e386136a00e519e0a9",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/9d82cb852512318a848e2050551eb305bf0872ac/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "336a42cb9ec3c417e13a97816271fec10cdf67e5",
             "full_remote_path": "perfetto-luci-artifacts/v34.0/linux-arm/trace_processor_shell"
         },
         "mac": {
-            "hash": "da5a123d53eb7df9f00681b4a42a3558cf461a30",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/24fae90b5e4ce0b2b970670e2795f9c258db5033/trace_processor_shell"
+            "hash": "fc7c372cbbe5cde63d5d13a9eefbb5668262eca5",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/313bfe34b8a790700c4550439d70b71fa665613e/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "c32364e05e22cdf82ee0866aedd11c0e2050809c",
             "full_remote_path": "perfetto-luci-artifacts/v34.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "cdad876cbc4e3e2170742ba6d5a933163edaef5a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/5595859b53c31e50af146ca66be8ac9f69f812ce/trace_processor_shell"
+            "hash": "02b7b3c80951e4fda942d49484f36880cfeb2648",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/313bfe34b8a790700c4550439d70b71fa665613e/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/vim/clang-format.vim b/tools/vim/clang-format.vim
index 478dbb3f..3d1970a 100644
--- a/tools/vim/clang-format.vim
+++ b/tools/vim/clang-format.vim
@@ -7,7 +7,7 @@
 " current line.)
 
 let s:script = expand('<sfile>:p:h') .
-  \'/../../buildtools/clang_format/script/clang-format.py'
+  \'/../../third_party/clang_format/script/clang-format.py'
 let s:shortcut = has('mac') ? "<D-I>" : "<C-I>"
 let s:pyf = has("python3") ? ":py3f" : ":pyf"
 
diff --git a/ui/display/manager/display_change_observer_unittest.cc b/ui/display/manager/display_change_observer_unittest.cc
index ca57d6c4..e369b8304 100644
--- a/ui/display/manager/display_change_observer_unittest.cc
+++ b/ui/display/manager/display_change_observer_unittest.cc
@@ -551,7 +551,12 @@
           .SetNativeMode(MakeDisplayMode(1920, 1080, true, 60))
           .SetColorSpace(display_color_space)
           .SetBitsPerChannel(10u)
-          .SetHDRStaticMetadata({600.0, 500.0, 0.01})
+          .SetHDRStaticMetadata(
+              {600.0, 500.0, 0.01,
+               gfx::HDRStaticMetadata::EotfMask({
+                   gfx::HDRStaticMetadata::Eotf::kGammaSdrRange,
+                   gfx::HDRStaticMetadata::Eotf::kPq,
+               })})
           .Build();
 
   ui::DeviceDataManager::CreateInstance();
diff --git a/ui/display/mojom/display_mojom_traits_unittest.cc b/ui/display/mojom/display_mojom_traits_unittest.cc
index 488442fa..dfe6771 100644
--- a/ui/display/mojom/display_mojom_traits_unittest.cc
+++ b/ui/display/mojom/display_mojom_traits_unittest.cc
@@ -297,7 +297,10 @@
   const bool color_correction_in_linear_space = true;
   const gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateREC709();
   const int32_t bits_per_channel = 8;
-  const gfx::HDRStaticMetadata hdr_static_metadata(100.0, 80.0, 0.0);
+  const gfx::HDRStaticMetadata hdr_static_metadata(
+      100.0, 80.0, 0.0,
+      gfx::HDRStaticMetadata::EotfMask(
+          {gfx::HDRStaticMetadata::Eotf::kGammaSdrRange}));
   const std::string display_name("whatever display_name");
   const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("a/cb");
   const int64_t product_code = 19;
@@ -355,7 +358,10 @@
   const bool color_correction_in_linear_space = true;
   const gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateREC709();
   const uint32_t bits_per_channel = 8u;
-  const gfx::HDRStaticMetadata hdr_static_metadata(100.0, 80.0, 0.0);
+  const gfx::HDRStaticMetadata hdr_static_metadata(
+      100.0, 80.0, 0.0,
+      gfx::HDRStaticMetadata::EotfMask(
+          {gfx::HDRStaticMetadata::Eotf::kGammaSdrRange}));
   const std::string display_name("whatever display_name");
   const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("z/b");
   const int64_t product_code = 9;
@@ -414,7 +420,10 @@
   const std::string display_name("HP Z24i");
   const gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateSRGB();
   const uint32_t bits_per_channel = 8u;
-  const gfx::HDRStaticMetadata hdr_static_metadata(100.0, 80.0, 0.0);
+  const gfx::HDRStaticMetadata hdr_static_metadata(
+      100.0, 80.0, 0.0,
+      gfx::HDRStaticMetadata::EotfMask(
+          {gfx::HDRStaticMetadata::Eotf::kGammaSdrRange}));
   const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("a/cb");
   const int64_t product_code = 139;
   const int32_t year_of_manufacture = 2018;
@@ -475,7 +484,12 @@
   const gfx::ColorSpace display_color_space =
       gfx::ColorSpace::CreateDisplayP3D65();
   const uint32_t bits_per_channel = 9u;
-  const gfx::HDRStaticMetadata hdr_static_metadata(200.0, 100.0, 0.0);
+  const gfx::HDRStaticMetadata hdr_static_metadata(
+      200.0, 100.0, 0.0,
+      gfx::HDRStaticMetadata::EotfMask({
+          gfx::HDRStaticMetadata::Eotf::kGammaSdrRange,
+          gfx::HDRStaticMetadata::Eotf::kPq,
+      }));
   const std::string display_name("");
   const base::FilePath sys_path;
   const int64_t product_code = 139;
diff --git a/ui/display/util/edid_parser.cc b/ui/display/util/edid_parser.cc
index e1ae6ef..f8728306 100644
--- a/ui/display/util/edid_parser.cc
+++ b/ui/display/util/edid_parser.cc
@@ -686,6 +686,10 @@
             if (supported_eotfs_bitfield[entry])
               supported_color_transfer_ids_.insert(kTransferIDMap[entry]);
           }
+          hdr_static_metadata_ =
+              absl::make_optional<gfx::HDRStaticMetadata>({});
+          hdr_static_metadata_->supported_eotf_mask =
+              base::checked_cast<uint8_t>(supported_eotfs_bitfield.to_ulong());
 
           // See CEA 861.3-2015, Sec.7.5.13, "HDR Static Metadata Data Block"
           // for details on the following calculations.
@@ -694,8 +698,6 @@
           if (length_of_data_block <= 3)
             break;
           const uint8_t desired_content_max_luminance = edid[data_offset + 4];
-          hdr_static_metadata_ =
-              absl::make_optional<gfx::HDRStaticMetadata>({});
           hdr_static_metadata_->max =
               50.0 * pow(2, desired_content_max_luminance / 32.0);
 
diff --git a/ui/display/util/edid_parser_unittest.cc b/ui/display/util/edid_parser_unittest.cc
index 6fefe72..2011809 100644
--- a/ui/display/util/edid_parser_unittest.cc
+++ b/ui/display/util/edid_parser_unittest.cc
@@ -734,7 +734,14 @@
        gfx::ColorSpace::MatrixID::BT2020_CL}},
      {gfx::ColorSpace::TransferID::BT709, gfx::ColorSpace::TransferID::PQ,
       gfx::ColorSpace::TransferID::HLG},
-     absl::make_optional<gfx::HDRStaticMetadata>(603.666, 530.095, 0.00454),
+     absl::make_optional<gfx::HDRStaticMetadata>(
+         603.666,
+         530.095,
+         0.00454,
+         gfx::HDRStaticMetadata::EotfMask({
+             gfx::HDRStaticMetadata::Eotf::kGammaSdrRange,
+             gfx::HDRStaticMetadata::Eotf::kPq,
+         })),
      24,
      kHDRMetadata,
      kHDRMetadataLength},
diff --git a/ui/gfx/hdr_static_metadata.cc b/ui/gfx/hdr_static_metadata.cc
index 4f61f8a..a82eb60 100644
--- a/ui/gfx/hdr_static_metadata.cc
+++ b/ui/gfx/hdr_static_metadata.cc
@@ -7,8 +7,14 @@
 namespace gfx {
 
 HDRStaticMetadata::HDRStaticMetadata() = default;
-HDRStaticMetadata::HDRStaticMetadata(double max, double max_avg, double min)
-    : max(max), max_avg(max_avg), min(min) {}
+HDRStaticMetadata::HDRStaticMetadata(double max,
+                                     double max_avg,
+                                     double min,
+                                     uint8_t supported_eotf_mask)
+    : max(max),
+      max_avg(max_avg),
+      min(min),
+      supported_eotf_mask(supported_eotf_mask) {}
 HDRStaticMetadata::HDRStaticMetadata(const HDRStaticMetadata& rhs) = default;
 HDRStaticMetadata& HDRStaticMetadata::operator=(const HDRStaticMetadata& rhs) =
     default;
diff --git a/ui/gfx/hdr_static_metadata.h b/ui/gfx/hdr_static_metadata.h
index 19b2c7a8..b031b165 100644
--- a/ui/gfx/hdr_static_metadata.h
+++ b/ui/gfx/hdr_static_metadata.h
@@ -5,6 +5,10 @@
 #ifndef UI_GFX_HDR_STATIC_METADATA_H_
 #define UI_GFX_HDR_STATIC_METADATA_H_
 
+#include <cstdint>
+#include <vector>
+
+#include "base/numerics/safe_conversions.h"
 #include "ui/gfx/color_space_export.h"
 
 namespace gfx {
@@ -13,6 +17,19 @@
 // Reflects CEA 861.G-2018, Sec.7.5.13, "HDR Static Metadata Data Block"
 // A value of 0.0 in any of the fields means that it's not indicated.
 struct COLOR_SPACE_EXPORT HDRStaticMetadata {
+  // See Table 43 Data Byte 1 - Electro-Optical Transfer Function
+  enum class Eotf {
+    // "If “Traditional Gamma - SDR Luminance Range” is indicated, then the
+    // maximum encoded luminance is typically mastered to 100 cd/m2"
+    kGammaSdrRange = 0,
+    // "If “Traditional Gamma – HDR Luminance Range” is indicated, then the
+    // maximum encoded luminance is understood to be the maximum luminance of
+    // the Sink device."
+    kGammaHdrRange = 1,
+    kPq = 2,
+    kHlg = 3,
+  };
+
   // "Desired Content Max Luminance Data. This is the content’s absolute peak
   // luminance (in cd/m2) (likely only in a small area of the screen) that the
   // display prefers for optimal content rendering."
@@ -24,14 +41,33 @@
   // "Desired Content Min Luminance. This is the minimum value of the content
   // (in cd/m2) that the display prefers for optimal content rendering."
   double min;
+  // "Electro-Optical Transfer Functions supported by the Sink." See Table 85
+  // Supported Electro-Optical Transfer Function.
+  uint8_t supported_eotf_mask;
 
   HDRStaticMetadata();
-  HDRStaticMetadata(double max, double max_avg, double min);
+  HDRStaticMetadata(double max,
+                    double max_avg,
+                    double min,
+                    uint8_t supported_eotf_mask);
   HDRStaticMetadata(const HDRStaticMetadata& rhs);
   HDRStaticMetadata& operator=(const HDRStaticMetadata& rhs);
 
   bool operator==(const HDRStaticMetadata& rhs) const {
-    return ((max == rhs.max) && (max_avg == rhs.max_avg) && (min == rhs.min));
+    return ((max == rhs.max) && (max_avg == rhs.max_avg) && (min == rhs.min) &&
+            supported_eotf_mask == rhs.supported_eotf_mask);
+  }
+
+  bool IsEotfSupported(Eotf eotf) const {
+    return (supported_eotf_mask & EotfMask({eotf})) != 0;
+  }
+
+  static uint8_t EotfMask(std::vector<Eotf> eotfs) {
+    int eotf_mask = 0;
+    for (const Eotf eotf : eotfs) {
+      eotf_mask |= (1 << static_cast<int>(eotf));
+    }
+    return base::checked_cast<uint8_t>(eotf_mask);
   }
 };
 
diff --git a/ui/gfx/mojom/hdr_static_metadata.mojom b/ui/gfx/mojom/hdr_static_metadata.mojom
index dd4acaf0..76c60e0 100644
--- a/ui/gfx/mojom/hdr_static_metadata.mojom
+++ b/ui/gfx/mojom/hdr_static_metadata.mojom
@@ -9,4 +9,5 @@
   float max;
   float max_avg;
   float min;
+  uint8 supported_eotf_mask;
 };
diff --git a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc
index 11005c7..d16831b 100644
--- a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc
+++ b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.cc
@@ -13,6 +13,7 @@
   output->max = data.max();
   output->max_avg = data.max_avg();
   output->min = data.min();
+  output->supported_eotf_mask = data.supported_eotf_mask();
   return true;
 }
 
diff --git a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h
index fe259c0..f445d28 100644
--- a/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h
+++ b/ui/gfx/mojom/hdr_static_metadata_mojom_traits.h
@@ -20,6 +20,9 @@
     return input.max_avg;
   }
   static float min(const gfx::HDRStaticMetadata& input) { return input.min; }
+  static uint8_t supported_eotf_mask(const gfx::HDRStaticMetadata& input) {
+    return input.supported_eotf_mask;
+  }
 
   static bool Read(gfx::mojom::HDRStaticMetadataDataView data,
                    gfx::HDRStaticMetadata* output);
diff --git a/ui/ozone/platform/drm/gpu/drm_display.cc b/ui/ozone/platform/drm/gpu/drm_display.cc
index 0b5248f..d868998 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.cc
+++ b/ui/ozone/platform/drm/gpu/drm_display.cc
@@ -341,38 +341,53 @@
   return privacy_screen_property_->SetPrivacyScreenProperty(enabled);
 }
 
-bool DrmDisplay::SetHDR10Mode() {
+gfx::HDRStaticMetadata::Eotf DrmDisplay::GetEotf(
+    const gfx::ColorSpace::TransferID transfer_id) {
+  if (!is_hdr_capable_) {
+    return gfx::HDRStaticMetadata::Eotf::kGammaSdrRange;
+  }
+
+  switch (transfer_id) {
+    case gfx::ColorSpace::TransferID::PQ:
+      return gfx::HDRStaticMetadata::Eotf::kPq;
+    case gfx::ColorSpace::TransferID::HLG:
+      return gfx::HDRStaticMetadata::Eotf::kHlg;
+    case gfx::ColorSpace::TransferID::SRGB_HDR:
+    case gfx::ColorSpace::TransferID::LINEAR_HDR:
+    case gfx::ColorSpace::TransferID::CUSTOM_HDR:
+    case gfx::ColorSpace::TransferID::PIECEWISE_HDR:
+    case gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
+      return gfx::HDRStaticMetadata::Eotf::kGammaHdrRange;
+    default:
+      NOTREACHED();
+      return gfx::HDRStaticMetadata::Eotf::kGammaSdrRange;
+  }
+}
+
+bool DrmDisplay::SetHdrOutputMetadata(const gfx::ColorSpace color_space) {
   DCHECK(connector_);
   DCHECK(hdr_static_metadata_.has_value());
-  ScopedDrmPropertyPtr color_space_property(
-      drm_->GetProperty(connector_.get(), kColorSpace));
-  if (!color_space_property) {
-    PLOG(INFO) << "'" << kColorSpace << "' property doesn't exist.";
-    return false;
-  }
-  if (!drm_->SetProperty(
-          connector_->connector_id, color_space_property->prop_id,
-          GetEnumValueForName(*drm_, color_space_property->prop_id,
-                              kColorSpaceBT2020RGBEnumName))) {
-    PLOG(INFO) << "Cannot set '" << kColorSpaceBT2020RGBEnumName
-               << "' to 'Colorspace' property.";
-    return false;
-  }
+  DCHECK(color_space.IsValid());
 
   drm_hdr_output_metadata* hdr_output_metadata =
       static_cast<drm_hdr_output_metadata*>(
           malloc(sizeof(drm_hdr_output_metadata)));
   hdr_output_metadata->metadata_type = 0;
   hdr_output_metadata->hdmi_metadata_type1.metadata_type = 0;
-  hdr_output_metadata->hdmi_metadata_type1.eotf = 2;  // PQ
+
+  gfx::HDRStaticMetadata::Eotf eotf = GetEotf(color_space.GetTransferID());
+  DCHECK(hdr_static_metadata_->IsEotfSupported(eotf));
+  hdr_output_metadata->hdmi_metadata_type1.eotf = static_cast<uint8_t>(eotf);
+
   hdr_output_metadata->hdmi_metadata_type1.max_cll = 0;
-  hdr_output_metadata->hdmi_metadata_type1.max_fall = 0;
+  hdr_output_metadata->hdmi_metadata_type1.max_fall =
+      hdr_static_metadata_->max_avg;
   hdr_output_metadata->hdmi_metadata_type1.max_display_mastering_luminance =
       hdr_static_metadata_->max;
   hdr_output_metadata->hdmi_metadata_type1.min_display_mastering_luminance =
       hdr_static_metadata_->min;
-  gfx::ColorSpace hdr10 = gfx::ColorSpace::CreateHDR10();
-  SkColorSpacePrimaries primaries = hdr10.GetPrimaries();
+
+  SkColorSpacePrimaries primaries = color_space.GetPrimaries();
   constexpr int kPrimariesFixedPoint = 50000;
   hdr_output_metadata->hdmi_metadata_type1.display_primaries[0].x =
       primaries.fRX * kPrimariesFixedPoint;
@@ -412,6 +427,27 @@
   return true;
 }
 
+bool DrmDisplay::SetHDR10Mode() {
+  DCHECK(connector_);
+  DCHECK(hdr_static_metadata_.has_value());
+  ScopedDrmPropertyPtr color_space_property(
+      drm_->GetProperty(connector_.get(), kColorSpace));
+  if (!color_space_property) {
+    PLOG(INFO) << "'" << kColorSpace << "' property doesn't exist.";
+    return false;
+  }
+  if (!drm_->SetProperty(
+          connector_->connector_id, color_space_property->prop_id,
+          GetEnumValueForName(*drm_, color_space_property->prop_id,
+                              kColorSpaceBT2020RGBEnumName))) {
+    PLOG(INFO) << "Cannot set '" << kColorSpaceBT2020RGBEnumName
+               << "' to 'Colorspace' property.";
+    return false;
+  }
+
+  return SetHdrOutputMetadata(gfx::ColorSpace::CreateHDR10());
+}
+
 void DrmDisplay::SetColorSpace(const gfx::ColorSpace& color_space) {
   // There's only something to do if the display supports HDR.
   if (!is_hdr_capable_)
diff --git a/ui/ozone/platform/drm/gpu/drm_display.h b/ui/ozone/platform/drm/gpu/drm_display.h
index 6bf0971..d3c005b7 100644
--- a/ui/ozone/platform/drm/gpu/drm_display.h
+++ b/ui/ozone/platform/drm/gpu/drm_display.h
@@ -91,6 +91,7 @@
       const std::vector<display::GammaRampRGBEntry>& degamma_lut,
       const std::vector<display::GammaRampRGBEntry>& gamma_lut);
   bool SetPrivacyScreen(bool enabled);
+  bool SetHdrOutputMetadata(const gfx::ColorSpace color_space);
   bool SetHDR10Mode();
   void SetColorSpace(const gfx::ColorSpace& color_space);
 
@@ -100,6 +101,8 @@
   void CommitGammaCorrection(
       const std::vector<display::GammaRampRGBEntry>& degamma_lut,
       const std::vector<display::GammaRampRGBEntry>& gamma_lut);
+  gfx::HDRStaticMetadata::Eotf GetEotf(
+      const gfx::ColorSpace::TransferID transfer_id);
 
   const int64_t display_id_;
   const int64_t base_connector_id_;
diff --git a/ui/webui/resources/cr_elements/cr_url_list_item/cr_url_list_item.html b/ui/webui/resources/cr_elements/cr_url_list_item/cr_url_list_item.html
index 2caba82c..50b4704 100644
--- a/ui/webui/resources/cr_elements/cr_url_list_item/cr_url_list_item.html
+++ b/ui/webui/resources/cr_elements/cr_url_list_item/cr_url_list_item.html
@@ -83,7 +83,7 @@
   @media (prefers-color-scheme: dark) {
     #iconContainer {
       --cr-icon-color: var(--google-grey-100);
-      background: rgba(var(--google-grey-200-rgb), .11);
+      background: var(--google-grey-800);
     }
   }
 
@@ -186,13 +186,19 @@
   }
 
   .folder {
-    --cr-icon-color: currentColor;
+    --cr-icon-color: var(--google-grey-700);
     --cr-icon-size: 16px;
     height: 16px;
     margin: 0;
     width: 16px;
   }
 
+  @media (prefers-color-scheme: dark) {
+    .folder {
+      --cr-icon-color: var(--google-grey-100);
+    }
+  }
+
   :host-context([chrome-refresh-2023]) .folder {
     --cr-icon-size: 20px;
     height: 20px;
@@ -208,6 +214,7 @@
     align-items: center;
     background: var(--google-grey-50);
     border-radius: var(--cr-url-list-item-count-border-radius) 0 0 0;
+    color: var(--google-grey-700);
     display: flex;
     grid-column: 2;
     grid-row: 2;
@@ -234,7 +241,8 @@
 
   @media (prefers-color-scheme: dark) {
     :host([size=large]) .count {
-      background: rgba(var(--google-grey-200-rgb), .11);
+      background: var(--google-grey-800);
+      color: var(--google-grey-500);
     }
   }