diff --git a/AUTHORS b/AUTHORS
index 55b8bc8..7f036859 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -916,6 +916,7 @@
 Md Raiyan bin Sayeed <mrbsayee@uwaterloo.ca>
 Md. Sadiqul Amin <sadiqul.amin@samsung.com>
 Md Sami Uddin <md.sami@samsung.com>
+Merajul Arefin <merajularefin@gmail.com>
 Micha Hanselmann <micha.hanselmann@gmail.com>
 Michael Cirone <mikecirone@gmail.com>
 Michael Constant <mconst@gmail.com>
diff --git a/DEPS b/DEPS
index c6f1a51..ff213fd 100644
--- a/DEPS
+++ b/DEPS
@@ -306,7 +306,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': 'e7e8a521abc8803b1f54642997eb592485627081',
+  'skia_revision': '3a3475d12f220b795821eabae517ab7cb6f12c9f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -377,7 +377,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '5725e8f3792e1a4a2463cb598704d27d9116e006',
+  'catapult_revision': 'b142d415308e1f3ace94f0f39807bf0198ee004f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
@@ -425,7 +425,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '920d43f256464487a350cefa02763ce325490f2f',
+  'quiche_revision': '4069625b5260ab0df30f733e17d6ab7d1703cbce',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -465,7 +465,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'cros_components_revision': '4d5c5a69d68bd814b9857800c594f5895cd1204e',
+  'cros_components_revision': 'b1b1b0b6ff08289f3f5d960947aa12db68775d41',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -497,7 +497,7 @@
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       'a88e6d6daa3e091f662c8a198b5d3a0d3a723705',
+  'libcxx_revision':       '1f70899ab6d9d2c3c455fa7adb3f3cbd7bfdf215',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:7367b0df0a0aa25440303998d54045bda73935a5',
@@ -821,7 +821,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '6569ea82c7d827a0bc59fbd91829ff57f424d301',
+    'a60e161b8d92adf6d1a44622e66d17ff3b52e6e5',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1193,7 +1193,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'd9dab49dc651e5c5104a3793fb5113cc474dc673',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '18bb70aed6953810172bda05512ea42d1f39ef81',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1698,7 +1698,7 @@
   },
 
   'src/third_party/re2/src':
-    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + '71857b0112ab6d9e1478f0212951c38c4a0ee1c8',
+    Var('chromium_git') + '/external/github.com/google/re2.git' + '@' + '9d0b5bf57cf0dc5388568127bcf079812d3f989e',
 
   'src/third_party/r8': {
       'packages': [
@@ -3952,7 +3952,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        'b24d334c6bbe2a5fbc7681a6a8b53a43698cf79e',
+        '379a082a7632ea69ede7b89fb2a9ff0b6899e148',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index c52d98d..96086130 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -5548,7 +5548,7 @@
         # ...' will not. This may require some tweaking to catch these cases
         # without triggering a lot of false positives. Leaving it naive and
         # less matchy for now.
-        r'/\b(?i)((black|white)list|master|slave)\b',  # nocheck
+        r'/(?i)\b((black|white)list|master|slave)\b',  # nocheck
         (
             'Please don\'t use blacklist, whitelist, '  # nocheck
             'or slave in your',  # nocheck
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
index 39ea3f2e..31ab40eb 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidScrollIntegrationTest.java
@@ -179,18 +179,18 @@
     }
 
     private static final String TEST_PAGE_COMMON_HEADERS =
-            "<meta name=\"viewport\" content=\""
-                    + "width=device-width, initial-scale=1, minimum-scale=1\"> "
-                    + "<style type=\"text/css\"> "
-                    + "   body { "
-                    + "      margin: 0px; "
-                    + "   } "
-                    + "   div { "
-                    + "      width:10000px; "
-                    + "      height:10000px; "
-                    + "      background-color: blue; "
-                    + "   } "
-                    + "</style> ";
+            """
+            <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
+            <style type="text/css">
+                body {
+                    margin: 0px;
+                }
+                div {
+                    width: 10000px;
+                    height: 10000px;
+                    background-color: blue;
+                }
+            </style>""";
     private static final String TEST_PAGE_COMMON_CONTENT = "<div>test div</div> ";
 
     private String makeTestPage(
@@ -198,30 +198,32 @@
         String content = TEST_PAGE_COMMON_CONTENT + extraContent;
         if (onscrollObserver != null) {
             content +=
-                    "<script> "
-                            + "   window.onscroll = function(oEvent) { "
-                            + "       "
-                            + onscrollObserver
-                            + ".notifyJava(); "
-                            + "   } "
-                            + "</script>";
+                    String.format(
+                            """
+                            <script>
+                            window.onscroll = function(oEvent) {
+                                %s.notifyJava();
+                            }
+                            </script>""",
+                            onscrollObserver);
         }
         if (firstFrameObserver != null) {
             content +=
-                    "<script> "
-                            + "   window.framesToIgnore = 20; "
-                            + "   window.onAnimationFrame = function(timestamp) { "
-                            + "     if (window.framesToIgnore == 0) { "
-                            + "         "
-                            + firstFrameObserver
-                            + ".notifyJava(); "
-                            + "     } else {"
-                            + "       window.framesToIgnore -= 1; "
-                            + "       window.requestAnimationFrame(window.onAnimationFrame); "
-                            + "     } "
-                            + "   }; "
-                            + "   window.requestAnimationFrame(window.onAnimationFrame); "
-                            + "</script>";
+                    String.format(
+                            """
+                            <script>
+                            window.framesToIgnore = 20;
+                            window.onAnimationFrame = function(timestamp) {
+                                if (window.framesToIgnore == 0) {
+                                    %s.notifyJava();
+                                } else {
+                                    window.framesToIgnore -= 1;
+                                    window.requestAnimationFrame(window.onAnimationFrame);
+                                }
+                            };
+                            window.requestAnimationFrame(window.onAnimationFrame);
+                            </script>""",
+                            firstFrameObserver);
         }
         return CommonResources.makeHtmlPageFrom(TEST_PAGE_COMMON_HEADERS, content);
     }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index af336e41..5b00904d 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -84,8 +84,8 @@
     "accelerometer/accelerometer_types.cc",
     "accelerometer/accelerometer_types.h",
     "accessibility/a11y_feature_type.h",
-    "accessibility/accessibility_controller_impl.cc",
-    "accessibility/accessibility_controller_impl.h",
+    "accessibility/accessibility_controller.cc",
+    "accessibility/accessibility_controller.h",
     "accessibility/accessibility_delegate.h",
     "accessibility/accessibility_event_handler_manager.cc",
     "accessibility/accessibility_event_handler_manager.h",
diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc
index 2fb83e8..453572ec 100644
--- a/ash/accelerators/accelerator_commands.cc
+++ b/ash/accelerators/accelerator_commands.cc
@@ -5,7 +5,7 @@
 #include "ash/accelerators/accelerator_commands.h"
 
 #include "ash/accelerators/accelerator_notifications.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc
index aab6a5ad..8c12d8884 100644
--- a/ash/accelerators/accelerator_controller_impl.cc
+++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -14,7 +14,7 @@
 #include "ash/accelerators/accelerator_notifications.h"
 #include "ash/accelerators/accelerator_shift_disable_capslock_state_machine.h"
 #include "ash/accelerators/debug_commands.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/devicetype.h"
 #include "ash/debug.h"
diff --git a/ash/accelerators/accelerator_controller_impl.h b/ash/accelerators/accelerator_controller_impl.h
index c0b5065..acd9c24 100644
--- a/ash/accelerators/accelerator_controller_impl.h
+++ b/ash/accelerators/accelerator_controller_impl.h
@@ -21,7 +21,7 @@
 #include "ash/accelerators/ash_accelerator_configuration.h"
 #include "ash/accelerators/exit_warning_handler.h"
 #include "ash/accelerators/tablet_volume_controller.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/accelerators.h"
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index a4701b0..3cf2cca 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -11,7 +11,7 @@
 #include "ash/accelerators/accelerator_notifications.h"
 #include "ash/accelerators/accelerator_table.h"
 #include "ash/accelerators/pre_target_accelerator_handler.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
diff --git a/ash/accelerators/spoken_feedback_toggler.cc b/ash/accelerators/spoken_feedback_toggler.cc
index bf1bed8..4ab63e8 100644
--- a/ash/accelerators/spoken_feedback_toggler.cc
+++ b/ash/accelerators/spoken_feedback_toggler.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "ash/accelerators/key_hold_detector.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ui/events/event.h"
 
diff --git a/ash/accelerators/spoken_feedback_toggler_unittest.cc b/ash/accelerators/spoken_feedback_toggler_unittest.cc
index 791e196..ba77dbc 100644
--- a/ash/accelerators/spoken_feedback_toggler_unittest.cc
+++ b/ash/accelerators/spoken_feedback_toggler_unittest.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/accelerators/spoken_feedback_toggler.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/wm/window_util.h"
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller.cc
similarity index 99%
rename from ash/accessibility/accessibility_controller_impl.cc
rename to ash/accessibility/accessibility_controller.cc
index 996a8f2..aa33d82 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller.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 "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 
 #include <map>
 #include <memory>
diff --git a/ash/accessibility/accessibility_controller_impl.h b/ash/accessibility/accessibility_controller.h
similarity index 98%
rename from ash/accessibility/accessibility_controller_impl.h
rename to ash/accessibility/accessibility_controller.h
index 9ec7d30..7e8513b 100644
--- a/ash/accessibility/accessibility_controller_impl.h
+++ b/ash/accessibility/accessibility_controller.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 ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_IMPL_H_
-#define ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_IMPL_H_
+#ifndef ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_H_
+#define ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_H_
 
 #include <memory>
 #include <optional>
@@ -789,4 +789,4 @@
 
 }  // namespace ash
 
-#endif  // ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_IMPL_H_
+#endif  // ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_H_
diff --git a/ash/accessibility/accessibility_controller_test_api_impl.cc b/ash/accessibility/accessibility_controller_test_api_impl.cc
index f8acb69..6993c77 100644
--- a/ash/accessibility/accessibility_controller_test_api_impl.cc
+++ b/ash/accessibility/accessibility_controller_test_api_impl.cc
@@ -5,7 +5,7 @@
 #include "ash/accessibility/accessibility_controller_test_api_impl.h"
 
 #include "ash/accelerators/accelerator_controller_impl.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "base/functional/callback.h"
 
diff --git a/ash/accessibility/accessibility_controller_unittest.cc b/ash/accessibility/accessibility_controller_unittest.cc
index 481d4228..d48f5af 100644
--- a/ash/accessibility/accessibility_controller_unittest.cc
+++ b/ash/accessibility/accessibility_controller_unittest.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 "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 
 #include <string>
 #include <utility>
diff --git a/ash/accessibility/autoclick/autoclick_controller.cc b/ash/accessibility/autoclick/autoclick_controller.cc
index 20fedb6..3dbe29d 100644
--- a/ash/accessibility/autoclick/autoclick_controller.cc
+++ b/ash/accessibility/autoclick/autoclick_controller.cc
@@ -6,7 +6,7 @@
 
 #include <tuple>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_drag_event_rewriter.h"
 #include "ash/accessibility/autoclick/autoclick_ring_handler.h"
 #include "ash/accessibility/autoclick/autoclick_scroll_position_handler.h"
diff --git a/ash/accessibility/autoclick/autoclick_unittest.cc b/ash/accessibility/autoclick/autoclick_unittest.cc
index a6c1d009..8e093b2 100644
--- a/ash/accessibility/autoclick/autoclick_unittest.cc
+++ b/ash/accessibility/autoclick/autoclick_unittest.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 "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
diff --git a/ash/accessibility/chromevox/key_accessibility_enabler_unittest.cc b/ash/accessibility/chromevox/key_accessibility_enabler_unittest.cc
index f3277714..a29896a2 100644
--- a/ash/accessibility/chromevox/key_accessibility_enabler_unittest.cc
+++ b/ash/accessibility/chromevox/key_accessibility_enabler_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/accessibility/chromevox/key_accessibility_enabler.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/accessibility/chromevox/spoken_feedback_enabler.cc b/ash/accessibility/chromevox/spoken_feedback_enabler.cc
index d28d450b..21b4e3f9 100644
--- a/ash/accessibility/chromevox/spoken_feedback_enabler.cc
+++ b/ash/accessibility/chromevox/spoken_feedback_enabler.cc
@@ -4,7 +4,7 @@
 
 #include "ash/accessibility/chromevox/spoken_feedback_enabler.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "base/numerics/safe_conversions.h"
 #include "ui/events/base_event_utils.h"
diff --git a/ash/accessibility/chromevox/touch_exploration_controller.cc b/ash/accessibility/chromevox/touch_exploration_controller.cc
index 7721c24..3f35041 100644
--- a/ash/accessibility/chromevox/touch_exploration_controller.cc
+++ b/ash/accessibility/chromevox/touch_exploration_controller.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/chromevox/touch_accessibility_enabler.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
diff --git a/ash/accessibility/chromevox/touch_exploration_manager.cc b/ash/accessibility/chromevox/touch_exploration_manager.cc
index 8c65edb..6e2dc93 100644
--- a/ash/accessibility/chromevox/touch_exploration_manager.cc
+++ b/ash/accessibility/chromevox/touch_exploration_manager.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/chromevox/touch_exploration_controller.h"
 #include "ash/accessibility/ui/accessibility_focus_ring_controller_impl.h"
 #include "ash/constants/ash_switches.h"
diff --git a/ash/accessibility/default_accessibility_delegate.cc b/ash/accessibility/default_accessibility_delegate.cc
index bfcb51e..9cd97ef8 100644
--- a/ash/accessibility/default_accessibility_delegate.cc
+++ b/ash/accessibility/default_accessibility_delegate.cc
@@ -6,7 +6,7 @@
 
 #include <limits>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 
 namespace ash {
diff --git a/ash/accessibility/dictation_nudge_controller_unittest.cc b/ash/accessibility/dictation_nudge_controller_unittest.cc
index b692e78..6ba15590 100644
--- a/ash/accessibility/dictation_nudge_controller_unittest.cc
+++ b/ash/accessibility/dictation_nudge_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/dictation_nudge.h"
 #include "ash/accessibility/dictation_nudge_controller.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/accessibility/magnifier/docked_magnifier_controller.cc b/ash/accessibility/magnifier/docked_magnifier_controller.cc
index 5f1181b..5c21fbb 100644
--- a/ash/accessibility/magnifier/docked_magnifier_controller.cc
+++ b/ash/accessibility/magnifier/docked_magnifier_controller.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/magnifier_utils.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/display/cursor_window_controller.h"
diff --git a/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc b/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc
index c52d35a..ebf607e 100644
--- a/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc
+++ b/ash/accessibility/magnifier/fullscreen_magnifier_controller.cc
@@ -9,7 +9,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_delegate.h"
 #include "ash/accessibility/magnifier/magnifier_utils.h"
 #include "ash/display/root_window_transformers.h"
diff --git a/ash/accessibility/scoped_a11y_override_window_setter.cc b/ash/accessibility/scoped_a11y_override_window_setter.cc
index b5f117d..9d93684 100644
--- a/ash/accessibility/scoped_a11y_override_window_setter.cc
+++ b/ash/accessibility/scoped_a11y_override_window_setter.cc
@@ -4,7 +4,7 @@
 
 #include "ash/accessibility/scoped_a11y_override_window_setter.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 
 namespace ash {
diff --git a/ash/accessibility/test_accessibility_controller_client.cc b/ash/accessibility/test_accessibility_controller_client.cc
index 0612f13..c8504253 100644
--- a/ash/accessibility/test_accessibility_controller_client.cc
+++ b/ash/accessibility/test_accessibility_controller_client.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "base/time/time.h"
 #include "ui/gfx/geometry/point_f.h"
 
diff --git a/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc b/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc
index be74242..0c9a5fb 100644
--- a/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc
+++ b/ash/accessibility/ui/accessibility_highlight_controller_unittest.cc
@@ -9,7 +9,7 @@
 #include <cmath>
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/ui/accessibility_cursor_ring_layer.h"
 #include "ash/accessibility/ui/accessibility_focus_ring_controller_impl.h"
 #include "ash/public/cpp/shell_window_ids.h"
diff --git a/ash/app_list/app_list_metrics_unittest.cc b/ash/app_list/app_list_metrics_unittest.cc
index d7bd942..0acff95f 100644
--- a/ash/app_list/app_list_metrics_unittest.cc
+++ b/ash/app_list/app_list_metrics_unittest.cc
@@ -7,7 +7,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_bubble_presenter.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/app_list_metrics.h"
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index 195ff1b..6ba6939 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -5,7 +5,7 @@
 #include <memory>
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_bubble_presenter.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/app_list_model_provider.h"
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index bc6ce36..0a00324a 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -9,7 +9,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_model_provider.h"
 #include "ash/app_list/app_list_util.h"
 #include "ash/app_list/model/app_list_folder_item.h"
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index f00a5ad..c7ddff7 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -9,7 +9,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_metrics.h"
 #include "ash/app_list/app_list_model_provider.h"
 #include "ash/app_list/app_list_util.h"
diff --git a/ash/app_list/views/remove_query_confirmation_dialog.cc b/ash/app_list/views/remove_query_confirmation_dialog.cc
index e76d8ba..e1b8068 100644
--- a/ash/app_list/views/remove_query_confirmation_dialog.cc
+++ b/ash/app_list/views/remove_query_confirmation_dialog.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/ash_typography.h"
 #include "ash/public/cpp/view_shadow.h"
 #include "ash/shell.h"
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc
index 0b73e57..8600b265 100644
--- a/ash/ash_prefs.cc
+++ b/ash/ash_prefs.cc
@@ -6,7 +6,7 @@
 
 #include "ash/accelerators/accelerator_prefs.h"
 #include "ash/accelerators/ash_accelerator_configuration.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/ambient/ambient_controller.h"
 #include "ash/ambient/managed/screensaver_images_policy_handler.h"
diff --git a/ash/assistant/assistant_controller_impl.cc b/ash/assistant/assistant_controller_impl.cc
index 43250ef..39cf200e 100644
--- a/ash/assistant/assistant_controller_impl.cc
+++ b/ash/assistant/assistant_controller_impl.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/assistant/assistant_interaction_controller_impl.cc b/ash/assistant/assistant_interaction_controller_impl.cc
index 2fb4447..483f6e5 100644
--- a/ash/assistant/assistant_interaction_controller_impl.cc
+++ b/ash/assistant/assistant_interaction_controller_impl.cc
@@ -7,7 +7,7 @@
 #include <optional>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/assistant/assistant_controller_impl.h"
 #include "ash/assistant/model/assistant_interaction_model_observer.h"
 #include "ash/assistant/model/assistant_query.h"
diff --git a/ash/autotest_private_api_utils.cc b/ash/autotest_private_api_utils.cc
index 46e1b846..a86de47 100644
--- a/ash/autotest_private_api_utils.cc
+++ b/ash/autotest_private_api_utils.cc
@@ -18,6 +18,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animator.h"
+#include "ui/display/screen.h"
 
 namespace ash {
 namespace {
@@ -153,8 +154,7 @@
 
 bool WaitForLauncherState(AppListViewState target_state,
                           base::OnceClosure closure) {
-  const bool in_tablet_mode =
-      Shell::Get()->tablet_mode_controller()->InTabletMode();
+  const bool in_tablet_mode = display::Screen::GetScreen()->InTabletMode();
   if (in_tablet_mode) {
     // App-list can't enter kPeeking or kHalf state in tablet mode. Thus
     // |target_state| should be either kClosed, kFullscreenAllApps or
diff --git a/ash/capture_mode/base_capture_mode_session.cc b/ash/capture_mode/base_capture_mode_session.cc
index 368d65a..372ba69 100644
--- a/ash/capture_mode/base_capture_mode_session.cc
+++ b/ash/capture_mode/base_capture_mode_session.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "ash/capture_mode/base_capture_mode_session.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
 #include "ash/capture_mode/capture_mode_types.h"
 #include "ash/capture_mode/capture_mode_util.h"
diff --git a/ash/capture_mode/capture_mode_camera_controller.cc b/ash/capture_mode/capture_mode_camera_controller.cc
index fef00e9..a7ad562 100644
--- a/ash/capture_mode/capture_mode_camera_controller.cc
+++ b/ash/capture_mode/capture_mode_camera_controller.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 #include <cstring>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/capture_mode/capture_mode_camera_preview_view.h"
 #include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_controller.h"
diff --git a/ash/capture_mode/capture_mode_camera_preview_view.h b/ash/capture_mode/capture_mode_camera_preview_view.h
index a6b7ee0..3b75c30 100644
--- a/ash/capture_mode/capture_mode_camera_preview_view.h
+++ b/ash/capture_mode/capture_mode_camera_preview_view.h
@@ -5,7 +5,7 @@
 #ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_PREVIEW_VIEW_H_
 #define ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_PREVIEW_VIEW_H_
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/capture_mode/camera_video_frame_renderer.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
diff --git a/ash/capture_mode/capture_mode_camera_unittests.cc b/ash/capture_mode/capture_mode_camera_unittests.cc
index aa89f0f..5a7b4348 100644
--- a/ash/capture_mode/capture_mode_camera_unittests.cc
+++ b/ash/capture_mode/capture_mode_camera_unittests.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 "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/capture_mode/capture_mode_bar_view.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
diff --git a/ash/capture_mode/capture_mode_demo_tools_controller.cc b/ash/capture_mode/capture_mode_demo_tools_controller.cc
index 747c63b8..5ed1a8a6 100644
--- a/ash/capture_mode/capture_mode_demo_tools_controller.cc
+++ b/ash/capture_mode/capture_mode_demo_tools_controller.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/capture_mode/capture_mode_session.h"
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index 50bfb011..6b239a3 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/magnifier_glass.h"
 #include "ash/capture_mode/capture_label_view.h"
 #include "ash/capture_mode/capture_mode_behavior.h"
@@ -609,10 +609,9 @@
     return;
   }
 
-  // Hide mouse cursor in tablet mode.
-  auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
-  if (tablet_mode_controller->InTabletMode() &&
-      !tablet_mode_controller->IsInDevTabletMode()) {
+  // Hide mouse cursor in tablet mode except for the dev tablet mode.
+  if (display::Screen::GetScreen()->InTabletMode() &&
+      !Shell::Get()->tablet_mode_controller()->IsInDevTabletMode()) {
     cursor_setter_->HideCursor();
     return;
   }
diff --git a/ash/capture_mode/capture_mode_test_util.cc b/ash/capture_mode/capture_mode_test_util.cc
index 95349548..b6aa310 100644
--- a/ash/capture_mode/capture_mode_test_util.cc
+++ b/ash/capture_mode/capture_mode_test_util.cc
@@ -5,7 +5,7 @@
 #include "ash/capture_mode/capture_mode_test_util.h"
 
 #include "ash/accessibility/a11y_feature_type.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/capture_mode/capture_mode_bar_view.h"
 #include "ash/capture_mode/capture_mode_controller.h"
diff --git a/ash/capture_mode/capture_mode_util.cc b/ash/capture_mode/capture_mode_util.cc
index 4d381a7..8e195d3 100644
--- a/ash/capture_mode/capture_mode_util.cc
+++ b/ash/capture_mode/capture_mode_util.cc
@@ -4,7 +4,7 @@
 
 #include "ash/capture_mode/capture_mode_util.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
 #include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_controller.h"
diff --git a/ash/display/cursor_window_controller_unittest.cc b/ash/display/cursor_window_controller_unittest.cc
index a83089e..02966430f 100644
--- a/ash/display/cursor_window_controller_unittest.cc
+++ b/ash/display/cursor_window_controller_unittest.cc
@@ -7,7 +7,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_constants.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/display/display_util.h"
diff --git a/ash/display/display_move_window_util.cc b/ash/display/display_move_window_util.cc
index e88085a..129702a 100644
--- a/ash/display/display_move_window_util.cc
+++ b/ash/display/display_move_window_util.cc
@@ -8,7 +8,7 @@
 #include <algorithm>
 #include <array>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/wm/bounds_tracker/window_bounds_tracker.h"
 #include "ash/wm/mru_window_tracker.h"
diff --git a/ash/display/screen_orientation_controller.cc b/ash/display/screen_orientation_controller.cc
index 4835700..63ae614 100644
--- a/ash/display/screen_orientation_controller.cc
+++ b/ash/display/screen_orientation_controller.cc
@@ -10,7 +10,6 @@
 #include "ash/constants/ash_switches.h"
 #include "ash/shell.h"
 #include "ash/wm/mru_window_tracker.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/window_state_observer.h"
 #include "ash/wm/window_util.h"
@@ -23,6 +22,7 @@
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
+#include "ui/display/screen.h"
 #include "ui/display/util/display_util.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/wm/public/activation_client.h"
@@ -666,8 +666,7 @@
     return;
   }
 
-  bool in_tablet_mode = Shell::Get()->tablet_mode_controller()->InTabletMode();
-  if (!in_tablet_mode) {
+  if (!display::Screen::GetScreen()->InTabletMode()) {
     if (IsAutoRotationAllowed()) {
       // We ignore windows and app requested orientation locks while the UI is
       // in clamshell mode when the device is physically in a tablet state.
diff --git a/ash/events/accessibility_event_rewriter.cc b/ash/events/accessibility_event_rewriter.cc
index 6fd032ce..b8e52a5 100644
--- a/ash/events/accessibility_event_rewriter.cc
+++ b/ash/events/accessibility_event_rewriter.cc
@@ -4,7 +4,7 @@
 
 #include "ash/events/accessibility_event_rewriter.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
 #include "ash/accessibility/switch_access/point_scan_controller.h"
diff --git a/ash/events/accessibility_event_rewriter_unittest.cc b/ash/events/accessibility_event_rewriter_unittest.cc
index 07a9aaff..2962e66 100644
--- a/ash/events/accessibility_event_rewriter_unittest.cc
+++ b/ash/events/accessibility_event_rewriter_unittest.cc
@@ -8,7 +8,7 @@
 #include <optional>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_constants.h"
 #include "ash/public/cpp/accessibility_event_rewriter_delegate.h"
 #include "ash/shell.h"
diff --git a/ash/events/select_to_speak_event_handler.cc b/ash/events/select_to_speak_event_handler.cc
index 7210c8a..fa27fe4b 100644
--- a/ash/events/select_to_speak_event_handler.cc
+++ b/ash/events/select_to_speak_event_handler.cc
@@ -4,7 +4,7 @@
 
 #include "ash/events/select_to_speak_event_handler.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_event_handler_manager.h"
 #include "ash/public/cpp/select_to_speak_event_handler_delegate.h"
 #include "ash/shell.h"
diff --git a/ash/events/select_to_speak_event_handler_unittest.cc b/ash/events/select_to_speak_event_handler_unittest.cc
index 5aebee5..1d1e9a8 100644
--- a/ash/events/select_to_speak_event_handler_unittest.cc
+++ b/ash/events/select_to_speak_event_handler_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <set>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/select_to_speak_event_handler_delegate.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
diff --git a/ash/frame/multitask_menu_nudge_controller_unittest.cc b/ash/frame/multitask_menu_nudge_controller_unittest.cc
index 0e70b9e..da1cf0d 100644
--- a/ash/frame/multitask_menu_nudge_controller_unittest.cc
+++ b/ash/frame/multitask_menu_nudge_controller_unittest.cc
@@ -42,7 +42,7 @@
 // Returns the nudge controller associated with `window`.
 chromeos::MultitaskMenuNudgeController* GetNudgeControllerForWindow(
     aura::Window* window) {
-  if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
+  if (display::Screen::GetScreen()->InTabletMode()) {
     return TabletModeControllerTestApi()
         .tablet_mode_window_manager()
         ->tablet_mode_multitask_menu_controller()
diff --git a/ash/game_dashboard/game_dashboard_button.cc b/ash/game_dashboard/game_dashboard_button.cc
index d79172a4..b92c295 100644
--- a/ash/game_dashboard/game_dashboard_button.cc
+++ b/ash/game_dashboard/game_dashboard_button.cc
@@ -43,9 +43,9 @@
 constexpr int kIconHeight = 20;
 constexpr gfx::RoundedCornersF kRoundedCornerRadius =
     gfx::RoundedCornersF(12.0f);
+constexpr gfx::Insets kArrowMargins = gfx::Insets::TLBR(0, 6, 0, 0);
 constexpr gfx::Insets kButtonBorderInsets = gfx::Insets::TLBR(0, 12, 0, 8);
 constexpr gfx::Insets kGamepadIconMargins = gfx::Insets::TLBR(0, 0, 0, 8);
-constexpr gfx::Insets kDropdownArrowMargins = gfx::Insets::TLBR(0, 6, 0, 0);
 
 // 30% opacity for disabled state.
 constexpr SkAlpha kAlphaForDisabled =
@@ -92,9 +92,9 @@
   title_view_ = AddChildView(
       bubble_utils::CreateLabel(ash::TypographyToken::kCrosButton2));
 
-  // Add the dropdown icon view.
-  dropdown_icon_view_ = AddChildView(std::make_unique<views::ImageView>());
-  dropdown_icon_view_->SetProperty(views::kMarginsKey, kDropdownArrowMargins);
+  // Add the arrow icon view.
+  arrow_icon_view_ = AddChildView(std::make_unique<views::ImageView>());
+  arrow_icon_view_->SetProperty(views::kMarginsKey, kArrowMargins);
 }
 
 GameDashboardButton::~GameDashboardButton() = default;
@@ -104,7 +104,7 @@
     return;
   }
   toggled_ = toggled;
-  UpdateDropDownArrow();
+  UpdateArrowIcon();
 }
 
 void GameDashboardButton::OnRecordingStarted() {
@@ -149,15 +149,15 @@
   UpdateViews();
 }
 
-void GameDashboardButton::UpdateDropDownArrow() {
-  DCHECK(dropdown_icon_view_);
-  const gfx::VectorIcon& dropdown_icon =
-      toggled_ ? kGdDropUpArrowIcon : kGdDropDownArrowIcon;
+void GameDashboardButton::UpdateArrowIcon() {
+  DCHECK(arrow_icon_view_);
+  const gfx::VectorIcon& arrow_icon =
+      toggled_ ? kGdButtonUpArrowIcon : kGdButtonDownArrowIcon;
   const SkColor icon_color =
       GetColor(GetColorProvider(), GetIconAndLabelEnabledColorId(is_recording_),
                GetEnabled());
-  dropdown_icon_view_->SetImage(
-      ui::ImageModel::FromVectorIcon(dropdown_icon, icon_color, kIconHeight));
+  arrow_icon_view_->SetImage(
+      ui::ImageModel::FromVectorIcon(arrow_icon, icon_color, kIconHeight));
 }
 
 void GameDashboardButton::UpdateViews() {
@@ -182,7 +182,7 @@
   gamepad_icon_view_->SetImage(ui::ImageModel::FromVectorIcon(
       chromeos::kGameDashboardGamepadIcon, icon_and_label_color, kIconHeight));
   title_view_->SetEnabledColor(icon_and_label_color);
-  UpdateDropDownArrow();
+  UpdateArrowIcon();
 }
 
 void GameDashboardButton::SetTitle(const std::u16string& title_text) {
diff --git a/ash/game_dashboard/game_dashboard_button.h b/ash/game_dashboard/game_dashboard_button.h
index c9c7b50..819451a 100644
--- a/ash/game_dashboard/game_dashboard_button.h
+++ b/ash/game_dashboard/game_dashboard_button.h
@@ -39,7 +39,7 @@
 // user to stop the recording.
 //
 // The first "icon_view" always shows the gamepad icon.
-// The second "icon_view" shows a dropdown arrow. Calling `SetToggled()` with
+// The second "icon_view" shows an up or down arrow. Calling `SetToggled()` with
 // true will replace the second "icon_view" with an the up arrow. Called with
 // false, it will show the down arrow.
 class GameDashboardButton : public views::Button {
@@ -78,9 +78,9 @@
  private:
   friend class GameDashboardContextTestApi;
 
-  // Updates the `dropdown_icon_view_` icon. If `toggled_` is true, it'll show
+  // Updates the icon in `arrow_icon_view_`. If `toggled_` is true, it'll show
   // the up arrow, otherwise the down arrow.
-  void UpdateDropDownArrow();
+  void UpdateArrowIcon();
 
   // Updates `is_recording_` with `is_recording`, then updates all the views.
   void UpdateRecordingState(bool is_recording);
@@ -97,7 +97,7 @@
   // Owned by views hierarchy.
   raw_ptr<views::ImageView, ExperimentalAsh> gamepad_icon_view_;
   raw_ptr<views::Label, ExperimentalAsh> title_view_;
-  raw_ptr<views::ImageView, ExperimentalAsh> dropdown_icon_view_;
+  raw_ptr<views::ImageView, ExperimentalAsh> arrow_icon_view_;
 
   // If true, the game window is being recorded, otherwise false.
   bool is_recording_ = false;
diff --git a/ash/keyboard/virtual_keyboard_controller_unittest.cc b/ash/keyboard/virtual_keyboard_controller_unittest.cc
index f715ee4..398832e 100644
--- a/ash/keyboard/virtual_keyboard_controller_unittest.cc
+++ b/ash/keyboard/virtual_keyboard_controller_unittest.cc
@@ -7,7 +7,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/ime/test_ime_controller_client.h"
 #include "ash/keyboard/keyboard_controller_impl.h"
diff --git a/ash/login/ui/local_authentication_request_view.cc b/ash/login/ui/local_authentication_request_view.cc
index 3702727..260058bb 100644
--- a/ash/login/ui/local_authentication_request_view.cc
+++ b/ash/login/ui/local_authentication_request_view.cc
@@ -4,7 +4,7 @@
 
 #include "ash/login/ui/local_authentication_request_view.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/login/ui/arrow_button_view.h"
 #include "ash/login/ui/local_authentication_request_widget.h"
diff --git a/ash/login/ui/login_password_view.cc b/ash/login/ui/login_password_view.cc
index 72db0cb..c2c68cff 100644
--- a/ash/login/ui/login_password_view.cc
+++ b/ash/login/ui/login_password_view.cc
@@ -4,7 +4,7 @@
 
 #include "ash/login/ui/login_password_view.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/login/login_screen_controller.h"
 #include "ash/login/ui/arrow_button_view.h"
 #include "ash/login/ui/hover_notifier.h"
diff --git a/ash/login/ui/pin_request_view_unittest.cc b/ash/login/ui/pin_request_view_unittest.cc
index 901a363..2419277 100644
--- a/ash/login/ui/pin_request_view_unittest.cc
+++ b/ash/login/ui/pin_request_view_unittest.cc
@@ -8,7 +8,7 @@
 #include <optional>
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/keyboard/keyboard_controller_impl.h"
 #include "ash/login/mock_login_screen_client.h"
 #include "ash/login/ui/arrow_button_view.h"
diff --git a/ash/public/cpp/tablet_mode.h b/ash/public/cpp/tablet_mode.h
index c64cd8b8..18220b4f 100644
--- a/ash/public/cpp/tablet_mode.h
+++ b/ash/public/cpp/tablet_mode.h
@@ -52,14 +52,6 @@
   virtual void RemoveObserver(TabletModeObserver* observer) = 0;
 
   // Deprecated, do NOT use this. Please use
-  // display::Screen::GetScreen()->InTabletMode() instead. To override tablet
-  // state for testing, use display::Screen::OverrideTabletStateForTesting.
-  // TODO(crbug.com/1502114): Remove this.
-  //
-  // Returns true if the system is in tablet mode.
-  virtual bool InTabletMode() const = 0;
-
-  // Deprecated, do NOT use this. Please use
   // display::Screen::GetScreen()->InTabletMode() instead.
   // TODO(crbug.com/1502114): Remove this.
   //
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index ee3a438..0d4ea27d 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -121,8 +121,8 @@
     "focus_mode_lamp.icon",
     "folder.icon",
     "four_files.icon",
-    "gd_drop_down_arrow.icon",
-    "gd_drop_up_arrow.icon",
+    "gd_button_down_arrow.icon",
+    "gd_button_up_arrow.icon",
     "gd_game_controls.icon",
     "gd_help.icon",
     "gd_record_game.icon",
diff --git a/ash/resources/vector_icons/gd_button_down_arrow.icon b/ash/resources/vector_icons/gd_button_down_arrow.icon
new file mode 100644
index 0000000..353397ed
--- /dev/null
+++ b/ash/resources/vector_icons/gd_button_down_arrow.icon
@@ -0,0 +1,13 @@
+// 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.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 13.83f, 6.67f,
+LINE_TO, 10, 10.79f,
+LINE_TO, 6.18f, 6.67f,
+LINE_TO, 5, 7.94f,
+LINE_TO, 10, 13.33f,
+LINE_TO, 15, 7.94f,
+LINE_TO, 13.83f, 6.67f,
+CLOSE
diff --git a/ash/resources/vector_icons/gd_button_up_arrow.icon b/ash/resources/vector_icons/gd_button_up_arrow.icon
new file mode 100644
index 0000000..14b99538
--- /dev/null
+++ b/ash/resources/vector_icons/gd_button_up_arrow.icon
@@ -0,0 +1,13 @@
+// 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.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 13.83f, 13.33f,
+LINE_TO, 10, 9.21f,
+LINE_TO, 6.18f, 13.33f,
+LINE_TO, 5, 12.06f,
+LINE_TO, 10, 6.67f,
+LINE_TO, 15, 12.06f,
+LINE_TO, 13.83f, 13.33f,
+CLOSE
diff --git a/ash/resources/vector_icons/gd_drop_down_arrow.icon b/ash/resources/vector_icons/gd_drop_down_arrow.icon
deleted file mode 100644
index e958810d..0000000
--- a/ash/resources/vector_icons/gd_drop_down_arrow.icon
+++ /dev/null
@@ -1,10 +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.
-
-CANVAS_DIMENSIONS, 20,
-MOVE_TO, 6, 8,
-LINE_TO, 10, 12,
-LINE_TO, 14, 8,
-H_LINE_TO, 6,
-CLOSE
diff --git a/ash/resources/vector_icons/gd_drop_up_arrow.icon b/ash/resources/vector_icons/gd_drop_up_arrow.icon
deleted file mode 100644
index 32395ee5..0000000
--- a/ash/resources/vector_icons/gd_drop_up_arrow.icon
+++ /dev/null
@@ -1,10 +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.
-
-CANVAS_DIMENSIONS, 20,
-MOVE_TO, 6, 12,
-LINE_TO, 10, 8,
-LINE_TO, 14, 12,
-H_LINE_TO, 6,
-CLOSE
diff --git a/ash/shelf/back_button_unittest.cc b/ash/shelf/back_button_unittest.cc
index cde287c..a9fec5d 100644
--- a/ash/shelf/back_button_unittest.cc
+++ b/ash/shelf/back_button_unittest.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/accelerators/accelerator_controller_impl.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/shelf/drag_handle.cc b/ash/shelf/drag_handle.cc
index 13a5cd6f..9df0f90 100644
--- a/ash/shelf/drag_handle.cc
+++ b/ash/shelf/drag_handle.cc
@@ -7,7 +7,7 @@
 #include <optional>
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/controls/contextual_tooltip.h"
 #include "ash/public/cpp/shelf_config.h"
diff --git a/ash/shelf/drag_handle_unittest.cc b/ash/shelf/drag_handle_unittest.cc
index b271a58..8d9181f 100644
--- a/ash/shelf/drag_handle_unittest.cc
+++ b/ash/shelf/drag_handle_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/shelf/drag_handle.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shelf/shelf_layout_manager.h"
 #include "ash/shelf/shelf_navigation_widget.h"
 #include "ash/shelf/shelf_widget.h"
diff --git a/ash/shelf/home_button_unittest.cc b/ash/shelf/home_button_unittest.cc
index cca7a3c..023e1d5 100644
--- a/ash/shelf/home_button_unittest.cc
+++ b/ash/shelf/home_button_unittest.cc
@@ -9,7 +9,7 @@
 #include <tuple>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/app_list/views/app_list_view.h"
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index e6112ab..3fa7e91 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -6,7 +6,7 @@
 
 #include <optional>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 63163d7c..d90379a 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -8,7 +8,7 @@
 #include <cmath>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/animation/animation_change_type.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/app_list_metrics.h"
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index fd1b13b..87927bd 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -10,7 +10,7 @@
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/accelerators/accelerator_table.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/test/app_list_test_helper.h"
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 5257fced..f9179fb 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -10,7 +10,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/shell.cc b/ash/shell.cc
index 33c3c2a3..27abf30 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -20,7 +20,7 @@
 #include "ash/accelerators/shortcut_input_handler.h"
 #include "ash/accelerators/spoken_feedback_toggler.h"
 #include "ash/accelerometer/accelerometer_reader.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_delegate.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/accessibility/chromevox/key_accessibility_enabler.h"
@@ -74,6 +74,7 @@
 #include "ash/events/event_rewriter_controller_impl.h"
 #include "ash/fast_ink/laser/laser_pointer_controller.h"
 #include "ash/focus_cycler.h"
+#include "ash/frame/multitask_menu_nudge_delegate_ash.h"
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/frame/snap_controller_impl.h"
 #include "ash/frame_throttler/frame_throttling_controller.h"
@@ -207,7 +208,6 @@
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/multi_display/multi_display_metrics_controller.h"
 #include "ash/wm/multi_display/persistent_window_controller.h"
-#include "ash/frame/multitask_menu_nudge_delegate_ash.h"
 #include "ash/wm/native_cursor_manager_ash.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/pip/pip_controller.h"
diff --git a/ash/style/system_toast_style.cc b/ash/style/system_toast_style.cc
index 7bc3b9a..23c5d34 100644
--- a/ash/style/system_toast_style.cc
+++ b/ash/style/system_toast_style.cc
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/scoped_a11y_override_window_setter.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/accessibility/accessibility_detailed_view.cc b/ash/system/accessibility/accessibility_detailed_view.cc
index 8e54745a..1c9dcdc 100644
--- a/ash/system/accessibility/accessibility_detailed_view.cc
+++ b/ash/system/accessibility/accessibility_detailed_view.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_delegate.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/constants/ash_pref_names.h"
diff --git a/ash/system/accessibility/accessibility_detailed_view_unittest.cc b/ash/system/accessibility/accessibility_detailed_view_unittest.cc
index 805b8d5d..353743a5 100644
--- a/ash/system/accessibility/accessibility_detailed_view_unittest.cc
+++ b/ash/system/accessibility/accessibility_detailed_view_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/system/accessibility/accessibility_feature_pod_controller.cc b/ash/system/accessibility/accessibility_feature_pod_controller.cc
index 608a629..82f24fa 100644
--- a/ash/system/accessibility/accessibility_feature_pod_controller.cc
+++ b/ash/system/accessibility/accessibility_feature_pod_controller.cc
@@ -6,7 +6,7 @@
 
 #include "ash/system/accessibility/accessibility_feature_pod_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_delegate.h"
 #include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/ash_view_ids.h"
diff --git a/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc b/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc
index ec1bf252..f7998cd 100644
--- a/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc
+++ b/ash/system/accessibility/accessibility_feature_pod_controller_unittest.cc
@@ -5,7 +5,7 @@
 #include "ash/system/accessibility/accessibility_feature_pod_controller.h"
 
 #include "ash/accessibility/a11y_feature_type.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/quick_settings_catalogs.h"
 #include "ash/shell.h"
 #include "ash/system/unified/feature_tile.h"
diff --git a/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc b/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc
index 9c7de5b9..6f65b36d 100644
--- a/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/autoclick_menu_bubble_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/autoclick_menu_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/public/cpp/locale_update_controller.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/system/accessibility/autoclick_menu_view.cc b/ash/system/accessibility/autoclick_menu_view.cc
index ba1ee36..42f21faff 100644
--- a/ash/system/accessibility/autoclick_menu_view.cc
+++ b/ash/system/accessibility/autoclick_menu_view.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/autoclick_menu_view.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
diff --git a/ash/system/accessibility/dictation_bubble_controller_unittest.cc b/ash/system/accessibility/dictation_bubble_controller_unittest.cc
index 7aac7a1..c6db12d4 100644
--- a/ash/system/accessibility/dictation_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/dictation_bubble_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/dictation_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
diff --git a/ash/system/accessibility/dictation_button_tray.cc b/ash/system/accessibility/dictation_button_tray.cc
index 7eae210..03ab7f9 100644
--- a/ash/system/accessibility/dictation_button_tray.cc
+++ b/ash/system/accessibility/dictation_button_tray.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/dictation_button_tray.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/tray_background_view_catalog.h"
 #include "ash/metrics/user_metrics_recorder.h"
diff --git a/ash/system/accessibility/dictation_button_tray_unittest.cc b/ash/system/accessibility/dictation_button_tray_unittest.cc
index 61eab445..984a106 100644
--- a/ash/system/accessibility/dictation_button_tray_unittest.cc
+++ b/ash/system/accessibility/dictation_button_tray_unittest.cc
@@ -5,7 +5,7 @@
 #include "ash/system/accessibility/dictation_button_tray.h"
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/constants/ash_features.h"
 #include "ash/display/window_tree_host_manager.h"
diff --git a/ash/system/accessibility/floating_accessibility_controller.cc b/ash/system/accessibility/floating_accessibility_controller.cc
index 1ac5b86..84b031d 100644
--- a/ash/system/accessibility/floating_accessibility_controller.cc
+++ b/ash/system/accessibility/floating_accessibility_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/floating_accessibility_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/system/accessibility/floating_accessibility_controller_unittest.cc b/ash/system/accessibility/floating_accessibility_controller_unittest.cc
index 2b6c071..5364b5a 100644
--- a/ash/system/accessibility/floating_accessibility_controller_unittest.cc
+++ b/ash/system/accessibility/floating_accessibility_controller_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/accessibility/a11y_feature_type.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/public/cpp/ime_info.h"
diff --git a/ash/system/accessibility/floating_accessibility_view.cc b/ash/system/accessibility/floating_accessibility_view.cc
index 819bf82..1acc2bb 100644
--- a/ash/system/accessibility/floating_accessibility_view.cc
+++ b/ash/system/accessibility/floating_accessibility_view.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/floating_accessibility_view.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/public/cpp/keyboard/keyboard_controller.h"
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
index c72b5c0..3f12602 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shell.h"
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller_unittest.cc b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller_unittest.cc
index 931e3a62..2e78f05b 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/shell.h"
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
index b492c26f..68e596e6 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/bubble/bubble_constants.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/public/cpp/shell_window_ids.h"
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller_unittest.cc b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller_unittest.cc
index 096644a3..83547b6 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/select_to_speak/select_to_speak_speed_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/shell.h"
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc b/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc
index c179fd1..ca5577a 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_tray.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/tray_background_view_catalog.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc b/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc
index 98c1aaa6..ef810141 100644
--- a/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc
+++ b/ash/system/accessibility/select_to_speak/select_to_speak_tray_unittest.cc
@@ -5,7 +5,7 @@
 #include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h"
 
 #include "ash/accelerators/accelerator_controller_impl.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
diff --git a/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller_unittest.cc b/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller_unittest.cc
index f055df21..d66323d 100644
--- a/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/system/accessibility/switch_access/switch_access_back_button_view.h"
 #include "ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h"
diff --git a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller_unittest.cc b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller_unittest.cc
index 126c7d2..e258409a 100644
--- a/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/switch_access/switch_access_menu_bubble_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/system/accessibility/switch_access/switch_access_back_button_bubble_controller.h"
 #include "ash/system/accessibility/switch_access/switch_access_back_button_view.h"
diff --git a/ash/system/accessibility/switch_access/switch_access_menu_view.cc b/ash/system/accessibility/switch_access/switch_access_menu_view.cc
index e08c7267..a8afcd08 100644
--- a/ash/system/accessibility/switch_access/switch_access_menu_view.cc
+++ b/ash/system/accessibility/switch_access/switch_access_menu_view.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/switch_access/switch_access_menu_view.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/bubble/bubble_constants.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
diff --git a/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc b/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc
index 22bd4dd..fc6ec3a 100644
--- a/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc
+++ b/ash/system/accessibility/unified_accessibility_detailed_view_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/accessibility/unified_accessibility_detailed_view_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/accessibility/accessibility_detailed_view.h"
diff --git a/ash/system/audio/audio_detailed_view.cc b/ash/system/audio/audio_detailed_view.cc
index 3cc4039..6d3e12f1 100644
--- a/ash/system/audio/audio_detailed_view.cc
+++ b/ash/system/audio/audio_detailed_view.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/audio/audio_effects_controller.cc b/ash/system/audio/audio_effects_controller.cc
index 881d034..a83b031 100644
--- a/ash/system/audio/audio_effects_controller.cc
+++ b/ash/system/audio/audio_effects_controller.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller_impl.h"
diff --git a/ash/system/audio/audio_effects_controller_unittest.cc b/ash/system/audio/audio_effects_controller_unittest.cc
index 0a59cc4..27b5a1d9 100644
--- a/ash/system/audio/audio_effects_controller_unittest.cc
+++ b/ash/system/audio/audio_effects_controller_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/shell.h"
diff --git a/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc b/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc
index f50fc29..8cbf723 100644
--- a/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc
+++ b/ash/system/audio/unified_audio_detailed_view_controller_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/audio/unified_audio_detailed_view_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/style/switch.h"
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index 26de3e8..1059b4cc7 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
diff --git a/ash/system/audio/unified_volume_view.h b/ash/system/audio/unified_volume_view.h
index dac3f2f7..f98cc8e4 100644
--- a/ash/system/audio/unified_volume_view.h
+++ b/ash/system/audio/unified_volume_view.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_VIEW_H_
 #define ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_VIEW_H_
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/ash_export.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/camera/autozoom_toast_controller.cc b/ash/system/camera/autozoom_toast_controller.cc
index 108ba6c..15860fe9 100644
--- a/ash/system/camera/autozoom_toast_controller.cc
+++ b/ash/system/camera/autozoom_toast_controller.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/system/camera/autozoom_controller_impl.h"
diff --git a/ash/system/caps_lock_notification_controller.cc b/ash/system/caps_lock_notification_controller.cc
index 851346a9..cb1d6dd47 100644
--- a/ash/system/caps_lock_notification_controller.cc
+++ b/ash/system/caps_lock_notification_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/caps_lock_notification_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/eche/eche_tray.cc b/ash/system/eche/eche_tray.cc
index 49b7b45..f136294 100644
--- a/ash/system/eche/eche_tray.cc
+++ b/ash/system/eche/eche_tray.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/constants/tray_background_view_catalog.h"
diff --git a/ash/system/focus_mode/focus_mode_detailed_view.cc b/ash/system/focus_mode/focus_mode_detailed_view.cc
index b984c2a3..07f4a5c 100644
--- a/ash/system/focus_mode/focus_mode_detailed_view.cc
+++ b/ash/system/focus_mode/focus_mode_detailed_view.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
 #include "ash/shell_delegate.h"
diff --git a/ash/system/focus_mode/focus_mode_tray_unittest.cc b/ash/system/focus_mode/focus_mode_tray_unittest.cc
index c432f25..9eefb30 100644
--- a/ash/system/focus_mode/focus_mode_tray_unittest.cc
+++ b/ash/system/focus_mode/focus_mode_tray_unittest.cc
@@ -187,21 +187,19 @@
   task_environment()->FastForwardBy(base::Seconds(1));
 
   // Define a margin of error for floating point math.
-  constexpr float allowed_difference = 0.01f;
+  constexpr float allowed_difference = 0.001f;
 
   // Progress should start near zero.
   EXPECT_NEAR(0.0, GetProgressIndicator()->progress().value(),
               allowed_difference);
 
   // Progress one quarter the way through the session should be near 0.25.
-  task_environment()->AdvanceClock(base::Minutes(10));
-  task_environment()->FastForwardBy(base::Seconds(1));
+  task_environment()->FastForwardBy(base::Minutes(10));
   EXPECT_NEAR(0.25, GetProgressIndicator()->progress().value(),
               allowed_difference);
 
   // Progress half way through the session should be near .5.
-  task_environment()->AdvanceClock(base::Minutes(10));
-  task_environment()->FastForwardBy(base::Seconds(1));
+  task_environment()->FastForwardBy(base::Minutes(10));
   EXPECT_NEAR(0.5, GetProgressIndicator()->progress().value(),
               allowed_difference);
 }
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 83a4c4af..911e6660 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ash_element_identifiers.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/tray_background_view_catalog.h"
diff --git a/ash/system/ime/unified_ime_detailed_view_controller.cc b/ash/system/ime/unified_ime_detailed_view_controller.cc
index 82f5d87f..0ce8bee 100644
--- a/ash/system/ime/unified_ime_detailed_view_controller.cc
+++ b/ash/system/ime/unified_ime_detailed_view_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/ime/unified_ime_detailed_view_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index eb533a84..82b606aa 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/accessibility/a11y_feature_type.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/tray_background_view_catalog.h"
 #include "ash/ime/ime_controller_impl.h"
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index b0f2e84..5ada450 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -6,7 +6,7 @@
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/accessibility/a11y_feature_type.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ime/ime_controller_impl.h"
 #include "ash/ime/test_ime_controller_client.h"
 #include "ash/public/cpp/ash_view_ids.h"
diff --git a/ash/system/overview/overview_button_tray_unittest.cc b/ash/system/overview/overview_button_tray_unittest.cc
index d18e7860..b5cb8e5 100644
--- a/ash/system/overview/overview_button_tray_unittest.cc
+++ b/ash/system/overview/overview_button_tray_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/overview/overview_button_tray.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/login_status.h"
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 6a940b97..ab35b38 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/tray_background_view_catalog.h"
diff --git a/ash/system/phonehub/phone_hub_tray.cc b/ash/system/phonehub/phone_hub_tray.cc
index 1508f794..b87061b 100644
--- a/ash/system/phonehub/phone_hub_tray.cc
+++ b/ash/system/phonehub/phone_hub_tray.cc
@@ -7,7 +7,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/tray_background_view_catalog.h"
 #include "ash/focus_cycler.h"
diff --git a/ash/system/power/power_button_display_controller.cc b/ash/system/power/power_button_display_controller.cc
index 6d69d5e..dc9ae814 100644
--- a/ash/system/power/power_button_display_controller.cc
+++ b/ash/system/power/power_button_display_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/power/power_button_display_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/media/media_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/system/power/scoped_backlights_forced_off.h"
diff --git a/ash/system/power/power_button_menu_view.cc b/ash/system/power/power_button_menu_view.cc
index 64f3eb18..6713730 100644
--- a/ash/system/power/power_button_menu_view.cc
+++ b/ash/system/power/power_button_menu_view.cc
@@ -21,7 +21,6 @@
 #include "ash/system/power/power_button_menu_view_util.h"
 #include "ash/system/user/login_status.h"
 #include "ash/wm/lock_state_controller.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/time/time.h"
@@ -32,6 +31,7 @@
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/compositor_extra/shadow.h"
+#include "ui/display/screen.h"
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
@@ -114,7 +114,7 @@
 PowerButtonMenuView::GetTransformDisplacement() const {
   TransformDisplacement transform_displacement;
   if (power_button_position_ == PowerButtonPosition::NONE ||
-      !Shell::Get()->tablet_mode_controller()->InTabletMode()) {
+      !display::Screen::GetScreen()->InTabletMode()) {
     transform_displacement.direction = TransformDirection::Y;
     transform_displacement.distance = kPowerButtonMenuTransformDistanceDp;
     return transform_displacement;
@@ -191,7 +191,7 @@
   const bool create_lock_screen = login_status != LoginStatus::LOCKED &&
                                   session_controller->CanLockScreen();
   const bool create_capture_mode =
-      Shell::Get()->tablet_mode_controller()->InTabletMode() &&
+      display::Screen::GetScreen()->InTabletMode() &&
       !session_controller->IsUserSessionBlocked() &&
       login_status != LoginStatus::KIOSK_APP;
   const bool create_feedback = login_status != LoginStatus::LOCKED &&
diff --git a/ash/system/privacy_hub/privacy_hub_notification.cc b/ash/system/privacy_hub/privacy_hub_notification.cc
index 9e5d347..631b47dc 100644
--- a/ash/system/privacy_hub/privacy_hub_notification.cc
+++ b/ash/system/privacy_hub/privacy_hub_notification.cc
@@ -8,7 +8,7 @@
 #include <optional>
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/public/cpp/projector/projector_session.h"
diff --git a/ash/system/privacy_screen/privacy_screen_toast_controller.cc b/ash/system/privacy_screen/privacy_screen_toast_controller.cc
index a98eb33a..aa4c5ba 100644
--- a/ash/system/privacy_screen/privacy_screen_toast_controller.cc
+++ b/ash/system/privacy_screen/privacy_screen_toast_controller.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/bubble/bubble_constants.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
diff --git a/ash/system/toast/system_toast_view.cc b/ash/system/toast/system_toast_view.cc
index 071e223..4e6dcd5 100644
--- a/ash/system/toast/system_toast_view.cc
+++ b/ash/system/toast/system_toast_view.cc
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/scoped_a11y_override_window_setter.h"
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/cpp/style/color_provider.h"
diff --git a/ash/system/tray/tray_background_view_unittest.cc b/ash/system/tray/tray_background_view_unittest.cc
index a6b4055..2cb55883 100644
--- a/ash/system/tray/tray_background_view_unittest.cc
+++ b/ash/system/tray/tray_background_view_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/tray/tray_background_view.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/root_window_controller.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 6d9a557..c6cf241 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -9,7 +9,7 @@
 #include <numeric>
 
 #include "ash/accelerators/accelerator_controller_impl.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/bubble/bubble_constants.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/accelerators.h"
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 625b187..62ae9cf 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/unified/unified_system_tray.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ash_element_identifiers.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/tray_background_view_catalog.h"
diff --git a/ash/system/unified/unified_system_tray_model.cc b/ash/system/unified/unified_system_tray_model.cc
index 2fc5a33..f7c9196 100644
--- a/ash/system/unified/unified_system_tray_model.cc
+++ b/ash/system/unified/unified_system_tray_model.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/unified/unified_system_tray_model.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
diff --git a/ash/system/unified/unified_system_tray_unittest.cc b/ash/system/unified/unified_system_tray_unittest.cc
index f13ab8fb..02f14d1 100644
--- a/ash/system/unified/unified_system_tray_unittest.cc
+++ b/ash/system/unified/unified_system_tray_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/unified/unified_system_tray.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/test/shell_test_api.h"
diff --git a/ash/system/video_conference/bubble/vc_tile_ui_controller.cc b/ash/system/video_conference/bubble/vc_tile_ui_controller.cc
index a41a503..1eb124cc 100644
--- a/ash/system/video_conference/bubble/vc_tile_ui_controller.cc
+++ b/ash/system/video_conference/bubble/vc_tile_ui_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/video_conference/bubble/vc_tile_ui_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/video_conference/bubble/bubble_view_ids.h"
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
index f9108bf..30afa8f9 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/tray_background_view_catalog.h"
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
 #include "ash/metrics/user_metrics_recorder.h"
diff --git a/ash/user_education/welcome_tour/welcome_tour_controller.cc b/ash/user_education/welcome_tour/welcome_tour_controller.cc
index c481267..9dea599c 100644
--- a/ash/user_education/welcome_tour/welcome_tour_controller.cc
+++ b/ash/user_education/welcome_tour/welcome_tour_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/user_education/welcome_tour/welcome_tour_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/ash_element_identifiers.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/user_education/welcome_tour/welcome_tour_controller_unittest.cc b/ash/user_education/welcome_tour/welcome_tour_controller_unittest.cc
index 925e299e..c161635 100644
--- a/ash/user_education/welcome_tour/welcome_tour_controller_unittest.cc
+++ b/ash/user_education/welcome_tour/welcome_tour_controller_unittest.cc
@@ -11,7 +11,7 @@
 #include <vector>
 
 #include "ash/accelerators/ash_accelerator_configuration.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/ash_element_identifiers.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/notifier_catalogs.h"
diff --git a/ash/webui/common/backend/accessibility_features.cc b/ash/webui/common/backend/accessibility_features.cc
index bab5e918..4553357 100644
--- a/ash/webui/common/backend/accessibility_features.cc
+++ b/ash/webui/common/backend/accessibility_features.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/webui/common/mojom/accessibility_features.mojom.h"
 #include "base/functional/callback.h"
diff --git a/ash/webui/common/backend/accessibility_features_unittest.cc b/ash/webui/common/backend/accessibility_features_unittest.cc
index deeace1..640101f 100644
--- a/ash/webui/common/backend/accessibility_features_unittest.cc
+++ b/ash/webui/common/backend/accessibility_features_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/webui/common/backend/accessibility_features.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/webui/common/mojom/accessibility_features.mojom.h"
diff --git a/ash/wm/desks/desk_bar_controller.cc b/ash/wm/desks/desk_bar_controller.cc
index 13dd2bf..3db76e4 100644
--- a/ash/wm/desks/desk_bar_controller.cc
+++ b/ash/wm/desks/desk_bar_controller.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/shelf/desk_button_widget.h"
 #include "ash/shelf/shelf.h"
diff --git a/ash/wm/desks/desk_mini_view.cc b/ash/wm/desks/desk_mini_view.cc
index 7c7a994..c80e08c 100644
--- a/ash/wm/desks/desk_mini_view.cc
+++ b/ash/wm/desks/desk_mini_view.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 
 #include "ash/accelerators/keyboard_code_util.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/desk_profiles_delegate.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/wm/desks/desk_name_view.cc b/ash/wm/desks/desk_name_view.cc
index 5951acb..606774e 100644
--- a/ash/wm/desks/desk_name_view.cc
+++ b/ash/wm/desks/desk_name_view.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/wm/desks/desk_bar_view_base.h"
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 23e90f7..b47c375 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -7,7 +7,7 @@
 #include <algorithm>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/notifier_catalogs.h"
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 023bfd8..42bd668a 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -6,7 +6,7 @@
 #include <string>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
 #include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
 #include "ash/app_list/app_list_controller_impl.h"
diff --git a/ash/wm/desks/templates/saved_desk_dialog_controller.cc b/ash/wm/desks/templates/saved_desk_dialog_controller.cc
index 640e3aa..ce754bd 100644
--- a/ash/wm/desks/templates/saved_desk_dialog_controller.cc
+++ b/ash/wm/desks/templates/saved_desk_dialog_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/desks/templates/saved_desk_dialog_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/system_dialog_delegate_view.h"
diff --git a/ash/wm/desks/templates/saved_desk_item_view.cc b/ash/wm/desks/templates/saved_desk_item_view.cc
index dcbbe62..4e0394c 100644
--- a/ash/wm/desks/templates/saved_desk_item_view.cc
+++ b/ash/wm/desks/templates/saved_desk_item_view.cc
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
diff --git a/ash/wm/desks/templates/saved_desk_save_desk_button_container.cc b/ash/wm/desks/templates/saved_desk_save_desk_button_container.cc
index aabe4beb..7158220f 100644
--- a/ash/wm/desks/templates/saved_desk_save_desk_button_container.cc
+++ b/ash/wm/desks/templates/saved_desk_save_desk_button_container.cc
@@ -6,7 +6,7 @@
 
 #include <array>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_observer.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/shell.h"
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc
index f7b8a92..c81efca 100644
--- a/ash/wm/desks/templates/saved_desk_unittest.cc
+++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -5,7 +5,7 @@
 #include <array>
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/constants/app_types.h"
 #include "ash/constants/ash_features.h"
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index 3c747fd5..ca0636d 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/cancel_mode.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/shell_window_ids.h"
diff --git a/ash/wm/overview/overview_focus_cycler_unittest.cc b/ash/wm/overview/overview_focus_cycler_unittest.cc
index 2ccfe2c..7777ffba 100644
--- a/ash/wm/overview/overview_focus_cycler_unittest.cc
+++ b/ash/wm/overview/overview_focus_cycler_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/overview/overview_focus_cycler.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/shell.h"
 #include "ash/style/close_button.h"
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index d9b1c5e7..4f264b95 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/metrics/histogram_macros.h"
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index 5152265..9b0e2f0 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -7,7 +7,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 574354e0..d4938b79 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/frame_throttler/frame_throttling_controller.h"
 #include "ash/metrics/user_metrics_recorder.h"
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index c1fc472..66fc740 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -11,7 +11,7 @@
 
 #include "ash/accelerators/accelerator_controller_impl.h"
 #include "ash/accelerators/exit_warning_handler.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/app_list/app_list_controller_impl.h"
diff --git a/ash/wm/overview/overview_utils.cc b/ash/wm/overview/overview_utils.cc
index 22fa5ac..85fe58f 100644
--- a/ash/wm/overview/overview_utils.cc
+++ b/ash/wm/overview/overview_utils.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index d413fe2..f501cd92 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -10,7 +10,7 @@
 #include <limits>
 #include <optional>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/app_types.h"
 #include "ash/constants/ash_features.h"
 #include "ash/display/screen_orientation_controller.h"
@@ -71,6 +71,7 @@
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/presentation_time_recorder.h"
 #include "ui/compositor/throughput_tracker.h"
+#include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/gfx/animation/tween.h"
@@ -491,8 +492,9 @@
   if (SnapGroupController* snap_group_controller = SnapGroupController::Get()) {
     snap_group_controller->AddObserver(this);
   }
-  split_view_type_ = IsInTabletMode() ? SplitViewType::kTabletType
-                                      : SplitViewType::kClamshellType;
+  split_view_type_ = display::Screen::GetScreen()->InTabletMode()
+                         ? SplitViewType::kTabletType
+                         : SplitViewType::kClamshellType;
 }
 
 SplitViewController::~SplitViewController() {
@@ -749,7 +751,8 @@
           std::make_unique<AutoSnapController>(root_window_);
     }
 
-    if (!IsInTabletMode() && IsInOverviewSession()) {
+    if (!display::Screen::GetScreen()->InTabletMode() &&
+        IsInOverviewSession()) {
       if (auto* root_window_controller =
               RootWindowController::ForWindow(window)) {
         // Start the clamshell split overview session. It is too late to create
@@ -955,9 +958,10 @@
       divider_position_ < 0
           ? CalculateDividerPosition(snap_position, snap_ratio)
           : divider_position_;
-  const int divider_width = IsInTabletMode() || split_view_divider_
-                                ? kSplitviewDividerShortSideLength
-                                : 0;
+  const int divider_width =
+      display::Screen::GetScreen()->InTabletMode() || split_view_divider_
+          ? kSplitviewDividerShortSideLength
+          : 0;
   return CalculateSnappedWindowBoundsInScreen(
       snap_position, window_for_minimum_size, divider_position, divider_width,
       IsResizingWithDivider());
@@ -1959,7 +1963,7 @@
 void SplitViewController::UpdateSnappedBounds(aura::Window* window) {
   DCHECK(IsWindowInSplitView(window));
   WindowState* window_state = WindowState::Get(window);
-  if (IsInTabletMode()) {
+  if (display::Screen::GetScreen()->InTabletMode()) {
     if (window->GetProperty(aura::client::kAppType) ==
         static_cast<int>(AppType::ARC_APP)) {
       // TODO(b/264962634): Remove this workaround. Probably, we can rewrite
@@ -2157,7 +2161,8 @@
   // but instead snap that window to the opposite side.
   if (previous_state &&
       *previous_state == chromeos::WindowStateType::kFloated &&
-      IsInTabletMode() && state_ != State::kBothSnapped) {
+      display::Screen::GetScreen()->InTabletMode() &&
+      state_ != State::kBothSnapped) {
     for (aura::Window* mru_window :
          Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(
              kActiveDesk)) {
@@ -2254,7 +2259,8 @@
                              ? SnapPosition::kSecondary
                              : SnapPosition::kPrimary);
 
-    if (reason == WindowDetachedReason::kWindowFloated && IsInTabletMode()) {
+    if (reason == WindowDetachedReason::kWindowFloated &&
+        display::Screen::GetScreen()->InTabletMode()) {
       // Maximize the other window, which will end split view.
       WMEvent event(WM_EVENT_MAXIMIZE);
       WindowState::Get(other_window)->OnWMEvent(&event);
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index 6151676..5e33f3e 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/splitview/split_view_utils.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/notifier_catalogs.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/public/cpp/system/toast_data.h"
@@ -19,7 +19,6 @@
 #include "ash/wm/snap_group/snap_group_controller.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_types.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_restore/window_restore_controller.h"
 #include "ash/wm/window_state.h"
@@ -568,19 +567,13 @@
       vertical_edge_inset);
 }
 
-bool IsInTabletMode() {
-  TabletModeController* tablet_mode_controller =
-      Shell::Get()->tablet_mode_controller();
-  return tablet_mode_controller && tablet_mode_controller->InTabletMode();
-}
-
 bool IsLayoutHorizontal(aura::Window* window) {
   return IsLayoutHorizontal(
       display::Screen::GetScreen()->GetDisplayNearestWindow(window));
 }
 
 bool IsLayoutHorizontal(const display::Display& display) {
-  if (IsInTabletMode()) {
+  if (display::Screen::GetScreen()->InTabletMode()) {
     return IsCurrentScreenOrientationLandscape();
   }
 
@@ -595,7 +588,7 @@
 }
 
 bool IsLayoutPrimary(const display::Display& display) {
-  if (IsInTabletMode()) {
+  if (display::Screen::GetScreen()->InTabletMode()) {
     return IsCurrentScreenOrientationPrimary();
   }
 
@@ -646,7 +639,7 @@
           root_window);
   const bool horizontal = IsLayoutHorizontal(root_window);
   const bool snap_left_or_top = IsPhysicalLeftOrTop(snap_position, root_window);
-  const bool in_tablet_mode = IsInTabletMode();
+  const bool in_tablet_mode = display::Screen::GetScreen()->InTabletMode();
   const int work_area_size = GetDividerPositionUpperLimit(root_window);
 
   // Edit `divider_position` if window restore is currently restoring a snapped
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 84de710..ccff601 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/shelf_config.h"
@@ -547,10 +547,6 @@
   tablet_mode_observers_.RemoveObserver(observer);
 }
 
-bool TabletModeController::InTabletMode() const {
-  return display::Screen::GetScreen()->InTabletMode();
-}
-
 bool TabletModeController::ForceUiTabletModeState(std::optional<bool> enabled) {
   if (!enabled.has_value()) {
     tablet_mode_behavior_ = kDefault;
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.h b/ash/wm/tablet_mode/tablet_mode_controller.h
index 96fd18d..1e47593 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.h
+++ b/ash/wm/tablet_mode/tablet_mode_controller.h
@@ -134,10 +134,6 @@
   // TabletMode:
   void AddObserver(TabletModeObserver* observer) override;
   void RemoveObserver(TabletModeObserver* observer) override;
-  // We are considered in tablet mode when |tablet_mode_window_manager_| is
-  // about to be initialized. When it is about to be shutdown, we are considered
-  // out of tablet mode.
-  bool InTabletMode() const override;
   bool AreInternalInputDeviceEventsBlocked() const override;
   bool ForceUiTabletModeState(std::optional<bool> enabled) override;
   void SetEnabledForTest(bool enabled) override;
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc b/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc
index 5ad75f0a..4f258156 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_test_api.cc
@@ -10,6 +10,7 @@
 #include "base/numerics/math_constants.h"
 #include "base/run_loop.h"
 #include "base/time/default_tick_clock.h"
+#include "ui/display/screen.h"
 #include "ui/events/devices/device_data_manager_test_api.h"
 #include "ui/events/devices/input_device.h"
 #include "ui/events/devices/touchpad_device.h"
@@ -148,4 +149,8 @@
   tablet_mode_controller_->SuspendDone(sleep_duration);
 }
 
+bool TabletModeControllerTestApi::IsTabletModeStarted() const {
+  return display::Screen::GetScreen()->InTabletMode();
+}
+
 }  // namespace ash
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_test_api.h b/ash/wm/tablet_mode/tablet_mode_controller_test_api.h
index ace198db..2b47d40 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_test_api.h
+++ b/ash/wm/tablet_mode/tablet_mode_controller_test_api.h
@@ -84,10 +84,6 @@
     return tablet_mode_controller_->CanUseUnstableLidAngle();
   }
 
-  bool IsTabletModeStarted() const {
-    return tablet_mode_controller_->InTabletMode();
-  }
-
   bool AreEventsBlocked() const {
     return tablet_mode_controller_->AreInternalInputDeviceEventsBlocked();
   }
@@ -102,6 +98,10 @@
 
   float GetLidAngle() const { return tablet_mode_controller_->lid_angle(); }
 
+  // Deprecated. Use display::Screen::GetScreen()->InTabletMode() instead.
+  // TODO(crbug.com/1502114): Remove this.
+  bool IsTabletModeStarted() const;
+
  private:
   raw_ptr<TabletModeController, DanglingUntriaged | ExperimentalAsh>
       tablet_mode_controller_;
diff --git a/ash/wm/window_cycle/window_cycle_controller.cc b/ash/wm/window_cycle/window_cycle_controller.cc
index 48cdcc0..dfe9cfc 100644
--- a/ash/wm/window_cycle/window_cycle_controller.cc
+++ b/ash/wm/window_cycle/window_cycle_controller.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/window_cycle/window_cycle_controller.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/events/event_rewriter_controller_impl.h"
 #include "ash/metrics/task_switch_metrics_recorder.h"
diff --git a/ash/wm/window_cycle/window_cycle_controller_unittest.cc b/ash/wm/window_cycle/window_cycle_controller_unittest.cc
index 44073a28..fdfc20f 100644
--- a/ash/wm/window_cycle/window_cycle_controller_unittest.cc
+++ b/ash/wm/window_cycle/window_cycle_controller_unittest.cc
@@ -8,7 +8,7 @@
 #include <memory>
 
 #include "ash/accelerators/accelerator_controller_impl.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/app_list/test/app_list_test_helper.h"
diff --git a/ash/wm/window_cycle/window_cycle_list.cc b/ash/wm/window_cycle/window_cycle_list.cc
index 00d2ee5d2..d79fe34 100644
--- a/ash/wm/window_cycle/window_cycle_list.cc
+++ b/ash/wm/window_cycle/window_cycle_list.cc
@@ -4,7 +4,7 @@
 
 #include "ash/wm/window_cycle/window_cycle_list.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/frame_throttler/frame_throttling_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
diff --git a/ash/wm/window_cycle/window_cycle_view.cc b/ash/wm/window_cycle/window_cycle_view.cc
index 96509df..811313a 100644
--- a/ash/wm/window_cycle/window_cycle_view.cc
+++ b/ash/wm/window_cycle/window_cycle_view.cc
@@ -8,7 +8,7 @@
 #include <optional>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/shell.h"
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 75a7104..662a3f306 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -7,7 +7,7 @@
 #include <absl/cleanup/cleanup.h>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/focus_cycler.h"
 #include "ash/metrics/pip_uma.h"
 #include "ash/public/cpp/app_types_util.h"
diff --git a/ash/wm/workspace/backdrop_controller.cc b/ash/wm/workspace/backdrop_controller.cc
index cb0b4d8d..610b8486 100644
--- a/ash/wm/workspace/backdrop_controller.cc
+++ b/ash/wm/workspace/backdrop_controller.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/accessibility_delegate.h"
 #include "ash/animation/animation_change_type.h"
 #include "ash/constants/app_types.h"
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index 93ca187..c50ddc2 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -6,7 +6,7 @@
 
 #include <algorithm>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/constants/ash_features.h"
 #include "ash/keyboard/ui/keyboard_ui_controller.h"
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 23ab272e..7f79a5e 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -8,7 +8,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/test_accessibility_controller_client.h"
 #include "ash/app_list/test/app_list_test_helper.h"
 #include "ash/constants/app_types.h"
diff --git a/build/config/siso/PRESUBMIT.py b/build/config/siso/PRESUBMIT.py
index e092596..dac9fee 100644
--- a/build/config/siso/PRESUBMIT.py
+++ b/build/config/siso/PRESUBMIT.py
@@ -22,5 +22,6 @@
       "Cq-Include-Trybots: luci.chromium.try:linux_chromium_asan_siso_rel_ng\n"
       "Cq-Include-Trybots: luci.chromium.try:linux_chromium_compile_siso_dbg_ng\n"
       "Cq-Include-Trybots: luci.chromium.try:mac-siso-rel\n"
+      "Cq-Include-Trybots: luci.chromium.try:win_chromium_compile_siso_dbg_ng\n"
   )
   return [output_api.PresubmitPromptWarning(message)]
diff --git a/build/config/siso/main.star b/build/config/siso/main.star
index 41307b6b..7df2627 100644
--- a/build/config/siso/main.star
+++ b/build/config/siso/main.star
@@ -27,6 +27,7 @@
         "./ash_clang_x64/obj/chrome/browser/browser/chrome_browser_interface_binders.o",
         "./obj/chrome/browser/ash/ash/autotest_private_api.o",
         "./obj/chrome/browser/ash/ash/chrome_browser_main_parts_ash.o",
+        "./obj/chrome/browser/ash/system_web_apps/apps/browser_tests/personalization_app_time_of_day_browsertest.o",
         "./obj/chrome/browser/ash/system_web_apps/apps/browser_tests/personalization_app_wallpaper_daily_refresh_browsertest.o",
         "./obj/chrome/browser/ash/system_web_apps/browser_tests/system_web_app_manager_browsertest.o",
         "./obj/chrome/browser/ash/unit_tests/wizard_controller_unittest.o",
@@ -38,17 +39,21 @@
         "./obj/chrome/test/browser_tests/app_list_client_impl_browsertest.o",
         "./obj/chrome/test/browser_tests/browser_non_client_frame_view_browsertest.o",
         "./obj/chrome/test/browser_tests/browser_non_client_frame_view_chromeos_browsertest.o",
+        "./obj/chrome/test/browser_tests/capture_mode_browsertest.o",
         "./obj/chrome/test/browser_tests/chrome_shelf_controller_browsertest.o",
         "./obj/chrome/test/browser_tests/device_local_account_browsertest.o",
         "./obj/chrome/test/browser_tests/file_manager_browsertest_base.o",
+        "./obj/chrome/test/browser_tests/file_tasks_browsertest.o",
         "./obj/chrome/test/browser_tests/full_restore_app_launch_handler_browsertest.o",
         "./obj/chrome/test/browser_tests/login_browsertest.o",
+        "./obj/chrome/test/browser_tests/login_ui_browsertest.o",
         "./obj/chrome/test/browser_tests/pwa_install_view_browsertest.o",
         "./obj/chrome/test/browser_tests/remote_apps_manager_browsertest.o",
         "./obj/chrome/test/browser_tests/safe_browsing_blocking_page_test.o",
         "./obj/chrome/test/browser_tests/save_card_bubble_views_browsertest.o",
         "./obj/chrome/test/browser_tests/scalable_iph_browsertest.o",
         "./obj/chrome/test/browser_tests/spoken_feedback_browsertest.o",
+        "./obj/chrome/test/browser_tests/webview_login_browsertest.o",
         "./obj/chrome/test/interactive_ui_tests/iban_bubble_view_uitest.o",
         "./obj/chrome/test/interactive_ui_tests/local_card_migration_uitest.o",
         "./obj/chrome/test/interactive_ui_tests/system_web_app_interactive_uitest.o",
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 1b430a62..e579c04 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision vars in //DEPS.
-  libcxx_revision = "a88e6d6daa3e091f662c8a198b5d3a0d3a723705"
+  libcxx_revision = "1f70899ab6d9d2c3c455fa7adb3f3cbd7bfdf215"
 }
diff --git a/chrome/VERSION b/chrome/VERSION
index 9587d9e..86643d59 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=122
 MINOR=0
-BUILD=6182
+BUILD=6183
 PATCH=0
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index 37fc47d..70feb82 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.ResettersForTesting;
+import org.chromium.base.ValueChangedCallback;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
@@ -108,7 +109,8 @@
     private final Context mContext;
     private final PropertyModel mModel;
     private final ObservableSupplier<TabModelFilter> mCurrentTabModelFilterSupplier;
-    private final Callback<TabModelFilter> mOnTabModelFilterChanged = this::onTabModelFilterChanged;
+    private final ValueChangedCallback<TabModelFilter> mOnTabModelFilterChanged =
+            new ValueChangedCallback<>(this::onTabModelFilterChanged);
     private final TabModelObserver mTabModelObserver;
     private final TabCreatorManager mTabCreatorManager;
     private final DialogController mDialogController;
@@ -120,7 +122,6 @@
     private final SnackbarManager mSnackbarManager;
     private final String mComponentName;
 
-    private TabModelFilter mCurrentTabModelFilter;
     private TabGroupTitleEditor mTabGroupTitleEditor;
     private Supplier<TabListEditorController> mTabListEditorControllerSupplier;
     private boolean mTabListEditorSetup;
@@ -273,7 +274,7 @@
                     }
                 };
 
-        onTabModelFilterChanged(
+        mOnTabModelFilterChanged.onResult(
                 mCurrentTabModelFilterSupplier.addObserver(mOnTabModelFilterChanged));
 
         // Setup ScrimView click Runnable.
@@ -401,7 +402,7 @@
 
     /** Destroy any members that needs clean up. */
     public void destroy() {
-        removeCurrentTabModelFilterObservers();
+        removeTabModelFilterObserver(mCurrentTabModelFilterSupplier.get());
         mCurrentTabModelFilterSupplier.removeObserver(mOnTabModelFilterChanged);
         KeyboardVisibilityDelegate.getInstance()
                 .removeKeyboardVisibilityListener(mKeyboardVisibilityListener);
@@ -743,20 +744,19 @@
         return true;
     }
 
-    private void onTabModelFilterChanged(TabModelFilter filter) {
-        if (mCurrentTabModelFilter == filter) return;
+    private void onTabModelFilterChanged(
+            @Nullable TabModelFilter newFilter, @Nullable TabModelFilter oldFilter) {
+        removeTabModelFilterObserver(oldFilter);
 
-        updateColorProperties(mContext, filter.isIncognito());
-
-        removeCurrentTabModelFilterObservers();
-
-        filter.addObserver(mTabModelObserver);
-        mCurrentTabModelFilter = filter;
+        if (newFilter != null) {
+            updateColorProperties(mContext, newFilter.isIncognito());
+            newFilter.addObserver(mTabModelObserver);
+        }
     }
 
-    private void removeCurrentTabModelFilterObservers() {
-        if (mCurrentTabModelFilter != null) {
-            mCurrentTabModelFilter.removeObserver(mTabModelObserver);
+    private void removeTabModelFilterObserver(@Nullable TabModelFilter filter) {
+        if (filter != null) {
+            filter.removeObserver(mTabModelObserver);
         }
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java
index e592385..1457c5c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMediator.java
@@ -12,7 +12,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import org.chromium.base.Callback;
+import org.chromium.base.ValueChangedCallback;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.chrome.browser.tab.Tab;
@@ -47,8 +47,8 @@
                 TabListEditorAction.ActionDelegate {
     private final Context mContext;
     private final @NonNull ObservableSupplier<TabModelFilter> mCurrentTabModelFilterSupplier;
-    private final @NonNull Callback<TabModelFilter> mOnTabModelFilterChanged =
-            this::onTabModelFilterChanged;
+    private final @NonNull ValueChangedCallback<TabModelFilter> mOnTabModelFilterChanged =
+            new ValueChangedCallback<>(this::onTabModelFilterChanged);
     private final TabListCoordinator mTabListCoordinator;
     private final TabListEditorCoordinator.ResetHandler mResetHandler;
     private final PropertyModel mModel;
@@ -61,7 +61,6 @@
             new ObservableSupplierImpl<>();
     private final List<Tab> mVisibleTabs = new ArrayList<>();
 
-    private TabModelFilter mCurrentTabModelFilter;
     private PropertyListModel<PropertyModel, PropertyKey> mActionListModel;
     private ListModelChangeProcessor mActionChangeProcessor;
     private TabListEditorMenu mTabListEditorMenu;
@@ -147,7 +146,7 @@
                     }
                 };
 
-        onTabModelFilterChanged(
+        mOnTabModelFilterChanged.onResult(
                 mCurrentTabModelFilterSupplier.addObserver(mOnTabModelFilterChanged));
 
         mNavigationProvider =
@@ -355,27 +354,26 @@
 
     /** Destroy any members that needs clean up. */
     public void destroy() {
-        removeCurrentTabModelFilterObserver();
+        removeTabModelFilterObserver(mCurrentTabModelFilterSupplier.get());
         mCurrentTabModelFilterSupplier.removeObserver(mOnTabModelFilterChanged);
     }
 
-    private void onTabModelFilterChanged(TabModelFilter newFilter) {
-        if (mCurrentTabModelFilterSupplier == newFilter) return;
+    private void onTabModelFilterChanged(
+            @Nullable TabModelFilter newFilter, @Nullable TabModelFilter oldFilter) {
+        removeTabModelFilterObserver(oldFilter);
 
-        // Incognito in both light/dark theme is the same as non-incognito mode in dark theme.
-        // Non-incognito mode and incognito in both light/dark themes in dark theme all look
-        // dark.
-        updateColors(newFilter.isIncognito());
-
-        removeCurrentTabModelFilterObserver();
-
-        newFilter.addObserver(mTabModelObserver);
-        mCurrentTabModelFilter = newFilter;
+        if (newFilter != null) {
+            // Incognito in both light/dark theme is the same as non-incognito mode in dark theme.
+            // Non-incognito mode and incognito in both light/dark themes in dark theme all look
+            // dark.
+            updateColors(newFilter.isIncognito());
+            newFilter.addObserver(mTabModelObserver);
+        }
     }
 
-    private void removeCurrentTabModelFilterObserver() {
-        if (mCurrentTabModelFilter != null) {
-            mCurrentTabModelFilter.removeObserver(mTabModelObserver);
+    private void removeTabModelFilterObserver(@Nullable TabModelFilter filter) {
+        if (filter != null) {
+            filter.removeObserver(mTabModelObserver);
         }
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index f4867bf..105be86 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -38,6 +38,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.Log;
 import org.chromium.base.ResettersForTesting;
+import org.chromium.base.ValueChangedCallback;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.shared_preferences.SharedPreferencesManager;
@@ -320,7 +321,8 @@
     private final ObservableSupplier<TabModelFilter> mCurrentTabModelFilterSupplier;
     // TODO(crbug/1505772): Refactor price drops so we don't need this.
     private final Supplier<TabModel> mRegularTabModelSupplier;
-    private final Callback<TabModelFilter> mOnTabModelFilterChanged = this::onTabModelFilterChanged;
+    private final ValueChangedCallback<TabModelFilter> mOnTabModelFilterChanged =
+            new ValueChangedCallback<>(this::onTabModelFilterChanged);
     private final TabActionListener mTabClosedListener;
     private final PseudoTab.TitleProvider mTitleProvider;
     private final SelectionDelegateProvider mSelectionDelegateProvider;
@@ -329,9 +331,6 @@
     private final TabListFaviconProvider mTabListFaviconProvider;
     private final PriceWelcomeMessageController mPriceWelcomeMessageController;
 
-    // This could come from mCurrentTabModelFilterSupplier, but we need to cache it for updating
-    // observers.
-    private TabModelFilter mCurrentTabModelFilter;
     private Size mDefaultGridCardSize;
     private String mComponentName;
     private ThumbnailProvider mThumbnailProvider;
@@ -1121,7 +1120,7 @@
     public void initWithNative() {
         mTabListFaviconProvider.initWithNative(Profile.getLastUsedRegularProfile());
 
-        onTabModelFilterChanged(
+        mOnTabModelFilterChanged.onResult(
                 mCurrentTabModelFilterSupplier.addObserver(mOnTabModelFilterChanged));
 
         mTabGroupTitleEditor =
@@ -1621,7 +1620,7 @@
         if (mListObserver != null) {
             mModel.removeObserver(mListObserver);
         }
-        removeCurrentTabModelFilterObservers();
+        removeTabModelFilterObservers(mCurrentTabModelFilterSupplier.get());
         mCurrentTabModelFilterSupplier.removeObserver(mOnTabModelFilterChanged);
 
         if (mComponentCallbacks != null) {
@@ -2135,22 +2134,22 @@
         ResettersForTesting.register(() -> mComponentName = oldValue);
     }
 
-    private void onTabModelFilterChanged(TabModelFilter filter) {
-        if (mCurrentTabModelFilter == filter) return;
+    private void onTabModelFilterChanged(
+            @Nullable TabModelFilter newFilter, @Nullable TabModelFilter oldFilter) {
+        removeTabModelFilterObservers(oldFilter);
 
-        removeCurrentTabModelFilterObservers();
-
-        filter.addObserver(mTabModelObserver);
-        if (filter instanceof TabGroupModelFilter groupFilter) {
-            groupFilter.addTabGroupObserver(mTabGroupObserver);
+        if (newFilter != null) {
+            newFilter.addObserver(mTabModelObserver);
+            if (newFilter instanceof TabGroupModelFilter newGroupFilter) {
+                newGroupFilter.addTabGroupObserver(mTabGroupObserver);
+            }
         }
-        mCurrentTabModelFilter = filter;
     }
 
-    private void removeCurrentTabModelFilterObservers() {
-        if (mCurrentTabModelFilter == null) return;
+    private void removeTabModelFilterObservers(@Nullable TabModelFilter filter) {
+        if (filter == null) return;
 
-        TabModel tabModel = mCurrentTabModelFilter.getTabModel();
+        TabModel tabModel = filter.getTabModel();
         if (tabModel != null) {
             // Observers are added when tabs are shown via addTabInfoToModel(). When switching
             // filters the TabObservers should be removed from all the tabs in the previous model.
@@ -2160,8 +2159,8 @@
                 tabModel.getTabAt(i).removeObserver(mTabObserver);
             }
         }
-        mCurrentTabModelFilter.removeObserver(mTabModelObserver);
-        if (mCurrentTabModelFilter instanceof TabGroupModelFilter groupFilter) {
+        filter.removeObserver(mTabModelObserver);
+        if (filter instanceof TabGroupModelFilter groupFilter) {
             groupFilter.removeTabGroupObserver(mTabGroupObserver);
         }
     }
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 e88e57c..31c1f1de 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
@@ -423,6 +423,10 @@
                 coordinator.getContext().getColor(R.color.omnibox_focused_fading_background_color));
     }
 
+    public static void resetHasAppendedMessagesForTesting() {
+        sAppendedMessagesForTesting = false;
+    }
+
     public static boolean hasAppendedMessagesForTesting() {
         return sAppendedMessagesForTesting;
     }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java
index 63f88b9..08a4ade 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIncognitoReauthPromoTest.java
@@ -15,23 +15,24 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import static org.chromium.base.test.util.Batch.PER_CLASS;
 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.createTabs;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.enterTabSwitcher;
 import static org.chromium.chrome.browser.tasks.tab_management.TabUiTestHelper.switchTabModel;
+import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
 
 import androidx.test.filters.MediumTest;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -41,6 +42,7 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.R;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
 /**
@@ -54,7 +56,7 @@
 @CommandLineFlags.Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
 @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
 @EnableFeatures({ChromeFeatureList.INCOGNITO_REAUTHENTICATION_FOR_ANDROID})
-@Batch(PER_CLASS)
+@DoNotBatch(reason = "Batching can cause message state to leak between tests.")
 public class TabGridIncognitoReauthPromoTest {
     @Rule
     public final ChromeTabbedActivityTestRule mActivityTestRule =
@@ -72,6 +74,12 @@
                 mActivityTestRule.getActivity().getTabModelSelector()::isTabStateInitialized);
     }
 
+    @After
+    public void tearDown() {
+        TestThreadUtils.runOnUiThreadBlocking(
+                TabSwitcherCoordinator::resetHasAppendedMessagesForTesting);
+    }
+
     @Test
     @MediumTest
     @DisabledTest(message = "https://crbug.com/1510419")
@@ -83,7 +91,7 @@
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
         CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
-        onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
+        onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
     }
 
     @Test
@@ -97,7 +105,7 @@
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
         CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
-        onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
+        onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         onView(withText(R.string.incognito_reauth_lock_action_text)).perform(click());
         onView(withId(R.id.snackbar)).check(matches(isDisplayed()));
@@ -122,13 +130,14 @@
     @DisabledTest(message = "https://crbug.com/1510419")
     public void testIncognitoPromoNotShownInRegularMode_WhenTogglingFromIncognito() {
         final ChromeTabbedActivity cta = mActivityTestRule.getActivity();
+
         createTabs(cta, false, 1);
         createTabs(cta, true, 1);
         enterTabSwitcher(cta);
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
         CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
-        onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
+        onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
 
         switchTabModel(cta, false);
         assertFalse(cta.getTabModelSelector().getCurrentModel().isIncognito());
@@ -146,7 +155,7 @@
 
         assertTrue(cta.getTabModelSelector().getCurrentModel().isIncognito());
         CriteriaHelper.pollUiThread(TabSwitcherCoordinator::hasAppendedMessagesForTesting);
-        onView(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
+        onViewWaiting(withId(R.id.large_message_card_item)).check(matches(isDisplayed()));
         onView(withId(R.id.secondary_action_button)).perform(click());
 
         onView(withId(R.id.large_message_card_item)).check(doesNotExist());
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
index 857495c..0999172 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridIphTest.java
@@ -52,6 +52,7 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.DoNotBatch;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -94,6 +95,7 @@
 // TODO(https://crbug.com/1362059): The message cards aren't shown the first time when entering GTS
 // with Start surface enabled.
 @DisableFeatures({ChromeFeatureList.ARCHIVE_TAB_SERVICE, ChromeFeatureList.START_SURFACE_ANDROID})
+@DoNotBatch(reason = "Batching can cause message state to leak between tests.")
 public class TabGridIphTest {
     private ModalDialogManager mModalDialogManager;
     private Tracker mTracker;
@@ -134,6 +136,8 @@
     @After
     public void tearDown() {
         ActivityTestUtils.clearActivityOrientation(mActivityTestRule.getActivity());
+        TestThreadUtils.runOnUiThreadBlocking(
+                TabSwitcherCoordinator::resetHasAppendedMessagesForTesting);
     }
 
     @Test
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
index 59372f5..d993dc3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -294,9 +294,6 @@
         @Override
         public void onLoadingComplete() {
             if (mIsDestroyed) return;
-
-            long loadTimeMs = (System.nanoTime() - mConstructedTimeNs) / 1000000;
-            RecordHistogram.recordTimesHistogram("Tab.NewTabOnload", loadTimeMs);
             mIsLoaded = true;
             NewTabPageUma.recordNtpImpression(NewTabPageUma.NTP_IMPRESSION_REGULAR);
             // If not visible when loading completes, wait until onShown is received.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabModalDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabModalDialogTest.java
index 3737c71..9079b56 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabModalDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabModalDialogTest.java
@@ -25,6 +25,7 @@
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.HistogramWatcher;
 import org.chromium.cc.input.BrowserControlsState;
 import org.chromium.chrome.browser.back_press.BackPressManager;
@@ -94,6 +95,7 @@
     @Test
     @SmallTest
     @Features.EnableFeatures(ChromeFeatureList.CCT_TAB_MODAL_DIALOG)
+    @DisabledTest(message = "https://crbug.com/1511082")
     public void testShowAndDismissTabModalDialog() throws InterruptedException {
         Context context = getInstrumentation().getTargetContext().getApplicationContext();
         Intent intent = CustomTabsIntentTestUtils.createMinimalCustomTabIntent(context, mTestPage);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 0e4b86c..ff07818 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -2688,10 +2688,6 @@
                             preferences.findPreference(
                                     SingleCategorySettings.DESKTOP_SITE_WINDOW_TOGGLE_KEY);
                     PrefService prefService = UserPrefs.get(getBrowserContextHandle());
-                    Assert.assertFalse(
-                            "Window setting should be OFF.",
-                            prefService.getBoolean(DESKTOP_SITE_WINDOW_SETTING_ENABLED));
-
                     preferences.onPreferenceChange(windowSettingPref, true);
                     Assert.assertTrue(
                             "Window setting should be ON.",
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 3212322..817f7a9 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -592,6 +592,12 @@
   <message name="IDS_SETTINGS_LANGUAGES_LANGUAGE_PACKS_NOTICE" desc="Privacy notice on the language and input settings page that informs users that language files are visible to all users on the device. See go/language-pack-ux slide 4 for more details.">
     Choose which languages to install on this device. Language files are shared among users to save disk space. <ph name="BEGIN_LINK_LEARN_MORE">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK_LEARN_MORE">&lt;/a&gt;</ph>
   </message>
+  <message name="IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_TITLE" desc="The label for the section where users can customize display language for supported apps.">
+    App languages
+  </message>
+  <message name="IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_DESCRIPTION" desc="The description for section under app languages title, where clicking this will bring user to app languages settings subpage.">
+    Customize display language for supported apps
+  </message>
 
   <!-- Suggestions Section -->
   <message name="IDS_SETTINGS_SUGGESTIONS_TITLE" desc="The label for the suggestions section, which contains features to help users type faster or more expressively such as suggesting personal information or suggesting emoji to use.">
@@ -1031,7 +1037,7 @@
     Can’t clean up storage until offline storage size is known.
   </message>
   <message name="IDS_SETTINGS_GOOGLE_DRIVE_SIGNED_IN_AS" desc="Label indicating account signed in with Google Drive in Settings: Files: Google Drive.">
-    Signed in as <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="DRIVE_ACCOUNT_EMAIL">$1<ex>john@google.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph>
+    Signed in as <ph name="SPAN_START">&lt;span id="driveAccountEmail"&gt;</ph><ph name="DRIVE_ACCOUNT_EMAIL">$1<ex>john@google.com</ex></ph><ph name="SPAN_END">&lt;/span&gt;</ph>
   </message>
   <message name="IDS_SETTINGS_GOOGLE_DRIVE_FILE_SYNC_TITLE" desc="Title for the everything offline feature in Settings: Files: Google Drive.">
     File sync
@@ -1109,7 +1115,7 @@
     File sync on
   </message>
   <message name="IDS_SETTINGS_GOOGLE_DRIVE_RECONNECT_AS" desc="Label indicating account that Google Drive would sign in as in Settings: Files: Google Drive.">
-    Connect <ph name="BEGIN_BOLD">&lt;strong&gt;</ph><ph name="DRIVE_ACCOUNT_EMAIL">$1<ex>john@google.com</ex></ph><ph name="END_BOLD">&lt;/strong&gt;</ph> to access your Drive files in the Files app
+    Connect <ph name="SPAN_START">&lt;span id="driveAccountEmail"&gt;</ph><ph name="DRIVE_ACCOUNT_EMAIL">$1<ex>john@google.com</ex></ph><ph name="SPAN_END">&lt;/span&gt;</ph> to access your Drive files in the Files app
   </message>
   <message name="IDS_SETTINGS_GOOGLE_DRIVE_CONNECT" desc="Action to connect Google Drive in Settings: Files: Google Drive.">
     Connect
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..d211ac5
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+bab19dbbb85523ab27091ee435bcb5414b179620
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_TITLE.png.sha1
new file mode 100644
index 0000000..9174611
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_TITLE.png.sha1
@@ -0,0 +1 @@
+049911fe582441c547c1d4330ad018f6580e9128
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_RECONNECT_AS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_RECONNECT_AS.png.sha1
index 65dcb5e9..a2aa3278 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_RECONNECT_AS.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_RECONNECT_AS.png.sha1
@@ -1 +1 @@
-d06f674df72a9c17a3faaf4f607324c9caf8a0bf
\ No newline at end of file
+27fc2221aaf28bf5fd06a2e1657524b40dc5fe2b
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_SIGNED_IN_AS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_SIGNED_IN_AS.png.sha1
index 8d14eca..9719ada 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_SIGNED_IN_AS.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_GOOGLE_DRIVE_SIGNED_IN_AS.png.sha1
@@ -1 +1 @@
-7679d3765070a7524a017623616873b23fef3161
\ No newline at end of file
+5f9a3d3706d41510aa86d9816ff09441adbbc15a
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2c987a5..a7907d8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -302,10 +302,6 @@
 #include "chrome/browser/ui/cocoa/screentime/screentime_features.h"
 #endif  // BUILDFLAG(IS_MAC)
 
-#if BUILDFLAG(ENABLE_CARDBOARD)
-#include "device/vr/public/cpp/features.h"
-#endif  // ENABLE_CARDBOARD
-
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/cws_info_service.h"
 #include "extensions/common/extension_features.h"
@@ -3976,11 +3972,6 @@
      flag_descriptions::kVerboseLoggingInNaclDescription, kOsAll,
      MULTI_VALUE_TYPE(kVerboseLoggingInNaclChoices)},
 #endif  // ENABLE_NACL
-#if BUILDFLAG(ENABLE_CARDBOARD)
-    {"enable-cardboard", flag_descriptions::kEnableCardboardName,
-     flag_descriptions::kEnableCardboardDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(device::features::kEnableCardboard)},
-#endif  // ENABLE_CARDBOARD
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     {"extensions-on-chrome-urls",
      flag_descriptions::kExtensionsOnChromeUrlsName,
diff --git a/chrome/browser/accessibility/DEPS b/chrome/browser/accessibility/DEPS
index 81b2219..38df7f9 100644
--- a/chrome/browser/accessibility/DEPS
+++ b/chrome/browser/accessibility/DEPS
@@ -10,6 +10,6 @@
 specific_include_rules = {
   'accessibility_extension_api_ash.cc': [
     "+services/accessibility/public/mojom/assistive_technology_type.mojom.h",
-    "+ash/accessibility/accessibility_controller_impl.h",
+    "+ash/accessibility/accessibility_controller.h",
   ]
 }
\ No newline at end of file
diff --git a/chrome/browser/accessibility/accessibility_extension_api_ash.cc b/chrome/browser/accessibility/accessibility_extension_api_ash.cc
index a4354378..6b903ed 100644
--- a/chrome/browser/accessibility/accessibility_extension_api_ash.cc
+++ b/chrome/browser/accessibility/accessibility_extension_api_ash.cc
@@ -9,7 +9,7 @@
 #include <set>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/public/cpp/accessibility_focus_ring_info.h"
 #include "ash/public/cpp/event_rewriter_controller.h"
diff --git a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.cc b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.cc
index 73e26d0..5a2beaa2 100644
--- a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.cc
+++ b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.cc
@@ -190,7 +190,7 @@
 }
 
 void AXMediaAppUntrustedHandler::DocumentUpdated(
-    const std::vector<gfx::Insets>& page_locations,
+    const std::vector<gfx::RectF>& page_locations,
     const std::vector<uint64_t>& dirty_pages) {
   // `page_locations` should contain the new locations of all pages, whilst
   // `dirty_pages` only the indices of all the pages that need to be OCRed.
@@ -218,12 +218,13 @@
 #endif  // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 }
 
-void AXMediaAppUntrustedHandler::ViewportUpdated(
-    const gfx::Insets& viewport_box, float scaleFactor) {}
+void AXMediaAppUntrustedHandler::ViewportUpdated(const gfx::RectF& viewport_box,
+                                                 float scaleFactor) {}
 
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-void AXMediaAppUntrustedHandler::UpdatePageLocation(uint64_t page_index,
-                                           const gfx::Insets& page_location) {
+void AXMediaAppUntrustedHandler::UpdatePageLocation(
+    uint64_t page_index,
+    const gfx::RectF& page_location) {
   CHECK_LT(page_index, static_cast<uint64_t>(pages_.size()));
   ui::AXTreeManager* tree_manager = pages_[page_index].get();
   if (!tree_manager) {
@@ -232,9 +233,7 @@
   ui::AXTree* tree = tree_manager->ax_tree();
   CHECK(tree->root());
   ui::AXNodeData root_data = tree->root()->data();
-  root_data.relative_bounds.bounds =
-      gfx::RectF(page_location.left(), page_location.top(),
-                 page_location.width(), page_location.height());
+  root_data.relative_bounds.bounds = page_location;
   ui::AXTreeUpdate location_update;
   location_update.root_id = tree->root()->id();
   location_update.nodes = {root_data};
diff --git a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.h b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.h
index 0d5ab0b..71aff80 100644
--- a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.h
+++ b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler.h
@@ -21,7 +21,6 @@
 #include "ui/accessibility/ax_mode.h"
 #include "ui/accessibility/ax_mode_observer.h"
 #include "ui/accessibility/platform/ax_platform.h"
-#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 
@@ -63,9 +62,9 @@
 
   bool IsOcrServiceEnabled() const;
   bool IsAccessibilityEnabled() const;
-  void DocumentUpdated(const std::vector<gfx::Insets>& page_locations,
+  void DocumentUpdated(const std::vector<gfx::RectF>& page_locations,
                        const std::vector<uint64_t>& dirty_pages);
-  void ViewportUpdated(const gfx::Insets& viewport_box, float scaleFactor);
+  void ViewportUpdated(const gfx::RectF& viewport_box, float scaleFactor);
 
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
   const std::vector<std::unique_ptr<ui::AXTreeManager>>& GetPagesForTesting() {
@@ -102,8 +101,7 @@
 
  private:
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-  void UpdatePageLocation(uint64_t page_index,
-                          const gfx::Insets& page_location);
+  void UpdatePageLocation(uint64_t page_index, const gfx::RectF& page_location);
   void OcrNextDirtyPageIfAny();
   void OnPageOcred(uint64_t dirty_page_index,
                    const ui::AXTreeUpdate& tree_update);
@@ -112,7 +110,7 @@
   base::ScopedObservation<ui::AXPlatform, ui::AXModeObserver>
       ax_mode_observation_{this};
 
-  std::vector<gfx::Insets> page_locations_;
+  std::vector<gfx::RectF> page_locations_;
   // This `BrowserContext` will always outlive the WebUI, so this is safe.
   raw_ref<content::BrowserContext> browser_context_;
   mojo::Remote<media_app_ui::mojom::OcrUntrustedPage> media_app_page_;
diff --git a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler_browsertest.cc b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler_browsertest.cc
index 2b45295..218be4c 100644
--- a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler_browsertest.cc
+++ b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_handler_browsertest.cc
@@ -28,7 +28,6 @@
 #include "ui/accessibility/ax_tree.h"
 #include "ui/accessibility/ax_tree_data.h"
 #include "ui/accessibility/ax_tree_manager.h"
-#include "ui/gfx/geometry/insets.h"
 
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 #include <vector>
@@ -41,6 +40,24 @@
 
 namespace {
 
+// Gap or padding between pages.
+constexpr uint64_t kTestPageGap = 2;
+// Width of a test page.
+constexpr uint64_t kTestPageWidth = 3;
+// Height of a test page.
+constexpr uint64_t kTestPageHeight = 8;
+
+std::vector<gfx::RectF> CreateFakePageBounds(const uint64_t num_pages) {
+  uint64_t x = 0, y = 0;
+  std::vector<gfx::RectF> fake_page_locations;
+  for (uint64_t i = 0; i < num_pages; ++i) {
+    gfx::RectF page_location(x, y + kTestPageGap * i + kTestPageHeight * i,
+                             kTestPageWidth, kTestPageHeight);
+    fake_page_locations.push_back(page_location);
+  }
+  return fake_page_locations;
+}
+
 class AXMediaAppUntrustedHandlerTest : public InProcessBrowserTest {
  public:
   AXMediaAppUntrustedHandlerTest() : feature_list_(features::kBacklightOcr) {}
@@ -99,7 +116,7 @@
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 IN_PROC_BROWSER_TEST_F(AXMediaAppUntrustedHandlerTest, DocumentUpdated) {
   handler_->DocumentUpdated(
-      /*page_locations=*/{gfx::Insets(1u), gfx::Insets(2u), gfx::Insets(3u)},
+      /*page_locations=*/CreateFakePageBounds(3),
       /*dirty_pages=*/{0u, 1u, 2u});
   WaitForOcringPages(3u);
 
@@ -123,18 +140,31 @@
   for (const std::unique_ptr<ui::AXTreeManager>& page : pages) {
     page->ax_tree()->UpdateDataForTesting(tree_data);
   }
-  EXPECT_EQ("AXTree\nid=-2 staticText name=Testing (1, 1)-(2, 2)\n",
+  EXPECT_EQ("AXTree\nid=-2 staticText name=Testing (0, 0)-(3, 8)\n",
             pages[0]->ax_tree()->ToString());
-  EXPECT_EQ("AXTree\nid=-3 staticText name=Testing (2, 2)-(4, 4)\n",
+  EXPECT_EQ("AXTree\nid=-3 staticText name=Testing (0, 10)-(3, 8)\n",
             pages[1]->ax_tree()->ToString());
-  EXPECT_EQ("AXTree\nid=-4 staticText name=Testing (3, 3)-(6, 6)\n",
+  EXPECT_EQ("AXTree\nid=-4 staticText name=Testing (0, 20)-(3, 8)\n",
             pages[2]->ax_tree()->ToString());
 
-  // Resize all pages, OCR the second page  again, and add an additional page to
-  // the end.
+  // Relocate or resize all pages, OCR the second page again, and add an
+  // additional page to the end. This scenario could happen if the second page
+  // was rotated.
   handler_->DocumentUpdated(
-      /*page_locations=*/{gfx::Insets(2u), gfx::Insets(3u), gfx::Insets(4u),
-                          gfx::Insets(5u)},
+      /*page_locations=*/
+      {
+          gfx::RectF(/*x=*/-3, /*y=*/0, /*width=*/kTestPageWidth,
+                     /*height=*/kTestPageHeight),
+          gfx::RectF(/*x=*/-3, /*y=*/10,
+                     /*width=*/kTestPageHeight,
+                     /*height=*/kTestPageWidth),
+          gfx::RectF(/*x=*/-3, /*y=*/15,
+                     /*width=*/kTestPageWidth,
+                     /*height=*/kTestPageHeight),
+          gfx::RectF(/*x=*/-3, /*y=*/25,
+                     /*width=*/kTestPageWidth,
+                     /*height=*/kTestPageHeight),
+      },
       /*dirty_pages=*/{1u, 3u});
   WaitForOcringPages(2u);
 
@@ -151,13 +181,13 @@
     page->ax_tree()->UpdateDataForTesting(tree_data);
   }
 
-  EXPECT_EQ("AXTree\nid=-2 staticText name=Testing (2, 2)-(4, 4)\n",
+  EXPECT_EQ("AXTree\nid=-2 staticText name=Testing (-3, 0)-(3, 8)\n",
             pages2[0]->ax_tree()->ToString());
-  EXPECT_EQ("AXTree\nid=-5 staticText name=Testing (3, 3)-(6, 6)\n",
+  EXPECT_EQ("AXTree\nid=-5 staticText name=Testing (-3, 10)-(8, 3)\n",
             pages2[1]->ax_tree()->ToString());
-  EXPECT_EQ("AXTree\nid=-4 staticText name=Testing (4, 4)-(8, 8)\n",
+  EXPECT_EQ("AXTree\nid=-4 staticText name=Testing (-3, 15)-(3, 8)\n",
             pages2[2]->ax_tree()->ToString());
-  EXPECT_EQ("AXTree\nid=-6 staticText name=Testing (5, 5)-(10, 10)\n",
+  EXPECT_EQ("AXTree\nid=-6 staticText name=Testing (-3, 25)-(3, 8)\n",
             pages2[3]->ax_tree()->ToString());
 }
 #endif  // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
diff --git a/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc
index d393035..d4426642 100644
--- a/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/shortcut_publisher_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/test/test_future.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
+#include "chrome/browser/apps/app_service/app_service_test.h"
 #include "chrome/browser/apps/app_service/publishers/app_publisher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/test/base/testing_profile.h"
@@ -99,6 +100,7 @@
     scoped_feature_list_.InitAndEnableFeature(
         chromeos::features::kCrosWebAppShortcutUiUpdate);
     profile_ = std::make_unique<TestingProfile>();
+    WaitForAppServiceProxyReady(proxy());
   }
 
   Profile* profile() { return profile_.get(); }
diff --git a/chrome/browser/ash/accessibility/accessibility_extension_api_browsertest.cc b/chrome/browser/ash/accessibility/accessibility_extension_api_browsertest.cc
index 6867dd5..77350da 100644
--- a/chrome/browser/ash/accessibility/accessibility_extension_api_browsertest.cc
+++ b/chrome/browser/ash/accessibility/accessibility_extension_api_browsertest.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 "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/shell.h"
diff --git a/chrome/browser/ash/accessibility/accessibility_manager.cc b/chrome/browser/ash/accessibility/accessibility_manager.cc
index bf5bc271..e740d6a2b 100644
--- a/chrome/browser/ash/accessibility/accessibility_manager.cc
+++ b/chrome/browser/ash/accessibility/accessibility_manager.cc
@@ -9,7 +9,7 @@
 
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/autoclick/autoclick_controller.h"
 #include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
 #include "ash/color_enhancement/color_enhancement_controller.h"
diff --git a/chrome/browser/ash/accessibility/chromevox_panel.cc b/chrome/browser/ash/accessibility/chromevox_panel.cc
index 2ecb342..236a84cb 100644
--- a/chrome/browser/ash/accessibility/chromevox_panel.cc
+++ b/chrome/browser/ash/accessibility/chromevox_panel.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
diff --git a/chrome/browser/ash/accessibility/dictation_bubble_test_helper.cc b/chrome/browser/ash/accessibility/dictation_bubble_test_helper.cc
index 5cf57a7..fa815b51 100644
--- a/chrome/browser/ash/accessibility/dictation_bubble_test_helper.cc
+++ b/chrome/browser/ash/accessibility/dictation_bubble_test_helper.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ash/accessibility/dictation_bubble_test_helper.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/shell.h"
 #include "ash/system/accessibility/dictation_bubble_view.h"
diff --git a/chrome/browser/ash/accessibility/select_to_speak_event_handler_delegate_impl.cc b/chrome/browser/ash/accessibility/select_to_speak_event_handler_delegate_impl.cc
index 00745d5..8ef01fc 100644
--- a/chrome/browser/ash/accessibility/select_to_speak_event_handler_delegate_impl.cc
+++ b/chrome/browser/ash/accessibility/select_to_speak_event_handler_delegate_impl.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/event_handler_common.h"
 #include "chrome/common/extensions/extension_constants.h"
diff --git a/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc b/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc
index 37a931b8..4f2533c 100644
--- a/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc
+++ b/chrome/browser/ash/accessibility/service/accessibility_service_client_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include <optional>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
 #include "ash/accessibility/ui/accessibility_focus_ring_controller_impl.h"
 #include "ash/accessibility/ui/accessibility_highlight_layer.h"
diff --git a/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc b/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
index 73462c5..1815176f 100644
--- a/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
+++ b/chrome/browser/ash/accessibility/service/autoclick_client_impl.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ash/accessibility/service/autoclick_client_impl.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "base/debug/stack_trace.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/chrome/browser/ash/accessibility/service/user_interface_impl.cc b/chrome/browser/ash/accessibility/service/user_interface_impl.cc
index 1c5f403..055cc534 100644
--- a/chrome/browser/ash/accessibility/service/user_interface_impl.cc
+++ b/chrome/browser/ash/accessibility/service/user_interface_impl.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ash/accessibility/service/user_interface_impl.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/accessibility_focus_ring_info.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "content/public/common/color_parser.h"
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index 4229de2..9cce7f80 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -6,7 +6,7 @@
 
 #include <queue>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
diff --git a/chrome/browser/ash/app_list/app_service/app_service_app_item_browsertest.cc b/chrome/browser/ash/app_list/app_service/app_service_app_item_browsertest.cc
index eaa9cb1..555a1927 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_app_item_browsertest.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_app_item_browsertest.cc
@@ -35,6 +35,7 @@
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
+#include "ui/display/screen.h"
 #include "ui/events/event_constants.h"
 
 using web_app::test::CrosapiParam;
@@ -212,7 +213,7 @@
                        ActivateAppRecordsNewInstallHistogram) {
   base::HistogramTester histograms;
   {
-    ASSERT_FALSE(ash::TabletMode::Get()->InTabletMode());
+    ASSERT_FALSE(display::Screen::GetScreen()->InTabletMode());
 
     // Simulate a user-installed chrome app item.
     std::unique_ptr<AppServiceAppItem> app_item =
diff --git a/chrome/browser/ash/app_mode/kiosk_system_session.cc b/chrome/browser/ash/app_mode/kiosk_system_session.cc
index 2e8b9bb..23e0568 100644
--- a/chrome/browser/ash/app_mode/kiosk_system_session.cc
+++ b/chrome/browser/ash/app_mode/kiosk_system_session.cc
@@ -6,7 +6,7 @@
 #include <memory>
 #include <optional>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
 #include "base/scoped_observation.h"
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
index 38a6ff8..7d6d91c 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_tree_tracker.cc
@@ -7,7 +7,7 @@
 #include <memory>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/accessibility/magnifier/docked_magnifier_controller.h"
 #include "ash/accessibility/magnifier/fullscreen_magnifier_controller.h"
 #include "ash/components/arc/arc_util.h"
diff --git a/chrome/browser/ash/crosapi/BUILD.gn b/chrome/browser/ash/crosapi/BUILD.gn
index 9b91b6ae..7213f77 100644
--- a/chrome/browser/ash/crosapi/BUILD.gn
+++ b/chrome/browser/ash/crosapi/BUILD.gn
@@ -171,6 +171,8 @@
     "lacros_data_backward_migration_mode_policy_observer.cc",
     "lacros_data_backward_migration_mode_policy_observer.h",
     "lacros_selection_loader.h",
+    "lacros_shelf_item_tracker.cc",
+    "lacros_shelf_item_tracker.h",
     "local_printer_ash.cc",
     "local_printer_ash.h",
     "login_ash.cc",
@@ -518,6 +520,7 @@
     "keystore_service_ash_unittest.cc",
     "lacros_availability_policy_observer_unittest.cc",
     "lacros_data_backward_migration_mode_policy_observer_unittest.cc",
+    "lacros_shelf_item_tracker_unittest.cc",
     "local_printer_ash_unittest.cc",
     "login_screen_storage_ash_unittest.cc",
     "login_state_ash_unittest.cc",
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc
index afedba4..fe66fb4 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.cc
+++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -70,6 +70,7 @@
 #include "chrome/browser/ash/crosapi/kerberos_in_browser_ash.h"
 #include "chrome/browser/ash/crosapi/keystore_service_ash.h"
 #include "chrome/browser/ash/crosapi/kiosk_session_service_ash.h"
+#include "chrome/browser/ash/crosapi/lacros_shelf_item_tracker.h"
 #include "chrome/browser/ash/crosapi/local_printer_ash.h"
 #include "chrome/browser/ash/crosapi/login_ash.h"
 #include "chrome/browser/ash/crosapi/login_screen_storage_ash.h"
@@ -148,6 +149,7 @@
 #include "chromeos/crosapi/mojom/image_writer.mojom.h"
 #include "chromeos/crosapi/mojom/kerberos_in_browser.mojom.h"
 #include "chromeos/crosapi/mojom/keystore_service.mojom.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
 #include "chromeos/crosapi/mojom/local_printer.mojom.h"
 #include "chromeos/crosapi/mojom/message_center.mojom.h"
 #include "chromeos/crosapi/mojom/multi_capture_service.mojom.h"
@@ -260,6 +262,7 @@
       kerberos_in_browser_ash_(std::make_unique<KerberosInBrowserAsh>()),
       keystore_service_ash_(std::make_unique<KeystoreServiceAsh>()),
       kiosk_session_service_ash_(std::make_unique<KioskSessionServiceAsh>()),
+      lacros_shelf_item_tracker_(std::make_unique<LacrosShelfItemTracker>()),
       local_printer_ash_(std::make_unique<LocalPrinterAsh>()),
       login_ash_(std::make_unique<LoginAsh>()),
       login_screen_storage_ash_(std::make_unique<LoginScreenStorageAsh>()),
@@ -683,6 +686,11 @@
   kiosk_session_service_ash_->BindReceiver(std::move(receiver));
 }
 
+void CrosapiAsh::BindLacrosShelfItemTracker(
+    mojo::PendingReceiver<mojom::LacrosShelfItemTracker> receiver) {
+  lacros_shelf_item_tracker_->BindReceiver(std::move(receiver));
+}
+
 void CrosapiAsh::BindLacrosAppPublisher(
     mojo::PendingReceiver<mojom::AppPublisher> receiver) {
   Profile* profile = ProfileManager::GetPrimaryUserProfile();
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h
index fbcea73..e9c0a65 100644
--- a/chrome/browser/ash/crosapi/crosapi_ash.h
+++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -17,6 +17,7 @@
 #include "chromeos/crosapi/mojom/editor_panel.mojom-forward.h"
 #include "chromeos/crosapi/mojom/emoji_picker.mojom-forward.h"
 #include "chromeos/crosapi/mojom/firewall_hole.mojom.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
 #include "chromeos/crosapi/mojom/task_manager.mojom.h"
 #include "chromeos/crosapi/mojom/telemetry_diagnostic_routine_service.mojom.h"
 #include "media/gpu/buildflags.h"
@@ -90,6 +91,7 @@
 class KerberosInBrowserAsh;
 class KeystoreServiceAsh;
 class KioskSessionServiceAsh;
+class LacrosShelfItemTracker;
 class LocalPrinterAsh;
 class LoginAsh;
 class LoginScreenStorageAsh;
@@ -276,6 +278,8 @@
       mojo::PendingReceiver<mojom::KeystoreService> receiver) override;
   void BindKioskSessionService(
       mojo::PendingReceiver<mojom::KioskSessionService> receiver) override;
+  void BindLacrosShelfItemTracker(
+      mojo::PendingReceiver<mojom::LacrosShelfItemTracker> receiver) override;
   void BindLacrosAppPublisher(
       mojo::PendingReceiver<mojom::AppPublisher> receiver) override;
   void BindLocalPrinter(
@@ -646,6 +650,7 @@
   std::unique_ptr<KerberosInBrowserAsh> kerberos_in_browser_ash_;
   std::unique_ptr<KeystoreServiceAsh> keystore_service_ash_;
   std::unique_ptr<KioskSessionServiceAsh> kiosk_session_service_ash_;
+  std::unique_ptr<LacrosShelfItemTracker> lacros_shelf_item_tracker_;
   std::unique_ptr<LocalPrinterAsh> local_printer_ash_;
   std::unique_ptr<LoginAsh> login_ash_;
   std::unique_ptr<LoginScreenStorageAsh> login_screen_storage_ash_;
diff --git a/chrome/browser/ash/crosapi/crosapi_util.cc b/chrome/browser/ash/crosapi/crosapi_util.cc
index e29f96b..161f4162 100644
--- a/chrome/browser/ash/crosapi/crosapi_util.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util.cc
@@ -100,6 +100,7 @@
 #include "chromeos/crosapi/mojom/kerberos_in_browser.mojom.h"
 #include "chromeos/crosapi/mojom/keystore_service.mojom.h"
 #include "chromeos/crosapi/mojom/kiosk_session_service.mojom.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
 #include "chromeos/crosapi/mojom/launcher_search.mojom.h"
 #include "chromeos/crosapi/mojom/local_printer.mojom.h"
 #include "chromeos/crosapi/mojom/login.mojom.h"
@@ -304,7 +305,7 @@
   return {T::Uuid_, T::Version_};
 }
 
-static_assert(crosapi::mojom::Crosapi::Version_ == 125,
+static_assert(crosapi::mojom::Crosapi::Version_ == 126,
               "If you add a new crosapi, please add it to "
               "kInterfaceVersionEntries below.");
 
@@ -375,6 +376,7 @@
     MakeInterfaceVersionEntry<crosapi::mojom::KerberosInBrowser>(),
     MakeInterfaceVersionEntry<crosapi::mojom::KeystoreService>(),
     MakeInterfaceVersionEntry<crosapi::mojom::KioskSessionService>(),
+    MakeInterfaceVersionEntry<crosapi::mojom::LacrosShelfItemTracker>(),
     MakeInterfaceVersionEntry<crosapi::mojom::LocalPrinter>(),
     MakeInterfaceVersionEntry<crosapi::mojom::Login>(),
     MakeInterfaceVersionEntry<crosapi::mojom::LoginScreenStorage>(),
diff --git a/chrome/browser/ash/crosapi/embedded_accessibility_helper_client_ash.cc b/chrome/browser/ash/crosapi/embedded_accessibility_helper_client_ash.cc
index 6763e13..48bde58 100644
--- a/chrome/browser/ash/crosapi/embedded_accessibility_helper_client_ash.cc
+++ b/chrome/browser/ash/crosapi/embedded_accessibility_helper_client_ash.cc
@@ -6,7 +6,7 @@
 
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chromeos/crosapi/mojom/embedded_accessibility_helper.mojom.h"
 
diff --git a/chrome/browser/ash/crosapi/lacros_shelf_item_tracker.cc b/chrome/browser/ash/crosapi/lacros_shelf_item_tracker.cc
new file mode 100644
index 0000000..66e95cdf
--- /dev/null
+++ b/chrome/browser/ash/crosapi/lacros_shelf_item_tracker.cc
@@ -0,0 +1,165 @@
+// 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/crosapi/lacros_shelf_item_tracker.h"
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "ash/public/cpp/shelf_item.h"
+#include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/shelf_types.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chrome/browser/ui/ash/shelf/lacros_shelf_item_controller.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
+#include "components/exo/shell_surface_util.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "ui/aura/env.h"
+
+namespace crosapi {
+
+LacrosShelfItemTracker::LacrosShelfItemTracker() {
+  env_observation_.Observe(aura::Env::GetInstance());
+}
+
+LacrosShelfItemTracker::~LacrosShelfItemTracker() = default;
+
+void LacrosShelfItemTracker::BindReceiver(
+    mojo::PendingReceiver<mojom::LacrosShelfItemTracker> pending_receiver) {
+  receivers_.Add(this, std::move(pending_receiver));
+}
+
+void LacrosShelfItemTracker::AddOrUpdateWindow(
+    mojom::WindowDataPtr window_data) {
+  std::string window_id = window_data->window_id;
+  auto it = lacros_prefixed_windows_.find(window_id);
+  if (it != lacros_prefixed_windows_.end() &&
+      it->second.window_data.has_value()) {
+    CHECK(window_data->item_id == it->second.window_data.value()->item_id)
+        << "The window has already been added under a different shelf item.";
+  }
+  lacros_prefixed_windows_[window_id].window_data = std::move(window_data);
+  MaybeAddToShelf(window_id);
+}
+
+void LacrosShelfItemTracker::OnWindowDestroying(aura::Window* window) {
+  lacros_prefixed_windows_observations_.RemoveObservation(window);
+
+  std::string window_id = *exo::GetShellApplicationId(window);
+
+  auto lacros_prefixed_windows_it = lacros_prefixed_windows_.find(window_id);
+  CHECK(lacros_prefixed_windows_it != lacros_prefixed_windows_.end());
+
+  // If |window_data| is non-null, then we should delete the window from
+  // |shelf_tem_windows_| as well. If it's the last window of the Shelf item,
+  // remove the item from Shelf.
+  if (lacros_prefixed_windows_it->second.window_data.has_value()) {
+    std::string item_id =
+        lacros_prefixed_windows_it->second.window_data.value()->item_id;
+    auto shelf_item_windows_it = shelf_item_windows_.find(item_id);
+    CHECK(shelf_item_windows_it != shelf_item_windows_.end());
+    std::set<std::string>& windows_under_item = shelf_item_windows_it->second;
+
+    auto window_it = windows_under_item.find(window_id);
+    CHECK(window_it != windows_under_item.end());
+    windows_under_item.erase(window_it);
+    if (windows_under_item.empty()) {
+      RemoveFromShelf(ash::ShelfID(item_id));
+    }
+  }
+
+  lacros_prefixed_windows_.erase(window_id);
+}
+
+void LacrosShelfItemTracker::OnWindowInitialized(aura::Window* window) {
+  if (!crosapi::browser_util::IsLacrosWindow(window)) {
+    return;
+  }
+
+  // If |IsLacrosWindow()| is true, then the ID cannot be null.
+  std::string window_id = *exo::GetShellApplicationId(window);
+  lacros_prefixed_windows_[window_id].window = window;
+  lacros_prefixed_windows_observations_.AddObservation(window);
+
+  MaybeAddToShelf(window_id);
+}
+
+ash::ShelfItemDelegate*
+LacrosShelfItemTracker::AddOrUpdateShelfItemAndReturnDelegate(
+    mojom::WindowDataPtr window_data) {
+  std::string item_id = window_data->item_id;
+  ash::ShelfID shelf_id(item_id);
+
+  ash::ShelfItem item;
+  item.id = shelf_id;
+  item.status = ash::STATUS_RUNNING;
+  item.type = ash::TYPE_APP;
+  if (!window_data->icon.isNull()) {
+    item.image = window_data->icon;
+  }
+
+  ash::ShelfModel* shelf_model = ash::ShelfModel::Get();
+  int index = ash::ShelfModel::Get()->ItemIndexByID(shelf_id);
+
+  if (index == -1) {
+    // If there is no existing item by the ID in the Shelf, we add.
+    mojom::InstanceType instance_type = window_data->instance_type;
+    std::unique_ptr<ash::ShelfItemDelegate> created_delegate =
+        CreateDelegateByInstanceType(shelf_id, instance_type);
+    shelf_model->Add(item, std::move(created_delegate));
+  } else {
+    // If the item already exists in the Shelf, we update.
+    ash::ShelfModel::Get()->Set(index, item);
+  }
+  return shelf_model->GetShelfItemDelegate(shelf_id);
+}
+
+void LacrosShelfItemTracker::RemoveFromShelf(const ash::ShelfID& shelf_id) {
+  ash::ShelfModel::Get()->RemoveItemAndTakeShelfItemDelegate(shelf_id);
+}
+
+void LacrosShelfItemTracker::MaybeAddToShelf(const std::string& window_id) {
+  auto lacros_prefixed_windows_it = lacros_prefixed_windows_.find(window_id);
+  CHECK(lacros_prefixed_windows_it != lacros_prefixed_windows_.end());
+
+  if (!lacros_prefixed_windows_it->second.window ||
+      !lacros_prefixed_windows_it->second.window_data.has_value()) {
+    return;
+  }
+
+  mojom::WindowDataPtr window_data =
+      lacros_prefixed_windows_it->second.window_data.value().Clone();
+  std::string item_id = window_data->item_id;
+
+  ash::ShelfItemDelegate* delegate =
+      AddOrUpdateShelfItemAndReturnDelegate(std::move(window_data));
+
+  // |delegate| must be non-null and a child class of
+  // |LacrosShelfItemController|.
+  static_cast<LacrosShelfItemController*>(delegate)->AddWindow(
+      lacros_prefixed_windows_it->second.window);
+
+  shelf_item_windows_[item_id].insert(window_id);
+}
+
+// Returned delegate must be a child of |LacrosShelfItemController|.
+std::unique_ptr<ash::ShelfItemDelegate>
+LacrosShelfItemTracker::CreateDelegateByInstanceType(
+    const ash::ShelfID& shelf_id,
+    mojom::InstanceType instance_type) {
+  switch (instance_type) {
+    case mojom::InstanceType::kIsolatedWebAppInstaller: {
+      // TODO(crbug.com/1479140): implement.
+      return nullptr;
+    }
+  }
+  return nullptr;
+}
+
+LacrosShelfItemTracker::WindowInfo::WindowInfo() = default;
+LacrosShelfItemTracker::WindowInfo::~WindowInfo() = default;
+}  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/lacros_shelf_item_tracker.h b/chrome/browser/ash/crosapi/lacros_shelf_item_tracker.h
new file mode 100644
index 0000000..367e8a80
--- /dev/null
+++ b/chrome/browser/ash/crosapi/lacros_shelf_item_tracker.h
@@ -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.
+#ifndef CHROME_BROWSER_ASH_CROSAPI_LACROS_SHELF_ITEM_TRACKER_H_
+#define CHROME_BROWSER_ASH_CROSAPI_LACROS_SHELF_ITEM_TRACKER_H_
+
+#include <map>
+#include <string>
+
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/aura/env.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
+
+namespace ash {
+class ShelfItemDelegate;
+struct ShelfID;
+}  // namespace ash
+
+namespace crosapi {
+
+// Receives Lacros windows and tracks them.
+// Also tracks the destruction of the windows.
+// Based on the tracked windows, this class can add new items to the Shelf and
+// remove existing items from the Shelf.
+class LacrosShelfItemTracker : public mojom::LacrosShelfItemTracker,
+                               public aura::EnvObserver,
+                               public aura::WindowObserver {
+ public:
+  LacrosShelfItemTracker();
+  ~LacrosShelfItemTracker() override;
+
+  void BindReceiver(
+      mojo::PendingReceiver<mojom::LacrosShelfItemTracker> receiver);
+
+  // mojom::LacrosShelfItemTracker override:
+  void AddOrUpdateWindow(mojom::WindowDataPtr window_data) override;
+
+  // aura::WindowObserver override:
+  void OnWindowDestroying(aura::Window* window) override;
+
+  // aura::EnvObserver override:
+  void OnWindowInitialized(aura::Window* window) override;
+
+ protected:
+  // Protected virtual for testing.
+  virtual ash::ShelfItemDelegate* AddOrUpdateShelfItemAndReturnDelegate(
+      mojom::WindowDataPtr window_data);
+
+  // Protected virtual for testing.
+  virtual void RemoveFromShelf(const ash::ShelfID& shelf_id);
+
+ private:
+  struct WindowInfo {
+    WindowInfo();
+    ~WindowInfo();
+    raw_ptr<aura::Window> window;
+    absl::optional<mojom::WindowDataPtr> window_data;
+  };
+
+  // If |window_id| is present in both |all_lacros_windows_| and
+  // |tracked_windows_| then we add the window to shelf.
+  void MaybeAddToShelf(const std::string& window_id);
+
+  // Virtual for testing.
+  virtual std::unique_ptr<ash::ShelfItemDelegate> CreateDelegateByInstanceType(
+      const ash::ShelfID& shelf_id,
+      mojom::InstanceType instance_type);
+
+  // A map for all windows whose app id starts with |LacrosAppIdPrefix| when
+  // initialized.
+  std::map<std::string, WindowInfo> lacros_prefixed_windows_;
+
+  // A map for all windows associated with a Shelf item this instance has
+  // created. Key: unique ID for a Shelf item. Value: a set of all window ID
+  // associated with the item.
+  std::map<std::string, std::set<std::string>> shelf_item_windows_;
+
+  // Observes all Lacros windows for destruction.
+  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
+      lacros_prefixed_windows_observations_{this};
+
+  // Observes aura::Env for newly created windows.
+  base::ScopedObservation<aura::Env, EnvObserver> env_observation_{this};
+
+  // Supports multiple receivers.
+  mojo::ReceiverSet<mojom::LacrosShelfItemTracker> receivers_;
+};
+
+}  // namespace crosapi
+
+#endif  // CHROME_BROWSER_ASH_CROSAPI_LACROS_SHELF_ITEM_TRACKER_H_
diff --git a/chrome/browser/ash/crosapi/lacros_shelf_item_tracker_unittest.cc b/chrome/browser/ash/crosapi/lacros_shelf_item_tracker_unittest.cc
new file mode 100644
index 0000000..c4a7089
--- /dev/null
+++ b/chrome/browser/ash/crosapi/lacros_shelf_item_tracker_unittest.cc
@@ -0,0 +1,278 @@
+// 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/crosapi/lacros_shelf_item_tracker.h"
+
+#include <memory>
+#include <string>
+
+#include "ash/public/cpp/shelf_model.h"
+#include "ash/public/cpp/shelf_types.h"
+#include "ash/test/ash_test_base.h"
+#include "chrome/browser/ui/ash/shelf/lacros_shelf_item_controller.h"
+#include "chromeos/crosapi/cpp/crosapi_constants.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
+#include "components/exo/window_properties.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace crosapi {
+
+namespace {
+
+gfx::ImageSkia CreateGreenSquareIcon() {
+  int size_px = 128;
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(size_px, size_px);
+  bitmap.eraseColor(SK_ColorGREEN);
+  return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
+}
+
+}  // namespace
+
+// This class does nothing.
+class TestShelfItemDelegate : public LacrosShelfItemController {
+ public:
+  explicit TestShelfItemDelegate(const ash::ShelfID& shelf_id)
+      : LacrosShelfItemController(shelf_id) {}
+  ~TestShelfItemDelegate() override = default;
+
+  void AddWindow(aura::Window* window) override {}
+
+  // ShelfItemDelegate:
+  void ExecuteCommand(bool from_context_menu,
+                      int64_t command_id,
+                      int32_t event_flags,
+                      int64_t display_id) override {}
+  void Close() override {}
+};
+
+class TestLacrosShelfItemTracker : public LacrosShelfItemTracker {
+ public:
+  TestLacrosShelfItemTracker() = default;
+
+  void RemoveFromShelf(const ash::ShelfID& shelf_id) override {
+    last_removed_shelf_id_ = shelf_id;
+    LacrosShelfItemTracker::RemoveFromShelf(shelf_id);
+  }
+
+  ash::ShelfItemDelegate* AddOrUpdateShelfItemAndReturnDelegate(
+      mojom::WindowDataPtr window_data) override {
+    last_updated_shelf_id_ = ash::ShelfID(window_data->item_id);
+    return LacrosShelfItemTracker::AddOrUpdateShelfItemAndReturnDelegate(
+        std::move(window_data));
+  }
+
+  const ash::ShelfID& last_updated_shelf_id() { return last_updated_shelf_id_; }
+  const ash::ShelfID& last_removed_shelf_id() { return last_removed_shelf_id_; }
+
+ private:
+  std::unique_ptr<ash::ShelfItemDelegate> CreateDelegateByInstanceType(
+      const ash::ShelfID& shelf_id,
+      mojom::InstanceType instance_type) override {
+    return std::make_unique<TestShelfItemDelegate>(shelf_id);
+  }
+
+  ash::ShelfID last_updated_shelf_id_;
+  ash::ShelfID last_removed_shelf_id_;
+};
+
+class LacrosShelfItemTrackerTest : public ash::AshTestBase {
+ public:
+  ash::ShelfModel* shelf_model() { return ash::ShelfModel::Get(); }
+
+  std::unique_ptr<aura::Window> CreateAndInitWindowWithId(std::string id) {
+    auto window = std::make_unique<aura::Window>(nullptr);
+    window->SetProperty(exo::kApplicationIdKey, id);
+    CHECK(*(window->GetProperty(exo::kApplicationIdKey)) == id);
+    window->Init(ui::LAYER_NOT_DRAWN);
+    return window;
+  }
+
+  std::string lacros_window_prefix() { return kLacrosAppIdPrefix; }
+
+  void AddOrUpdateWindow(std::string item_id,
+                         std::string window_id,
+                         gfx::ImageSkia icon) {
+    mojom::WindowDataPtr window_data = mojom::WindowData::New(
+        item_id, window_id, mojom::InstanceType::kIsolatedWebAppInstaller,
+        icon);
+    tracker_.AddOrUpdateWindow(std::move(window_data));
+  }
+
+  const ash::ShelfID& last_updated_shelf_id() {
+    return tracker_.last_updated_shelf_id();
+  }
+  const ash::ShelfID& last_removed_shelf_id() {
+    return tracker_.last_removed_shelf_id();
+  }
+
+ private:
+  TestLacrosShelfItemTracker tracker_;
+};
+
+TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemInitWindowBeforeCrosapi) {
+  std::string item_id = "test_item_id";
+  std::string window_id = lacros_window_prefix() + "test_window_id";
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  auto window = CreateAndInitWindowWithId(window_id);
+  AddOrUpdateWindow(item_id, window_id, icon);
+
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+  EXPECT_TRUE(shelf_model()->items()[0].image.BackedBySameObjectAs(icon));
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
+}
+
+TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemInitWindowAfterCrosapi) {
+  std::string item_id = "test_item_id";
+  std::string window_id = lacros_window_prefix() + " test_window_id";
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  AddOrUpdateWindow(item_id, window_id, icon);
+  auto window = CreateAndInitWindowWithId(window_id);
+
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+  EXPECT_TRUE(shelf_model()->items()[0].image.BackedBySameObjectAs(icon));
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
+}
+
+TEST_F(LacrosShelfItemTrackerTest, IgnoresNotLacrosWindow) {
+  std::string item_id = "test_item_id";
+  std::string window_id = " test_window_id";
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  AddOrUpdateWindow(item_id, window_id, icon);
+  auto window = CreateAndInitWindowWithId(window_id);
+
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID());
+}
+
+TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemUpdateIcon) {
+  std::string item_id = "test_item_id";
+  std::string window_id = lacros_window_prefix() + " test_window_id";
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  auto window = CreateAndInitWindowWithId(window_id);
+  AddOrUpdateWindow(item_id, window_id, gfx::ImageSkia());
+
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+  EXPECT_TRUE(shelf_model()->items()[0].image.isNull());
+
+  AddOrUpdateWindow(item_id, window_id, icon);
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+  EXPECT_TRUE(shelf_model()->items()[0].image.BackedBySameObjectAs(icon));
+}
+
+TEST_F(LacrosShelfItemTrackerTest, SingleShelfItemMultipleWindow) {
+  std::string item_id = "test_item_id";
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  std::string window_id_0 = lacros_window_prefix() + " test_window_id_0";
+  auto window_0 = CreateAndInitWindowWithId(window_id_0);
+  AddOrUpdateWindow(item_id, window_id_0, icon);
+
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
+
+  std::string window_id_1 = lacros_window_prefix() + " test_window_id_1";
+  auto window_1 = CreateAndInitWindowWithId(window_id_1);
+  AddOrUpdateWindow(item_id, window_id_1, icon);
+
+  EXPECT_EQ(shelf_model()->item_count(), 1);
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
+
+  std::string window_id_2 = lacros_window_prefix() + " test_window_id_2";
+  auto window_2 = CreateAndInitWindowWithId(window_id_2);
+  AddOrUpdateWindow(item_id, window_id_2, icon);
+
+  EXPECT_EQ(shelf_model()->item_count(), 1);
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id"));
+}
+
+TEST_F(LacrosShelfItemTrackerTest, MultipleShelfItem) {
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  std::string item_id_0 = "test_item_id_0";
+  std::string window_id_0 = lacros_window_prefix() + " test_window_id_0";
+  auto window_0 = CreateAndInitWindowWithId(window_id_0);
+  AddOrUpdateWindow(item_id_0, window_id_0, icon);
+
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id_0"));
+
+  std::string item_id_1 = "test_item_id_1";
+  std::string window_id_1 = lacros_window_prefix() + " test_window_id_1";
+  auto window_1 = CreateAndInitWindowWithId(window_id_1);
+  AddOrUpdateWindow(item_id_1, window_id_1, icon);
+
+  EXPECT_EQ(shelf_model()->item_count(), 2);
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id_1"));
+
+  std::string item_id_2 = "test_item_id_2";
+  std::string window_id_2 = lacros_window_prefix() + " test_window_id_2";
+  auto window_2 = CreateAndInitWindowWithId(window_id_2);
+  AddOrUpdateWindow(item_id_2, window_id_2, icon);
+
+  EXPECT_EQ(shelf_model()->item_count(), 3);
+  EXPECT_EQ(last_updated_shelf_id(), ash::ShelfID("test_item_id_2"));
+}
+
+TEST_F(LacrosShelfItemTrackerTest, ShelfItemRemovedWhenAllWindowDestoryed) {
+  std::string item_id = "test_item_id";
+  gfx::ImageSkia icon = CreateGreenSquareIcon();
+
+  ASSERT_EQ(last_updated_shelf_id(), ash::ShelfID());
+  ASSERT_EQ(shelf_model()->item_count(), 0);
+
+  std::string window_id_0 = lacros_window_prefix() + " test_window_id_0";
+  auto window_0 = CreateAndInitWindowWithId(window_id_0);
+  AddOrUpdateWindow(item_id, window_id_0, icon);
+
+  ASSERT_EQ(shelf_model()->item_count(), 1);
+
+  std::string window_id_1 = lacros_window_prefix() + " test_window_id_1";
+  auto window_1 = CreateAndInitWindowWithId(window_id_1);
+  AddOrUpdateWindow(item_id, window_id_1, icon);
+
+  EXPECT_EQ(shelf_model()->item_count(), 1);
+
+  std::string window_id_2 = lacros_window_prefix() + " test_window_id_2";
+  auto window_2 = CreateAndInitWindowWithId(window_id_2);
+  AddOrUpdateWindow(item_id, window_id_2, icon);
+
+  EXPECT_EQ(shelf_model()->item_count(), 1);
+
+  window_2.reset();
+  EXPECT_EQ(shelf_model()->item_count(), 1);
+
+  window_0.reset();
+  EXPECT_EQ(shelf_model()->item_count(), 1);
+
+  window_1.reset();
+  EXPECT_EQ(shelf_model()->item_count(), 0);
+}
+
+}  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/test_controller_ash.cc b/chrome/browser/ash/crosapi/test_controller_ash.cc
index 8504dd6..4636e63 100644
--- a/chrome/browser/ash/crosapi/test_controller_ash.cc
+++ b/chrome/browser/ash/crosapi/test_controller_ash.cc
@@ -8,7 +8,7 @@
 #include <utility>
 #include <vector>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_controller_impl.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/shelf_item_delegate.h"
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
index 9e553b08..5d9a7417 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
@@ -12,7 +12,7 @@
 #include <sstream>
 #include <utility>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/app_list/app_list_public_test_util.h"
 #include "ash/components/arc/arc_prefs.h"
 #include "ash/components/arc/metrics/arc_metrics_constants.h"
diff --git a/chrome/browser/ash/input_method/autocorrect_manager.cc b/chrome/browser/ash/input_method/autocorrect_manager.cc
index a999dd23..ff130f2 100644
--- a/chrome/browser/ash/input_method/autocorrect_manager.cc
+++ b/chrome/browser/ash/input_method/autocorrect_manager.cc
@@ -1226,8 +1226,10 @@
   // autocorrect.
   return !(
       suggestion_provider_ &&
-      suggestion_provider_ ==
-          ime::AutocorrectSuggestionProvider::kUsEnglish840 &&
+      (suggestion_provider_ ==
+           ime::AutocorrectSuggestionProvider::kUsEnglish840 ||
+       suggestion_provider_ ==
+           ime::AutocorrectSuggestionProvider::kUsEnglish840V2) &&
       base::FeatureList::IsEnabled(ash::features::kImeFstDecoderParamsUpdate));
 }
 
diff --git a/chrome/browser/ash/input_method/autocorrect_manager_unittest.cc b/chrome/browser/ash/input_method/autocorrect_manager_unittest.cc
index b2fa65fb..64e1177 100644
--- a/chrome/browser/ash/input_method/autocorrect_manager_unittest.cc
+++ b/chrome/browser/ash/input_method/autocorrect_manager_unittest.cc
@@ -2871,6 +2871,7 @@
         AutocorrectSuggestionProvider::kUsEnglishPrebundled,
         AutocorrectSuggestionProvider::kUsEnglishDownloaded,
         AutocorrectSuggestionProvider::kUsEnglish840,
+        AutocorrectSuggestionProvider::kUsEnglish840V2,
     }),
     [](const testing::TestParamInfo<AutocorrectSuggestionProvider> info) {
       return ToString(info.param);
@@ -2956,16 +2957,31 @@
   EXPECT_TRUE(manager_.DisabledByInvalidExperimentContext());
 }
 
-TEST_F(AutocorrectManagerTest,
-       IsNotDisabledWhenUserInDefaultBucketAndAllRequiredConstraintsMet) {
+class EnabledByValidSuggestionProvider
+    : public AutocorrectManagerTest,
+      public testing::WithParamInterface<AutocorrectSuggestionProvider> {};
+
+INSTANTIATE_TEST_SUITE_P(
+    AutocorrectManagerTest,
+    EnabledByValidSuggestionProvider,
+    testing::ValuesIn<>({
+        AutocorrectSuggestionProvider::kUsEnglish840,
+        AutocorrectSuggestionProvider::kUsEnglish840V2,
+    }),
+    [](const testing::TestParamInfo<AutocorrectSuggestionProvider> info) {
+      return ToString(info.param);
+    });
+
+TEST_P(EnabledByValidSuggestionProvider,
+       IsNotDisabledWhenUserInDefaultBucketAndValidSuggestionProviderUsed) {
+  const AutocorrectSuggestionProvider& provider = GetParam();
   feature_list_.Reset();
   feature_list_.InitWithFeatures(RequiredForAutocorrectByDefault(),
                                  DisabledFeatures());
 
   manager_.OnActivate(kUsEnglishEngineId);
   manager_.OnFocus(kContextId);
-  manager_.OnConnectedToSuggestionProvider(
-      AutocorrectSuggestionProvider::kUsEnglish840);
+  manager_.OnConnectedToSuggestionProvider(provider);
 
   EXPECT_FALSE(manager_.DisabledByInvalidExperimentContext());
 }
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
index 4359d2f..cd6b2d1 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_unittest.cc
@@ -820,6 +820,10 @@
             "DownloadedUs840",
             /*provider=*/AutocorrectSuggestionProvider::kUsEnglish840,
             /*autocorrect_enabled=*/true},
+        InputMethodMetadataCase{
+            "DownloadedUs840V2",
+            /*provider=*/AutocorrectSuggestionProvider::kUsEnglish840V2,
+            /*autocorrect_enabled=*/true},
     }),
     [](const testing::TestParamInfo<InputMethodMetadataCase> info) {
       return info.param.test_name;
diff --git a/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc b/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc
index b91c3c3..0553b3f 100644
--- a/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/login_screen_accessibility_policy_browsertest.cc
@@ -4,7 +4,7 @@
 
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
 #include "base/command_line.h"
diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.cc b/chrome/browser/bookmarks/android/bookmark_bridge.cc
index 07156e1..ba72796 100644
--- a/chrome/browser/bookmarks/android/bookmark_bridge.cc
+++ b/chrome/browser/bookmarks/android/bookmark_bridge.cc
@@ -144,7 +144,9 @@
         PartnerBookmarksShim::BuildForBrowserContext(
             chrome::GetBrowserContextRedirectedInIncognito(profile)),
         std::make_unique<ReadingListManagerImpl>(
-            ReadingListModelFactory::GetForBrowserContext(profile)),
+            ReadingListModelFactory::GetForBrowserContext(profile),
+            base::BindRepeating([](int64_t* id) { return (*id)++; },
+                                base::Owned(std::make_unique<int64_t>(0)))),
         page_image_service::ImageServiceFactory::GetForBrowserContext(profile));
     model->SetUserData(kBookmarkBridgeUserDataKey,
                        base::WrapUnique(bookmark_bridge));
diff --git a/chrome/browser/bookmarks/android/bookmark_bridge_unittest.cc b/chrome/browser/bookmarks/android/bookmark_bridge_unittest.cc
index f7fb646..56c3c36 100644
--- a/chrome/browser/bookmarks/android/bookmark_bridge_unittest.cc
+++ b/chrome/browser/bookmarks/android/bookmark_bridge_unittest.cc
@@ -91,7 +91,10 @@
     EXPECT_TRUE(storage_ptr->TriggerLoadCompletion());
     // TODO(crbug.com/1501998): Add account reading list manager support.
     auto local_or_syncable_reading_list_manager =
-        std::make_unique<ReadingListManagerImpl>(reading_list_model_.get());
+        std::make_unique<ReadingListManagerImpl>(
+            reading_list_model_.get(),
+            base::BindRepeating([](int64_t* id) { return (*id)++; },
+                                base::Owned(std::make_unique<int64_t>(0))));
     local_or_syncable_reading_list_manager_ =
         local_or_syncable_reading_list_manager.get();
 
diff --git a/chrome/browser/compose/chrome_compose_client_unittest.cc b/chrome/browser/compose/chrome_compose_client_unittest.cc
index 466481f..547192c 100644
--- a/chrome/browser/compose/chrome_compose_client_unittest.cc
+++ b/chrome/browser/compose/chrome_compose_client_unittest.cc
@@ -175,6 +175,19 @@
     }
   }
 
+  void EnableAutoCompose() {
+    scoped_feature_list_.Reset();
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        /*enabled_features=*/{{compose::features::kEnableCompose,
+                               {{"auto_submit_with_selection", "true"}}},
+                              {optimization_guide::features::
+                                   kOptimizationGuideModelExecution,
+                               {}}},
+        /*disabled_features=*/{});
+    // Needed for feature flags to apply.
+    compose::ResetConfigForTesting();
+  }
+
   void ShowDialogAndBindMojo(ComposeCallback callback = base::NullCallback()) {
     ShowDialogAndBindMojoWithFieldData(field_data_, std::move(callback));
   }
@@ -1365,6 +1378,7 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestAutoCompose) {
+  EnableAutoCompose();
   base::test::TestFuture<void> execute_model_future;
   // Make model execution hang
   EXPECT_CALL(session(), ExecuteModel(_, _))
@@ -1392,6 +1406,7 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestAutoComposeTooLong) {
+  EnableAutoCompose();
   EXPECT_CALL(session(), ExecuteModel(_, _)).Times(0);
 
   std::u16string words(compose::GetComposeConfig().input_max_chars - 3, u'a');
@@ -1409,6 +1424,7 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestAutoComposeTooFewWords) {
+  EnableAutoCompose();
   EXPECT_CALL(session(), ExecuteModel(_, _)).Times(0);
   std::u16string words(40, u'a');
   words += u" b";
@@ -1422,6 +1438,7 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestAutoComposeTooManyWords) {
+  EnableAutoCompose();
   EXPECT_CALL(session(), ExecuteModel(_, _)).Times(0);
 
   std::u16string words = u"b";
@@ -1439,24 +1456,15 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestAutoComposeDisabled) {
+  // Auto compose is disabled by default.
   EXPECT_CALL(session(), ExecuteModel(_, _)).Times(0);
 
-  scoped_feature_list_.Reset();
-  scoped_feature_list_.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{compose::features::kEnableCompose,
-                             {{"auto_submit_with_selection", "false"}}},
-                            {optimization_guide::features::
-                                 kOptimizationGuideModelExecution,
-                             {}}},
-      /*disabled_features=*/{});
-  // Needed for feature flags to apply.
-  compose::ResetConfigForTesting();
-
   SetSelection(u"testing alpha bravo charlie");
   ShowDialogAndBindMojo();
 }
 
 TEST_F(ChromeComposeClientTest, TestNoAutoComposeWithPopup) {
+  EnableAutoCompose();
   EXPECT_CALL(session(), ExecuteModel(_, _)).Times(0);
   SetSelection(u"a");  // too short to cause auto compose.
 
@@ -1476,6 +1484,7 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestAutoComposeWithRepeatedRightClick) {
+  EnableAutoCompose();
   base::test::TestFuture<void> execute_model_future;
   EXPECT_CALL(session(), ExecuteModel(_, _))
       .WillOnce(base::test::RunOnceClosure(execute_model_future.GetCallback()));
@@ -1503,6 +1512,7 @@
 }
 
 TEST_F(ChromeComposeClientTest, TestNoAutoComposeWithoutConsent) {
+  EnableAutoCompose();
   EXPECT_CALL(session(), ExecuteModel(_, _)).Times(0);
 
   SetPrefsForComposeConsentState(compose::mojom::ConsentState::kUnset);
diff --git a/chrome/browser/download/android/download_controller.cc b/chrome/browser/download/android/download_controller.cc
index ac266f3..8bd3334d 100644
--- a/chrome/browser/download/android/download_controller.cc
+++ b/chrome/browser/download/android/download_controller.cc
@@ -236,10 +236,7 @@
   // Closing an empty page on external app download leaves a bad user experience
   // as user don't know whether a download is kicked off, or if Chrome just
   // ignores the URL. Show the download page instead.
-  if (base::FeatureList::IsEnabled(
-          chrome::android::kDownloadHomeForExternalApp) &&
-      !base::FeatureList::IsEnabled(chrome::android::kChromeNewDownloadTab) &&
-      tab_model->GetTabAt(tab_index)->GetLaunchType() ==
+  if (tab_model->GetTabAt(tab_index)->GetLaunchType() ==
           static_cast<int>(TabModel::TabLaunchType::FROM_EXTERNAL_APP)) {
     DownloadManagerService::GetInstance()->OpenDownloadsPage(
         Profile::FromBrowserContext(web_contents->GetBrowserContext()),
diff --git a/chrome/browser/download/download_ui_model.cc b/chrome/browser/download/download_ui_model.cc
index 324f9991..fe26fffb 100644
--- a/chrome/browser/download/download_ui_model.cc
+++ b/chrome/browser/download/download_ui_model.cc
@@ -46,6 +46,7 @@
 #if !BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/ui/browser.h"
 #include "components/url_formatter/elide_url.h"
+#include "ui/gfx/text_elider.h"
 #include "ui/views/vector_icons.h"
 #endif
 
@@ -259,11 +260,15 @@
   if (!ShouldPromoteOrigin()) {
     return GetStatusText();
   }
-  // TODO(crbug.com/1409167): Avoid calling the deprecated function.
-  const GURL url = GetOriginalURL().DeprecatedGetOriginAsURL();
-  return url.is_valid()
-             ? url_formatter::ElideUrl(url, font_list, available_pixel_width)
-             : GetStatusText();
+  if (const GURL url = GetOriginalURL(); url.is_valid()) {
+    std::u16string url_string = url_formatter::FormatUrlForSecurityDisplay(url);
+    // available_pixel_width can be 0 before the view is inflated.
+    return available_pixel_width <= 0
+               ? url_string
+               : gfx::ElideText(url_string, font_list, available_pixel_width,
+                                gfx::ELIDE_TAIL);
+  }
+  return GetStatusText();
 }
 #endif
 
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.cc b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
index 00dc1be7..44f9a0a 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.cc
@@ -56,8 +56,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/extensions/extensions_dialogs.h"
-#include "chrome/browser/ui/safety_hub/menu_notification_service_factory.h"
-#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
 #include "chrome/browser/web_applications/extension_status_utils.h"
@@ -2761,27 +2759,6 @@
   Respond(NoArguments());
 }
 
-DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction::
-    DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction() =
-        default;
-DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction::
-    ~DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction() =
-        default;
-
-ExtensionFunction::ResponseAction
-DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction::Run() {
-  content::WebContents* web_contents = GetSenderWebContents();
-  if (!web_contents) {
-    return RespondNow(Error(kCouldNotFindWebContentsError));
-  }
-
-  Profile* profile = Profile::FromBrowserContext(browser_context());
-  SafetyHubMenuNotificationServiceFactory::GetForProfile(profile)
-      ->DismissActiveNotificationOfModule(
-          safety_hub::SafetyHubModuleType::EXTENSIONS);
-  return RespondNow(NoArguments());
-}
-
 }  // namespace api
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chrome/browser/extensions/api/developer_private/developer_private_api.h
index 52c2db96..c826ae40 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_api.h
+++ b/chrome/browser/extensions/api/developer_private/developer_private_api.h
@@ -995,28 +995,6 @@
   absl::optional<bool> accept_bubble_for_testing_;
 };
 
-class DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction
-    : public DeveloperPrivateAPIFunction {
- public:
-  DECLARE_EXTENSION_FUNCTION(
-      "developerPrivate.dismissSafetyHubExtensionsMenuNotification",
-      DEVELOPERPRIVATE_DISMISSSAFETYHUBEXTENSIONSMENUNOTIFICATION)
-  DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction();
-
-  DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction(
-      const DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction&) =
-      delete;
-  DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction& operator=(
-      const DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction&) =
-      delete;
-
-  ResponseAction Run() override;
-
- private:
-  ~DeveloperPrivateDismissSafetyHubExtensionsMenuNotificationFunction()
-      override;
-};
-
 }  // namespace api
 
 }  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 744fec8..0b51e78f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2183,11 +2183,6 @@
     "expiry_milestone": 125
   },
   {
-    "name": "enable-cardboard",
-    "owners": [ "alcooper@chromium.org", "chrome-xr-eng@google.com"],
-    "expiry_milestone": 122
-  },
-  {
     "name": "enable-cast-streaming-av1",
     "owners": [ "jophba@chromium.org", "openscreen-eng@chromium.org" ],
     "expiry_milestone": 135
@@ -7644,7 +7639,7 @@
   {
     "name": "tab-drag-drop",
     "owners": [ "ranjithkagathi@google.com", "gurmeetk@google.com" ],
-    "expiry_milestone": 122
+    "expiry_milestone": 130
   },
   {
     "name": "tab-grid-compositional-layout",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 799c5cf6..b2992e30 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -7704,14 +7704,6 @@
     "rather than crashing. If enabled, DCHECKs will crash the calling process.";
 #endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
 
-#if BUILDFLAG(ENABLE_CARDBOARD)
-const char kEnableCardboardName[] = "Enable Cardboard VR WebXR Runtime";
-const char kEnableCardboardDescription[] =
-    "Enables the use of the Cardboard SDK runtime for WebXR instead of the"
-    "Google VR Services (or GVR) runtime to start a WebXR-based immersive-vr"
-    "session.";
-#endif  // ENABLE_CARDBOARD
-
 #if BUILDFLAG(ENABLE_NACL)
 const char kNaclName[] = "Native Client";
 const char kNaclDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 268d7b0..ad76d83 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -4438,11 +4438,6 @@
 extern const char kDcheckIsFatalDescription[];
 #endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)
 
-#if BUILDFLAG(ENABLE_CARDBOARD)
-extern const char kEnableCardboardName[];
-extern const char kEnableCardboardDescription[];
-#endif  // ENABLE_CARDBOARD
-
 #if BUILDFLAG(ENABLE_NACL)
 extern const char kNaclName[];
 extern const char kNaclDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 825b9f3..42f84513 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -210,7 +210,6 @@
     &kCCTTextFragmentLookupApiEnabled,
     &kDontAutoHideBrowserControls,
     &kCacheDeprecatedSystemLocationSetting,
-    &kChromeNewDownloadTab,
     &kChromeSharingHub,
     &kChromeSurveyNextAndroid,
     &kCommandLineOnNonRooted,
@@ -613,10 +612,6 @@
              "CacheDeprecatedSystemLocationSetting",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kChromeNewDownloadTab,
-             "ChromeNewDownloadTab",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kChromeSharingHub,
              "ChromeSharingHub",
              base::FEATURE_ENABLED_BY_DEFAULT);
@@ -701,10 +696,6 @@
              "DownloadAutoResumptionThrottling",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kDownloadHomeForExternalApp,
-             "DownloadHomeForExternalApp",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kDragDropIntoOmnibox,
              "DragDropIntoOmnibox",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 0f64c0c9a..73b0523 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -67,7 +67,6 @@
 BASE_DECLARE_FEATURE(kCCTTextFragmentLookupApiEnabled);
 BASE_DECLARE_FEATURE(kDontAutoHideBrowserControls);
 BASE_DECLARE_FEATURE(kCacheDeprecatedSystemLocationSetting);
-BASE_DECLARE_FEATURE(kChromeNewDownloadTab);
 BASE_DECLARE_FEATURE(kChromeShareScreenshot);
 BASE_DECLARE_FEATURE(kChromeSharingHub);
 BASE_DECLARE_FEATURE(kChromeSharingHubLaunchAdjacent);
@@ -90,7 +89,6 @@
 BASE_DECLARE_FEATURE(kDelayTransitionsForAnimation);
 BASE_DECLARE_FEATURE(kDontPrefetchLibraries);
 BASE_DECLARE_FEATURE(kDownloadAutoResumptionThrottling);
-BASE_DECLARE_FEATURE(kDownloadHomeForExternalApp);
 BASE_DECLARE_FEATURE(kDrawEdgeToEdge);
 BASE_DECLARE_FEATURE(kDrawNativeEdgeToEdge);
 BASE_DECLARE_FEATURE(kDrawWebEdgeToEdge);
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc
index 764d924..96169351 100644
--- a/chrome/browser/media/router/media_router_feature.cc
+++ b/chrome/browser/media/router/media_router_feature.cc
@@ -76,6 +76,11 @@
 #else
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_CHROMEOS)
+
+BASE_FEATURE(kCastSilentlyRemoveVcOnNavigation,
+             "CastSilentlyRemoveVcOnNavigation",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 namespace {
diff --git a/chrome/browser/media/router/media_router_feature.h b/chrome/browser/media/router/media_router_feature.h
index 66587baf..88f648e7 100644
--- a/chrome/browser/media/router/media_router_feature.h
+++ b/chrome/browser/media/router/media_router_feature.h
@@ -57,6 +57,13 @@
 // `kCastMirroringPlayoutDelayMs`.
 BASE_DECLARE_FEATURE(kCastMirroringPlayoutDelay);
 
+// When enabled, Cast virtual connections are removed without explicitly sending
+// a close connection request to the receiver when the sender webpage navigates
+// away.
+// TODO(crbug.com/1508704): Remove the flag when confident that the default-
+// enabled feature is not causing a regression.
+BASE_DECLARE_FEATURE(kCastSilentlyRemoveVcOnNavigation);
+
 extern const base::FeatureParam<int> kCastMirroringPlayoutDelayMs;
 
 // Registers |kMediaRouterCastAllowAllIPs| with local state pref |registry|.
diff --git a/chrome/browser/media/router/providers/cast/app_activity_unittest.cc b/chrome/browser/media/router/providers/cast/app_activity_unittest.cc
index a1a3fb8..017d7c37 100644
--- a/chrome/browser/media/router/providers/cast/app_activity_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/app_activity_unittest.cc
@@ -349,7 +349,22 @@
 
   EXPECT_CALL(message_handler_, CloseConnection(kChannelId, "theClientId1",
                                                 session_->destination_id()));
-  activity_->CloseConnectionOnReceiver("theClientId1");
+  activity_->CloseConnectionOnReceiver(
+      "theClientId1", blink::mojom::PresentationConnectionCloseReason::CLOSED);
+}
+
+TEST_F(AppActivityTest, RemoveConnectionOnReceiver) {
+  SetUpSession();
+  AddMockClient("theClientId1");
+
+  // If the close reason is not `CLOSED`, then we call RemoveConnection()
+  // instead of CloseConnection() to avoid sending a close request to the
+  // receiver.
+  EXPECT_CALL(message_handler_, RemoveConnection(kChannelId, "theClientId1",
+                                                 session_->destination_id()));
+  activity_->CloseConnectionOnReceiver(
+      "theClientId1",
+      blink::mojom::PresentationConnectionCloseReason::WENT_AWAY);
 }
 
 TEST_F(AppActivityTest, ForwardInternalMediaMessage) {
diff --git a/chrome/browser/media/router/providers/cast/cast_activity.cc b/chrome/browser/media/router/providers/cast/cast_activity.cc
index a352190..8a07eda6 100644
--- a/chrome/browser/media/router/providers/cast/cast_activity.cc
+++ b/chrome/browser/media/router/providers/cast/cast_activity.cc
@@ -5,10 +5,13 @@
 #include "chrome/browser/media/router/providers/cast/cast_activity.h"
 
 #include "base/containers/contains.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
+#include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/media/router/providers/cast/cast_internal_message_util.h"
 #include "chrome/browser/media/router/providers/cast/cast_session_client_impl.h"
 #include "components/media_router/common/discovery/media_sink_internal.h"
+#include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 
 namespace media_router {
 
@@ -155,12 +158,22 @@
                                 std::move(callback));
 }
 
-void CastActivity::CloseConnectionOnReceiver(const std::string& client_id) {
+void CastActivity::CloseConnectionOnReceiver(
+    const std::string& client_id,
+    blink::mojom::PresentationConnectionCloseReason reason) {
   CastSession* session = GetSession();
-  if (!session)
+  if (!session) {
     return;
-  message_handler_->CloseConnection(cast_channel_id(), client_id,
-                                    session->destination_id());
+  }
+  if (reason == blink::mojom::PresentationConnectionCloseReason::CLOSED ||
+      !base::FeatureList::IsEnabled(kCastSilentlyRemoveVcOnNavigation)) {
+    message_handler_->CloseConnection(cast_channel_id(), client_id,
+                                      session->destination_id());
+
+  } else {
+    message_handler_->RemoveConnection(cast_channel_id(), client_id,
+                                       session->destination_id());
+  }
 }
 
 void CastActivity::HandleLeaveSession(const std::string& client_id) {
diff --git a/chrome/browser/media/router/providers/cast/cast_activity.h b/chrome/browser/media/router/providers/cast/cast_activity.h
index 82d87047..13fea13 100644
--- a/chrome/browser/media/router/providers/cast/cast_activity.h
+++ b/chrome/browser/media/router/providers/cast/cast_activity.h
@@ -20,6 +20,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
 
 namespace cast_channel {
 class CastMessageHandler;
@@ -135,7 +136,9 @@
 
   // Closes any virtual connection between |client_id| and this session on the
   // receiver.
-  virtual void CloseConnectionOnReceiver(const std::string& client_id);
+  virtual void CloseConnectionOnReceiver(
+      const std::string& client_id,
+      blink::mojom::PresentationConnectionCloseReason reason);
 
   // Called when the client given by |client_id| requests to leave the session.
   // This will also cause all clients within the session with matching origin
diff --git a/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc b/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc
index cdeafb5..ba84dab 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc
+++ b/chrome/browser/media/router/providers/cast/cast_session_client_impl.cc
@@ -122,7 +122,7 @@
 }
 
 void CastSessionClientImpl::DidClose(PresentationConnectionCloseReason reason) {
-  activity_->CloseConnectionOnReceiver(client_id());
+  activity_->CloseConnectionOnReceiver(client_id(), reason);
 }
 
 void CastSessionClientImpl::SendErrorCodeToClient(
@@ -272,7 +272,7 @@
   if (connection_remote_)
     connection_remote_->DidClose(close_reason);
   TearDownPresentationConnection();
-  activity_->CloseConnectionOnReceiver(client_id());
+  activity_->CloseConnectionOnReceiver(client_id(), close_reason);
 }
 
 void CastSessionClientImpl::TerminateConnection() {
diff --git a/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc b/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc
index 7c69886..1a7e27a 100644
--- a/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc
+++ b/chrome/browser/media/router/providers/cast/cast_session_client_unittest.cc
@@ -302,12 +302,16 @@
 }
 
 TEST_F(CastSessionClientImplTest, CloseConnection) {
-  EXPECT_CALL(activity_, CloseConnectionOnReceiver("theClientId"));
+  EXPECT_CALL(activity_,
+              CloseConnectionOnReceiver(
+                  "theClientId", PresentationConnectionCloseReason::CLOSED));
   client_->CloseConnection(PresentationConnectionCloseReason::CLOSED);
 }
 
 TEST_F(CastSessionClientImplTest, DidCloseConnection) {
-  EXPECT_CALL(activity_, CloseConnectionOnReceiver("theClientId"));
+  EXPECT_CALL(activity_,
+              CloseConnectionOnReceiver(
+                  "theClientId", PresentationConnectionCloseReason::WENT_AWAY));
   client_->DidClose(PresentationConnectionCloseReason::WENT_AWAY);
 }
 
diff --git a/chrome/browser/media/router/providers/cast/mock_app_activity.h b/chrome/browser/media/router/providers/cast/mock_app_activity.h
index c0e9d8a..0812861 100644
--- a/chrome/browser/media/router/providers/cast/mock_app_activity.h
+++ b/chrome/browser/media/router/providers/cast/mock_app_activity.h
@@ -31,7 +31,9 @@
   MOCK_METHOD2(StopSessionOnReceiver,
                void(const std::string& client_id,
                     cast_channel::ResultCallback callback));
-  MOCK_METHOD1(CloseConnectionOnReceiver, void(const std::string& client_id));
+  MOCK_METHOD2(CloseConnectionOnReceiver,
+               void(const std::string& client_id,
+                    blink::mojom::PresentationConnectionCloseReason reason));
   MOCK_METHOD1(SendStopSessionMessageToClients,
                void(const std::string& hash_token));
   MOCK_METHOD1(HandleLeaveSession, void(const std::string& client_id));
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl.cc b/chrome/browser/reading_list/android/reading_list_manager_impl.cc
index 043fb5d..6fda8fc 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl.cc
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl.cc
@@ -49,15 +49,16 @@
 }  // namespace
 
 ReadingListManagerImpl::ReadingListManagerImpl(
-    ReadingListModel* reading_list_model)
+    ReadingListModel* reading_list_model,
+    const IdGenerationFunction& id_gen_func)
     : reading_list_model_(reading_list_model),
-      maximum_id_(0L),
+      id_gen_func_(id_gen_func),
       loaded_(false),
       performing_batch_update_(false),
       changes_applied_during_batch_(false) {
   DCHECK(reading_list_model_);
   root_ = std::make_unique<BookmarkNode>(
-      maximum_id_++, base::Uuid::GenerateRandomV4(), GURL());
+      id_gen_func_.Run(), base::Uuid::GenerateRandomV4(), GURL());
   root_->SetTitle(l10n_util::GetStringUTF16(IDS_READ_LATER_TITLE));
   DCHECK(root_->is_folder());
   reading_list_model_->AddObserver(this);
@@ -316,7 +317,7 @@
 
   // Add a new node.
   auto new_node = std::make_unique<BookmarkNode>(
-      maximum_id_++, base::Uuid::GenerateRandomV4(), entry->URL());
+      id_gen_func_.Run(), base::Uuid::GenerateRandomV4(), entry->URL());
   bool success = SyncToBookmark(*entry, new_node.get());
   return success ? root_->Add(std::move(new_node)) : nullptr;
 }
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl.h b/chrome/browser/reading_list/android/reading_list_manager_impl.h
index cd17c2b..c32a4e95 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl.h
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl.h
@@ -5,12 +5,12 @@
 #ifndef CHROME_BROWSER_READING_LIST_ANDROID_READING_LIST_MANAGER_IMPL_H_
 #define CHROME_BROWSER_READING_LIST_ANDROID_READING_LIST_MANAGER_IMPL_H_
 
-#include "base/memory/raw_ptr.h"
-#include "chrome/browser/reading_list/android/reading_list_manager.h"
-
 #include <memory>
 
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
+#include "chrome/browser/reading_list/android/reading_list_manager.h"
 #include "components/reading_list/core/reading_list_model_observer.h"
 
 class ReadingListModel;
@@ -20,10 +20,15 @@
 // list nodes as children. Only has one level of children.
 // 2. Talk to reading list model, and sync with the in memory bookmark tree.
 // 3. Talk to observers to report model change events.
+// TODO(crbug.com/1510550): Refactor this to be part of the bookmarks dir.
+// - Better renamed to ReadingListAsBookmarkAdapter when moved.
 class ReadingListManagerImpl : public ReadingListManager,
                                public ReadingListModelObserver {
  public:
-  explicit ReadingListManagerImpl(ReadingListModel* reading_list_model);
+  using IdGenerationFunction = base::RepeatingCallback<int64_t(void)>;
+
+  ReadingListManagerImpl(ReadingListModel* reading_list_model,
+                         const IdGenerationFunction& id_gen_func);
   ~ReadingListManagerImpl() override;
 
  private:
@@ -82,8 +87,8 @@
   // The bookmark root for reading list articles.
   std::unique_ptr<bookmarks::BookmarkNode> root_;
 
-  // Auto increment bookmark id. Will not be persisted and only used in memory.
-  int64_t maximum_id_;
+  // Function to generate an id for reading list nodes.
+  const IdGenerationFunction id_gen_func_;
 
   // Whether the |reading_list_model_| is loaded.
   bool loaded_;
diff --git a/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc b/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
index 9606002c..fd30938 100644
--- a/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
+++ b/chrome/browser/reading_list/android/reading_list_manager_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/reading_list/android/reading_list_manager_impl.h"
 
+#include <cstdint>
 #include <memory>
 #include <string>
 #include <utility>
@@ -69,8 +70,10 @@
     reading_list_model_ = std::make_unique<ReadingListModelImpl>(
         std::move(storage), syncer::StorageType::kUnspecified,
         syncer::WipeModelUponSyncDisabledBehavior::kNever, &clock_);
-    manager_ =
-        std::make_unique<ReadingListManagerImpl>(reading_list_model_.get());
+    manager_ = std::make_unique<ReadingListManagerImpl>(
+        reading_list_model_.get(),
+        base::BindRepeating([](int64_t* id) { return (*id)++; },
+                            base::Owned(std::make_unique<int64_t>(0))));
     manager_->AddObserver(observer());
 
     return storage_ptr;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 3117d8e..428a595 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -521,7 +521,10 @@
     // The context menu will be created inside the extension frame.
     extension_frame_ =
         pdf_extension_test_util::GetOnlyPdfExtensionHost(web_contents);
-    EXPECT_TRUE(extension_frame_);
+    if (!extension_frame_) {
+      ADD_FAILURE() << "Failed to get PDF extension frame.";
+      return nullptr;
+    }
     EXPECT_NE(extension_frame_, web_contents->GetPrimaryMainFrame());
 
     if (!UseOopif()) {
@@ -529,7 +532,10 @@
           web_contents->GetBrowserContext()->GetGuestManager();
       WebContents* guest_contents =
           guest_manager->GetFullPageGuest(web_contents);
-      EXPECT_TRUE(guest_contents);
+      if (!guest_contents) {
+        ADD_FAILURE() << "Failed to get guest WebContents.";
+        return nullptr;
+      }
       EXPECT_EQ(extension_frame_, guest_contents->GetPrimaryMainFrame());
     }
 
@@ -2792,6 +2798,7 @@
   }
 
   std::unique_ptr<TestRenderViewContextMenu> menu = SetupAndCreateMenu();
+  ASSERT_TRUE(menu);
 
   // The full page related items such as 'reload' should be there.
   ASSERT_TRUE(menu->IsItemPresent(IDC_RELOAD));
@@ -2800,6 +2807,7 @@
 IN_PROC_BROWSER_TEST_P(PdfPluginContextMenuBrowserTestWithOopifOverride,
                        FullPagePdfFullscreenItems) {
   std::unique_ptr<TestRenderViewContextMenu> menu = SetupAndCreateMenu();
+  ASSERT_TRUE(menu);
 
   // Test that the 'Rotate' items exist and are enabled.
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_ROTATECW));
@@ -2819,6 +2827,7 @@
 IN_PROC_BROWSER_TEST_P(PdfPluginContextMenuBrowserTestWithOopifOverride,
                        CopyWithoutText) {
   std::unique_ptr<TestRenderViewContextMenu> menu = SetupAndCreateMenu();
+  ASSERT_TRUE(menu);
 
   // Test that 'Copy' doesn't exist.
   ASSERT_FALSE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPY));
@@ -2829,6 +2838,7 @@
   std::unique_ptr<TestRenderViewContextMenu> menu =
       SetupAndCreateMenuWithPdfInfo(
           {/*selection_text=*/u"text", /*can_copy=*/true});
+  ASSERT_TRUE(menu);
 
   // Test that 'Copy' exists and it is enabled.
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPY));
@@ -2840,6 +2850,7 @@
   std::unique_ptr<TestRenderViewContextMenu> menu =
       SetupAndCreateMenuWithPdfInfo(
           {/*selection_text=*/u"text", /*can_copy=*/false});
+  ASSERT_TRUE(menu);
 
   // Test that 'Copy' exists and it is disabled.
   ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_COPY));
@@ -2859,6 +2870,7 @@
   }
 
   std::unique_ptr<TestRenderViewContextMenu> menu = SetupAndCreateMenu();
+  ASSERT_TRUE(menu);
   content::RenderFrameHost* target_rfh =
       pdf_frame_util::FindPdfChildFrame(extension_frame());
   auto cb = [](base::OnceClosure quit_loop,
@@ -2943,6 +2955,7 @@
 // TODO(crbug.com/1443346): Re-enable this test.
 IN_PROC_BROWSER_TEST_P(PdfOcrContextMenuBrowserTest, DISABLED_PdfOcr) {
   std::unique_ptr<TestRenderViewContextMenu> menu = SetupAndCreateMenu();
+  ASSERT_TRUE(menu);
   ASSERT_EQ(menu->IsItemPresent(IDC_CONTENT_CONTEXT_PDF_OCR),
             IsPdfOcrEnabled() && IsScreenReaderEnabled() && IsComponentReady());
 }
diff --git a/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts b/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts
index ceea303..6699124 100644
--- a/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts
+++ b/chrome/browser/resources/ash/settings/os_files_page/files_settings_card.ts
@@ -234,9 +234,15 @@
       return this.i18n('googleDriveNotSignedInSublabel');
     }
 
-    return (this.isBulkPinningEnabled_ && this.bulkPinningPrefEnabled_) ?
-        this.i18n('googleDriveFileSyncOnSublabel') :
-        this.i18n('googleDriveSignedInAs');
+    if (this.isBulkPinningEnabled_ && this.bulkPinningPrefEnabled_) {
+      return this.i18n('googleDriveFileSyncOnSublabel');
+    }
+
+    const tempEl = document.createElement('div');
+    tempEl.innerHTML =
+        this.i18nAdvanced('googleDriveSignedInAs', {attrs: ['id']});
+
+    return tempEl.innerText;
   }
 
   private computeOneDriveSignedInLabel_(): string {
diff --git a/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.html b/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.html
index 0216e0c..d4f41e3 100644
--- a/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.html
+++ b/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.html
@@ -1,16 +1,14 @@
-<style include="settings-shared"></style>
+<style include="settings-shared">
+  /* This id is in the string returned from getDriveAccountStatusLabel_() */
+  #driveAccountEmail {
+    font: var(--cros-button-2-font);
+  }
+</style>
 
 <div class="settings-box two-line first">
-  <template is="dom-if" if="[[driveDisabled_]]" restamp>
-    <div class="start">
-      $i18nRaw{googleDriveReconnectAs}
-    </div>
-  </template>
-  <template is="dom-if" if="[[!driveDisabled_]]" restamp>
-    <div class="start">
-      $i18nRaw{googleDriveSignedInAs}
-    </div>
-  </template>
+  <div class="start"
+      inner-h-t-m-l="[[getDriveAccountStatusLabel_(driveDisabled_)]]">
+  </div>
   <controlled-button id="driveConnectDisconnect"
       on-click="onConnectDisconnectClick_"
       pref="{{prefs.gdata.disabled}}"
diff --git a/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts b/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts
index f701ff1..1e056a4 100644
--- a/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts
+++ b/chrome/browser/resources/ash/settings/os_files_page/google_drive_subpage.ts
@@ -375,6 +375,12 @@
         setInterval(this.updateContentCacheSize_.bind(this), 5000);
   }
 
+  private getDriveAccountStatusLabel_(): TrustedHTML {
+    return this.driveDisabled_ ?
+        this.i18nAdvanced('googleDriveReconnectAs', {attrs: ['id']}) :
+        this.i18nAdvanced('googleDriveSignedInAs', {attrs: ['id']});
+  }
+
   /**
    * Returns the value for the button to Connect/Disconnect Google drive
    * depending on the current state.
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/input_page.ts b/chrome/browser/resources/ash/settings/os_languages_page/input_page.ts
index e7dec65..49979657 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/input_page.ts
+++ b/chrome/browser/resources/ash/settings/os_languages_page/input_page.ts
@@ -651,7 +651,13 @@
     const status = this.languageHelper.getImeLanguagePackStatus(imeId);
     switch (status) {
       case chrome.inputMethodPrivate.LanguagePackStatus.ERROR_NEEDS_REBOOT:
-        return this.i18n('inputMethodLanguagePacksNeedsRebootError');
+      // We currently have a string - `inputMethodLanguagePacksNeedsRebootError`
+      // in WebUI,
+      // `IDS_OS_SETTINGS_INPUT_METHOD_LANGUAGE_PACKS_NEEDS_REBOOT_ERROR` in the
+      // GRD file - to special case the `ERROR_NEEDS_REBOOT` case. However, the
+      // string is not finalised, and therefore should not be shown to the user.
+      // TODO: b/315725816 - Either finalise the string and add it here, or
+      // remove the string altogether.
       case chrome.inputMethodPrivate.LanguagePackStatus.ERROR_OTHER:
         return this.i18n('inputMethodLanguagePacksGeneralError');
       default:
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.html b/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.html
index 8bf9b74..9955610e7 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.html
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.html
@@ -75,6 +75,17 @@
   </template>
 </div>
 
+<template is="dom-if" if="[[isPerAppLanguageEnabled_]]">
+  <cr-link-row
+      id="appLanguagesSection"
+      class="hr"
+      label="$i18n{appLanguagesTitle}"
+      sub-label="$i18n{appLanguagesDescription}"
+      on-click="onAppLanguagesClick_"
+      role-description="$i18n{subpageArrowRoleDescription}">
+  </cr-link-row>
+</template>
+
 <div class="hr bottom-margin">
   <h2 aria-hidden="true">
     [[getLanguagePreferenceTitle_(languageSettingsV2Update2Enabled_)]]
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.ts b/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.ts
index 24a5569..d76ff8f 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.ts
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_languages_page_v2.ts
@@ -116,6 +116,13 @@
         },
       },
 
+      isPerAppLanguageEnabled_: {
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean('isPerAppLanguageEnabled');
+        },
+      },
+
       languageSettingsV2Update2Enabled_: Boolean,
     };
   }
@@ -149,6 +156,7 @@
   private isGuest_: boolean;
   private isSecondaryUser_: boolean;
   private primaryUserEmail_: string;
+  private isPerAppLanguageEnabled_: boolean;
   // TODO: b/263823772 - Inline this variable.
   private languageSettingsV2Update2Enabled_ = true;
 
@@ -188,6 +196,13 @@
   }
 
   /**
+   * Navigates to app languages subpage.
+   */
+  private onAppLanguagesClick_(): void {
+    // TODO(b/261200827): Route to App Languages page.
+  }
+
+  /**
    * Stamps and opens the Add Languages dialog, registering a listener to
    * disable the dialog's dom-if again on close.
    */
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
index cb2fc67..26b623d61 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_e2e_test_base.js
@@ -14,7 +14,7 @@
     super.testGenCppIncludes();
     GEN(`
 #include "ash/keyboard/ui/keyboard_util.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_pref_names.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
     `);
diff --git a/chrome/browser/resources/chromeos/borealis_installer/app.ts b/chrome/browser/resources/chromeos/borealis_installer/app.ts
index 23347f02..10ffd4c 100644
--- a/chrome/browser/resources/chromeos/borealis_installer/app.ts
+++ b/chrome/browser/resources/chromeos/borealis_installer/app.ts
@@ -78,7 +78,10 @@
     this.listenerIds.push(
         this.router.onProgressUpdate.addListener(
             (progressFraction: number, progressLabel: string) => {
-              this.installerProgress = progressFraction * 100;
+              // Multiply by 100 to get percentage then round to 2 decimal
+              // places.
+              this.installerProgress =
+                  Math.round(progressFraction * 100 * 100) / 100;
               this.progressLabel = progressLabel;
             }),
         this.router.onInstallFinished.addListener(
diff --git a/chrome/browser/resources/extensions/manager.ts b/chrome/browser/resources/extensions/manager.ts
index b999806..a8ae24bb 100644
--- a/chrome/browser/resources/extensions/manager.ts
+++ b/chrome/browser/resources/extensions/manager.ts
@@ -612,10 +612,6 @@
     const toPage = newPage.page;
     let data: chrome.developerPrivate.ExtensionInfo|undefined;
     let activityLogPlaceholder;
-    if (newPage.page === Page.LIST) {
-      // Dismiss menu notifications for extensions module of Safety Hub.
-      this.delegate.dismissSafetyHubExtensionsMenuNotification();
-    }
     if (newPage.extensionId) {
       data = this.getData_(newPage.extensionId);
       if (!data) {
diff --git a/chrome/browser/resources/extensions/service.ts b/chrome/browser/resources/extensions/service.ts
index 4d6c63b..eb91927f 100644
--- a/chrome/browser/resources/extensions/service.ts
+++ b/chrome/browser/resources/extensions/service.ts
@@ -31,7 +31,6 @@
   getProfileConfiguration(): Promise<chrome.developerPrivate.ProfileInfo>;
   getExtensionsInfo(): Promise<chrome.developerPrivate.ExtensionInfo[]>;
   getExtensionSize(id: string): Promise<string>;
-  dismissSafetyHubExtensionsMenuNotification(): void;
 }
 
 export class Service implements ServiceInterface {
@@ -495,10 +494,6 @@
     return chrome.developerPrivate.updateSiteAccess(site, updates);
   }
 
-  dismissSafetyHubExtensionsMenuNotification() {
-    chrome.developerPrivate.dismissSafetyHubExtensionsMenuNotification();
-  }
-
   static getInstance(): ServiceInterface {
     return instance || (instance = new Service());
   }
diff --git a/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search/wallpaper_search.html b/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search/wallpaper_search.html
index f633aab..edb63f7c 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search/wallpaper_search.html
+++ b/chrome/browser/resources/side_panel/customize_chrome/wallpaper_search/wallpaper_search.html
@@ -305,6 +305,11 @@
     line-height: 16px;
     color: var(--color-side-panel-card-secondary-foreground);
   }
+
+  #footer a {
+    color: var(--cr-link-color);
+    text-decoration: none;
+  }
 </style>
 <div class="sp-card">
   <sp-heading id="heading" on-back-button-click="onBackClick_"
diff --git a/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc b/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
index 4dd8791..bdbc42c 100644
--- a/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
+++ b/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/sync/test/integration/web_apps_sync_test_base.h"
 
 #include "base/containers/extend.h"
-#include "chrome/common/chrome_features.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/apps/link_capturing/link_capturing_features.h"
@@ -13,6 +12,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
+#include "chrome/common/chrome_features.h"
 #include "chromeos/ash/components/standalone_browser/feature_refs.h"
 #endif
 
@@ -30,9 +30,6 @@
 #if BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/1357905): Update test driver to work with new UI.
   disabled_features.push_back(apps::features::kLinkCapturingUiUpdate);
-#else
-  // TOOD(b/313492499): Update test driver to work with new intent picker UI.
-  disabled_features.push_back(features::kDesktopPWAsLinkCapturing);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 8898abf..228f161d7 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2823,6 +2823,7 @@
       "ash/shelf/extension_uninstaller.h",
       "ash/shelf/lacros_app_window.cc",
       "ash/shelf/lacros_app_window.h",
+      "ash/shelf/lacros_shelf_item_controller.h",
       "ash/shelf/settings_window_observer.cc",
       "ash/shelf/settings_window_observer.h",
       "ash/shelf/shelf_app_updater.cc",
diff --git a/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc b/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc
index a6c5d25..992153e 100644
--- a/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc
+++ b/chrome/browser/ui/ash/accessibility/accessibility_controller_client.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/ash/accessibility/accessibility_controller_client.h"
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/wm/desks/templates/saved_desk_util.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
diff --git a/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc b/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
index f021f97..59b4afc 100644
--- a/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/accessibility/accessibility_controller_client_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <optional>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/public/cpp/accessibility_controller_enums.h"
 #include "ash/test/ash_test_base.h"
 #include "base/time/time.h"
diff --git a/chrome/browser/ui/ash/desks/desks_client.cc b/chrome/browser/ui/ash/desks/desks_client.cc
index 3504655d..44e1a30 100644
--- a/chrome/browser/ui/ash/desks/desks_client.cc
+++ b/chrome/browser/ui/ash/desks/desks_client.cc
@@ -8,7 +8,7 @@
 #include <memory>
 #include <string>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/desk_template.h"
 #include "ash/public/cpp/session/session_controller.h"
diff --git a/chrome/browser/ui/ash/shelf/lacros_shelf_item_controller.h b/chrome/browser/ui/ash/shelf/lacros_shelf_item_controller.h
new file mode 100644
index 0000000..5c638d0
--- /dev/null
+++ b/chrome/browser/ui/ash/shelf/lacros_shelf_item_controller.h
@@ -0,0 +1,28 @@
+// 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_ASH_SHELF_LACROS_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_LACROS_SHELF_ITEM_CONTROLLER_H_
+
+#include "ash/public/cpp/shelf_item_delegate.h"
+#include "ash/public/cpp/shelf_model_observer.h"
+#include "ash/public/cpp/shelf_types.h"
+
+namespace aura {
+class Window;
+}  // namespace aura
+
+// This is a common base shelf item controller for windows that are owned by
+// Lacros.
+class LacrosShelfItemController : public ash::ShelfItemDelegate {
+ public:
+  explicit LacrosShelfItemController(const ash::ShelfID& shelf_id)
+      : ash::ShelfItemDelegate(shelf_id) {}
+
+  // This method is called by LacrosShelfItemTracker to hand off a window to the
+  // controller.
+  virtual void AddWindow(aura::Window* window) = 0;
+};
+
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_LACROS_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc b/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
index 91935cc8..287173944 100644
--- a/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
+++ b/chrome/browser/ui/quick_answers/quick_answers_browsertest.cc
@@ -230,8 +230,8 @@
 
     std::unique_ptr<TranslationResult> translation_result =
         std::make_unique<TranslationResult>();
-    translation_result->text_to_translate = kSourceText;
-    translation_result->translated_text = kTranslatedText;
+    translation_result->text_to_translate = base::UTF16ToUTF8(kSourceText);
+    translation_result->translated_text = base::UTF16ToUTF8(kTranslatedText);
     translation_result->target_locale = "en";
     translation_result->source_locale = "it";
 
diff --git a/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc b/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc
index 1f3185f5..3415f6f 100644
--- a/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc
+++ b/chrome/browser/ui/quick_answers/ui/quick_answers_util.cc
@@ -11,6 +11,8 @@
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
+#include "ui/views/controls/separator.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 
@@ -115,6 +117,18 @@
   return child_view;
 }
 
+std::unique_ptr<views::Separator> CreateSeparatorView() {
+  std::unique_ptr<views::Separator> separator =
+      views::Builder<views::Separator>()
+          .SetOrientation(views::Separator::Orientation::kHorizontal)
+          .SetColorId(cros_tokens::kSeparatorColor)
+          .Build();
+  separator->SetProperty(
+      views::kMarginsKey,
+      gfx::Insets::TLBR(kContentDoubleSpacing, 0, kContentDoubleSpacing, 0));
+  return separator;
+}
+
 GURL GetDetailsUrlForQuery(const std::string& query) {
   // TODO(b/240619915): Refactor so that we can access the request metadata
   // instead of just the query itself.
diff --git a/chrome/browser/ui/quick_answers/ui/quick_answers_util.h b/chrome/browser/ui/quick_answers/ui/quick_answers_util.h
index d7d7287..0b5f098 100644
--- a/chrome/browser/ui/quick_answers/ui/quick_answers_util.h
+++ b/chrome/browser/ui/quick_answers/ui/quick_answers_util.h
@@ -8,6 +8,7 @@
 #include "components/vector_icons/vector_icons.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_list.h"
+#include "ui/views/controls/separator.h"
 #include "ui/views/view.h"
 
 namespace quick_answers {
@@ -15,6 +16,10 @@
 // Constants.
 inline constexpr int kContentHeaderWidth = 252;
 inline constexpr int kContentTextWidth = 280;
+inline constexpr int kContentSingleSpacing = 8;
+inline constexpr int kContentDoubleSpacing = 16;
+inline constexpr gfx::Insets kUnderLineIndentation =
+    gfx::Insets::TLBR(0, 0, kContentSingleSpacing, 0);
 inline constexpr char kGoogleSansFont[] = "Google Sans";
 inline constexpr char kRobotoFont[] = "Roboto";
 
@@ -47,6 +52,9 @@
     views::View* container,
     std::unique_ptr<views::View> view = std::make_unique<views::View>());
 
+// Creates a separator view with |kContentDoubleSpacing| vertical margins.
+std::unique_ptr<views::Separator> CreateSeparatorView();
+
 // Return the GURL that will link to the google search result for the
 // query text.
 GURL GetDetailsUrlForQuery(const std::string& query);
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc
index def14ac..519944c6 100644
--- a/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/quick_answers/ui/quick_answers_text_label.h"
 #include "chrome/browser/ui/quick_answers/ui/quick_answers_util.h"
 #include "chromeos/components/quick_answers/quick_answers_model.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/views/controls/label.h"
@@ -39,7 +40,48 @@
   // TODO (b/265258270): Populate translation view contents.
   content_view_ = GetContentView();
 
-  AddHeaderViewsTo(content_view_, translation_result_.source_locale);
+  // Source language.
+  AddLanguageTitle(translation_result_.source_locale, true);
+
+  // Text to translate.
+  AddLanguageText(translation_result_.text_to_translate);
+
+  // Separator.
+  content_view_->AddChildView(CreateSeparatorView());
+
+  // Target language.
+  AddLanguageTitle(translation_result_.target_locale, false);
+
+  // Translated text.
+  AddLanguageText(translation_result_.translated_text);
+}
+
+void RichAnswersTranslationView::AddLanguageTitle(const std::string& locale,
+                                                  bool is_header_view) {
+  auto locale_name = l10n_util::GetDisplayNameForLocale(
+      locale, translation_result_.target_locale, true);
+
+  if (is_header_view) {
+    AddHeaderViewsTo(content_view_, base::UTF16ToUTF8(locale_name));
+  } else {
+    auto* second_header_view = AddFillLayoutChildView(
+        content_view_,
+        QuickAnswersTextLabel::CreateLabelWithStyle(
+            base::UTF16ToUTF8(locale_name),
+            GetFontList(TypographyToken::kCrosButton2), kContentHeaderWidth,
+            /*is_multi_line=*/false, cros_tokens::kCrosSysSecondary));
+    second_header_view->SetProperty(views::kMarginsKey, kUnderLineIndentation);
+  }
+}
+
+void RichAnswersTranslationView::AddLanguageText(
+    const std::string& language_text) {
+  AddFillLayoutChildView(
+      content_view_,
+      QuickAnswersTextLabel::CreateLabelWithStyle(
+          language_text, GetFontList(TypographyToken::kCrosTitle1),
+          kContentTextWidth,
+          /*is_multi_line=*/true, cros_tokens::kCrosSysOnSurface));
 }
 
 BEGIN_METADATA(RichAnswersTranslationView, RichAnswersView)
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h
index 623ae2bb..5477c80 100644
--- a/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_translation_view.h
@@ -31,6 +31,8 @@
 
  private:
   void InitLayout();
+  void AddLanguageTitle(const std::string& locale, bool is_header_view);
+  void AddLanguageText(const std::string& language_text);
 
   raw_ptr<views::View> content_view_ = nullptr;
 
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc b/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
index 4c1dc5d..2ab3d48 100644
--- a/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_view.cc
@@ -276,8 +276,9 @@
 }
 
 void RichAnswersView::AddHeaderViewsTo(views::View* container_view,
-                                       std::string header_text) {
+                                       const std::string& header_text) {
   auto* header_view = AddFillLayoutChildView(container_view);
+  header_view->SetProperty(views::kMarginsKey, kUnderLineIndentation);
   auto* header_label_container = header_view->AddChildView(
       views::Builder<views::FlexLayoutView>()
           .SetOrientation(views::LayoutOrientation::kHorizontal)
diff --git a/chrome/browser/ui/quick_answers/ui/rich_answers_view.h b/chrome/browser/ui/quick_answers/ui/rich_answers_view.h
index 1f47398..17a4666 100644
--- a/chrome/browser/ui/quick_answers/ui/rich_answers_view.h
+++ b/chrome/browser/ui/quick_answers/ui/rich_answers_view.h
@@ -76,7 +76,8 @@
 
   void AddSettingsButtonTo(views::View* container_view);
 
-  void AddHeaderViewsTo(views::View* container_view, std::string header_text);
+  void AddHeaderViewsTo(views::View* container_view,
+                        const std::string& header_text);
 
   // FocusSearch::GetFocusableViewsCallback to poll currently focusable views.
   std::vector<views::View*> GetFocusableViews();
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service.cc b/chrome/browser/ui/safety_hub/menu_notification_service.cc
index 54109ed9..759a37a 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service.cc
@@ -94,14 +94,6 @@
           base::Unretained(this)));
 }
 
-void SafetyHubMenuNotificationService::UpdateResultGetterForTesting(
-    safety_hub::SafetyHubModuleType type,
-    base::RepeatingCallback<
-        std::optional<std::unique_ptr<SafetyHubService::Result>>()>
-        result_getter) {
-  module_info_map_[type]->result_getter = result_getter;
-}
-
 SafetyHubMenuNotificationService::~SafetyHubMenuNotificationService() {
   registrar_.RemoveAll();
 }
@@ -242,13 +234,15 @@
   }
 }
 
-void SafetyHubMenuNotificationService::DismissActiveNotificationOfModule(
-    safety_hub::SafetyHubModuleType module) {
-  SafetyHubMenuNotification* notification =
-      module_info_map_.at(module)->notification.get();
-  if (notification->IsCurrentlyActive()) {
-    notification->Dismiss();
-  }
+void SafetyHubMenuNotificationService::DismissPasswordNotification() {
+  // TODO(crbug.com/1443466): Uncomment the following lines in
+  // crrev.com/c/4982626.
+  // SafetyHubMenuNotification* notification =
+  //     module_info_map_.at(safety_hub::SafetyHubModuleType::PASSWORDS)
+  //         ->notification.get();
+  // if (notification->IsCurrentlyActive()) {
+  //   notification->Dismiss();
+  // }
 }
 
 std::optional<safety_hub::SafetyHubModuleType>
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service.h b/chrome/browser/ui/safety_hub/menu_notification_service.h
index b36b5c5..f169067 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service.h
+++ b/chrome/browser/ui/safety_hub/menu_notification_service.h
@@ -86,9 +86,8 @@
   // Dismisses all the active menu notifications.
   void DismissActiveNotification();
 
-  // Dismisses the active menu notification of the specified module.
-  void DismissActiveNotificationOfModule(
-      safety_hub::SafetyHubModuleType module);
+  // Dismisses the active menu notification of the password module.
+  void DismissPasswordNotification();
 
   // Returns the module of the notification that is currently active.
   std::optional<safety_hub::SafetyHubModuleType> GetModuleOfActiveNotification()
@@ -98,12 +97,6 @@
   SafetyHubMenuNotification* GetNotificationForTesting(
       safety_hub::SafetyHubModuleType service_type);
 
-  void UpdateResultGetterForTesting(
-      safety_hub::SafetyHubModuleType type,
-      base::RepeatingCallback<
-          std::optional<std::unique_ptr<SafetyHubService::Result>>()>
-          result_getter);
-
  private:
   // Gets the latest result from each Safety Hub service. Will return
   // std::nullopt when there is no result from one of the services.
diff --git a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
index c8bbc57..ef46dc80 100644
--- a/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
+++ b/chrome/browser/ui/safety_hub/menu_notification_service_unittest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h"
 #include "chrome/browser/ui/safety_hub/password_status_check_service.h"
 #include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h"
-#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
 #include "chrome/browser/ui/safety_hub/safety_hub_test_util.h"
 #include "chrome/browser/ui/safety_hub/unused_site_permissions_service_factory.h"
 #include "chrome/common/chrome_features.h"
@@ -467,18 +466,5 @@
   EXPECT_FALSE(
       menu_notification_service()->GetNotificationToShow().has_value());
 
-  // Create mock password menu notification.
-  const std::string& kOrigin = "https://www.example.com";
-  SetMockCredentialEntry(kOrigin, true);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_TRUE(notification.has_value());
-  ExpectPluralString(
-      IDS_SETTINGS_SAFETY_HUB_COMPROMISED_PASSWORDS_MENU_NOTIFICATION, 1,
-      notification.value().label);
-
-  // The notification should no longer appear after it has been dismissed.
-  menu_notification_service()->DismissActiveNotificationOfModule(
-      safety_hub::SafetyHubModuleType::PASSWORDS);
-  notification = menu_notification_service()->GetNotificationToShow();
-  EXPECT_FALSE(notification.has_value());
+  // TODO(crbug.com/1443466): Add test for DismissPasswordNotification().
 }
diff --git a/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc b/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc
index 2b359dd..d84abec 100644
--- a/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc
+++ b/chrome/browser/ui/search/new_tab_page_navigation_throttle_browsertest.cc
@@ -279,11 +279,9 @@
   CoreTabHelper* core_tab_helper =
       CoreTabHelper::FromWebContents(web_contents());
   core_tab_helper->set_new_tab_start_time(base::TimeTicks().Now());
-  histogram_tester.ExpectTotalCount("Tab.NewTabOnload.Other", 0);
 
   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), ntp_url));
   EXPECT_TRUE(core_tab_helper->new_tab_start_time().is_null());
-  histogram_tester.ExpectTotalCount("Tab.NewTabOnload.Other", 1);
 
   core_tab_helper->set_new_tab_start_time(base::TimeTicks().Now());
   GURL fenced_frame_url =
@@ -293,7 +291,6 @@
           web_contents()->GetPrimaryMainFrame(), fenced_frame_url);
   EXPECT_NE(nullptr, fenced_frame_host);
   EXPECT_FALSE(core_tab_helper->new_tab_start_time().is_null());
-  histogram_tester.ExpectTotalCount("Tab.NewTabOnload.Other", 1);
 }
 
 IN_PROC_BROWSER_TEST_F(NewTabPageNavigationThrottleFencedFrameTest,
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index 2c4a1343..32fc5ae1 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -106,19 +106,12 @@
   if (core_tab_helper->new_tab_start_time().is_null())
     return;
 
-  base::TimeDelta duration =
-      base::TimeTicks::Now() - core_tab_helper->new_tab_start_time();
   if (IsCacheableNTP(contents)) {
     if (google_util::IsGoogleDomainUrl(
             contents->GetController().GetLastCommittedEntry()->GetURL(),
             google_util::ALLOW_SUBDOMAIN,
             google_util::DISALLOW_NON_STANDARD_PORTS)) {
-      UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Google", duration);
-    } else {
-      UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Other", duration);
     }
-  } else {
-    UMA_HISTOGRAM_TIMES("Tab.NewTabOnload.Local", duration);
   }
   core_tab_helper->set_new_tab_start_time(base::TimeTicks());
 }
diff --git a/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc b/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc
index 50d4ff0..a1c6632 100644
--- a/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/bookmark_sub_menu_model.cc
@@ -64,7 +64,7 @@
   }
   AddItemWithStringId(IDC_SHOW_BOOKMARK_MANAGER, IDS_BOOKMARK_MANAGER);
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   AddItemWithStringId(IDC_IMPORT_SETTINGS, IDS_IMPORT_SETTINGS_MENU_LABEL);
 #endif
 
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
index dab94d1..1931607 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
@@ -38,7 +38,12 @@
 
 class IntentPickerBrowserTest : public web_app::WebAppNavigationBrowserTest {
  public:
-  IntentPickerBrowserTest() = default;
+  IntentPickerBrowserTest() {
+    std::vector<base::test::FeatureRef> disabled_features = {
+        // TODO(crbug.com/1001189): Stop disabling Paint Holding.
+        blink::features::kPaintHolding};
+    scoped_feature_list_.InitWithFeatures({}, disabled_features);
+  }
 
   template <typename Action>
   testing::AssertionResult DoAndWaitForIntentPickerIconUpdate(Action action) {
@@ -111,6 +116,9 @@
     EXPECT_EQ(test_web_app_id(), app_info[0].launch_name);
     EXPECT_EQ(GetAppName(), app_info[0].display_name);
   }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Tests to do with the behavior of the intent picker icon in the omnibox. Does
@@ -119,31 +127,7 @@
 // separately in intent_chip_button_browsertest.cc.
 class IntentPickerIconBrowserTest
     : public IntentPickerBrowserTest,
-      public ::testing::WithParamInterface<std::tuple<std::string, bool>> {
- public:
-  // TODO(crbug.com/1001189): Stop disabling Paint Holding.
-  IntentPickerIconBrowserTest() {
-    feature_list_.InitWithFeaturesAndParameters(
-        apps::test::GetFeaturesToEnableLinkCapturingUX(
-            /*override_captures_by_default=*/IsLinkCapturingEnabled()),
-        {blink::features::kPaintHolding});
-  }
-
-  bool IsLinkCapturingEnabled() { return std::get<bool>(GetParam()); }
-
-  std::string rel() { return std::get<std::string>(GetParam()); }
-
-  bool IsDefaultOnEnabled() {
-#if BUILDFLAG(IS_CHROMEOS)
-    return false;
-#else
-    return IsLinkCapturingEnabled();
-#endif  // BUILDFLAG(IS_CHROMEOS)
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
+      public ::testing::WithParamInterface<std::string> {};
 
 // Tests that clicking a link from a tabbed browser to outside the scope of an
 // installed app does not show the intent picker.
@@ -155,7 +139,7 @@
       https_server().GetURL(GetAppUrlHost(), GetOutOfScopeUrlPath());
   NavigateToLaunchingPage(browser());
   ASSERT_TRUE(ExpectLinkClickNotCapturedIntoAppBrowser(
-      browser(), out_of_scope_url, rel()));
+      browser(), out_of_scope_url, GetParam()));
 
   views::Button* intent_picker_view = GetIntentPickerIcon();
   EXPECT_FALSE(intent_picker_view->GetVisible());
@@ -175,9 +159,6 @@
 #endif
 IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                        MAYBE_NavigationToInScopeLinkShowsIntentPicker) {
-  if (IsDefaultOnEnabled()) {
-    GTEST_SKIP() << "Default On will launch app by default";
-  }
   InstallTestWebApp();
 
   const GURL in_scope_url =
@@ -187,8 +168,8 @@
 
   base::RunLoop run_loop;
   tab_helper->SetIconUpdateCallbackForTesting(run_loop.QuitClosure());
-  ASSERT_TRUE(
-      ExpectLinkClickNotCapturedIntoAppBrowser(browser(), in_scope_url, rel()));
+  ASSERT_TRUE(ExpectLinkClickNotCapturedIntoAppBrowser(browser(), in_scope_url,
+                                                       GetParam()));
   run_loop.Run();
 
   views::Button* intent_picker_icon = GetIntentPickerIcon();
@@ -216,9 +197,9 @@
   views::Button* intent_picker_icon = GetIntentPickerIcon();
 
   // OpenNewTab opens a new tab and focus on the new tab.
-  OpenNewTab(in_scope_url, /*rel=*/rel());
+  OpenNewTab(in_scope_url, /*rel=*/GetParam());
   EXPECT_TRUE(intent_picker_icon->GetVisible());
-  OpenNewTab(out_of_scope_url, /*rel=*/rel());
+  OpenNewTab(out_of_scope_url, /*rel=*/GetParam());
   EXPECT_FALSE(intent_picker_icon->GetVisible());
 
   chrome::SelectPreviousTab(browser());
@@ -228,7 +209,7 @@
 }
 
 // Tests that the navigation in iframe doesn't affect intent picker icon
-IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
+IN_PROC_BROWSER_TEST_F(IntentPickerIconBrowserTest,
                        IframeNavigationDoesNotAffectIntentPicker) {
   InstallTestWebApp();
 
@@ -278,11 +259,11 @@
 
   OpenNewTab(in_scope_url);
   EXPECT_TRUE(intent_picker_icon->GetVisible());
-  ASSERT_TRUE(DoAndWaitForIntentPickerIconUpdate(
-      [this, redirect_url, out_of_scope_url] {
-        ClickLinkAndWaitForURL(GetWebContents(), redirect_url, out_of_scope_url,
-                               LinkTarget::SELF, rel());
-      }));
+  ASSERT_TRUE(DoAndWaitForIntentPickerIconUpdate([this, redirect_url,
+                                                  out_of_scope_url] {
+    ClickLinkAndWaitForURL(GetWebContents(), redirect_url, out_of_scope_url,
+                           LinkTarget::SELF, GetParam());
+  }));
   EXPECT_FALSE(intent_picker_icon->GetVisible());
 }
 
@@ -296,7 +277,7 @@
 #define MAYBE_DoNotShowIconAndBubbleOnServicePages \
   DoNotShowIconAndBubbleOnServicePages
 #endif
-IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
+IN_PROC_BROWSER_TEST_F(IntentPickerIconBrowserTest,
                        MAYBE_DoNotShowIconAndBubbleOnServicePages) {
   InstallTestWebApp();
 
@@ -330,7 +311,7 @@
 #else
 #define MAYBE_DoNotShowIconOnErrorPages DoNotShowIconOnErrorPages
 #endif  // BUILDFLAG(IS_MAC)
-IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
+IN_PROC_BROWSER_TEST_F(IntentPickerIconBrowserTest,
                        MAYBE_DoNotShowIconOnErrorPages) {
   InstallTestWebApp();
   InstallTestWebApp("www.google.com", "/");
@@ -367,7 +348,7 @@
 #else
 #define MAYBE_PushStateURLChangeTest PushStateURLChangeTest
 #endif
-IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
+IN_PROC_BROWSER_TEST_F(IntentPickerIconBrowserTest,
                        MAYBE_PushStateURLChangeTest) {
   // Note: The test page is served from embedded_test_server() as https_server()
   // always returns empty responses.
@@ -397,29 +378,17 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     IntentPickerIconBrowserTest,
-    testing::Combine(testing::Values("", "noopener", "noreferrer", "nofollow"),
-#if BUILDFLAG(IS_CHROMEOS)
-                     testing::Values(false)),
-#else
-                     testing::Values(true, false)),
-#endif  // BUILDFLAG(IS_CHROMEOS)
-    [](const testing::TestParamInfo<std::tuple<std::string, bool>>& info) {
-      std::string test_name;
-      test_name = std::get<std::string>(info.param);
-      test_name.append(std::get<bool>(info.param) ? "DefaultOn" : "DefaultOff");
-      return test_name;
-    });
+    testing::Values("", "noopener", "noreferrer", "nofollow"));
 
 class IntentPickerIconBrowserBubbleTest
     : public IntentPickerBrowserTest,
       public testing::WithParamInterface<bool> {
  public:
-  // TODO(crbug.com/1001189): Stop disabling Paint Holding.
   IntentPickerIconBrowserBubbleTest() {
     feature_list_.InitWithFeaturesAndParameters(
         apps::test::GetFeaturesToEnableLinkCapturingUX(
             /*override_captures_by_default=*/GetParam()),
-        {blink::features::kPaintHolding});
+        {});
   }
   bool LinkCapturingEnabledByDefault() const { return GetParam(); }
 
@@ -485,11 +454,6 @@
                            return info.param ? "DefaultOn" : "DefaultOff";
                          });
 
-// This test only works when link capturing is set to default off for desktop
-// platforms, as prerendering navigations are aborted during link captured app
-// launches. See LinkCapturingNavigationThrottle::MaybeCreate for more
-// information.
-// TODO(b/297256243): Investigate prerendering integration with link capturing.
 class IntentPickerIconPrerenderingBrowserTest
     : public IntentPickerIconBrowserTest {
  public:
@@ -558,14 +522,7 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     IntentPickerIconPrerenderingBrowserTest,
-    testing::Combine(testing::Values("", "noopener", "noreferrer", "nofollow"),
-                     testing::Values(false)),
-    [](const testing::TestParamInfo<std::tuple<std::string, bool>>& info) {
-      std::string test_name;
-      test_name = std::get<std::string>(info.param);
-      test_name.append("DefaultOff");
-      return test_name;
-    });
+    testing::Values("", "noopener", "noreferrer", "nofollow"));
 
 class IntentPickerIconFencedFrameBrowserTest
     : public IntentPickerIconBrowserTest {
@@ -609,15 +566,4 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     IntentPickerIconFencedFrameBrowserTest,
-    testing::Combine(testing::Values("", "noopener", "noreferrer", "nofollow"),
-#if BUILDFLAG(IS_CHROMEOS)
-                     testing::Values(false)),
-#else
-                     testing::Values(true, false)),
-#endif
-    [](const testing::TestParamInfo<std::tuple<std::string, bool>>& info) {
-      std::string test_name;
-      test_name = std::get<std::string>(info.param);
-      test_name.append(std::get<bool>(info.param) ? "DefaultOn" : "DefaultOff");
-      return test_name;
-    });
+    testing::Values("", "noopener", "noreferrer", "nofollow"));
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
index f618f50..9b110ff 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
@@ -4575,9 +4575,6 @@
 #if BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/1357905): Update test driver to work with new UI.
   disabled_features.push_back(apps::features::kLinkCapturingUiUpdate);
-#else
-  // TOOD(b/313492499): Update test driver to work with new intent picker UI.
-  disabled_features.push_back(features::kDesktopPWAsLinkCapturing);
 #endif  // BUILDFLAG(IS_CHROMEOS)
   scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
 }
diff --git a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
index 8d51f837..29762fd 100644
--- a/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
+++ b/chrome/browser/ui/web_applications/pwa_mixed_content_browsertest.cc
@@ -170,7 +170,7 @@
   const GURL app_url = GetMixedContentAppURL();
   const webapps::AppId app_id = InstallPWA(app_url);
 
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), GetMixedContentAppURL()));
+  NavigateToURLAndWait(browser(), GetMixedContentAppURL());
   content::WebContents* tab_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_EQ(tab_contents->GetLastCommittedURL(), GetMixedContentAppURL());
@@ -220,7 +220,7 @@
   const GURL app_url = GetSecureIFrameAppURL();
   const webapps::AppId app_id = InstallPWA(app_url);
 
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+  NavigateToURLAndWait(browser(), app_url);
   CheckMixedContentFailedToLoad(browser());
 
   Browser* const app_browser = ReparentWebContentsIntoAppBrowser(
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index 3f249c2..f3651fb9 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -94,7 +94,7 @@
 }  // namespace
 
 webapps::AppId InstallWebAppFromPage(Browser* browser, const GURL& app_url) {
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, app_url));
+  NavigateToURLAndWait(browser, app_url);
 
   webapps::AppId app_id;
   base::RunLoop run_loop;
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 96a661e1..4dcd1cef 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -1373,7 +1373,7 @@
   const GURL app_url = GetSecureAppURL();
   const webapps::AppId app_id = InstallPWA(app_url);
 
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+  NavigateToURLAndWait(browser(), app_url);
   content::WebContents* tab_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_EQ(tab_contents->GetLastCommittedURL(), app_url);
@@ -1390,10 +1390,10 @@
   std::unique_ptr<OsIntegrationTestOverrideImpl::BlockingRegistration>
       registration = OsIntegrationTestOverrideImpl::OverrideForTesting();
 
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(
+  NavigateToURLAndWait(
       browser(),
       https_server()->GetURL(
-          "/banners/manifest_test_page.html?manifest=manifest_one_icon.json")));
+          "/banners/manifest_test_page.html?manifest=manifest_one_icon.json"));
 
   // Wait for OS hooks and installation to complete and the app to launch.
   base::RunLoop run_loop_install;
@@ -1699,7 +1699,7 @@
 IN_PROC_BROWSER_TEST_F(WebAppBrowserTest, ReparentLastBrowserTab) {
   const GURL app_url = GetSecureAppURL();
   const webapps::AppId app_id = InstallPWA(app_url);
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+  NavigateToURLAndWait(browser(), app_url);
 
   Browser* const app_browser = ReparentWebAppForActiveTab(browser());
   ASSERT_EQ(app_browser->app_controller()->app_id(), app_id);
@@ -1824,7 +1824,7 @@
   const webapps::AppId app_id = InstallWebApp(std::move(web_app_info));
 
   base::HistogramTester tester;
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+  NavigateToURLAndWait(browser(), app_url);
   content::WebContents* tab_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_EQ(tab_contents->GetLastCommittedURL(), app_url);
@@ -2533,7 +2533,7 @@
   const GURL app_url = GetSecureAppURL();
   InstallPWA(app_url);
 
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+  NavigateToURLAndWait(browser(), app_url);
   content::WebContents* tab_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_EQ(tab_contents->GetLastCommittedURL(), app_url);
diff --git a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
index 9825e215..ddaca20 100644
--- a/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_controller_browsertest.cc
@@ -141,7 +141,7 @@
   EXPECT_TRUE(new_contents);
   WaitForLoadStop(new_contents);
 
-  EXPECT_EQ(url, contents->GetController().GetLastCommittedEntry()->GetURL());
+  EXPECT_EQ(url, new_contents->GetLastCommittedURL());
   EXPECT_EQ(
       content::PAGE_TYPE_NORMAL,
       new_contents->GetController().GetLastCommittedEntry()->GetPageType());
@@ -167,7 +167,7 @@
     const GURL& url) {
   auto* manager = webapps::TestAppBannerManagerDesktop::FromWebContents(
       browser->tab_strip_model()->GetActiveWebContents());
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, url));
+  NavigateToURLAndWait(browser, url);
   return manager->WaitForInstallableCheck();
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc b/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc
index 89fd9e1..c477002e 100644
--- a/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_dark_mode_browsertest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/test/base/ui_test_utils.h"
 #include "components/embedder_support/switches.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "content/public/test/browser_test.h"
@@ -241,7 +240,7 @@
     UpdateAwaiter update_awaiter(provider.install_manager());
 
     serve_token = false;
-    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kTestWebAppUrl)));
+    NavigateToURLAndWait(browser(), GURL(kTestWebAppUrl));
 
     update_awaiter.AwaitUpdate();
   }
diff --git a/chrome/browser/ui/web_applications/web_app_launch_handler_browsertest.cc b/chrome/browser/ui/web_applications/web_app_launch_handler_browsertest.cc
index b4533eb9..416dde0 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_handler_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_handler_browsertest.cc
@@ -205,7 +205,7 @@
   // start_url again.
   {
     GURL alt_url = embedded_test_server()->GetURL("/web_apps/basic.html");
-    EXPECT_TRUE(ui_test_utils::NavigateToURL(app_browser, alt_url));
+    NavigateToURLAndWait(app_browser, alt_url);
     EXPECT_EQ(app_web_contents->GetLastCommittedURL(), alt_url);
 
     Browser* app_browser_2 = LaunchWebAppBrowserAndWait(app_id);
@@ -222,7 +222,7 @@
 
     chrome::NewTab(browser());
     EXPECT_EQ(browser()->tab_strip_model()->count(), 2);
-    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), start_url));
+    NavigateToURLAndWait(browser(), start_url);
     ReparentWebAppForActiveTab(browser());
     EXPECT_EQ(browser()->tab_strip_model()->count(), 1);
 
@@ -642,7 +642,7 @@
     UpdateAwaiter update_awaiter(provider.install_manager());
 
     serve_token = false;
-    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kTestWebAppUrl)));
+    NavigateToURLAndWait(browser(), GURL(kTestWebAppUrl));
 
     update_awaiter.AwaitUpdate();
   }
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 876d84b..94b5926 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -67,13 +67,13 @@
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/components/arc/arc_features.h"
 #include "ash/components/arc/session/connection_holder.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ash/apps/apk_web_app_service.h"
 #include "chrome/browser/ash/crosapi/crosapi_ash.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crosapi/web_app_service_ash.h"
+#include "chrome/browser/ui/webui/ash/settings/os_settings_features_util.h"
 #include "chromeos/crosapi/mojom/web_app_service.mojom.h"
 #endif
 
@@ -424,7 +424,7 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   shelf_delegate_.SetPinned(app_id, pinned);
 #else
-  NOTREACHED();
+  NOTIMPLEMENTED();
 #endif
 }
 
@@ -440,7 +440,7 @@
   apps::AppServiceProxyFactory::GetForProfile(profile_)->SetResizeLocked(
       app_id, locked);
 #else
-  NOTREACHED();
+  NOTIMPLEMENTED();
 #endif
 }
 
@@ -523,17 +523,17 @@
   // On ChromeOS, apps should always open in a new window,
   // hence window mode changes are not allowed.
 #if BUILDFLAG(IS_CHROMEOS)
-  NOTREACHED();
+  NOTIMPLEMENTED();
 #else
   auto* provider = web_app::WebAppProvider::GetForLocalAppsUnchecked(profile_);
 
   // Changing window mode is not allowed for isolated web apps.
   if (provider->registrar_unsafe().IsIsolated(app_id)) {
-    NOTREACHED();
-  } else {
-    apps::AppServiceProxyFactory::GetForProfile(profile_)->SetWindowMode(
-        app_id, window_mode);
+    return;
   }
+
+  apps::AppServiceProxyFactory::GetForProfile(profile_)->SetWindowMode(
+      app_id, window_mode);
 #endif
 }
 
@@ -541,7 +541,7 @@
     const std::string& app_id,
     apps::RunOnOsLoginMode run_on_os_login_mode) {
 #if BUILDFLAG(IS_CHROMEOS)
-  NOTREACHED();
+  NOTIMPLEMENTED();
 #else
   apps::AppServiceProxyFactory::GetForProfile(profile_)->SetRunOnOsLoginMode(
       app_id, run_on_os_login_mode);
@@ -631,7 +631,7 @@
   app->resize_locked = update.ResizeLocked().value_or(false);
   app->hide_resize_locked = !update.ResizeLocked().has_value();
 
-  if (base::FeatureList::IsEnabled(arc::kPerAppLanguage)) {
+  if (ash::settings::IsPerAppLanguageEnabled(profile_)) {
     const std::string& system_locale =
         g_browser_process->GetApplicationLocale();
     // Translate supported locales.
@@ -807,7 +807,7 @@
   apps::AppServiceProxyFactory::GetForProfile(profile_)->SetAppLocale(
       app_id, locale_tag);
 #else
-  NOTREACHED();
+  NOTIMPLEMENTED();
 #endif
 }
 
diff --git a/chrome/browser/ui/webui/ash/settings/os_settings_features_util.cc b/chrome/browser/ui/webui/ash/settings/os_settings_features_util.cc
index 717d6ac..db778e0 100644
--- a/chrome/browser/ui/webui/ash/settings/os_settings_features_util.cc
+++ b/chrome/browser/ui/webui/ash/settings/os_settings_features_util.cc
@@ -58,4 +58,9 @@
       IsFullRestoreAvailableForProfile(profile);
 }
 
+bool IsPerAppLanguageEnabled(const Profile* profile) {
+  return base::FeatureList::IsEnabled(arc::kPerAppLanguage) &&
+         arc::IsArcPlayStoreEnabledForProfile(profile);
+}
+
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/ash/settings/os_settings_features_util.h b/chrome/browser/ui/webui/ash/settings/os_settings_features_util.h
index 6909683..f12b210 100644
--- a/chrome/browser/ui/webui/ash/settings/os_settings_features_util.h
+++ b/chrome/browser/ui/webui/ash/settings/os_settings_features_util.h
@@ -29,6 +29,9 @@
 // Determines if app restore settings are available for `profile`.
 bool IsAppRestoreAvailableForProfile(const Profile* profile);
 
+// Determines if per-app language settings are available for `profile`.
+bool IsPerAppLanguageEnabled(const Profile* profile);
+
 }  // namespace ash::settings
 
 #endif  // CHROME_BROWSER_UI_WEBUI_ASH_SETTINGS_OS_SETTINGS_FEATURES_UTIL_H_
diff --git a/chrome/browser/ui/webui/ash/settings/pages/a11y/DEPS b/chrome/browser/ui/webui/ash/settings/pages/a11y/DEPS
index 02f2c7f..9a617e6 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/a11y/DEPS
+++ b/chrome/browser/ui/webui/ash/settings/pages/a11y/DEPS
@@ -1,5 +1,5 @@
 specific_include_rules = {
   'switch_access_handler.cc': [
-    "+ash/accessibility/accessibility_controller_impl.h",
+    "+ash/accessibility/accessibility_controller.h",
   ]
 }
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/ash/settings/pages/a11y/switch_access_handler.cc b/chrome/browser/ui/webui/ash/settings/pages/a11y/switch_access_handler.cc
index 4c58b10..a1f70c3 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/a11y/switch_access_handler.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/a11y/switch_access_handler.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/ash_constants.h"
 #include "ash/constants/ash_pref_names.h"
 #include "base/functional/bind.h"
diff --git a/chrome/browser/ui/webui/ash/settings/pages/languages/languages_section.cc b/chrome/browser/ui/webui/ash/settings/pages/languages/languages_section.cc
index 35b5f42..a65a7f6 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/languages/languages_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/languages/languages_section.cc
@@ -101,6 +101,9 @@
        IDS_OS_SETTINGS_LANGUAGES_GOOGLE_ACCOUNT_LANGUAGE_DESCRIPTION},
       {"manageGoogleAccountLanguageLabel",
        IDS_OS_SETTINGS_LANGUAGES_MANAGE_GOOGLE_ACCOUNT_LANGUAGE_LABEL},
+      {"appLanguagesTitle", IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_TITLE},
+      {"appLanguagesDescription",
+       IDS_OS_SETTINGS_LANGUAGES_APP_LANGUAGES_DESCRIPTION},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -166,6 +169,8 @@
       {"noSearchResults", IDS_SEARCH_NO_RESULTS},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
+  html_source->AddBoolean("isPerAppLanguageEnabled",
+                          IsPerAppLanguageEnabled(profile()));
 
   AddLanguagesPageStringsV2(html_source);
 
diff --git a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
index 5cd9015..e9c50fd8 100644
--- a/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
+++ b/chrome/browser/ui/webui/extensions/extension_settings_browsertest.cc
@@ -15,10 +15,6 @@
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/safety_hub/extensions_result.h"
-#include "chrome/browser/ui/safety_hub/menu_notification_service_factory.h"
-#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
-#include "chrome/browser/ui/safety_hub/safety_hub_test_util.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/extensions/extension_settings_test_base.h"
@@ -216,46 +212,3 @@
          });
       )"));
 }
-
-IN_PROC_BROWSER_TEST_F(ExtensionsActivityLogTest,
-                       TestSafetyHubMenuNotificationDismissed) {
-  Profile* profile = browser()->profile();
-  InstallGoodExtension();
-  SafetyHubMenuNotificationService* notification_service =
-      SafetyHubMenuNotificationServiceFactory::GetForProfile(profile);
-  // No unpublished extensions yet, so there shouldn't be a menu notifications.
-  absl::optional<MenuNotificationEntry> notification =
-      notification_service->GetNotificationToShow();
-  ASSERT_FALSE(notification.has_value());
-
-  // Make all extensions unpublished.
-  const extensions::CWSInfoService::CWSInfo cws_info_unpublished{
-      /*is_present=*/true,
-      /*is_live=*/false,
-      /*last_update_time=*/base::Time::Now(),
-      /*violation_type=*/extensions::CWSInfoService::CWSViolationType::kNone,
-      /*unpublished_long_ago=*/true,
-      /*no_privacy_practice=*/false};
-  testing::NiceMock<safety_hub_test_util::MockCWSInfoService>
-      mock_cws_info_service(profile);
-  ON_CALL(mock_cws_info_service, GetCWSInfo)
-      .WillByDefault(testing::Return(cws_info_unpublished));
-  // Use the mock CWS info service for the menu notification service.
-  notification_service->UpdateResultGetterForTesting(
-      safety_hub::SafetyHubModuleType::EXTENSIONS,
-      base::BindRepeating(&SafetyHubExtensionsResult::GetResult,
-                          base::Unretained(&mock_cws_info_service), profile,
-                          /*only_unpublished_extensions=*/true));
-
-  // An extension was unpublished, so we now should get an associated menu
-  // notification.
-  notification = notification_service->GetNotificationToShow();
-  ASSERT_TRUE(notification.has_value());
-
-  // When the user visits the extensions page, notifications for the extension
-  // module of Safety Hub should be dismissed.
-  ASSERT_TRUE(
-      ui_test_utils::NavigateToURL(browser(), GURL("chrome://extensions/")));
-  notification = notification_service->GetNotificationToShow();
-  ASSERT_FALSE(notification.has_value());
-}
diff --git a/chrome/browser/ui/webui/settings/safety_hub_handler.cc b/chrome/browser/ui/webui/settings/safety_hub_handler.cc
index 28bb2773..dea8e9c 100644
--- a/chrome/browser/ui/webui/settings/safety_hub_handler.cc
+++ b/chrome/browser/ui/webui/settings/safety_hub_handler.cc
@@ -377,15 +377,7 @@
 void SafetyHubHandler::HandleDismissPasswordMenuNotification(
     const base::Value::List& args) {
   SafetyHubMenuNotificationServiceFactory::GetForProfile(profile_)
-      ->DismissActiveNotificationOfModule(
-          safety_hub::SafetyHubModuleType::PASSWORDS);
-}
-
-void SafetyHubHandler::HandleDismissExtensionsMenuNotification(
-    const base::Value::List& args) {
-  SafetyHubMenuNotificationServiceFactory::GetForProfile(profile_)
-      ->DismissActiveNotificationOfModule(
-          safety_hub::SafetyHubModuleType::EXTENSIONS);
+      ->DismissPasswordNotification();
 }
 
 void SafetyHubHandler::HandleBlockNotificationPermissionForOrigins(
@@ -706,11 +698,6 @@
           &SafetyHubHandler::HandleDismissPasswordMenuNotification,
           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "dismissSafetyHubExtensionsMenuNotification",
-      base::BindRepeating(
-          &SafetyHubHandler::HandleDismissExtensionsMenuNotification,
-          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "blockNotificationPermissionForOrigins",
       base::BindRepeating(
           &SafetyHubHandler::HandleBlockNotificationPermissionForOrigins,
diff --git a/chrome/browser/ui/webui/settings/safety_hub_handler.h b/chrome/browser/ui/webui/settings/safety_hub_handler.h
index 3c1555f49..c6bc20b3 100644
--- a/chrome/browser/ui/webui/settings/safety_hub_handler.h
+++ b/chrome/browser/ui/webui/settings/safety_hub_handler.h
@@ -154,9 +154,6 @@
   // Handles dismissing the menu notifications for the password module.
   void HandleDismissPasswordMenuNotification(const base::Value::List& args);
 
-  // Handles dismissing the menu notifications for the extensions module.
-  void HandleDismissExtensionsMenuNotification(const base::Value::List& args);
-
   // Returns the data for Safe Browsing card.
   void HandleGetSafeBrowsingCardData(const base::Value::List& args);
 
diff --git a/chrome/browser/vr/chrome_xr_integration_client.cc b/chrome/browser/vr/chrome_xr_integration_client.cc
index 0c336f96..e94d1b82 100644
--- a/chrome/browser/vr/chrome_xr_integration_client.cc
+++ b/chrome/browser/vr/chrome_xr_integration_client.cc
@@ -156,8 +156,7 @@
 #endif  // BUILDFLAG(ENABLE_OPENXR)
 #if BUILDFLAG(ENABLE_CARDBOARD)
   if (!preferred_vr_runtime_added &&
-      IsEnabled(command_line, switches::kWebXrRuntimeCardboard,
-                &device::features::kEnableCardboard)) {
+      IsEnabled(command_line, switches::kWebXrRuntimeCardboard)) {
     base::android::ScopedJavaLocalRef<jobject>
         j_vr_compositor_delegate_provider =
             vr::Java_VrCompositorDelegateProviderImpl_Constructor(
diff --git a/chrome/browser/web_applications/commands/compute_app_size_command_browsertest.cc b/chrome/browser/web_applications/commands/compute_app_size_command_browsertest.cc
index 3f5463da..8acf952 100644
--- a/chrome/browser/web_applications/commands/compute_app_size_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/compute_app_size_command_browsertest.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/test/run_until.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
@@ -10,7 +9,6 @@
 #include "chrome/browser/web_applications/commands/compute_app_size_command.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
-#include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -24,7 +22,7 @@
 
   GURL app_url = embedded_test_server()->GetURL("/web_apps/basic.html");
   webapps::AppId app_id = InstallWebAppFromPage(browser(), app_url);
-  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+  NavigateToURLAndWait(browser(), app_url);
 
   const char* script = R"(
         localStorage.setItem('data', 'data'.repeat(5000));
@@ -40,13 +38,16 @@
   // renderer process. As updates to quota manager usage occurs on a different
   // sequence to this procress, it requires multiple events. Due to all of this,
   // we are resorting to polling for non-zero values.
-  base::test::TestFuture<absl::optional<ComputeAppSizeCommand::Size>>
-      app_size_future;
-  provider().scheduler().ComputeAppSize(app_id, app_size_future.GetCallback());
-  EXPECT_TRUE(base::test::RunUntil([&]() {
-    return app_size_future.Get().value().app_size_in_bytes != 0u &&
-           app_size_future.Get().value().data_size_in_bytes != 0u;
-  }));
+
+  while (true) {
+    base::test::TestFuture<absl::optional<ComputeAppSizeCommand::Size>>
+        app_size;
+    provider().scheduler().ComputeAppSize(app_id, app_size.GetCallback());
+    if (app_size.Get().value().app_size_in_bytes != 0u &&
+        app_size.Get().value().data_size_in_bytes != 0u) {
+      break;
+    }
+  }
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_scope_extensions_browsertest.cc b/chrome/browser/web_applications/web_app_scope_extensions_browsertest.cc
index 3a626a5..a8f4c089 100644
--- a/chrome/browser/web_applications/web_app_scope_extensions_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_scope_extensions_browsertest.cc
@@ -472,7 +472,7 @@
   {
     UpdateAwaiter update_awaiter(provider.install_manager());
     serve_token = false;
-    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(kTestWebAppUrl)));
+    NavigateToURLAndWait(browser(), GURL(kTestWebAppUrl));
     update_awaiter.AwaitUpdate();
   }
 
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 4c212fc..90d0a46 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1702403939-f8050953af8eace9d59c81c55793f0e2edf1337d.profdata
+chrome-android32-main-1702425214-915c1314fa46f8dbbb5bf3cfd8d8a86c1896adc2.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 9992beb5..35bd600 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1702403939-b69e446c9cbd1b1c857bd308ec679d1fbd9283b3.profdata
+chrome-android64-main-1702425214-14c0ac1ffc03747544aa7a7cf4c0a61557c0b42f.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 958b33e..14cfc7d 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1702317545-bce5603ee20ea953fc3ced7d4ef29453e44a2eef.profdata
+chrome-linux-main-1702425214-c0eeefb29930be2c4e15f71c555b06034cc26f56.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index fea470a0..72bbf93 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1702417416-f8eb9e7a6d56ddde3b987cf15a89bfbe8fe9df56.profdata
+chrome-mac-arm-main-1702432768-94aea22f9e9b926edeb7c28cc770df1bfae9ddff.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 93617a34..d44343ba 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1702403939-1296a11473f600abcce0932212782023fe7d7d59.profdata
+chrome-mac-main-1702425214-1fdb9a0a29f3689c8e41dfe04aedc8409376b4b1.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 188e0161..bdc14a0 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1702403939-d072c4166b2f7f8b6a9a21b2db4ac423e39b15b9.profdata
+chrome-win-arm64-main-1702425214-5e919e3462b94a38d972768c6c40f439958542b5.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5929d96..d6363a1 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1702403939-cac1cc41042be78ec1e27a25d1c6178cdd5499df.profdata
+chrome-win32-main-1702425214-5070cd26284fc4b60e0b182054fcf04b72f2bb33.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index f05a0f5e..9916f7f 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1702403939-33e962efda81c03f382513973002bf1ecd4df861.profdata
+chrome-win64-main-1702425214-bc7d81d4f7f7000ffc8054316eef6b5b4f620ce8.profdata
diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl
index ec86080..34d594e 100644
--- a/chrome/common/extensions/api/developer_private.idl
+++ b/chrome/common/extensions/api/developer_private.idl
@@ -846,10 +846,6 @@
         DOMString[] extensionIds,
         optional VoidCallback callback);
 
-    // Dismisses the menu notification for the extensions module in Safety Hub
-    // if one is active.
-    static void dismissSafetyHubExtensionsMenuNotification();
-
     [nocompile, deprecated="Use openDevTools"]
         static void inspect(InspectOptions options,
                             optional VoidCallback callback);
diff --git a/chrome/renderer/wallet/boarding_pass_extractor.cc b/chrome/renderer/wallet/boarding_pass_extractor.cc
index 502ebf4..ee4bdc15 100644
--- a/chrome/renderer/wallet/boarding_pass_extractor.cc
+++ b/chrome/renderer/wallet/boarding_pass_extractor.cc
@@ -7,9 +7,98 @@
 #include "chrome/common/chrome_isolated_world_ids.h"
 #include "content/public/renderer/render_frame.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_script_source.h"
+#include "v8/include/v8-isolate.h"
 
 namespace wallet {
 
+namespace {
+const std::string GetDefaultExtractionScript() {
+  std::string script = R"###(
+    // Check if the str contains 'boarding pass'.
+    function containBoardingPass(str) {
+        return str.toLowerCase().replace(/\s+/g, '').includes('boardingpass');
+    }
+
+    // Scale up the base64 encoded image to be detectable by BarcodeDetector.
+    function scaleUpIfNeeded(img) {
+        if (img.src.startsWith('data:image/')
+            && (img.width < 500 || image.height < 500)) {
+            let canvas = new OffscreenCanvas(500, 500);
+            let ctx = canvas.getContext('2d');
+            ctx.ImageSmoothingEnabled = false;
+            ctx.drawImage(img, 0, 0, 500, 500);
+            return canvas;
+        }
+        return img;
+    }
+
+    // Check if the rawValue is a valid BCBP string.
+    function isValidBoardingPass(rawValue) {
+        return rawValue.length > 60 && /^M[1-9]/.test(rawValue);
+    }
+
+    // Detect and decode boarding passes on current web page.
+    async function detectBoardingPass() {
+        var results = [];
+        // Check if BarcodeDetector is supported by current browser.
+        if (!'BarcodeDetector' in window) {
+            return [];
+        }
+
+        // Check if current URL or page title contains 'boarding pass'.
+        if (!containBoardingPass(window.location.href)
+            && !containBoardingPass(document.title)) {
+            return [];
+        }
+
+        var detector;
+        try {
+            detector = new BarcodeDetector();
+        } catch (error) {
+            return [];
+        }
+        for (let i = 0; i < document.images.length; ++i) {
+            try {
+                const barcodes = await detector.detect(
+                    scaleUpIfNeeded(document.images[i]));
+                for (const barcode of barcodes) {
+                    // Check if it's a valid boarding pass
+                    if (barcode.rawValue
+                        && isValidBoardingPass(barcode.rawValue)) {
+                        results.push(barcode.rawValue);
+                    }
+                }
+            } catch (error) {
+            }
+        }
+        return results;
+    }
+
+    detected_results_promise = detectBoardingPass();
+  )###";
+  return script;
+}
+
+std::vector<std::string> ConvertResultsToStrings(base::Value& value) {
+  std::vector<std::string> results;
+
+  if (!value.is_list()) {
+    return results;
+  }
+
+  for (const auto& item : value.GetList()) {
+    if (!item.is_string()) {
+      continue;
+    }
+    results.push_back(item.GetString());
+  }
+  return results;
+}
+}  // namespace
+
 BoardingPassExtractor::BoardingPassExtractor(
     content::RenderFrame* render_frame,
     service_manager::BinderRegistry* registry)
@@ -35,7 +124,39 @@
 
 void BoardingPassExtractor::ExtractBoardingPass(
     ExtractBoardingPassCallback callback) {
-  // TODO(crbug/1502408): Implement boarding pass extractor in render process
+  ExtractBoardingPassWithScript(GetDefaultExtractionScript(),
+                                std::move(callback));
+}
+
+void BoardingPassExtractor::ExtractBoardingPassWithScript(
+    const std::string& script,
+    ExtractBoardingPassCallback callback) {
+  blink::WebLocalFrame* main_frame = render_frame()->GetWebFrame();
+  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
+  blink::WebScriptSource source =
+      blink::WebScriptSource(blink::WebString::FromUTF8(script));
+
+  main_frame->RequestExecuteScript(
+      ISOLATED_WORLD_ID_CHROME_INTERNAL, base::make_span(&source, 1u),
+      blink::mojom::UserActivationOption::kDoNotActivate,
+      blink::mojom::EvaluationTiming::kAsynchronous,
+      blink::mojom::LoadEventBlockingOption::kDoNotBlock,
+      base::BindOnce(&BoardingPassExtractor::OnBoardingPassExtracted,
+                     base::Unretained(this), std::move(callback)),
+      blink::BackForwardCacheAware::kAllow,
+      blink::mojom::WantResultOption::kWantResult,
+      blink::mojom::PromiseResultOption::kAwait);
+}
+
+void BoardingPassExtractor::OnBoardingPassExtracted(
+    ExtractBoardingPassCallback callback,
+    absl::optional<base::Value> results,
+    base::TimeTicks start_time) {
+  if (results.has_value()) {
+    std::move(callback).Run(ConvertResultsToStrings(*results));
+  } else {
+    std::move(callback).Run(std::vector<std::string>());
+  }
 }
 
 }  // namespace wallet
diff --git a/chrome/renderer/wallet/boarding_pass_extractor.h b/chrome/renderer/wallet/boarding_pass_extractor.h
index 73229c43..0a19be6 100644
--- a/chrome/renderer/wallet/boarding_pass_extractor.h
+++ b/chrome/renderer/wallet/boarding_pass_extractor.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_RENDERER_WALLET_BOARDING_PASS_EXTRACTOR_H_
 #define CHROME_RENDERER_WALLET_BOARDING_PASS_EXTRACTOR_H_
 
+#include "base/values.h"
 #include "chrome/common/wallet/boarding_pass_extractor.mojom.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -24,6 +25,9 @@
   // mojom::BoardingPassExtractor
   void ExtractBoardingPass(ExtractBoardingPassCallback callback) override;
 
+  void ExtractBoardingPassWithScript(const std::string& script,
+                                     ExtractBoardingPassCallback callback);
+
   // content::RenderFrameObserver
   void OnDestruct() override;
 
@@ -31,6 +35,10 @@
   void BindReceiver(
       mojo::PendingReceiver<mojom::BoardingPassExtractor> receiver);
 
+  void OnBoardingPassExtracted(ExtractBoardingPassCallback callback,
+                               absl::optional<base::Value> results,
+                               base::TimeTicks start_time);
+
   mojo::Receiver<mojom::BoardingPassExtractor> receiver_{this};
 };
 }  // namespace wallet
diff --git a/chrome/renderer/wallet/boarding_pass_extractor_browsertest.cc b/chrome/renderer/wallet/boarding_pass_extractor_browsertest.cc
new file mode 100644
index 0000000..45d8f49
--- /dev/null
+++ b/chrome/renderer/wallet/boarding_pass_extractor_browsertest.cc
@@ -0,0 +1,67 @@
+// 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/renderer/wallet/boarding_pass_extractor.h"
+
+#include "chrome/test/base/chrome_render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace wallet {
+
+class BoardingPassExtractorBrowserTest : public ChromeRenderViewTest {
+ public:
+};
+
+TEST_F(BoardingPassExtractorBrowserTest, ExtractBoardingPassWithScript) {
+  auto extractor_ = std::make_unique<BoardingPassExtractor>(
+      GetMainRenderFrame(), registry_.get());
+
+  const char* html = R"HTML(
+      <html>
+        <head>
+          <title>Google Boarding Pass Page</title>
+        </head>
+        <body>
+        </body>
+      </html>
+    )HTML";
+  LoadHTML(html);
+
+  const std::string script = R"###(
+    results = ['M1TEST', 'M2TEST']
+  )###";
+
+  extractor_->ExtractBoardingPassWithScript(
+      script,
+      base::BindOnce([](const std::vector<std::string>& boarding_passes) {
+        EXPECT_EQ(boarding_passes.size(), 2u);
+        EXPECT_EQ(boarding_passes[0], "M1TEST");
+        EXPECT_EQ(boarding_passes[1], "M2TEST");
+      }));
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(BoardingPassExtractorBrowserTest, ExtractBoardingPassTitleNotMatch) {
+  auto extractor_ = std::make_unique<BoardingPassExtractor>(
+      GetMainRenderFrame(), registry_.get());
+
+  const char* html = R"HTML(
+      <html>
+        <head>
+          <title>Google Testing Page</title>
+        </head>
+        <body>
+        </body>
+      </html>
+    )HTML";
+  LoadHTML(html);
+
+  extractor_->ExtractBoardingPass(
+      base::BindOnce([](const std::vector<std::string>& boarding_passes) {
+        EXPECT_EQ(boarding_passes.size(), 0u);
+      }));
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace wallet
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index c557d2e7..b8d1dca9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1343,6 +1343,7 @@
       "../renderer/cart/commerce_hint_agent_renderer_browsertest.cc",
       "../renderer/commerce/commerce_web_extractor_browsertest.cc",
       "../renderer/safe_browsing/phishing_classifier_browsertest.cc",
+      "../renderer/wallet/boarding_pass_extractor_browsertest.cc",
       "android/browsertests_apk/android_browsertests_jni_onload.cc",
       "base/android/android_browser_test_browsertest_android.cc",
     ]
diff --git a/chrome/test/data/webui/extensions/test_service.ts b/chrome/test/data/webui/extensions/test_service.ts
index 3e9aa8e..3d760fc 100644
--- a/chrome/test/data/webui/extensions/test_service.ts
+++ b/chrome/test/data/webui/extensions/test_service.ts
@@ -33,7 +33,6 @@
       'deleteErrors',
       'deleteItem',
       'deleteItems',
-      'dismissSafetyHubExtensionsMenuNotification',
       'uninstallItem',
       'downloadActivities',
       'getExtensionActivityLog',
@@ -391,8 +390,4 @@
     this.methodCalled('updateSiteAccess', site, updates);
     return Promise.resolve();
   }
-
-  dismissSafetyHubExtensionsMenuNotification() {
-    this.methodCalled('dismissSafetyHubExtensionsMenuNotification');
-  }
 }
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index f81b28c4..6d363530 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -313,11 +313,11 @@
     "os_settings_search_box/test_os_settings_search_box_browser_proxy.ts",
 
     "os_settings_ui/os_settings_ui_about_page_test.ts",
+    "os_settings_ui/os_settings_ui_hats_test.ts",
     "os_settings_ui/os_settings_ui_menu_test.ts",
     "os_settings_ui/os_settings_ui_page_availability_test.ts",
     "os_settings_ui/os_settings_ui_page_visibility_revamp_test.ts",
     "os_settings_ui/os_settings_ui_test.ts",
-    "os_settings_ui/os_settings_hats_ui_test.ts",
     "os_settings_ui/os_settings_ui_toolbar_test.ts",
     "os_settings_ui/page_availability_test_helpers.ts",
     "os_settings_ui/scroll_restoration_test.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/os_languages_page/os_languages_page_v2_test.ts b/chrome/test/data/webui/settings/chromeos/os_languages_page/os_languages_page_v2_test.ts
index b67f460..d8d1c21 100644
--- a/chrome/test/data/webui/settings/chromeos/os_languages_page/os_languages_page_v2_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_languages_page/os_languages_page_v2_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {LanguageHelper, LanguagesBrowserProxyImpl, LanguagesMetricsProxyImpl, LanguagesPageInteraction, LanguageState, LifetimeBrowserProxyImpl, OsSettingsChangeDeviceLanguageDialogElement, OsSettingsLanguagesPageV2Element, SettingsLanguagesElement} from 'chrome://os-settings/lazy_load.js';
-import {CrActionMenuElement, CrCheckboxElement, CrPolicyIndicatorElement, CrSettingsPrefs, Router, routes, settingMojom, SettingsPrefsElement} from 'chrome://os-settings/os_settings.js';
+import {CrActionMenuElement, CrCheckboxElement, CrLinkRowElement, CrPolicyIndicatorElement, CrSettingsPrefs, Router, routes, settingMojom, SettingsPrefsElement} from 'chrome://os-settings/os_settings.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.js';
@@ -11,6 +11,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertGE, assertGT, assertLT, assertNull, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {fakeDataBind, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
 
 import {FakeLanguageSettingsPrivate, getFakeLanguagePrefs} from '../fake_language_settings_private.js';
 import {FakeSettingsPrivate} from '../fake_settings_private.js';
@@ -791,4 +792,50 @@
     assertNull(
         page.shadowRoot!.querySelector('#changeDeviceLanguagePolicyIndicator'));
   });
+
+  suite('app languages settings', () => {
+    let page: OsSettingsLanguagesPageV2Element;
+
+    function createPage(): void {
+      page = document.createElement('os-settings-languages-page-v2');
+      document.body.appendChild(page);
+      flush();
+    }
+
+    setup(() => {
+      assert(window.trustedTypes);
+      document.body.innerHTML =
+          window.trustedTypes.emptyHTML as unknown as string;
+    });
+
+    teardown(() => {
+      page.remove();
+    });
+
+    test('Enable perAppLanguage flag, show app languages section', () => {
+      loadTimeData.overrideValues({
+        isPerAppLanguageEnabled: true,
+      });
+      createPage();
+      const appLanguagesSection =
+          page.shadowRoot!.querySelector<CrLinkRowElement>(
+              '#appLanguagesSection');
+      assertTrue(
+          isVisible(appLanguagesSection),
+          '#appLanguagesSection is not visible.');
+    });
+
+    test('Disable perAppLanguage flag, hide app languages section', () => {
+      loadTimeData.overrideValues({
+        isPerAppLanguageEnabled: false,
+      });
+      createPage();
+      const appLanguagesSection =
+          page.shadowRoot!.querySelector<CrLinkRowElement>(
+              '#appLanguagesSection');
+      assertFalse(
+          isVisible(appLanguagesSection),
+          '#appLanguagesSection is not hidden.');
+    });
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index 32d761a..9bb82731 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -1085,7 +1085,6 @@
    {disabled: ['ash::features::kOsSettingsRevampWayfinding']},
  ],
  ['OsSearchPageSearchSubpage', 'os_search_page/search_subpage_test.js'],
- ['OsSettingsHatsUi', 'os_settings_ui/os_settings_hats_ui_test.js'],
  [
    'OsSettingsMain',
    'os_settings_main/os_settings_main_test.js',
@@ -1110,13 +1109,41 @@
    'OsSettingsSearchBox',
    'os_settings_search_box/os_settings_search_box_test.js'
  ],
- ['OsSettingsUi', 'os_settings_ui/os_settings_ui_test.js'],
+ [
+   'OsSettingsUi',
+   'os_settings_ui/os_settings_ui_test.js',
+   {disabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
+ [
+   'OsSettingsUiRevamp',
+   'os_settings_ui/os_settings_ui_test.js',
+   {enabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
  [
    'OsSettingsUiAboutPage',
    'os_settings_ui/os_settings_ui_about_page_test.js',
    {disabled: ['ash::features::kOsSettingsRevampWayfinding']},
  ],
- ['OsSettingsUiMenu', 'os_settings_ui/os_settings_ui_menu_test.js'],
+ [
+   'OsSettingsUiHats',
+   'os_settings_ui/os_settings_ui_hats_test.js',
+   {disabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
+ [
+   'OsSettingsUiHatsRevamp',
+   'os_settings_ui/os_settings_ui_hats_test.js',
+   {enabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
+ [
+   'OsSettingsUiMenu',
+   'os_settings_ui/os_settings_ui_menu_test.js',
+   {disabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
+ [
+   'OsSettingsUiMenuRevamp',
+   'os_settings_ui/os_settings_ui_menu_test.js',
+   {enabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
  [
    'OsSettingsUiPageAvailability',
    'os_settings_ui/os_settings_ui_page_availability_test.js',
@@ -1136,7 +1163,16 @@
    'OsSettingsUiScrollRestoration',
    'os_settings_ui/scroll_restoration_test.js',
  ],
- ['OsSettingsUiToolbar', 'os_settings_ui/os_settings_ui_toolbar_test.js'],
+ [
+   'OsSettingsUiToolbar',
+   'os_settings_ui/os_settings_ui_toolbar_test.js',
+   {disabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
+ [
+   'OsSettingsUiToolbarRevamp',
+   'os_settings_ui/os_settings_ui_toolbar_test.js',
+   {enabled: ['ash::features::kOsSettingsRevampWayfinding']},
+ ],
  [
    'OsSettingsUiUserActionRecorder',
    'os_settings_ui/user_action_recorder_test.js',
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_hats_ui_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_hats_test.ts
similarity index 72%
rename from chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_hats_ui_test.ts
rename to chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_hats_test.ts
index da0d1885..f02be3e 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_hats_ui_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_hats_test.ts
@@ -6,24 +6,33 @@
 
 import 'chrome://os-settings/os_settings.js';
 
-import {CrSettingsPrefs, CrToolbarSearchFieldElement, OsSettingsHatsBrowserProxyImpl, OsSettingsSearchBoxElement, OsSettingsUiElement, OsToolbarElement} from 'chrome://os-settings/os_settings.js';
+import {AccountManagerBrowserProxyImpl} from 'chrome://os-settings/lazy_load.js';
+import {CrSettingsPrefs, CrToolbarSearchFieldElement, OsSettingsHatsBrowserProxyImpl, OsSettingsSearchBoxElement, OsSettingsUiElement} from 'chrome://os-settings/os_settings.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
+import {TestAccountManagerBrowserProxy} from '../os_people_page/test_account_manager_browser_proxy.js';
+
 import {TestOsSettingsHatsBrowserProxy} from './test_os_settings_hats_browser_proxy.js';
 
-suite('OSSettingsUiHats', function() {
-  let browserProxy: TestOsSettingsHatsBrowserProxy|null = null;
+suite('<os-settings-ui> HaTS', () => {
+  let browserProxy: TestOsSettingsHatsBrowserProxy;
   let field: CrToolbarSearchFieldElement|null;
   let searchBox: OsSettingsSearchBoxElement|null;
-  let toolbar: OsToolbarElement|null;
   let ui: OsSettingsUiElement;
+  let testAccountManagerBrowserProxy: TestAccountManagerBrowserProxy;
 
-  suiteSetup(async function() {
+  suiteSetup(async () => {
     browserProxy = new TestOsSettingsHatsBrowserProxy();
     OsSettingsHatsBrowserProxyImpl.setInstanceForTesting(browserProxy);
 
+    // Setup fake accounts. There must be a device account available for the
+    // Accounts menu item in <os-settings-menu>.
+    testAccountManagerBrowserProxy = new TestAccountManagerBrowserProxy();
+    AccountManagerBrowserProxyImpl.setInstanceForTesting(
+        testAccountManagerBrowserProxy);
+
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     ui = document.createElement('os-settings-ui');
     document.body.appendChild(ui);
@@ -31,11 +40,19 @@
     flush();
   });
 
+  suiteTeardown(() => {
+    ui.remove();
+  });
+
+  teardown(() => {
+    browserProxy.reset();
+    testAccountManagerBrowserProxy.reset();
+  });
+
   test(
       'sendSettingsHats is sent when user shifts focus off the Settings page',
       async () => {
         window.dispatchEvent(new Event('blur'));
-        assert(browserProxy);
         await browserProxy.whenCalled('sendSettingsHats');
       });
 
@@ -60,7 +77,7 @@
     }
 
     function retrieveSearchBox(): void {
-      toolbar = ui.shadowRoot!.querySelector('os-toolbar');
+      const toolbar = ui.shadowRoot!.querySelector('os-toolbar');
       assert(toolbar);
       searchBox = toolbar.shadowRoot!.querySelector('os-settings-search-box');
       assert(searchBox);
@@ -68,7 +85,7 @@
       assert(field);
     }
 
-    teardown(async function() {
+    teardown(async () => {
       await simulateSearch('');
     });
 
@@ -78,7 +95,6 @@
           retrieveSearchBox();
           const searchQuery = 'query 1';
           await simulateSearch(searchQuery);
-          assert(browserProxy);
           await browserProxy.whenCalled('settingsUsedSearch');
         });
   });
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_menu_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_menu_test.ts
index 850cba6f..5df3579b 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_menu_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_menu_test.ts
@@ -4,6 +4,7 @@
 
 import 'chrome://os-settings/os_settings.js';
 
+import {AccountManagerBrowserProxyImpl} from 'chrome://os-settings/lazy_load.js';
 import {CrDrawerElement, CrSettingsPrefs, OsSettingsMenuElement, OsSettingsUiElement, Router, routes, routesMojom, setNearbyShareSettingsForTesting} from 'chrome://os-settings/os_settings.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {DomIf, flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -11,13 +12,23 @@
 import {FakeNearbyShareSettings} from 'chrome://webui-test/nearby_share/shared/fake_nearby_share_settings.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
+import {TestAccountManagerBrowserProxy} from '../os_people_page/test_account_manager_browser_proxy.js';
+
 suite('<os-settings-ui> menu', () => {
+  const isRevampWayfindingEnabled =
+      loadTimeData.getBoolean('isRevampWayfindingEnabled');
   let ui: OsSettingsUiElement;
   let fakeNearbySettings: FakeNearbyShareSettings;
+  let testAccountManagerBrowserProxy: TestAccountManagerBrowserProxy;
 
   suiteSetup(() => {
     fakeNearbySettings = new FakeNearbyShareSettings();
     setNearbyShareSettingsForTesting(fakeNearbySettings);
+
+    // Setup fake accounts.
+    testAccountManagerBrowserProxy = new TestAccountManagerBrowserProxy();
+    AccountManagerBrowserProxyImpl.setInstanceForTesting(
+        testAccountManagerBrowserProxy);
   });
 
   async function createElement(): Promise<OsSettingsUiElement> {
@@ -31,6 +42,7 @@
   teardown(() => {
     ui.remove();
     Router.getInstance().resetRouteForTesting();
+    testAccountManagerBrowserProxy.reset();
   });
 
   test('Drawer can open and close', async () => {
@@ -102,69 +114,71 @@
     assertEquals('', router.getQueryParameters().toString());
   });
 
-  test('Advanced section in menu and main page behavior', async () => {
-    ui = await createElement();
-    const drawerTemplate =
-        ui.shadowRoot!.querySelector<DomIf>('#drawerTemplate');
-    assertTrue(!!drawerTemplate);
-    drawerTemplate.if = true;
-    flush();
+  if (!isRevampWayfindingEnabled) {
+    test('Advanced section in menu and main page behavior', async () => {
+      ui = await createElement();
+      const drawerTemplate =
+          ui.shadowRoot!.querySelector<DomIf>('#drawerTemplate');
+      assertTrue(!!drawerTemplate);
+      drawerTemplate.if = true;
+      flush();
 
-    const main = ui.shadowRoot!.querySelector('os-settings-main');
-    assertTrue(!!main);
-    const mainPageContainer =
-        main.shadowRoot!.querySelector('main-page-container');
-    assertTrue(!!mainPageContainer);
-    const mainPageAdvancedToggle =
-        mainPageContainer.shadowRoot!.querySelector<HTMLButtonElement>(
-            '#advancedToggle');
-    assertTrue(!!mainPageAdvancedToggle);
-    const floatingMenu = ui.shadowRoot!.querySelector<OsSettingsMenuElement>(
-        '#left os-settings-menu');
-    assertTrue(!!floatingMenu);
-    const drawerMenu = ui.shadowRoot!.querySelector<OsSettingsMenuElement>(
-        '#drawer os-settings-menu');
-    assertTrue(!!drawerMenu);
+      const main = ui.shadowRoot!.querySelector('os-settings-main');
+      assertTrue(!!main);
+      const mainPageContainer =
+          main.shadowRoot!.querySelector('main-page-container');
+      assertTrue(!!mainPageContainer);
+      const mainPageAdvancedToggle =
+          mainPageContainer.shadowRoot!.querySelector<HTMLButtonElement>(
+              '#advancedToggle');
+      assertTrue(!!mainPageAdvancedToggle);
+      const floatingMenu = ui.shadowRoot!.querySelector<OsSettingsMenuElement>(
+          '#left os-settings-menu');
+      assertTrue(!!floatingMenu);
+      const drawerMenu = ui.shadowRoot!.querySelector<OsSettingsMenuElement>(
+          '#drawer os-settings-menu');
+      assertTrue(!!drawerMenu);
 
-    // Advanced section should not be expanded
-    assertFalse(main.advancedToggleExpanded);
-    assertFalse(drawerMenu.advancedOpened);
-    assertFalse(floatingMenu.advancedOpened);
+      // Advanced section should not be expanded
+      assertFalse(main.advancedToggleExpanded);
+      assertFalse(drawerMenu.advancedOpened);
+      assertFalse(floatingMenu.advancedOpened);
 
-    mainPageAdvancedToggle.click();
-    flush();
+      mainPageAdvancedToggle.click();
+      flush();
 
-    // Advanced section should be expanded
-    assertTrue(main.advancedToggleExpanded);
-    assertTrue(drawerMenu.advancedOpened);
-    assertTrue(floatingMenu.advancedOpened);
+      // Advanced section should be expanded
+      assertTrue(main.advancedToggleExpanded);
+      assertTrue(drawerMenu.advancedOpened);
+      assertTrue(floatingMenu.advancedOpened);
 
-    // Collapse 'Advanced' in the menu.
-    const advancedButton =
-        drawerMenu.shadowRoot!.querySelector<HTMLButtonElement>(
-            '#advancedButton');
-    assertTrue(!!advancedButton);
-    advancedButton.click();
-    flush();
+      // Collapse 'Advanced' in the menu.
+      const advancedButton =
+          drawerMenu.shadowRoot!.querySelector<HTMLButtonElement>(
+              '#advancedButton');
+      assertTrue(!!advancedButton);
+      advancedButton.click();
+      flush();
 
-    // Collapsing it in the menu should not collapse it in the main area.
-    assertTrue(main.advancedToggleExpanded);
-    assertFalse(drawerMenu.advancedOpened);
-    assertFalse(floatingMenu.advancedOpened);
+      // Collapsing it in the menu should not collapse it in the main area.
+      assertTrue(main.advancedToggleExpanded);
+      assertFalse(drawerMenu.advancedOpened);
+      assertFalse(floatingMenu.advancedOpened);
 
-    // Expand both 'Advanced's again.
-    advancedButton.click();
-    flush();
+      // Expand both 'Advanced's again.
+      advancedButton.click();
+      flush();
 
-    // Collapse 'Advanced' in the main area.
-    main.set('advancedToggleExpanded', false);
-    flush();
+      // Collapse 'Advanced' in the main area.
+      main.set('advancedToggleExpanded', false);
+      flush();
 
-    // Collapsing it in the main area should not collapse it in the menu.
-    assertTrue(drawerMenu.advancedOpened);
-    assertTrue(floatingMenu.advancedOpened);
-    assertFalse(main.advancedToggleExpanded);
-  });
+      // Collapsing it in the main area should not collapse it in the menu.
+      assertTrue(drawerMenu.advancedOpened);
+      assertTrue(floatingMenu.advancedOpened);
+      assertFalse(main.advancedToggleExpanded);
+    });
+  }
 
   suite('When in kiosk mode', () => {
     setup(() => {
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts
index d2073d0..c6cb33f 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_test.ts
@@ -2,22 +2,37 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/**
+ * @fileoverview
+ * Suite of tests for the overall OS Settings UI.
+ */
+
 import 'chrome://os-settings/os_settings.js';
 
+import {AccountManagerBrowserProxyImpl} from 'chrome://os-settings/lazy_load.js';
 import {CrDrawerElement, CrSettingsPrefs, MainPageContainerElement, OsSettingsMainElement, OsSettingsUiElement} from 'chrome://os-settings/os_settings.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
-/** @fileoverview Suite of tests for the OS Settings ui and main page. */
+import {TestAccountManagerBrowserProxy} from '../os_people_page/test_account_manager_browser_proxy.js';
 
-suite('OSSettingsUi', function() {
+suite('OSSettingsUi', () => {
   let ui: OsSettingsUiElement;
   let settingsMain: OsSettingsMainElement|null;
   let mainPageContainer: MainPageContainerElement|null;
+  let testAccountManagerBrowserProxy: TestAccountManagerBrowserProxy;
 
-  suiteSetup(async function() {
+  suiteSetup(async () => {
+    // Setup fake accounts. There must be a device account available for the
+    // Accounts menu item in <os-settings-menu>.
+    testAccountManagerBrowserProxy = new TestAccountManagerBrowserProxy();
+    AccountManagerBrowserProxyImpl.setInstanceForTesting(
+        testAccountManagerBrowserProxy);
+
+    // Create only one element instance for the entire suite since this element
+    // is large and expensive to render to the DOM.
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     ui = document.createElement('os-settings-ui');
     document.body.appendChild(ui);
@@ -38,7 +53,15 @@
     flush();
   });
 
-  test('Update required end of life banner visibility', function() {
+  suiteTeardown(() => {
+    ui.remove();
+  });
+
+  teardown(() => {
+    testAccountManagerBrowserProxy.reset();
+  });
+
+  test('Update required end of life banner visibility', () => {
     flush();
     assert(mainPageContainer);
     assertEquals(
@@ -52,7 +75,7 @@
         '#updateRequiredEolBanner'));
   });
 
-  test('Update required end of life banner close button click', function() {
+  test('Update required end of life banner close button click', () => {
     assert(mainPageContainer);
     mainPageContainer.set('showUpdateRequiredEolBanner_', true);
     flush();
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_toolbar_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_toolbar_test.ts
index 51361d1..3b7716e 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_toolbar_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_toolbar_test.ts
@@ -4,19 +4,31 @@
 
 import 'chrome://os-settings/os_settings.js';
 
+import {AccountManagerBrowserProxyImpl} from 'chrome://os-settings/lazy_load.js';
 import {CrSettingsPrefs, OsSettingsUiElement, Router, routes, setNearbyShareSettingsForTesting} from 'chrome://os-settings/os_settings.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertFalse, assertNull, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeNearbyShareSettings} from 'chrome://webui-test/nearby_share/shared/fake_nearby_share_settings.js';
 
+import {TestAccountManagerBrowserProxy} from '../os_people_page/test_account_manager_browser_proxy.js';
+
 suite('<os-settings-ui> toolbar', () => {
+  const isRevampWayfindingEnabled =
+      loadTimeData.getBoolean('isRevampWayfindingEnabled');
   let ui: OsSettingsUiElement;
   let fakeNearbySettings: FakeNearbyShareSettings;
+  let testAccountManagerBrowserProxy: TestAccountManagerBrowserProxy;
 
   suiteSetup(() => {
     fakeNearbySettings = new FakeNearbyShareSettings();
     setNearbyShareSettingsForTesting(fakeNearbySettings);
+
+    // Setup fake accounts. There must be a device account available for the
+    // Accounts menu item in <os-settings-menu>.
+    testAccountManagerBrowserProxy = new TestAccountManagerBrowserProxy();
+    AccountManagerBrowserProxyImpl.setInstanceForTesting(
+        testAccountManagerBrowserProxy);
   });
 
   async function createElement(): Promise<OsSettingsUiElement> {
@@ -27,26 +39,33 @@
     return element;
   }
 
+  setup(() => {
+    Router.getInstance().navigateTo(routes.BASIC);
+  });
+
   teardown(() => {
     ui.remove();
+    testAccountManagerBrowserProxy.reset();
     Router.getInstance().resetRouteForTesting();
   });
 
-  test('Toolbar shadow is always shown for subpages', async () => {
-    ui = await createElement();
-    const shadowEl = ui.shadowRoot!.querySelector('#cr-container-shadow-top');
-    assertTrue(!!shadowEl, 'Shadow container element should exist');
+  if (!isRevampWayfindingEnabled) {
+    test('Toolbar shadow is always shown for subpages', async () => {
+      ui = await createElement();
+      const shadowEl = ui.shadowRoot!.querySelector('#cr-container-shadow-top');
+      assertTrue(!!shadowEl, 'Shadow container element should exist');
 
-    assertFalse(
-        shadowEl.classList.contains('has-shadow'),
-        'Main page should not show shadow ' + shadowEl.className);
+      assertFalse(
+          shadowEl.classList.contains('has-shadow'),
+          'Main page should not show shadow ' + shadowEl.className);
 
-    Router.getInstance().navigateTo(routes.POWER);
-    flush();
-    assertTrue(
-        shadowEl.classList.contains('has-shadow'),
-        'Sub-page should show shadow ' + shadowEl.className);
-  });
+      Router.getInstance().navigateTo(routes.POWER);
+      flush();
+      assertTrue(
+          shadowEl.classList.contains('has-shadow'),
+          'Sub-page should show shadow ' + shadowEl.className);
+    });
+  }
 
   test('Menu icon shows only in narrow mode', async () => {
     ui = await createElement();
diff --git a/chrome/updater/tag.cc b/chrome/updater/tag.cc
index 8b06a20..d5a58837 100644
--- a/chrome/updater/tag.cc
+++ b/chrome/updater/tag.cc
@@ -14,9 +14,11 @@
 #include <vector>
 
 #include "base/containers/contains.h"
+#include "base/containers/span.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/strings/escape.h"
@@ -764,7 +766,7 @@
 
 std::unique_ptr<tagging::BinaryInterface> CreateBinary(
     const base::FilePath& file,
-    const std::vector<uint8_t>& contents) {
+    base::span<const uint8_t> contents) {
   if (file.MatchesExtension(FILE_PATH_LITERAL(".exe"))) {
     return CreatePEBinary(contents);
   } else if (file.MatchesExtension(FILE_PATH_LITERAL(".msi"))) {
@@ -784,10 +786,13 @@
     return ParseTagBuffer(ReadFileTail(file));
   }
 
-  // TODO(crbug.com/1472820): Can we do similar for EXEs, or can we consider PE
-  // parsing to be a safer implementation?
-  const std::vector<uint8_t> contents = ReadEntireFile(file);
-  std::unique_ptr<tagging::BinaryInterface> bin = CreateBinary(file, contents);
+  base::MemoryMappedFile mapped_file;
+  if (!mapped_file.Initialize(file)) {
+    LOG(ERROR) << __func__ << ": Unknown or empty file: " << file;
+    return {};
+  }
+  std::unique_ptr<tagging::BinaryInterface> bin =
+      CreateBinary(file, mapped_file.bytes());
   if (!bin) {
     LOG(ERROR) << __func__ << ": Could not parse binary: " << file;
     return {};
diff --git a/chrome/updater/tag_unittest.cc b/chrome/updater/tag_unittest.cc
index 9d2beefb..ad200dd 100644
--- a/chrome/updater/tag_unittest.cc
+++ b/chrome/updater/tag_unittest.cc
@@ -1378,6 +1378,12 @@
          "defaultbrowser",
          true},
 
+        // empty tag string.
+        {"GUH-untagged.msi", "", true},
+
+        // already tagged.
+        {"GUH-brand-only.msi", "brand=QAQA", true},
+
         // unknown tag argument `unknowntagarg`.
         {"GUH-untagged.msi",
          "appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}&iid={2D8C18E9-8D3A-"
@@ -1402,7 +1408,7 @@
     ASSERT_EQ(tagging::BinaryWriteTag(msi_file, GetParam().tag_string, 8206,
                                       out_msi_file),
               GetParam().expected_success);
-    if (GetParam().expected_success) {
+    if (GetParam().expected_success && !GetParam().tag_string.empty()) {
       tagging::TagArgs tag_args;
       ASSERT_EQ(tagging::Parse(GetParam().tag_string, {}, &tag_args),
                 tagging::ErrorCode::kSuccess);
diff --git a/chrome/updater/win/installer/installer.cc b/chrome/updater/win/installer/installer.cc
index eddf6197..0cb917e 100644
--- a/chrome/updater/win/installer/installer.cc
+++ b/chrome/updater/win/installer/installer.cc
@@ -64,7 +64,6 @@
              ? tagging::BinaryReadTagString(base::FilePath(path.get()))
              : std::string();
 }
-
 }  // namespace
 
 // This structure passes data back and forth for the processing
@@ -205,67 +204,50 @@
   return exit_code;
 }
 
-ProcessExitResult BuildCommandLineArguments(const wchar_t* cmd_line,
-                                            wchar_t* cmd_line_args,
-                                            size_t cmd_line_args_capacity) {
+ProcessExitResult BuildInstallerCommandLineArguments(
+    const wchar_t* cmd_line,
+    wchar_t* cmd_line_args,
+    size_t cmd_line_args_capacity) {
   CHECK(cmd_line);
   CHECK(cmd_line_args);
   CHECK(cmd_line_args_capacity);
 
   *cmd_line_args = '\0';
-  CommandString args;
 
   // Append the command line arguments in `cmd_line` first.
-  int num_args = 0;
-  base::win::ScopedLocalAllocTyped<wchar_t*> argv(
-      ::CommandLineToArgvW(cmd_line, &num_args));
-  for (int i = 1; i != num_args; ++i) {
-    if (!args.append(L" ") ||
-        !args.append(
-            base::CommandLine::QuoteForCommandLineToArgvW(argv.get()[i])
-                .c_str())) {
-      return ProcessExitResult(COMMAND_STRING_OVERFLOW);
-    }
-  }
+  base::CommandLine args = base::CommandLine::FromString(cmd_line);
 
   // Handle the tag. Use the tag from the --tag command line argument if such
   // argument exists. If --tag is present in `argv`, then it is going to be
   // handed over to the updater, along with the other arguments. Otherwise, try
   // extracting a tag embedded in the program image of the meta installer.
-  if (![&argv, num_args] {
-        // Returns true if the --tag argument is present on the command line.
-        constexpr wchar_t kTagSwitch[] = L"--tag=";
-        for (int i = 1; i != num_args; ++i) {
-          if (memcmp(argv.get()[i], kTagSwitch, sizeof(kTagSwitch)) == 0) {
-            return true;
-          }
-        }
-        return false;
-      }()) {
+  if (!args.HasSwitch(kTagSwitch)) {
     const std::string tag = ExtractTag();
     if (!tag.empty()) {
-      if (!args.append(L" --tag=") ||
-          !args.append(base::SysUTF8ToWide(tag).c_str())) {
-        return ProcessExitResult(COMMAND_STRING_OVERFLOW);
-      }
+      args.AppendSwitchASCII(kTagSwitch, tag.c_str());
     }
   }
 
   // If there is nothing, return an error.
-  if (!args.length()) {
+  if (args.GetSwitches().size() == 0 && args.GetArgs().size() == 0) {
     return ProcessExitResult(INVALID_OPTION);
   }
 
   // Append logging-related arguments for debugging purposes.
-  if (!args.append(
-          base::SysUTF8ToWide(base::StrCat({" --", kEnableLoggingSwitch, " --",
-                                            kLoggingModuleSwitch, "=",
-                                            kLoggingModuleSwitchValue}))
-              .c_str())) {
+  if (!args.HasSwitch(kEnableLoggingSwitch)) {
+    args.AppendSwitch(kEnableLoggingSwitch);
+  }
+
+  if (!args.HasSwitch(kLoggingModuleSwitch)) {
+    args.AppendSwitchASCII(kLoggingModuleSwitch, kLoggingModuleSwitchValue);
+  }
+
+  std::wstring args_str = args.GetArgumentsString();
+  if (args_str.size() >= cmd_line_args_capacity) {
     return ProcessExitResult(COMMAND_STRING_OVERFLOW);
   }
 
-  SafeStrCopy(cmd_line_args, cmd_line_args_capacity, args.get());
+  SafeStrCopy(cmd_line_args, cmd_line_args_capacity, args_str.c_str());
   return ProcessExitResult(SUCCESS_EXIT_CODE);
 }
 
@@ -347,7 +329,7 @@
   }
 
   CommandString cmd_line_args;
-  ProcessExitResult args_result = BuildCommandLineArguments(
+  ProcessExitResult args_result = BuildInstallerCommandLineArguments(
       ::GetCommandLineW(), cmd_line_args.get(), cmd_line_args.capacity());
   if (args_result.exit_code != SUCCESS_EXIT_CODE) {
     return args_result;
diff --git a/chrome/updater/win/installer/installer.h b/chrome/updater/win/installer/installer.h
index 49ed74e..1a372dd 100644
--- a/chrome/updater/win/installer/installer.h
+++ b/chrome/updater/win/installer/installer.h
@@ -32,12 +32,19 @@
   bool IsSuccess() const { return exit_code == SUCCESS_EXIT_CODE; }
 };
 
+inline constexpr size_t kInstallerMaxCommandString = MAX_PATH * 4;
+
 // A stack-based string large enough to hold an executable to run
 // (which is a path), plus a few extra arguments.
-using CommandString = StackString<MAX_PATH * 4>;
+using CommandString = StackString<kInstallerMaxCommandString>;
 
 std::optional<base::FilePath> FindOfflineDir(const base::FilePath& unpack_path);
 
+ProcessExitResult BuildInstallerCommandLineArguments(
+    const wchar_t* cmd_line,
+    wchar_t* cmd_line_args,
+    size_t cmd_line_args_capacity);
+
 // Handles elevating the installer, waiting for the installer process, and
 // returning the resulting process exit code.
 ProcessExitResult HandleRunElevated(const base::CommandLine& command_line);
diff --git a/chrome/updater/win/installer/installer_win_unittest.cc b/chrome/updater/win/installer/installer_win_unittest.cc
index 97de22a2..5e4b2141 100644
--- a/chrome/updater/win/installer/installer_win_unittest.cc
+++ b/chrome/updater/win/installer/installer_win_unittest.cc
@@ -7,15 +7,42 @@
 #include <shlobj.h>
 
 #include <optional>
+#include <string>
 
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/strcat.h"
+#include "base/strings/sys_string_conversions.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/win/installer/exit_code.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+constexpr char tag_switch[] = "--tag=";
+constexpr char enable_logging_switch[] = "--enable-logging";
+constexpr char logging_module_switch[] = "--vmodule=";
+
+void ExpectExactlyOneOccurrence(const std::wstring& search_string,
+                                const std::wstring& substring) {
+  auto pos = search_string.find(substring);
+  EXPECT_NE(pos, std::wstring::npos);
+  pos = search_string.find(substring, pos + substring.size());
+  EXPECT_EQ(pos, std::wstring::npos);
+}
+
+void ExpectSwitchValue(const std::wstring& cmd_line,
+                       const std::wstring& switch_str,
+                       const std::wstring& expected_value) {
+  auto pos = cmd_line.find(switch_str);
+  EXPECT_NE(pos, std::wstring::npos);
+  pos += switch_str.size();
+  EXPECT_LE(pos + expected_value.size(), cmd_line.size());
+  EXPECT_EQ(cmd_line.substr(pos, expected_value.size()), expected_value);
+}
+}  // namespace
+
 // Tests that `HandleRunElevated` returns `UNABLE_TO_ELEVATE_METAINSTALLER` when
 // not elevated and called with `kCmdLineExpectElevated` argument.
 TEST(InstallerTest, HandleRunElevated) {
@@ -56,3 +83,87 @@
   EXPECT_EQ(offline_dir->BaseName(),
             base::FilePath(L"{8D5D0563-F2A0-40E3-932D-AFEAE261A9D1}"));
 }
+
+TEST(BuildInstallerCommandLineArgumentsTest, EnableLoggingSwitch) {
+  // Test that --enable-logging switch is added if none is provided.
+  updater::CommandString cmd_line_args;
+  std::wstring command_line_str(L"UpdaterSetup.exe");
+  // Add a tag switch to bypass attempting to parse a tag.
+  command_line_str = base::SysUTF8ToWide(
+      base::StrCat({"UpdaterSetup.exe ", tag_switch, "fake_tag"}));
+  updater::ProcessExitResult exit_result =
+      updater::BuildInstallerCommandLineArguments(command_line_str.c_str(),
+                                                  cmd_line_args.get(),
+                                                  cmd_line_args.capacity());
+  EXPECT_EQ(exit_result.exit_code, updater::SUCCESS_EXIT_CODE);
+  ExpectExactlyOneOccurrence(std::wstring(cmd_line_args.get()),
+                             base::SysUTF8ToWide(enable_logging_switch));
+
+  // Test that no --enable-logging switch is added if one is provided.
+  cmd_line_args.clear();
+  // Add a tag switch to bypass attempting to parse a tag.
+  command_line_str = base::SysUTF8ToWide(base::StrCat(
+      {"UpdaterSetup.exe ", tag_switch, "fake_tag ", enable_logging_switch}));
+  exit_result = updater::BuildInstallerCommandLineArguments(
+      command_line_str.c_str(), cmd_line_args.get(), cmd_line_args.capacity());
+  EXPECT_EQ(exit_result.exit_code, updater::SUCCESS_EXIT_CODE);
+  ExpectExactlyOneOccurrence(std::wstring(cmd_line_args.get()),
+                             base::SysUTF8ToWide(enable_logging_switch));
+}
+
+TEST(BuildInstallerCommandLineArgumentsTest, LoggingModuleSwitch) {
+  // Test that --vmodule switch is added if none is provided.
+  updater::CommandString cmd_line_args;
+  std::wstring command_line_str(L"UpdaterSetup.exe");
+  // Add a tag switch to bypass attempting to parse a tag.
+  command_line_str = base::SysUTF8ToWide(
+      base::StrCat({"UpdaterSetup.exe ", tag_switch, "fake_tag"}));
+  updater::ProcessExitResult exit_result =
+      updater::BuildInstallerCommandLineArguments(command_line_str.c_str(),
+                                                  cmd_line_args.get(),
+                                                  cmd_line_args.capacity());
+  EXPECT_EQ(exit_result.exit_code, updater::SUCCESS_EXIT_CODE);
+  ExpectExactlyOneOccurrence(std::wstring(cmd_line_args.get()),
+                             base::SysUTF8ToWide(logging_module_switch));
+
+  // Test that no --vmodule switch is added if one is provided.
+  cmd_line_args.clear();
+  // Add a tag switch to bypass attempting to parse a tag.
+  command_line_str = base::SysUTF8ToWide(
+      base::StrCat({"UpdaterSetup.exe ", tag_switch, "fake_tag ",
+                    logging_module_switch, "fake_module"}));
+  exit_result = updater::BuildInstallerCommandLineArguments(
+      command_line_str.c_str(), cmd_line_args.get(), cmd_line_args.capacity());
+  EXPECT_EQ(exit_result.exit_code, updater::SUCCESS_EXIT_CODE);
+  ExpectExactlyOneOccurrence(std::wstring(cmd_line_args.get()),
+                             base::SysUTF8ToWide(logging_module_switch));
+  ExpectSwitchValue(command_line_str,
+                    base::SysUTF8ToWide(logging_module_switch),
+                    std::wstring(L"fake_module"));
+}
+
+TEST(BuildInstallerCommandLineArgumentsTest, CommandStringOverflow) {
+  updater::CommandString cmd_line_args;
+  std::wstring command_line_str(L"UpdaterSetup.exe");
+  std::string long_tag(updater::kInstallerMaxCommandString + 1, 'A');
+  command_line_str = base::SysUTF8ToWide(
+      base::StrCat({"UpdaterSetup.exe ", tag_switch, long_tag}));
+  updater::ProcessExitResult exit_result =
+      updater::BuildInstallerCommandLineArguments(command_line_str.c_str(),
+                                                  cmd_line_args.get(),
+                                                  cmd_line_args.capacity());
+  EXPECT_EQ(exit_result.exit_code, updater::COMMAND_STRING_OVERFLOW);
+}
+
+TEST(BuildInstallerCommandLineArgumentsTest, NoArguments) {
+  // Passing in no arguments on the command line will attempt to
+  // extract the embedded tag, but since the test executable is not
+  // tagged this should not add any --tag switches.
+  updater::CommandString cmd_line_args;
+  std::wstring command_line_str(L"UpdaterSetup.exe");
+  updater::ProcessExitResult exit_result =
+      updater::BuildInstallerCommandLineArguments(command_line_str.c_str(),
+                                                  cmd_line_args.get(),
+                                                  cmd_line_args.capacity());
+  EXPECT_EQ(exit_result.exit_code, updater::INVALID_OPTION);
+}
diff --git a/chromeos/ash/services/libassistant/grpc/BUILD.gn b/chromeos/ash/services/libassistant/grpc/BUILD.gn
index e6e4219..5e53519 100644
--- a/chromeos/ash/services/libassistant/grpc/BUILD.gn
+++ b/chromeos/ash/services/libassistant/grpc/BUILD.gn
@@ -19,8 +19,6 @@
     "assistant_client_impl.cc",
     "assistant_client_impl.h",
     "assistant_client_observer.h",
-    "assistant_client_v1.cc",
-    "assistant_client_v1.h",
   ]
 
   deps = [
@@ -140,7 +138,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "assistant_client_v1_unittests.cc",
     "external_services/grpc_services_initializer_unittests.cc",
     "grpc_http_connection_client_unittests.cc",
   ]
diff --git a/chromeos/ash/services/libassistant/grpc/assistant_client.h b/chromeos/ash/services/libassistant/grpc/assistant_client.h
index d7fee860..e3ee762 100644
--- a/chromeos/ash/services/libassistant/grpc/assistant_client.h
+++ b/chromeos/ash/services/libassistant/grpc/assistant_client.h
@@ -177,7 +177,6 @@
       base::OnceCallback<void(
           const ::assistant::api::GetAssistantSettingsResponse&)> on_done) = 0;
   virtual void SetLocaleOverride(const std::string& locale) = 0;
-  virtual void SetDeviceAttributes(bool enable_dark_mode) = 0;
   virtual std::string GetDeviceId() = 0;
 
   // Audio-related functionality:
diff --git a/chromeos/ash/services/libassistant/grpc/assistant_client_impl.cc b/chromeos/ash/services/libassistant/grpc/assistant_client_impl.cc
index 1c0642f..969fb026 100644
--- a/chromeos/ash/services/libassistant/grpc/assistant_client_impl.cc
+++ b/chromeos/ash/services/libassistant/grpc/assistant_client_impl.cc
@@ -5,29 +5,32 @@
 #include "chromeos/ash/services/libassistant/grpc/assistant_client_impl.h"
 
 #include <memory>
+#include <string>
 
-#include "base/check.h"
-#include "base/containers/flat_set.h"
 #include "base/functional/bind.h"
-#include "base/functional/callback_forward.h"
-#include "base/functional/callback_helpers.h"
-#include "base/notreached.h"
+#include "base/functional/callback.h"
+#include "base/logging.h"
 #include "base/system/sys_info.h"
-#include "chromeos/ash/services/libassistant/callback_utils.h"
-#include "chromeos/ash/services/libassistant/grpc/assistant_client_v1.h"
+#include "base/time/time.h"
+#include "chromeos/ash/services/libassistant/grpc/assistant_client.h"
 #include "chromeos/ash/services/libassistant/grpc/external_services/action_service.h"
 #include "chromeos/ash/services/libassistant/grpc/grpc_libassistant_client.h"
 #include "chromeos/ash/services/libassistant/grpc/services_status_observer.h"
+#include "chromeos/ash/services/libassistant/grpc/utils/media_status_utils.h"
 #include "chromeos/ash/services/libassistant/grpc/utils/timer_utils.h"
+#include "chromeos/ash/services/libassistant/public/cpp/assistant_timer.h"
 #include "chromeos/assistant/internal/grpc_transport/request_utils.h"
 #include "chromeos/assistant/internal/internal_constants.h"
 #include "chromeos/assistant/internal/internal_util.h"
 #include "chromeos/assistant/internal/libassistant/shared_headers.h"
 #include "chromeos/assistant/internal/libassistant_util.h"
+#include "chromeos/assistant/internal/proto/shared/proto/settings_ui.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/alarm_timer_interface.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/audio_utils_interface.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/bootup_settings_interface.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/config_settings_interface.pb.h"
+#include "chromeos/assistant/internal/proto/shared/proto/v2/delegate/event_handler_interface.pb.h"
+#include "chromeos/assistant/internal/proto/shared/proto/v2/device_state_event.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/display_interface.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/experiment_interface.pb.h"
 #include "chromeos/assistant/internal/proto/shared/proto/v2/query_interface.pb.h"
@@ -39,8 +42,14 @@
 
 using ::assistant::api::EnableListeningRequest;
 using ::assistant::api::EnableListeningResponse;
+using ::assistant::api::GetAssistantSettingsResponse;
+using ::assistant::api::OnAlarmTimerEventRequest;
+using ::assistant::api::OnDeviceStateEventRequest;
+using ::assistant::api::OnSpeakerIdEnrollmentEventRequest;
 using ::assistant::api::SetLocaleOverrideRequest;
 using ::assistant::api::SetLocaleOverrideResponse;
+using ::assistant::api::UpdateAssistantSettingsResponse;
+using ::assistant::ui::SettingsUiUpdate;
 
 // Rpc call config constants.
 constexpr int kMaxRpcRetries = 5;
@@ -82,7 +91,7 @@
     std::unique_ptr<assistant_client::AssistantManager> assistant_manager,
     const std::string& libassistant_service_address,
     const std::string& assistant_service_address)
-    : AssistantClientV1(std::move(assistant_manager)),
+    : AssistantClient(std::move(assistant_manager)),
       grpc_services_(libassistant_service_address, assistant_service_address),
       libassistant_client_(grpc_services_.GrpcLibassistantClient()) {}
 
@@ -177,6 +186,10 @@
       kDefaultStateConfig);
 }
 
+void AssistantClientImpl::ResetAllDataAndShutdown() {
+  assistant_manager()->ResetAllDataAndShutdown();
+}
+
 void AssistantClientImpl::SendDisplayRequest(
     const OnDisplayRequestRequest& request) {
   libassistant_client_->CallServiceMethod(
@@ -191,6 +204,22 @@
   grpc_services_.AddAssistantDisplayEventObserver(observer);
 }
 
+void AssistantClientImpl::ResumeCurrentStream() {
+  assistant_manager()->GetMediaManager()->Resume();
+}
+
+void AssistantClientImpl::PauseCurrentStream() {
+  assistant_manager()->GetMediaManager()->Pause();
+}
+
+void AssistantClientImpl::SetExternalPlaybackState(
+    const MediaStatus& status_proto) {
+  assistant_client::MediaStatus media_status;
+  ConvertMediaStatusToV1FromV2(status_proto, &media_status);
+  assistant_manager()->GetMediaManager()->SetExternalPlaybackState(
+      media_status);
+}
+
 void AssistantClientImpl::AddDeviceStateEventObserver(
     GrpcServicesObserver<OnDeviceStateEventRequest>* observer) {
   grpc_services_.AddDeviceStateEventObserver(observer);
@@ -348,6 +377,10 @@
       kDefaultStateConfig);
 }
 
+std::string AssistantClientImpl::GetDeviceId() {
+  return assistant_manager()->GetDeviceId();
+}
+
 void AssistantClientImpl::EnableListening(bool listening_enabled) {
   EnableListeningRequest request;
   request.set_enable(listening_enabled);
diff --git a/chromeos/ash/services/libassistant/grpc/assistant_client_impl.h b/chromeos/ash/services/libassistant/grpc/assistant_client_impl.h
index db442e9..9a73f18a 100644
--- a/chromeos/ash/services/libassistant/grpc/assistant_client_impl.h
+++ b/chromeos/ash/services/libassistant/grpc/assistant_client_impl.h
@@ -10,18 +10,16 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ref.h"
-#include "base/scoped_observation.h"
-#include "chromeos/ash/services/libassistant/grpc/assistant_client_v1.h"
+#include "chromeos/ash/services/libassistant/grpc/assistant_client.h"
 #include "chromeos/ash/services/libassistant/grpc/external_services/grpc_services_initializer.h"
 #include "chromeos/ash/services/libassistant/grpc/services_status_provider.h"
 
 namespace ash::libassistant {
 
 class GrpcLibassistantClient;
+class ServicesStatusObserver;
 
-// This class wraps the libassistant grpc client and exposes V2 APIs for
-// ChromeOS to use.
-class AssistantClientImpl : public AssistantClientV1 {
+class AssistantClientImpl : public AssistantClient {
  public:
   AssistantClientImpl(
       std::unique_ptr<assistant_client::AssistantManager> assistant_manager,
@@ -30,7 +28,7 @@
 
   ~AssistantClientImpl() override;
 
-  // AssistantClientV1 overrides:
+  // AssistantClient:
   void StartServices(ServicesStatusObserver* services_status_observer) override;
   bool StartGrpcServices() override;
   void StartGrpcHttpConnectionClient(
@@ -49,9 +47,13 @@
   void GetSpeakerIdEnrollmentInfo(
       const GetSpeakerIdEnrollmentInfoRequest& request,
       base::OnceCallback<void(bool user_model_exists)> on_done) override;
+  void ResetAllDataAndShutdown() override;
   void SendDisplayRequest(const OnDisplayRequestRequest& request) override;
   void AddDisplayEventObserver(
       GrpcServicesObserver<OnAssistantDisplayEventRequest>* observer) override;
+  void ResumeCurrentStream() override;
+  void PauseCurrentStream() override;
+  void SetExternalPlaybackState(const MediaStatus& status_proto) override;
   void AddDeviceStateEventObserver(
       GrpcServicesObserver<OnDeviceStateEventRequest>* observer) override;
   void AddMediaActionFallbackEventObserver(
@@ -81,11 +83,12 @@
       override;
   void GetAssistantSettings(
       const ::assistant::ui::SettingsUiSelector& selector,
-      const std::string& use_id,
+      const std::string& user_id,
       base::OnceCallback<
           void(const ::assistant::api::GetAssistantSettingsResponse&)> on_done)
       override;
   void SetLocaleOverride(const std::string& locale) override;
+  std::string GetDeviceId() override;
 
   // Audio-related functionality:
   void EnableListening(bool listening_enabled) override;
diff --git a/chromeos/ash/services/libassistant/grpc/assistant_client_v1.cc b/chromeos/ash/services/libassistant/grpc/assistant_client_v1.cc
deleted file mode 100644
index a68a58c..0000000
--- a/chromeos/ash/services/libassistant/grpc/assistant_client_v1.cc
+++ /dev/null
@@ -1,345 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromeos/ash/services/libassistant/grpc/assistant_client_v1.h"
-
-#include <string>
-
-#include "base/functional/bind.h"
-#include "base/functional/callback.h"
-#include "base/functional/callback_forward.h"
-#include "base/logging.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/notreached.h"
-#include "base/synchronization/lock.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/time/time.h"
-#include "chromeos/ash/services/libassistant/callback_utils.h"
-#include "chromeos/ash/services/libassistant/grpc/assistant_client.h"
-#include "chromeos/ash/services/libassistant/grpc/utils/media_status_utils.h"
-#include "chromeos/ash/services/libassistant/grpc/utils/settings_utils.h"
-#include "chromeos/ash/services/libassistant/grpc/utils/timer_utils.h"
-#include "chromeos/ash/services/libassistant/public/cpp/assistant_timer.h"
-#include "chromeos/assistant/internal/grpc_transport/request_utils.h"
-#include "chromeos/assistant/internal/internal_util.h"
-#include "chromeos/assistant/internal/libassistant/shared_headers.h"
-#include "chromeos/assistant/internal/proto/shared/proto/conversation.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/settings_ui.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/update_settings_ui.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/v2/config_settings_interface.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/v2/delegate/event_handler_interface.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/v2/device_state_event.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/v2/display_interface.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/v2/speaker_id_enrollment_event.pb.h"
-#include "chromeos/assistant/internal/proto/shared/proto/v2/speaker_id_enrollment_interface.pb.h"
-
-namespace ash::libassistant {
-
-namespace {
-
-using ::assistant::api::GetAssistantSettingsResponse;
-using ::assistant::api::OnAlarmTimerEventRequest;
-using ::assistant::api::OnDeviceStateEventRequest;
-using ::assistant::api::OnSpeakerIdEnrollmentEventRequest;
-using ::assistant::api::UpdateAssistantSettingsResponse;
-using ::assistant::api::events::SpeakerIdEnrollmentEvent;
-using ::assistant::ui::SettingsUiUpdate;
-using assistant_client::SpeakerIdEnrollmentUpdate;
-
-// A macro which ensures we are running on the calling sequence.
-#define ENSURE_CALLING_SEQUENCE(method, ...)                                \
-  if (!task_runner_->RunsTasksInCurrentSequence()) {                        \
-    task_runner_->PostTask(                                                 \
-        FROM_HERE,                                                          \
-        base::BindOnce(method, weak_factory_.GetWeakPtr(), ##__VA_ARGS__)); \
-    return;                                                                 \
-  }
-
-}  // namespace
-
-////////////////////////////////////////////////////////////////////////////////
-//   AssistantClientV1::DeviceStateListener
-////////////////////////////////////////////////////////////////////////////////
-
-class AssistantClientV1::DeviceStateListener
-    : public assistant_client::DeviceStateListener {
- public:
-  explicit DeviceStateListener(AssistantClientV1* assistant_client)
-      : assistant_client_(assistant_client),
-        task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
-  DeviceStateListener(const DeviceStateListener&) = delete;
-  DeviceStateListener& operator=(const DeviceStateListener&) = delete;
-  ~DeviceStateListener() override = default;
-
-  // assistant_client::DeviceStateListener:
-  // Called from the Libassistant thread.
-  void OnStartFinished() override {
-    ENSURE_CALLING_SEQUENCE(&DeviceStateListener::OnStartFinished);
-
-    // Now |AssistantManager| is fully started, add media manager listener.
-    assistant_client_->AddMediaManagerListener();
-  }
-
- private:
-  raw_ptr<AssistantClientV1, ExperimentalAsh> assistant_client_ = nullptr;
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  base::WeakPtrFactory<DeviceStateListener> weak_factory_{this};
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//   AssistantClientV1::MediaManagerListener
-////////////////////////////////////////////////////////////////////////////////
-
-class AssistantClientV1::MediaManagerListener
-    : public assistant_client::MediaManager::Listener {
- public:
-  explicit MediaManagerListener(AssistantClientV1* assistant_client)
-      : assistant_client_(assistant_client),
-        task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
-  MediaManagerListener(const MediaManagerListener&) = delete;
-  MediaManagerListener& operator=(const MediaManagerListener&) = delete;
-  ~MediaManagerListener() override = default;
-
-  // assistant_client::MediaManager::Listener:
-  // Called from the Libassistant thread.
-  void OnPlaybackStateChange(
-      const assistant_client::MediaStatus& media_status) override {
-    ENSURE_CALLING_SEQUENCE(&MediaManagerListener::OnPlaybackStateChange,
-                            media_status);
-
-    OnDeviceStateEventRequest request;
-    auto* status = request.mutable_event()
-                       ->mutable_on_state_changed()
-                       ->mutable_new_state()
-                       ->mutable_media_status();
-    ConvertMediaStatusToV2FromV1(media_status, status);
-    assistant_client_->NotifyDeviceStateEvent(request);
-  }
-
- private:
-  raw_ptr<AssistantClientV1, ExperimentalAsh> assistant_client_ = nullptr;
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-  base::WeakPtrFactory<MediaManagerListener> weak_factory_{this};
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//   AssistantClientV1
-////////////////////////////////////////////////////////////////////////////////
-
-AssistantClientV1::AssistantClientV1(
-    std::unique_ptr<assistant_client::AssistantManager> manager)
-    : AssistantClient(std::move(manager)),
-      device_state_listener_(std::make_unique<DeviceStateListener>(this)),
-      media_manager_listener_(std::make_unique<MediaManagerListener>(this)) {
-  assistant_manager()->AddDeviceStateListener(device_state_listener_.get());
-}
-
-AssistantClientV1::~AssistantClientV1() {
-  // Some listeners (e.g. MediaManagerListener) require that they outlive
-  // `assistant_manager_`. Reset `assistant_manager_` in the parent class first
-  // before any listener in this class gets destructed.
-  ResetAssistantManager();
-}
-
-void AssistantClientV1::StartServices(
-    ServicesStatusObserver* services_status_observer) {
-  DCHECK(services_status_observer);
-  services_status_observer_ = services_status_observer;
-}
-
-bool AssistantClientV1::StartGrpcServices() {
-  return true;
-}
-
-void AssistantClientV1::StartGrpcHttpConnectionClient(
-    assistant_client::HttpConnectionFactory*) {
-  NOTIMPLEMENTED();
-}
-
-void AssistantClientV1::AddExperimentIds(
-    const std::vector<std::string>& exp_ids) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::AddSpeakerIdEnrollmentEventObserver(
-    GrpcServicesObserver<OnSpeakerIdEnrollmentEventRequest>* observer) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::RemoveSpeakerIdEnrollmentEventObserver(
-    GrpcServicesObserver<OnSpeakerIdEnrollmentEventRequest>* observer) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::StartSpeakerIdEnrollment(
-    const StartSpeakerIdEnrollmentRequest& request) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::CancelSpeakerIdEnrollment(
-    const CancelSpeakerIdEnrollmentRequest& request) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::GetSpeakerIdEnrollmentInfo(
-    const ::assistant::api::GetSpeakerIdEnrollmentInfoRequest& request,
-    base::OnceCallback<void(bool user_model_exists)> on_done) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::ResetAllDataAndShutdown() {
-  assistant_manager()->ResetAllDataAndShutdown();
-}
-
-void AssistantClientV1::SendDisplayRequest(
-    const OnDisplayRequestRequest& request) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::AddDisplayEventObserver(
-    GrpcServicesObserver<OnAssistantDisplayEventRequest>* observer) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::ResumeCurrentStream() {
-  assistant_manager()->GetMediaManager()->Resume();
-}
-
-void AssistantClientV1::PauseCurrentStream() {
-  assistant_manager()->GetMediaManager()->Pause();
-}
-
-void AssistantClientV1::SetExternalPlaybackState(
-    const MediaStatus& status_proto) {
-  assistant_client::MediaStatus media_status;
-  ConvertMediaStatusToV1FromV2(status_proto, &media_status);
-  assistant_manager()->GetMediaManager()->SetExternalPlaybackState(
-      media_status);
-}
-
-void AssistantClientV1::AddDeviceStateEventObserver(
-    GrpcServicesObserver<OnDeviceStateEventRequest>* observer) {
-  device_state_event_observer_list_.AddObserver(observer);
-}
-
-void AssistantClientV1::AddMediaActionFallbackEventObserver(
-    GrpcServicesObserver<OnMediaActionFallbackEventRequest>* observer) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::SendVoicelessInteraction(
-    const ::assistant::api::Interaction& interaction,
-    const std::string& description,
-    const ::assistant::api::VoicelessOptions& options,
-    base::OnceCallback<void(bool)> on_done) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::RegisterActionModule(
-    assistant_client::ActionModule* action_module) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::StartVoiceInteraction() {
-  assistant_manager()->StartAssistantInteraction();
-}
-
-void AssistantClientV1::StopAssistantInteraction(bool cancel_conversation) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::AddConversationStateEventObserver(
-    GrpcServicesObserver<OnConversationStateEventRequest>* observer) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::SetAuthenticationInfo(const AuthTokens& tokens) {
-  assistant_manager()->SetAuthTokens(tokens);
-}
-
-void AssistantClientV1::SetInternalOptions(const std::string& locale,
-                                           bool spoken_feedback_enabled) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::UpdateAssistantSettings(
-    const SettingsUiUpdate& settings,
-    const std::string& user_id,
-    base::OnceCallback<void(const UpdateAssistantSettingsResponse&)> on_done) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::GetAssistantSettings(
-    const ::assistant::ui::SettingsUiSelector& selector,
-    const std::string& user_id,
-    base::OnceCallback<void(const GetAssistantSettingsResponse&)> on_done) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::AddMediaManagerListener() {
-  assistant_manager()->GetMediaManager()->AddListener(
-      media_manager_listener_.get());
-}
-
-void AssistantClientV1::NotifyDeviceStateEvent(
-    const OnDeviceStateEventRequest& request) {
-  for (auto& observer : device_state_event_observer_list_) {
-    observer.OnGrpcMessage(request);
-  }
-}
-
-void AssistantClientV1::NotifyAllServicesReady() {
-  services_status_observer_->OnServicesStatusChanged(
-      ServicesStatus::ONLINE_ALL_SERVICES_AVAILABLE);
-}
-
-void AssistantClientV1::SetLocaleOverride(const std::string& locale) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::SetDeviceAttributes(bool enable_dark_mode) {
-  // We don't actually do anything here besides caching the passed in value
-  // because dark mode is set through |SetOptions| for V1.
-  dark_mode_enabled_ = enable_dark_mode;
-}
-
-std::string AssistantClientV1::GetDeviceId() {
-  return assistant_manager()->GetDeviceId();
-}
-
-void AssistantClientV1::EnableListening(bool listening_enabled) {
-  assistant_manager()->EnableListening(listening_enabled);
-}
-
-void AssistantClientV1::AddTimeToTimer(const std::string& id,
-                                       const base::TimeDelta& duration) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::PauseTimer(const std::string& timer_id) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::RemoveTimer(const std::string& timer_id) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::ResumeTimer(const std::string& timer_id) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::GetTimers(
-    base::OnceCallback<void(const std::vector<assistant::AssistantTimer>&)>
-        on_done) {
-  NOTREACHED_NORETURN();
-}
-
-void AssistantClientV1::AddAlarmTimerEventObserver(
-    GrpcServicesObserver<::assistant::api::OnAlarmTimerEventRequest>*
-        observer) {
-  NOTREACHED_NORETURN();
-}
-
-}  // namespace ash::libassistant
diff --git a/chromeos/ash/services/libassistant/grpc/assistant_client_v1.h b/chromeos/ash/services/libassistant/grpc/assistant_client_v1.h
deleted file mode 100644
index 04b9cb9a..0000000
--- a/chromeos/ash/services/libassistant/grpc/assistant_client_v1.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_ASH_SERVICES_LIBASSISTANT_GRPC_ASSISTANT_CLIENT_V1_H_
-#define CHROMEOS_ASH_SERVICES_LIBASSISTANT_GRPC_ASSISTANT_CLIENT_V1_H_
-
-#include <optional>
-
-#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 "chromeos/ash/services/libassistant/grpc/assistant_client.h"
-#include "chromeos/assistant/internal/libassistant/shared_headers.h"
-
-namespace ash::libassistant {
-
-class ServicesStatusObserver;
-
-class AssistantClientV1 : public AssistantClient {
- public:
-  AssistantClientV1(
-      std::unique_ptr<assistant_client::AssistantManager> assistant_manager);
-  ~AssistantClientV1() override;
-
-  // AssistantClient:
-  void StartServices(ServicesStatusObserver* services_status_observer) override;
-  bool StartGrpcServices() override;
-  void StartGrpcHttpConnectionClient(
-      assistant_client::HttpConnectionFactory*) override;
-  void AddExperimentIds(const std::vector<std::string>& exp_ids) override;
-  void AddSpeakerIdEnrollmentEventObserver(
-      GrpcServicesObserver<OnSpeakerIdEnrollmentEventRequest>* observer)
-      override;
-  void RemoveSpeakerIdEnrollmentEventObserver(
-      GrpcServicesObserver<OnSpeakerIdEnrollmentEventRequest>* observer)
-      override;
-  void StartSpeakerIdEnrollment(
-      const StartSpeakerIdEnrollmentRequest& request) override;
-  void CancelSpeakerIdEnrollment(
-      const CancelSpeakerIdEnrollmentRequest& request) override;
-  void GetSpeakerIdEnrollmentInfo(
-      const GetSpeakerIdEnrollmentInfoRequest& request,
-      base::OnceCallback<void(bool user_model_exists)> on_done) override;
-  void ResetAllDataAndShutdown() override;
-  void SendDisplayRequest(const OnDisplayRequestRequest& request) override;
-  void AddDisplayEventObserver(
-      GrpcServicesObserver<OnAssistantDisplayEventRequest>* observer) override;
-  void ResumeCurrentStream() override;
-  void PauseCurrentStream() override;
-  void SetExternalPlaybackState(const MediaStatus& status_proto) override;
-  void AddDeviceStateEventObserver(
-      GrpcServicesObserver<OnDeviceStateEventRequest>* observer) override;
-  void AddMediaActionFallbackEventObserver(
-      GrpcServicesObserver<OnMediaActionFallbackEventRequest>* observer)
-      override;
-  void SendVoicelessInteraction(
-      const ::assistant::api::Interaction& interaction,
-      const std::string& description,
-      const ::assistant::api::VoicelessOptions& options,
-      base::OnceCallback<void(bool)> on_done) override;
-  void RegisterActionModule(
-      assistant_client::ActionModule* action_module) override;
-  void StartVoiceInteraction() override;
-  void StopAssistantInteraction(bool cancel_conversation) override;
-  void AddConversationStateEventObserver(
-      GrpcServicesObserver<OnConversationStateEventRequest>* observer) override;
-  void SetAuthenticationInfo(const AuthTokens& tokens) override;
-  void SetInternalOptions(const std::string& locale,
-                          bool spoken_feedback_enabled) override;
-  void UpdateAssistantSettings(
-      const ::assistant::ui::SettingsUiUpdate& settings,
-      const std::string& user_id,
-      base::OnceCallback<void(
-          const ::assistant::api::UpdateAssistantSettingsResponse&)> on_done)
-      override;
-  void GetAssistantSettings(
-      const ::assistant::ui::SettingsUiSelector& selector,
-      const std::string& user_id,
-      base::OnceCallback<
-          void(const ::assistant::api::GetAssistantSettingsResponse&)> on_done)
-      override;
-  void SetLocaleOverride(const std::string& locale) override;
-  void SetDeviceAttributes(bool enable_dark_mode) override;
-  std::string GetDeviceId() override;
-  void EnableListening(bool listening_enabled) override;
-  void AddTimeToTimer(const std::string& id,
-                      const base::TimeDelta& duration) override;
-  void PauseTimer(const std::string& timer_id) override;
-  void RemoveTimer(const std::string& timer_id) override;
-  void ResumeTimer(const std::string& timer_id) override;
-  void GetTimers(
-      base::OnceCallback<void(const std::vector<assistant::AssistantTimer>&)>
-          on_done) override;
-  void AddAlarmTimerEventObserver(
-      GrpcServicesObserver<::assistant::api::OnAlarmTimerEventRequest>*
-          observer) override;
-
- private:
-  class DeviceStateListener;
-  class DisplayConnectionImpl;
-  class MediaManagerListener;
-  class AssistantManagerDelegateImpl;
-
-  void AddMediaManagerListener();
-
-  void NotifyDeviceStateEvent(const OnDeviceStateEventRequest& request);
-
-  void NotifyAllServicesReady();
-
-  std::optional<bool> dark_mode_enabled_;
-
-  std::unique_ptr<DeviceStateListener> device_state_listener_;
-
-  std::unique_ptr<MediaManagerListener> media_manager_listener_;
-
-  base::ObserverList<GrpcServicesObserver<OnDeviceStateEventRequest>>
-      device_state_event_observer_list_;
-
-  raw_ptr<ServicesStatusObserver, ExperimentalAsh> services_status_observer_ =
-      nullptr;
-
-  base::WeakPtrFactory<AssistantClientV1> weak_factory_{this};
-};
-
-}  // namespace ash::libassistant
-
-#endif  // CHROMEOS_ASH_SERVICES_LIBASSISTANT_GRPC_ASSISTANT_CLIENT_V1_H_
diff --git a/chromeos/ash/services/libassistant/grpc/assistant_client_v1_unittests.cc b/chromeos/ash/services/libassistant/grpc/assistant_client_v1_unittests.cc
deleted file mode 100644
index 134c188..0000000
--- a/chromeos/ash/services/libassistant/grpc/assistant_client_v1_unittests.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-
-#include "base/functional/callback_helpers.h"
-#include "base/test/task_environment.h"
-#include "chromeos/ash/services/libassistant/grpc/assistant_client_v1.h"
-#include "chromeos/ash/services/libassistant/grpc/services_status_observer.h"
-#include "chromeos/assistant/internal/test_support/fake_assistant_manager.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash::libassistant {
-
-namespace {
-
-class AssistantManagerMock : public chromeos::assistant::FakeAssistantManager {
- public:
-  AssistantManagerMock() = default;
-  AssistantManagerMock(const AssistantManagerMock&) = delete;
-  AssistantManagerMock& operator=(const AssistantManagerMock&) = delete;
-  ~AssistantManagerMock() override = default;
-
-  // chromeos::assistant::FakeAssistantManager implementation:
-  MOCK_METHOD(void, EnableListening, (bool value));
-  MOCK_METHOD(void, SetAuthTokens, (const AssistantClient::AuthTokens&));
-};
-
-class MockServicesStatusObserver : public ServicesStatusObserver {
- public:
-  MockServicesStatusObserver() = default;
-  MockServicesStatusObserver(const MockServicesStatusObserver&) = delete;
-  MockServicesStatusObserver& operator=(const MockServicesStatusObserver&) =
-      delete;
-  ~MockServicesStatusObserver() override = default;
-
-  // ServicesStatusObserver:
-  MOCK_METHOD(void, OnServicesStatusChanged, (ServicesStatus status));
-};
-
-}  // namespace
-
-class AssistantClientV1Test : public testing::Test {
- public:
-  AssistantClientV1Test() = default;
-  AssistantClientV1Test(const AssistantClientV1Test&) = delete;
-  AssistantClientV1Test& operator=(const AssistantClientV1Test&) = delete;
-  ~AssistantClientV1Test() override = default;
-
-  void SetUp() override {
-    auto assistant_manager = std::make_unique<AssistantManagerMock>();
-    assistant_client_ =
-        std::make_unique<AssistantClientV1>(std::move(assistant_manager));
-  }
-
-  // Start Libassistant services.
-  void StartServices(ServicesStatusObserver* observer) {
-    assistant_client_->StartServices(observer);
-  }
-
-  AssistantClientV1& v1_client() { return *assistant_client_; }
-
-  AssistantManagerMock& assistant_manager_mock() {
-    return *reinterpret_cast<AssistantManagerMock*>(
-        assistant_client_->assistant_manager());
-  }
-
- private:
-  base::test::SingleThreadTaskEnvironment environment_;
-  std::unique_ptr<AssistantClientV1> assistant_client_ = nullptr;
-};
-
-TEST_F(AssistantClientV1Test, ShouldNotifyServicesStarted) {
-  MockServicesStatusObserver services_status_observer;
-  StartServices(&services_status_observer);
-}
-
-TEST_F(AssistantClientV1Test, ShouldSetListeningEnabled) {
-  MockServicesStatusObserver services_status_observer;
-  StartServices(&services_status_observer);
-
-  EXPECT_CALL(assistant_manager_mock(), EnableListening(true));
-
-  v1_client().EnableListening(true);
-}
-
-TEST_F(AssistantClientV1Test, ShouldSetAuthenticationTokens) {
-  MockServicesStatusObserver services_status_observer;
-  StartServices(&services_status_observer);
-
-  const AssistantClient::AuthTokens expected = {{"user", "token"}};
-  EXPECT_CALL(assistant_manager_mock(), SetAuthTokens(expected));
-
-  v1_client().SetAuthenticationInfo(expected);
-}
-
-}  // namespace ash::libassistant
diff --git a/chromeos/ash/services/libassistant/grpc/utils/settings_utils.cc b/chromeos/ash/services/libassistant/grpc/utils/settings_utils.cc
index 7b241355..de65b40 100644
--- a/chromeos/ash/services/libassistant/grpc/utils/settings_utils.cc
+++ b/chromeos/ash/services/libassistant/grpc/utils/settings_utils.cc
@@ -16,68 +16,9 @@
 using ::assistant::api::GetAssistantSettingsResponse;
 using ::assistant::api::ResponseDetails;
 using ::assistant::api::UpdateAssistantSettingsResponse;
-using assistant_client::VoicelessResponse;
 
 }  // namespace
 
-GetAssistantSettingsResponse ToGetSettingsResponseProto(
-    const assistant_client::VoicelessResponse& voiceless_response) {
-  GetAssistantSettingsResponse proto;
-  auto* details = proto.mutable_response_details();
-  int status = static_cast<int>(voiceless_response.status);
-  if (ResponseDetails::Status_IsValid(status))
-    details->set_status(static_cast<ResponseDetails::Status>(status));
-
-  // Either a serialized |GetSettingsUiResponse| proto or an error message will
-  // be filled in upon success or failure of the GetAssistantSettingsRequest.
-  switch (voiceless_response.status) {
-    case VoicelessResponse::Status::SUCCESS:
-      proto.mutable_get_settings_ui_response()->ParseFromString(
-          voiceless_response.response_proto);
-      break;
-    case VoicelessResponse::Status::COMMUNICATION_ERROR:
-    case VoicelessResponse::Status::NO_RESPONSE_ERROR:
-    case VoicelessResponse::Status::DESERIALIZATION_ERROR:
-    case VoicelessResponse::Status::S3_ERROR:
-      // Sets the error message if the status falls in the category of failure.
-      details->set_error_message(voiceless_response.error_message);
-      break;
-  }
-
-  return proto;
-}
-
-UpdateAssistantSettingsResponse ToUpdateSettingsResponseProto(
-    const VoicelessResponse& voiceless_response) {
-  UpdateAssistantSettingsResponse proto;
-  auto* details = proto.mutable_response_details();
-  int status = static_cast<int>(voiceless_response.status);
-  if (ResponseDetails::Status_IsValid(status)) {
-    // We assume that VoicelessResponse::Status and ResponseDetails::Status is
-    // always synced.
-    details->set_status(static_cast<ResponseDetails::Status>(status));
-  }
-
-  // Either a serialized |UpdateSettingsUiResponse| proto or an error message
-  // will be filled in upon success or failure of the
-  // UpdateAssistantSettingsRequest.
-  switch (voiceless_response.status) {
-    case VoicelessResponse::Status::SUCCESS:
-      proto.mutable_update_settings_ui_response()->ParseFromString(
-          voiceless_response.response_proto);
-      break;
-    case VoicelessResponse::Status::COMMUNICATION_ERROR:
-    case VoicelessResponse::Status::NO_RESPONSE_ERROR:
-    case VoicelessResponse::Status::DESERIALIZATION_ERROR:
-    case VoicelessResponse::Status::S3_ERROR:
-      // Sets the error message if the status falls in the category of failure.
-      details->set_error_message(voiceless_response.error_message);
-      break;
-  }
-
-  return proto;
-}
-
 std::string UnwrapGetAssistantSettingsResponse(
     const GetAssistantSettingsResponse& response,
     bool include_header) {
diff --git a/chromeos/ash/services/libassistant/grpc/utils/settings_utils.h b/chromeos/ash/services/libassistant/grpc/utils/settings_utils.h
index 6888df7..6a7535f 100644
--- a/chromeos/ash/services/libassistant/grpc/utils/settings_utils.h
+++ b/chromeos/ash/services/libassistant/grpc/utils/settings_utils.h
@@ -7,22 +7,8 @@
 
 #include "chromeos/assistant/internal/proto/shared/proto/v2/config_settings_interface.pb.h"
 
-namespace assistant_client {
-struct VoicelessResponse;
-}  // namespace assistant_client
-
 namespace ash::libassistant {
 
-// Populates a |GetAssistantSettingsResponse| proto with corresponding fields
-// from a |VoicelessResponse| struct.
-::assistant::api::GetAssistantSettingsResponse ToGetSettingsResponseProto(
-    const assistant_client::VoicelessResponse& voiceless_response);
-
-// Populates an |UpdateAssistantSettingsResponse| proto with corresponding
-// fields from a |VoicelessResponse| struct.
-::assistant::api::UpdateAssistantSettingsResponse ToUpdateSettingsResponseProto(
-    const assistant_client::VoicelessResponse& voiceless_response);
-
 // Returns a serialized proto of |SettingsUi| by default (or a serialized proto
 // of |GetSettingsUiResponse| if |include_header| is true) if the response
 // status is ok, otherwise returns an empty string.
diff --git a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc
index c98efa15..c34a485ea 100644
--- a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc
+++ b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc
@@ -30,26 +30,6 @@
   return assistant_timers;
 }
 
-void ConvertAssistantTimerToProtoTimer(
-    const AssistantTimer& input,
-    ::assistant::api::params::Timer* output) {
-  output->set_timer_id(input.id);
-  if (output->TimerStatus_IsValid(static_cast<int>(input.state))) {
-    output->set_status(
-        static_cast<::assistant::api::params::Timer::TimerStatus>(input.state));
-  }
-  output->set_original_duration(input.original_duration.InMilliseconds());
-
-  if (input.state == AssistantTimerState::kPaused) {
-    output->set_remaining_duration(input.remaining_time.InMilliseconds());
-  } else {
-    output->set_expire_time(
-        (input.fire_time - base::Time::UnixEpoch()).InMilliseconds());
-  }
-
-  output->set_label(input.label);
-}
-
 void ConvertProtoTimerToAssistantTimer(
     const ::assistant::api::params::Timer& input,
     AssistantTimer* output) {
diff --git a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h
index 8e648ea..6e34b9f 100644
--- a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h
+++ b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h
@@ -12,7 +12,6 @@
 namespace api {
 
 namespace params {
-enum class TimerStatus;
 class Timer;
 class TimerParams;
 }  // namespace params
@@ -26,9 +25,6 @@
 std::vector<assistant::AssistantTimer> ConstructAssistantTimersFromProto(
     const ::assistant::api::params::TimerParams& timer_params);
 
-void ConvertAssistantTimerToProtoTimer(const assistant::AssistantTimer& input,
-                                       ::assistant::api::params::Timer* output);
-
 void ConvertProtoTimerToAssistantTimer(
     const ::assistant::api::params::Timer& input,
     assistant::AssistantTimer* output);
diff --git a/chromeos/ash/services/libassistant/media_controller_unittest.cc b/chromeos/ash/services/libassistant/media_controller_unittest.cc
index 4c78a23..399c52b 100644
--- a/chromeos/ash/services/libassistant/media_controller_unittest.cc
+++ b/chromeos/ash/services/libassistant/media_controller_unittest.cc
@@ -123,9 +123,6 @@
   void SetUp() override {
     service_tester_.Start();
     service_tester_.assistant_manager().SetMediaManager(&media_manager_);
-    service_tester_.assistant_manager()
-        .device_state_listener()
-        ->OnStartFinished();
     media_controller_->OnAssistantClientRunning(&assistant_client());
   }
 
diff --git a/chromeos/ash/services/libassistant/test_support/fake_assistant_client.cc b/chromeos/ash/services/libassistant/test_support/fake_assistant_client.cc
index 9185a43..7f87332 100644
--- a/chromeos/ash/services/libassistant/test_support/fake_assistant_client.cc
+++ b/chromeos/ash/services/libassistant/test_support/fake_assistant_client.cc
@@ -110,8 +110,6 @@
   return assistant_manager()->GetDeviceId();
 }
 
-void FakeAssistantClient::SetDeviceAttributes(bool enable_dark_mode) {}
-
 void FakeAssistantClient::EnableListening(bool listening_enabled) {}
 
 void FakeAssistantClient::AddTimeToTimer(const std::string& id,
diff --git a/chromeos/ash/services/libassistant/test_support/fake_assistant_client.h b/chromeos/ash/services/libassistant/test_support/fake_assistant_client.h
index 55f1d0e..fd9af8b2 100644
--- a/chromeos/ash/services/libassistant/test_support/fake_assistant_client.h
+++ b/chromeos/ash/services/libassistant/test_support/fake_assistant_client.h
@@ -80,7 +80,6 @@
           void(const ::assistant::api::GetAssistantSettingsResponse&)> on_done)
       override;
   void SetLocaleOverride(const std::string& locale) override;
-  void SetDeviceAttributes(bool enable_dark_mode) override;
   std::string GetDeviceId() override;
   void EnableListening(bool listening_enabled) override;
   void AddTimeToTimer(const std::string& id,
diff --git a/chromeos/components/quick_answers/quick_answers_model.h b/chromeos/components/quick_answers/quick_answers_model.h
index 47824a2..eea8000 100644
--- a/chromeos/components/quick_answers/quick_answers_model.h
+++ b/chromeos/components/quick_answers/quick_answers_model.h
@@ -259,9 +259,8 @@
   TranslationResult& operator=(const TranslationResult& other);
   ~TranslationResult();
 
-  // TODO(b/278929409): Migrate to `std::string` for strings in structs.
-  std::u16string text_to_translate;
-  std::u16string translated_text;
+  std::string text_to_translate;
+  std::string translated_text;
   std::string source_locale;
   std::string target_locale;
 };
diff --git a/chromeos/components/quick_answers/translation_response_parser.cc b/chromeos/components/quick_answers/translation_response_parser.cc
index 06515589..a3d70e2 100644
--- a/chromeos/components/quick_answers/translation_response_parser.cc
+++ b/chromeos/components/quick_answers/translation_response_parser.cc
@@ -63,7 +63,7 @@
 
   std::unique_ptr<TranslationResult> translation_result =
       std::make_unique<TranslationResult>();
-  translation_result->translated_text = base::UTF8ToUTF16(translated_text);
+  translation_result->translated_text = translated_text;
   std::move(complete_callback_).Run(std::move(translation_result));
 }
 
diff --git a/chromeos/components/quick_answers/translation_response_parser_unittest.cc b/chromeos/components/quick_answers/translation_response_parser_unittest.cc
index 197da5c..5e9587cc 100644
--- a/chromeos/components/quick_answers/translation_response_parser_unittest.cc
+++ b/chromeos/components/quick_answers/translation_response_parser_unittest.cc
@@ -68,7 +68,7 @@
       std::make_unique<std::string>(kTranslationResponse));
   WaitForResponse();
   ASSERT_TRUE(translation_result_);
-  EXPECT_EQ(u"translated text", translation_result_->translated_text);
+  EXPECT_EQ("translated text", translation_result_->translated_text);
 }
 
 TEST_F(TranslationResponseParserTest,
@@ -89,7 +89,7 @@
   WaitForResponse();
   ASSERT_TRUE(translation_result_);
   // Should correctly unescape ampersand character codes.
-  EXPECT_EQ(u"don't mess with me", translation_result_->translated_text);
+  EXPECT_EQ("don't mess with me", translation_result_->translated_text);
 }
 
 TEST_F(TranslationResponseParserTest, ProcessResponseNoResults) {
diff --git a/chromeos/components/quick_answers/translation_result_loader.cc b/chromeos/components/quick_answers/translation_result_loader.cc
index 03b186954..92d0214 100644
--- a/chromeos/components/quick_answers/translation_result_loader.cc
+++ b/chromeos/components/quick_answers/translation_result_loader.cc
@@ -111,8 +111,7 @@
     return;
   }
 
-  translation_result->text_to_translate =
-      base::UTF8ToUTF16(intent_info.intent_text);
+  translation_result->text_to_translate = intent_info.intent_text;
   translation_result->source_locale = intent_info.source_language;
   translation_result->target_locale = intent_info.device_language;
 
@@ -122,7 +121,7 @@
       BuildTranslationTitleText(intent_info)));
   quick_answer->first_answer_row.push_back(
       std::make_unique<QuickAnswerResultText>(
-          base::UTF16ToUTF8(translation_result->translated_text)));
+          translation_result->translated_text));
 
   std::unique_ptr<QuickAnswersSession> session =
       std::make_unique<QuickAnswersSession>();
diff --git a/chromeos/components/quick_answers/translation_result_loader_unittest.cc b/chromeos/components/quick_answers/translation_result_loader_unittest.cc
index 0d6224a..4b4f18b 100644
--- a/chromeos/components/quick_answers/translation_result_loader_unittest.cc
+++ b/chromeos/components/quick_answers/translation_result_loader_unittest.cc
@@ -125,9 +125,8 @@
   raw_ptr<TranslationResult> translation_result =
       session->structured_result->translation_result.get();
   EXPECT_EQ(kTestTranslationIntent.intent_text,
-            base::UTF16ToUTF8(translation_result->text_to_translate));
-  EXPECT_EQ(kTestTranslationResult,
-            base::UTF16ToUTF8(translation_result->translated_text));
+            translation_result->text_to_translate);
+  EXPECT_EQ(kTestTranslationResult, translation_result->translated_text);
   EXPECT_EQ(kTestTranslationIntent.device_language,
             translation_result->target_locale);
   EXPECT_EQ(kTestTranslationIntent.source_language,
diff --git a/chromeos/components/quick_answers/utils/quick_answers_utils.cc b/chromeos/components/quick_answers/utils/quick_answers_utils.cc
index 6acee0dd..f51bbfdd 100644
--- a/chromeos/components/quick_answers/utils/quick_answers_utils.cc
+++ b/chromeos/components/quick_answers/utils/quick_answers_utils.cc
@@ -93,13 +93,6 @@
                                    locale_name);
 }
 
-std::string BuildTranslationTitleText(const std::string& query_text,
-                                      const std::string& locale_name) {
-  return l10n_util::GetStringFUTF8(IDS_QUICK_ANSWERS_TRANSLATION_TITLE_TEXT,
-                                   base::UTF8ToUTF16(query_text),
-                                   base::UTF8ToUTF16(locale_name));
-}
-
 std::string BuildUnitConversionResultText(const std::string& result_value,
                                           const std::string& name) {
   return l10n_util::GetStringFUTF8(
diff --git a/chromeos/components/quick_answers/utils/quick_answers_utils.h b/chromeos/components/quick_answers/utils/quick_answers_utils.h
index 8f2ddde..4732d5f 100644
--- a/chromeos/components/quick_answers/utils/quick_answers_utils.h
+++ b/chromeos/components/quick_answers/utils/quick_answers_utils.h
@@ -24,10 +24,6 @@
 // Build title text for Quick Answers translation result.
 std::string BuildTranslationTitleText(const IntentInfo& intent_info);
 
-// Build title text for Quick Answers translation result.
-std::string BuildTranslationTitleText(const std::string& query_text,
-                                      const std::string& locale_name);
-
 // Build display text for Quick Answers unit conversion result.
 std::string BuildUnitConversionResultText(const std::string& result_value,
                                           const std::string& name);
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index 1624acfe..418613a 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -66,6 +66,7 @@
     "keystore_error.mojom",
     "keystore_service.mojom",
     "kiosk_session_service.mojom",
+    "lacros_shelf_item_tracker.mojom",
     "launcher_search.mojom",
     "local_printer.mojom",
     "login.mojom",
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index f68b9c5..7a4ab63 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -61,6 +61,7 @@
 import "chromeos/crosapi/mojom/image_writer.mojom";
 import "chromeos/crosapi/mojom/kerberos_in_browser.mojom";
 import "chromeos/crosapi/mojom/keystore_service.mojom";
+import "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom";
 import "chromeos/crosapi/mojom/launcher_search.mojom";
 import "chromeos/crosapi/mojom/local_printer.mojom";
 import "chromeos/crosapi/mojom/login.mojom";
@@ -152,8 +153,8 @@
 // please note the milestone when you added it, to help us reason about
 // compatibility between the client applications and older ash-chrome binaries.
 //
-// Next version: 126
-// Next method id: 128
+// Next version: 127
+// Next method id: 129
 [Stable, Uuid="8b79c34f-2bf8-4499-979a-b17cac522c1e",
  RenamedFrom="crosapi.mojom.AshChromeService"]
 interface Crosapi {
@@ -463,6 +464,12 @@
   // Added in M87.
   BindKeystoreService@2(pending_receiver<KeystoreService> receiver);
 
+  // Binds the LacrosShelfItemTracker interface for integrating ephemeral Lacros
+  // Windows with Ash Shelf.
+  [MinVersion=126]
+  BindLacrosShelfItemTracker@128(
+      pending_receiver<LacrosShelfItemTracker> receiver);
+
   // Binds the Lacros app publisher service, which allows Lacros to connect the
   // Lacros app to the app service.
   // Added in M118.
diff --git a/chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom b/chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom
new file mode 100644
index 0000000..7fbe449
--- /dev/null
+++ b/chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom
@@ -0,0 +1,44 @@
+// 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.
+
+module crosapi.mojom;
+
+import "ui/gfx/image/mojom/image.mojom";
+
+[Stable, Extensible]
+enum InstanceType {
+  [Default] kIsolatedWebAppInstaller,
+};
+
+
+[Stable]
+struct WindowData {
+  // The key of the Shelf item. The caller is responsible for defining a unique
+  // ID for each Shelf item they want to add. If a Shelf item with the |item_id|
+  // already exists, the existing Shelf item will be updated.
+  string item_id@0;
+
+  // Unique wayland ID for the window.
+  string window_id@1;
+
+  // Type of the instance that owns the window, used to create the associated
+  // ShelfItemDelegate.
+  InstanceType instance_type@2;
+
+  // The Shelf item's icon.
+  gfx.mojom.ImageSkia? icon@3;
+};
+
+
+// Implemented in ash-chrome, called by lacros-chrome. This interface is used by
+// Lacros to integrate its windows with Ash shelf.
+[Stable, Uuid="ae203cbb-22b8-4c26-8a3d-2e01f73aa94d"]
+interface LacrosShelfItemTracker{
+
+  // Adds or updates the window to the tracker. Tracker will decide whether to
+  // add a new item to the Shelf depending on the |item_id|.
+  AddOrUpdateWindow@0(WindowData window_data);
+
+};
+
diff --git a/chromeos/lacros/lacros_service.cc b/chromeos/lacros/lacros_service.cc
index aaaa1d2..6b78e4ff 100644
--- a/chromeos/lacros/lacros_service.cc
+++ b/chromeos/lacros/lacros_service.cc
@@ -73,6 +73,7 @@
 #include "chromeos/crosapi/mojom/kerberos_in_browser.mojom.h"
 #include "chromeos/crosapi/mojom/keystore_service.mojom.h"
 #include "chromeos/crosapi/mojom/kiosk_session_service.mojom.h"
+#include "chromeos/crosapi/mojom/lacros_shelf_item_tracker.mojom.h"
 #include "chromeos/crosapi/mojom/launcher_search.mojom.h"
 #include "chromeos/crosapi/mojom/local_printer.mojom.h"
 #include "chromeos/crosapi/mojom/login.mojom.h"
@@ -444,6 +445,10 @@
                   &Crosapi::BindDeviceLocalAccountExtensionService,
                   Crosapi::MethodMinVersions::
                       kBindDeviceLocalAccountExtensionServiceMinVersion>();
+  ConstructRemote<
+      crosapi::mojom::LacrosShelfItemTracker,
+      &crosapi::mojom::Crosapi::BindLacrosShelfItemTracker,
+      Crosapi::MethodMinVersions::kBindLacrosShelfItemTrackerMinVersion>();
   ConstructRemote<crosapi::mojom::LocalPrinter,
                   &crosapi::mojom::Crosapi::BindLocalPrinter,
                   Crosapi::MethodMinVersions::kBindLocalPrinterMinVersion>();
diff --git a/clank b/clank
index 6569ea8..a60e161 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 6569ea82c7d827a0bc59fbd91829ff57f424d301
+Subproject commit a60e161b8d92adf6d1a44622e66d17ff3b52e6e5
diff --git a/components/compose/core/browser/config.h b/components/compose/core/browser/config.h
index 16e8a306..5eadfa4 100644
--- a/components/compose/core/browser/config.h
+++ b/components/compose/core/browser/config.h
@@ -20,7 +20,7 @@
   unsigned int inner_text_max_bytes = 1024 * 1024;
   // Whether to send a compose when the dialog is first opened,
   // if there is an acceptable input text selected.
-  bool auto_submit_with_selection = true;
+  bool auto_submit_with_selection = false;
   // If nudging is enabled, show the popup when focus appears on a field with no
   // saved state.
   bool popup_with_no_saved_state = false;
diff --git a/components/exo/keyboard_unittest.cc b/components/exo/keyboard_unittest.cc
index a6bade9..b0e3c60 100644
--- a/components/exo/keyboard_unittest.cc
+++ b/components/exo/keyboard_unittest.cc
@@ -5,7 +5,7 @@
 #include "components/exo/keyboard.h"
 
 #include "ash/accelerators/accelerator_controller_impl.h"
-#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_controller.h"
 #include "ash/constants/app_types.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
diff --git a/components/exo/shell_surface.cc b/components/exo/shell_surface.cc
index b600dea..7a23ad9 100644
--- a/components/exo/shell_surface.cc
+++ b/components/exo/shell_surface.cc
@@ -51,6 +51,9 @@
 // change.
 constexpr int kDefaultCompositorLockTimeoutMs = 100;
 
+// Compositor lock timeout for slower changes (e.g. display scale change).
+constexpr int kSlowCompositorLockTimeoutMs = 500;
+
 gfx::Rect GetClientBoundsInScreen(views::Widget* widget) {
   gfx::Rect window_bounds = widget->GetWindowBoundsInScreen();
   // Account for popup windows not having a non-client view.
@@ -605,8 +608,23 @@
 
     // A window state change will send a configuration event. Avoid sending
     // two configuration events for the same change.
-    if (!window_state_is_changing_)
+    if (!window_state_is_changing_) {
+      if (!configure_callback_.is_null()) {
+        // Lock when the display scale changes and we are a maximized window to
+        // prevent flashes.
+        if (reason != ui::PropertyChangeReason::FROM_ANIMATION &&
+            ash::WindowState::Get(window)->IsMaximizedOrFullscreenOrPinned()) {
+          ui::Compositor* compositor =
+              widget_->GetNativeWindow()->layer()->GetCompositor();
+          // TODO(crbug.com/1399478): See if we can rid of the slow lock timeout
+          // by adjusting the order of resize of windows to top to bottom.
+          configure_compositor_lock_ = compositor->GetCompositorLock(
+              nullptr, base::Milliseconds(kSlowCompositorLockTimeoutMs));
+        }
+      }
+
       Configure();
+    }
   }
 }
 
diff --git a/components/exo/shell_surface_unittest.cc b/components/exo/shell_surface_unittest.cc
index f65dc8b..98fc7733 100644
--- a/components/exo/shell_surface_unittest.cc
+++ b/components/exo/shell_surface_unittest.cc
@@ -4275,4 +4275,72 @@
             shell_surface->host_window()->GetSurfaceId());
 }
 
+TEST_F(ShellSurfaceTest,
+       DisplayScaleChangeTakesCompositorLockForMaximizedWindow) {
+  std::unique_ptr<ShellSurface> shell_surface =
+      test::ShellSurfaceBuilder({256, 256})
+          .SetWindowState(chromeos::WindowStateType::kMaximized)
+          .BuildShellSurface();
+  auto* surface = shell_surface->root_surface();
+
+  uint32_t serial = 0;
+  auto configure_callback = base::BindRepeating(
+      [](uint32_t* const serial_ptr, const gfx::Rect& bounds,
+         chromeos::WindowStateType state_type, bool resizing, bool activated,
+         const gfx::Vector2d& origin_offset, float raster_scale,
+         std::optional<chromeos::WindowStateType>) { return ++(*serial_ptr); },
+      &serial);
+  shell_surface->set_configure_callback(configure_callback);
+
+  ui::Compositor* compositor =
+      shell_surface->GetWidget()->GetNativeWindow()->layer()->GetCompositor();
+  EXPECT_FALSE(compositor->IsLocked());
+
+  auto* display_manager = ash::Shell::Get()->display_manager();
+  const auto display_id = display_manager->GetDisplayAt(0).id();
+  display_manager->ZoomDisplay(display_id, /*up=*/true);
+
+  // Compositor locked until maximized window updates.
+  EXPECT_TRUE(compositor->IsLocked());
+
+  shell_surface->AcknowledgeConfigure(serial);
+  EXPECT_TRUE(compositor->IsLocked());
+
+  surface->Commit();
+  EXPECT_FALSE(compositor->IsLocked());
+
+  display_manager->ZoomDisplay(display_id, /*up=*/false);
+
+  // Compositor locked until maximized window updates.
+  EXPECT_TRUE(compositor->IsLocked());
+
+  shell_surface->AcknowledgeConfigure(serial);
+  EXPECT_TRUE(compositor->IsLocked());
+
+  surface->Commit();
+  EXPECT_FALSE(compositor->IsLocked());
+}
+
+TEST_F(ShellSurfaceTest,
+       DisplayScaleChangeDoesNotTakeCompositorLockForFreeformWindow) {
+  std::unique_ptr<ShellSurface> shell_surface =
+      test::ShellSurfaceBuilder({256, 256}).BuildShellSurface();
+
+  ui::Compositor* compositor =
+      shell_surface->GetWidget()->GetNativeWindow()->layer()->GetCompositor();
+  EXPECT_FALSE(compositor->IsLocked());
+
+  auto* display_manager = ash::Shell::Get()->display_manager();
+  const auto display_id = display_manager->GetDisplayAt(0).id();
+  display_manager->ZoomDisplay(display_id, /*up=*/true);
+
+  // Should not take compositor lock.
+  EXPECT_FALSE(compositor->IsLocked());
+
+  display_manager->ZoomDisplay(display_id, /*up=*/false);
+
+  // Should not take compositor lock.
+  EXPECT_FALSE(compositor->IsLocked());
+}
+
 }  // namespace exo
diff --git a/components/media_router/common/providers/cast/channel/cast_message_handler.cc b/components/media_router/common/providers/cast/channel/cast_message_handler.cc
index b5fdea1..865cd5ac 100644
--- a/components/media_router/common/providers/cast/channel/cast_message_handler.cc
+++ b/components/media_router/common/providers/cast/channel/cast_message_handler.cc
@@ -128,21 +128,28 @@
   }
 
   VirtualConnection connection(socket->id(), source_id, destination_id);
-  if (virtual_connections_.find(connection) == virtual_connections_.end())
+  if (virtual_connections_.find(connection) == virtual_connections_.end()) {
     return;
-
+  }
   VLOG(1) << "Closing VC for channel: " << connection.channel_id
           << ", source: " << connection.source_id
           << ", dest: " << connection.destination_id;
+  // Assume the virtual connection close will succeed.  Eventually the receiver
+  // will remove the connection even if it doesn't succeed.
   socket->transport()->SendMessage(
       CreateVirtualConnectionClose(connection.source_id,
                                    connection.destination_id),
       base::BindOnce(&CastMessageHandler::OnMessageSent,
                      weak_ptr_factory_.GetWeakPtr()));
 
-  // Assume the virtual connection close will succeed.  Eventually the receiver
-  // will remove the connection even if it doesn't.
-  virtual_connections_.erase(connection);
+  RemoveConnection(channel_id, source_id, destination_id);
+}
+
+void CastMessageHandler::RemoveConnection(int channel_id,
+                                          const std::string& source_id,
+                                          const std::string& destination_id) {
+  virtual_connections_.erase(
+      VirtualConnection(channel_id, source_id, destination_id));
 }
 
 CastMessageHandler::PendingRequests*
diff --git a/components/media_router/common/providers/cast/channel/cast_message_handler.h b/components/media_router/common/providers/cast/channel/cast_message_handler.h
index c1bff090..a518e142 100644
--- a/components/media_router/common/providers/cast/channel/cast_message_handler.h
+++ b/components/media_router/common/providers/cast/channel/cast_message_handler.h
@@ -202,6 +202,13 @@
                                const std::string& source_id,
                                const std::string& destination_id);
 
+  // Removes the virtual connection on (|source_id|, |destination_id|) on the
+  // device given by |channel_id| without sending a close request. Call
+  // CloseConnection() instead to close and then remove a connection.
+  virtual void RemoveConnection(int channel_id,
+                                const std::string& source_id,
+                                const std::string& destination_id);
+
   // Sends an app availability for |app_id| to the device given by |socket|.
   // |callback| is always invoked asynchronously, and will be invoked when a
   // response is received, or if the request timed out. No-ops if there is
diff --git a/components/media_router/common/providers/cast/channel/cast_message_handler_unittest.cc b/components/media_router/common/providers/cast/channel/cast_message_handler_unittest.cc
index b58d662..a773b53 100644
--- a/components/media_router/common/providers/cast/channel/cast_message_handler_unittest.cc
+++ b/components/media_router/common/providers/cast/channel/cast_message_handler_unittest.cc
@@ -421,6 +421,19 @@
                             VirtualConnectionType::kStrong);
 }
 
+TEST_F(CastMessageHandlerTest, RemoveConnection) {
+  ExpectEnsureConnection();
+  handler_.EnsureConnection(channel_id_, kSourceId, kDestinationId,
+                            VirtualConnectionType::kStrong);
+
+  // Just removing a connection shouldn't send out a close request.
+  EXPECT_CALL(
+      *transport_,
+      SendMessage_(HasMessageType(CastMessageType::kCloseConnection), _))
+      .Times(0);
+  handler_.RemoveConnection(channel_id_, kSourceId, kDestinationId);
+}
+
 TEST_F(CastMessageHandlerTest, LaunchSession) {
   base::HistogramTester histogram_tester;
   cast_socket_.SetFlags(
diff --git a/components/media_router/common/providers/cast/channel/cast_test_util.h b/components/media_router/common/providers/cast/channel/cast_test_util.h
index 2bee0c2..7af4914 100644
--- a/components/media_router/common/providers/cast/channel/cast_test_util.h
+++ b/components/media_router/common/providers/cast/channel/cast_test_util.h
@@ -209,6 +209,10 @@
               (int, const std::string&, const std::string&),
               (override));
   MOCK_METHOD(void,
+              RemoveConnection,
+              (int, const std::string&, const std::string&),
+              (override));
+  MOCK_METHOD(void,
               RequestAppAvailability,
               (CastSocket * socket,
                const std::string& app_id,
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 95744c5..8359531 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -2313,7 +2313,12 @@
   bool is_open_tab_match =
       match.from_keyword &&
       match.provider->type() == AutocompleteProvider::TYPE_OPEN_TAB;
-  if (is_open_tab_match) {
+  // Also switch the window disposition for tab switch actions. The action
+  // itself will already open with SWITCH_TO_TAB disposition, but the change
+  // is needed earlier for metrics.
+  bool is_tab_switch_action =
+      action && action->ActionId() == OmniboxActionId::TAB_SWITCH;
+  if (is_open_tab_match || is_tab_switch_action) {
     disposition = WindowOpenDisposition::SWITCH_TO_TAB;
   }
 
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index b24d334..379a082 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit b24d334c6bbe2a5fbc7681a6a8b53a43698cf79e
+Subproject commit 379a082a7632ea69ede7b89fb2a9ff0b6899e148
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index ec9aa7c..2837bec 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -58,7 +58,6 @@
           std::make_unique<FieldTrialRecorder>(field_trial_register_.get())),
       profile_prefs_(init_params->profile_prefs.get()),
       creation_time_(clock_->Now()) {
-  stats::BackgroundUmaRecorder::GetInstance().Initialize();
   base::UmaHistogramMediumTimes(
       "SegmentationPlatform.Init.ProcessCreationToServiceCreationLatency",
       base::SysInfo::Uptime());
diff --git a/components/segmentation_platform/internal/stats_unittest.cc b/components/segmentation_platform/internal/stats_unittest.cc
index f888eb9..663a0f9 100644
--- a/components/segmentation_platform/internal/stats_unittest.cc
+++ b/components/segmentation_platform/internal/stats_unittest.cc
@@ -382,7 +382,9 @@
   base::test::TaskEnvironment task_env_;
 };
 
-TEST_F(BackgroundRecorderTest, Blah) {
+// TODO(ssid): This test may cause delay in the test framework, fix and re-enable the test.
+// Tracking: b/299529800.
+TEST_F(BackgroundRecorderTest, DISABLED_Recording) {
   BackgroundUmaRecorder& recorder = BackgroundUmaRecorder::GetInstance();
   recorder.InitializeForTesting(task_env_.GetMainThreadTaskRunner());
   int counter = 0;
diff --git a/components/translate/content/browser/partial_translate_manager.cc b/components/translate/content/browser/partial_translate_manager.cc
index f0002b41..4b83f4a 100644
--- a/components/translate/content/browser/partial_translate_manager.cc
+++ b/components/translate/content/browser/partial_translate_manager.cc
@@ -30,7 +30,7 @@
 
 void PartialTranslateManager::StartPartialTranslate(
     content::WebContents* web_contents,
-    PartialTranslateRequest request,
+    const PartialTranslateRequest& request,
     PartialTranslateCallback callback) {
   // Invalidate any ongoing request.
   weak_ptr_factory_.InvalidateWeakPtrs();
diff --git a/components/translate/content/browser/partial_translate_manager.h b/components/translate/content/browser/partial_translate_manager.h
index a6841f9..a324471 100644
--- a/components/translate/content/browser/partial_translate_manager.h
+++ b/components/translate/content/browser/partial_translate_manager.h
@@ -40,7 +40,7 @@
   // Whether or not |source_language| should be applied as a hint for backend
   // language detection. Otherwise, backend translation is forced using
   // |source_language|.
-  bool apply_lang_hint;
+  bool apply_lang_hint = false;
 };
 
 // Indicates the outcome of a Partial Translate request.
@@ -87,7 +87,7 @@
   // call |callback| once the request is completed (unless another request
   // subsumes it).
   void StartPartialTranslate(content::WebContents* web_contents,
-                             PartialTranslateRequest request,
+                             const PartialTranslateRequest& request,
                              PartialTranslateCallback callback);
 
  private:
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index f7bbdea4..5f65516 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -174,7 +174,14 @@
 
 }  // namespace
 
-class ServiceWorkerJobTest : public testing::Test {
+enum class StorageKeyTestCase {
+  kFirstParty,
+  kThirdParty,
+};
+
+class ServiceWorkerJobTest
+    : public testing::Test,
+      public testing::WithParamInterface<StorageKeyTestCase> {
  public:
   ServiceWorkerJobTest()
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME,
@@ -193,6 +200,26 @@
   }
   ServiceWorkerRegistry* registry() const { return context()->registry(); }
 
+  bool UseFirstPartyStorageKey() {
+    return GetParam() == StorageKeyTestCase::kFirstParty;
+  }
+
+  blink::StorageKey GetTestStorageKey(const GURL& scope_url) {
+    auto scope_origin = url::Origin::Create(scope_url);
+    if (UseFirstPartyStorageKey()) {
+      return blink::StorageKey::CreateFirstParty(std::move(scope_origin));
+    } else {
+      // For simplicity create a third-party storage key by setting the ancestor
+      // chain bit to kCrossSite.
+      auto storage_key = blink::StorageKey::Create(
+          scope_origin, net::SchemefulSite(scope_origin),
+          blink::mojom::AncestorChainBit::kCrossSite,
+          /*third_party_partitioning_allowed=*/true);
+      EXPECT_TRUE(storage_key.IsThirdPartyContext());
+      return storage_key;
+    }
+  }
+
  protected:
   scoped_refptr<ServiceWorkerRegistration> RunRegisterJob(
       const GURL& script_url,
@@ -305,11 +332,10 @@
                                                        const GURL& scope_url) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = scope_url;
+  blink::StorageKey storage_key = GetTestStorageKey(scope_url);
 
-  scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
-      script_url,
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(scope_url)),
-      options);
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      RunRegisterJob(script_url, storage_key, options);
 
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
   registration->SetTaskRunnerForTest(runner);
@@ -318,19 +344,31 @@
   observer.RunUntilActivated(registration->installing_version(), runner);
 
   ServiceWorkerContainerHost* container_host = CreateControllee();
-  container_host->UpdateUrls(
-      scope_url, url::Origin::Create(scope_url),
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(scope_url)));
+  container_host->UpdateUrls(scope_url, url::Origin::Create(scope_url),
+                             storage_key);
   container_host->SetControllerRegistration(registration,
                                             /*notify_controllerchange=*/false);
   return registration;
 }
 
-TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) {
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ServiceWorkerJobTest,
+    testing::ValuesIn({StorageKeyTestCase::kFirstParty,
+                       StorageKeyTestCase::kThirdParty}),
+    [](const testing::TestParamInfo<StorageKeyTestCase>& info) {
+      switch (info.param) {
+        case (StorageKeyTestCase::kFirstParty):
+          return "FirstPartyStorageKey";
+        case (StorageKeyTestCase::kThirdParty):
+          return "ThirdPartyStorageKey";
+      }
+    });
+
+TEST_P(ServiceWorkerJobTest, SameDocumentSameRegistration) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   GURL url("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(url));
+  const blink::StorageKey key = GetTestStorageKey(url);
 
   options.scope = url;
   scoped_refptr<ServiceWorkerRegistration> original_registration =
@@ -355,11 +393,10 @@
   ASSERT_EQ(registration1, registration2);
 }
 
-TEST_F(ServiceWorkerJobTest, SameMatchSameRegistration) {
+TEST_P(ServiceWorkerJobTest, SameMatchSameRegistration) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   GURL url("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(url));
+  const blink::StorageKey key = GetTestStorageKey(url);
 
   options.scope = url;
   scoped_refptr<ServiceWorkerRegistration> original_registration =
@@ -389,12 +426,11 @@
   ASSERT_EQ(registration1, registration2);
 }
 
-TEST_F(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) {
+TEST_P(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) {
   const GURL scope1("https://www.example.com/one");
   const GURL scope2("https://www.example.com/two");
   const GURL script_url("https://www.example.com/service_worker.js");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(script_url));
+  const blink::StorageKey key = GetTestStorageKey(script_url);
   blink::mojom::ServiceWorkerRegistrationOptions options1;
   options1.scope = scope1;
   blink::mojom::ServiceWorkerRegistrationOptions options2;
@@ -450,16 +486,15 @@
 };
 
 // Make sure basic registration is working.
-TEST_F(ServiceWorkerJobTest, Register) {
+TEST_P(ServiceWorkerJobTest, Register) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
   auto* worker =
       helper_->AddNewPendingServiceWorker<RecordInstallActivateWorker>(
           helper_.get());
-  scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
-      GURL("https://www.example.com/service_worker.js"),
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope)),
-      options);
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      RunRegisterJob(GURL("https://www.example.com/service_worker.js"),
+                     GetTestStorageKey(options.scope), options);
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
   registration->SetTaskRunnerForTest(runner);
   TestServiceWorkerObserver observer(helper_->context_wrapper());
@@ -473,11 +508,10 @@
 }
 
 // Make sure registrations are cleaned up when they are unregistered.
-TEST_F(ServiceWorkerJobTest, Unregister) {
+TEST_P(ServiceWorkerJobTest, Unregister) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), key, options);
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
@@ -518,15 +552,14 @@
   EXPECT_FALSE(registration);
 }
 
-TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) {
+TEST_P(ServiceWorkerJobTest, Unregister_NothingRegistered) {
   GURL scope("https://www.example.com/");
 
-  RunUnregisterJob(
-      scope, blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
-      blink::ServiceWorkerStatusCode::kErrorNotFound);
+  RunUnregisterJob(scope, GetTestStorageKey(scope),
+                   blink::ServiceWorkerStatusCode::kErrorNotFound);
 }
 
-TEST_F(ServiceWorkerJobTest, UnregisterImmediate) {
+TEST_P(ServiceWorkerJobTest, UnregisterImmediate) {
   const GURL scope("https://www.example.com/one/");
 
   scoped_refptr<ServiceWorkerRegistration> registration =
@@ -537,7 +570,7 @@
 
   base::RunLoop run_loop;
   job_coordinator()->Unregister(
-      scope, blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
+      scope, GetTestStorageKey(scope),
       /*is_immediate=*/true,
       SaveUnregistration(blink::ServiceWorkerStatusCode::kOk,
                          run_loop.QuitClosure()));
@@ -552,11 +585,10 @@
 
 // Make sure registering a new script creates a new version and shares an
 // existing registration.
-TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
+TEST_P(ServiceWorkerJobTest, RegisterNewScript) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> old_registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), key, options);
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
@@ -583,12 +615,11 @@
 
 // Make sure that when registering a duplicate scope+script_url
 // combination, that the same registration is used.
-TEST_F(ServiceWorkerJobTest, RegisterDuplicateScript) {
+TEST_P(ServiceWorkerJobTest, RegisterDuplicateScript) {
   GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   scoped_refptr<ServiceWorkerRegistration> old_registration =
       RunRegisterJob(script_url, key, options);
@@ -646,28 +677,27 @@
   }
 };
 
-TEST_F(ServiceWorkerJobTest, Register_FailToStartWorker) {
+TEST_P(ServiceWorkerJobTest, Register_FailToStartWorker) {
   helper_->AddPendingInstanceClient(
       std::make_unique<FailStartInstanceClient>(helper_.get()));
 
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
-      GURL("https://www.example.com/service_worker.js"),
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope)),
-      options, blink::ServiceWorkerStatusCode::kErrorNetwork);
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      RunRegisterJob(GURL("https://www.example.com/service_worker.js"),
+                     GetTestStorageKey(options.scope), options,
+                     blink::ServiceWorkerStatusCode::kErrorNetwork);
 
   ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(nullptr), registration);
 }
 
 // Register and then unregister the scope, in parallel. Job coordinator should
 // process jobs until the last job.
-TEST_F(ServiceWorkerJobTest, ParallelRegUnreg) {
+TEST_P(ServiceWorkerJobTest, ParallelRegUnreg) {
   GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> registration =
       RunRegisterJob(script_url, key, options);
 
@@ -682,10 +712,9 @@
 // Register conflicting scripts for the same scope. The most recent
 // registration should win, and the old registration should have been
 // shutdown.
-TEST_F(ServiceWorkerJobTest, ParallelRegNewScript) {
+TEST_P(ServiceWorkerJobTest, ParallelRegNewScript) {
   GURL scope("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(scope));
+  const blink::StorageKey key = GetTestStorageKey(scope);
 
   GURL script_url1("https://www.example.com/service_worker1.js");
   scoped_refptr<ServiceWorkerRegistration> registration1 =
@@ -710,11 +739,10 @@
 // Register the exact same scope + script. Requests should be
 // coalesced such that both callers get the exact same registration
 // object.
-TEST_F(ServiceWorkerJobTest, ParallelRegSameScript) {
+TEST_P(ServiceWorkerJobTest, ParallelRegSameScript) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   GURL script_url("https://www.example.com/service_worker1.js");
   scoped_refptr<ServiceWorkerRegistration> registration1 =
@@ -732,10 +760,9 @@
 }
 
 // Call simulataneous unregister calls.
-TEST_F(ServiceWorkerJobTest, ParallelUnreg) {
+TEST_P(ServiceWorkerJobTest, ParallelUnreg) {
   GURL scope("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(scope));
+  const blink::StorageKey key = GetTestStorageKey(scope);
 
   GURL script_url("https://www.example.com/service_worker.js");
   RunUnregisterJob(scope, key, blink::ServiceWorkerStatusCode::kErrorNotFound);
@@ -752,18 +779,16 @@
   ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
 }
 
-TEST_F(ServiceWorkerJobTest, AbortAll_Register) {
+TEST_P(ServiceWorkerJobTest, AbortAll_Register) {
   GURL script_url1("https://www1.example.com/service_worker.js");
   GURL script_url2("https://www2.example.com/service_worker.js");
 
   blink::mojom::ServiceWorkerRegistrationOptions options1;
   options1.scope = GURL("https://www1.example.com/");
-  const blink::StorageKey key1 =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options1.scope));
+  const blink::StorageKey key1 = GetTestStorageKey(options1.scope);
   blink::mojom::ServiceWorkerRegistrationOptions options2;
   options2.scope = GURL("https://www2.example.com/");
-  const blink::StorageKey key2 =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options2.scope));
+  const blink::StorageKey key2 = GetTestStorageKey(options2.scope);
 
   scoped_refptr<ServiceWorkerRegistration> registration1;
   base::RunLoop run_loop;
@@ -802,7 +827,7 @@
   EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration2);
 }
 
-TEST_F(ServiceWorkerJobTest, AbortAll_Unregister) {
+TEST_P(ServiceWorkerJobTest, AbortAll_Unregister) {
   GURL scope1("https://www1.example.com/");
   GURL scope2("https://www2.example.com/");
 
@@ -811,13 +836,13 @@
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
   job_coordinator()->Unregister(
-      scope1, blink::StorageKey::CreateFirstParty(url::Origin::Create(scope1)),
+      scope1, GetTestStorageKey(scope1),
       /*is_immediate=*/false,
       SaveUnregistration(blink::ServiceWorkerStatusCode::kErrorAbort,
                          barrier_closure));
 
   job_coordinator()->Unregister(
-      scope2, blink::StorageKey::CreateFirstParty(url::Origin::Create(scope2)),
+      scope2, GetTestStorageKey(scope2),
       /*is_immediate=*/false,
       SaveUnregistration(blink::ServiceWorkerStatusCode::kErrorAbort,
                          barrier_closure));
@@ -827,12 +852,11 @@
   run_loop.Run();
 }
 
-TEST_F(ServiceWorkerJobTest, AbortAll_RegUnreg) {
+TEST_P(ServiceWorkerJobTest, AbortAll_RegUnreg) {
   GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   scoped_refptr<ServiceWorkerRegistration> registration;
   base::RunLoop run_loop;
@@ -861,16 +885,14 @@
   EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
 }
 
-TEST_F(ServiceWorkerJobTest, AbortScope) {
+TEST_P(ServiceWorkerJobTest, AbortScope) {
   GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options1;
   options1.scope = GURL("https://www.example.com/1");
-  const blink::StorageKey key1 =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options1.scope));
+  const blink::StorageKey key1 = GetTestStorageKey(options1.scope);
   blink::mojom::ServiceWorkerRegistrationOptions options2;
   options2.scope = GURL("https://www.example.com/2");
-  const blink::StorageKey key2 =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options2.scope));
+  const blink::StorageKey key2 = GetTestStorageKey(options2.scope);
 
   scoped_refptr<ServiceWorkerRegistration> registration1;
   base::RunLoop run_loop;
@@ -910,12 +932,11 @@
 
 // Tests that the waiting worker enters the 'redundant' state upon
 // unregistration.
-TEST_F(ServiceWorkerJobTest, UnregisterWaitingSetsRedundant) {
+TEST_P(ServiceWorkerJobTest, UnregisterWaitingSetsRedundant) {
   GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> registration =
       RunRegisterJob(script_url, key, options);
   ASSERT_TRUE(registration.get());
@@ -949,11 +970,10 @@
 
 // Tests that the active worker enters the 'redundant' state upon
 // unregistration.
-TEST_F(ServiceWorkerJobTest, UnregisterActiveSetsRedundant) {
+TEST_P(ServiceWorkerJobTest, UnregisterActiveSetsRedundant) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), key, options);
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
@@ -979,12 +999,11 @@
 
 // Tests that the active worker enters the 'redundant' state upon
 // unregistration.
-TEST_F(ServiceWorkerJobTest,
+TEST_P(ServiceWorkerJobTest,
        UnregisterActiveSetsRedundant_WaitForNoControllee) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), key, options);
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
@@ -1015,12 +1034,11 @@
   EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, version->status());
 }
 
-TEST_F(ServiceWorkerJobTest, RegisterSameWhileUninstalling) {
+TEST_P(ServiceWorkerJobTest, RegisterSameWhileUninstalling) {
   GURL script("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       RunRegisterJob(script, key, options);
@@ -1058,12 +1076,11 @@
   EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, version->status());
 }
 
-TEST_F(ServiceWorkerJobTest, RegisterSameWhileUninstallingAndUnregister) {
+TEST_P(ServiceWorkerJobTest, RegisterSameWhileUninstallingAndUnregister) {
   GURL script("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       RunRegisterJob(script, key, options);
@@ -1111,13 +1128,12 @@
                     .size());
 }
 
-TEST_F(ServiceWorkerJobTest, RegisterWhileUninstalling) {
+TEST_P(ServiceWorkerJobTest, RegisterWhileUninstalling) {
   GURL script1("https://www.example.com/service_worker.js");
   GURL script2("https://www.example.com/service_worker.js?new");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   auto* initial_client =
       helper_->AddNewPendingInstanceClient<FakeEmbeddedWorkerInstanceClient>(
@@ -1170,13 +1186,12 @@
   EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, new_version->status());
 }
 
-TEST_F(ServiceWorkerJobTest, RegisterAndUnregisterWhileUninstalling) {
+TEST_P(ServiceWorkerJobTest, RegisterAndUnregisterWhileUninstalling) {
   GURL script1("https://www.example.com/service_worker.js");
   GURL script2("https://www.example.com/service_worker.js?new");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       RunRegisterJob(script1, key, options);
@@ -1235,13 +1250,12 @@
   EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, new_version->status());
 }
 
-TEST_F(ServiceWorkerJobTest, RegisterSameScriptMultipleTimesWhileUninstalling) {
+TEST_P(ServiceWorkerJobTest, RegisterSameScriptMultipleTimesWhileUninstalling) {
   GURL script1("https://www.example.com/service_worker.js");
   GURL script2("https://www.example.com/service_worker.js?new");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   auto* initial_client =
       helper_->AddNewPendingInstanceClient<FakeEmbeddedWorkerInstanceClient>(
@@ -1306,7 +1320,7 @@
 
 // Make sure that the new version is cleared up after trying to register a
 // script with bad origin. (see https://crbug.com/1312995)
-TEST_F(ServiceWorkerJobTest, RegisterBadOrigin) {
+TEST_P(ServiceWorkerJobTest, RegisterBadOrigin) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   // http fails the trustworthiness check.
   options.scope = GURL("http://www.example.com/");
@@ -1315,8 +1329,7 @@
   base::RunLoop run_loop;
   scoped_refptr<ServiceWorkerRegistration> registration;
   job_coordinator()->Register(
-      script_url, options,
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope)),
+      script_url, options, GetTestStorageKey(options.scope),
       blink::mojom::FetchClientSettingsObject::New(),
       /*requesting_frame_id=*/GlobalRenderFrameHostId(),
       /*ancestor_frame_type=*/blink::mojom::AncestorFrameType::kNormalFrame,
@@ -1370,12 +1383,11 @@
       blink::mojom::ServiceWorkerFetchHandlerType::kNoHandler;
 };
 
-TEST_F(ServiceWorkerJobTest, FetchHandlerType) {
+TEST_P(ServiceWorkerJobTest, FetchHandlerType) {
   GURL script("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   scoped_refptr<ServiceWorkerRegistration> registration;
 
   auto* fetch_handler_worker =
@@ -1408,16 +1420,15 @@
 // Test that clients are alerted of new registrations if they are
 // in-scope, so that Clients.claim() or ServiceWorkerContainer.ready work
 // correctly.
-TEST_F(ServiceWorkerJobTest, AddRegistrationToMatchingerHosts) {
+TEST_P(ServiceWorkerJobTest, AddRegistrationToMatchingerHosts) {
   GURL scope("https://www.example.com/scope/");
   GURL in_scope("https://www.example.com/scope/page");
   GURL out_scope("https://www.example.com/page");
 
   // Make an in-scope client.
   ServiceWorkerContainerHost* client = CreateControllee();
-  client->UpdateUrls(
-      in_scope, url::Origin::Create(in_scope),
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(in_scope)));
+  client->UpdateUrls(in_scope, url::Origin::Create(in_scope),
+                     GetTestStorageKey(in_scope));
 
   // Make an in-scope reserved client.
   std::unique_ptr<ServiceWorkerContainerHostAndInfo> host_and_info =
@@ -1425,24 +1436,20 @@
                                           /*are_ancestors_secure=*/true);
   base::WeakPtr<ServiceWorkerContainerHost> reserved_client =
       host_and_info->host;
-  reserved_client->UpdateUrls(
-      in_scope, url::Origin::Create(in_scope),
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(in_scope)));
+  reserved_client->UpdateUrls(in_scope, url::Origin::Create(in_scope),
+                              GetTestStorageKey(in_scope));
 
   // Make an out-scope client.
   ServiceWorkerContainerHost* out_scope_client = CreateControllee();
-  out_scope_client->UpdateUrls(
-      out_scope, url::Origin::Create(out_scope),
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(out_scope)));
+  out_scope_client->UpdateUrls(out_scope, url::Origin::Create(out_scope),
+                               GetTestStorageKey(out_scope));
 
   // Make a new registration.
   GURL script("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = scope;
-  scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
-      script,
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope)),
-      options);
+  scoped_refptr<ServiceWorkerRegistration> registration =
+      RunRegisterJob(script, GetTestStorageKey(options.scope), options);
 
   EXPECT_EQ(registration.get(), client->MatchRegistration());
   EXPECT_EQ(registration.get(), reserved_client->MatchRegistration());
@@ -1598,7 +1605,8 @@
   }
 
   scoped_refptr<ServiceWorkerRegistration> SetupInitialRegistration(
-      const GURL& test_origin) {
+      const GURL& test_origin,
+      const blink::StorageKey& test_storage_key) {
     blink::mojom::ServiceWorkerRegistrationOptions options;
     options.scope = test_origin.Resolve(kScope);
     scoped_refptr<ServiceWorkerRegistration> registration;
@@ -1608,8 +1616,7 @@
     AddPendingInstanceClient(std::move(client));
     base::RunLoop run_loop;
     job_coordinator()->Register(
-        test_origin.Resolve(kScript), options,
-        blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope)),
+        test_origin.Resolve(kScript), options, test_storage_key,
         blink::mojom::FetchClientSettingsObject::New(),
         /*requesting_frame_id=*/GlobalRenderFrameHostId(),
         /*ancestor_frame_type=*/blink::mojom::AncestorFrameType::kNormalFrame,
@@ -1754,15 +1761,28 @@
   raw_ptr<UpdateJobTestHelper> update_helper_;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ServiceWorkerUpdateJobTest,
+    testing::ValuesIn({StorageKeyTestCase::kFirstParty,
+                       StorageKeyTestCase::kThirdParty}),
+    [](const testing::TestParamInfo<StorageKeyTestCase>& info) {
+      switch (info.param) {
+        case (StorageKeyTestCase::kFirstParty):
+          return "FirstPartyStorageKey";
+        case (StorageKeyTestCase::kThirdParty):
+          return "ThirdPartyStorageKey";
+      }
+    });
+
 // Make sure that the same registration is used and the update_via_cache value
 // is updated when registering a service worker with the same parameter except
 // for updateViaCache.
-TEST_F(ServiceWorkerUpdateJobTest, RegisterWithDifferentUpdateViaCache) {
+TEST_P(ServiceWorkerUpdateJobTest, RegisterWithDifferentUpdateViaCache) {
   const GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   scoped_refptr<ServiceWorkerRegistration> old_registration =
       RunRegisterJob(script_url, key, options);
@@ -1806,9 +1826,10 @@
   EXPECT_EQ(new_registration_by_scope, old_registration);
 }
 
-TEST_F(ServiceWorkerUpdateJobTest, Update_NoChange) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_NoChange) {
   scoped_refptr<ServiceWorkerRegistration> registration =
-      update_helper_->SetupInitialRegistration(GURL(kNoChangeOrigin));
+      update_helper_->SetupInitialRegistration(
+          GURL(kNoChangeOrigin), GetTestStorageKey(GURL(kNoChangeOrigin)));
   ASSERT_TRUE(registration.get());
   ASSERT_EQ(4u, update_helper_->state_change_log_.size());
   EXPECT_EQ(ServiceWorkerVersion::INSTALLING,
@@ -1838,13 +1859,14 @@
   EXPECT_FALSE(update_helper_->update_found_);
 }
 
-TEST_F(ServiceWorkerUpdateJobTest, Update_BumpLastUpdateCheckTime) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_BumpLastUpdateCheckTime) {
   const base::Time kToday = base::Time::Now();
   const base::Time kYesterday = kToday - base::Days(1) - base::Hours(1);
   const GURL kNewVersionOrigin("https://newversion/");
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      update_helper_->SetupInitialRegistration(GURL(kNoChangeOrigin));
+      update_helper_->SetupInitialRegistration(
+          GURL(kNoChangeOrigin), GetTestStorageKey(GURL(kNoChangeOrigin)));
   ASSERT_TRUE(registration.get());
 
   registration->AddListener(update_helper_);
@@ -1880,7 +1902,8 @@
     EXPECT_LT(kYesterday, registration->last_update_check());
     EXPECT_FALSE(update_helper_->update_found_);
     registration->RemoveListener(update_helper_);
-    registration = update_helper_->SetupInitialRegistration(kNewVersionOrigin);
+    registration = update_helper_->SetupInitialRegistration(
+        kNewVersionOrigin, GetTestStorageKey(kNewVersionOrigin));
     ASSERT_TRUE(registration.get());
   }
 
@@ -1921,11 +1944,12 @@
   }
 }
 
-TEST_F(ServiceWorkerUpdateJobTest, Update_NewVersion) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_NewVersion) {
   const GURL kNewVersionOrigin("https://newversion/");
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      update_helper_->SetupInitialRegistration(kNewVersionOrigin);
+      update_helper_->SetupInitialRegistration(
+          kNewVersionOrigin, GetTestStorageKey(kNewVersionOrigin));
   ASSERT_TRUE(registration.get());
   update_helper_->state_change_log_.clear();
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
@@ -2047,15 +2071,14 @@
 
 // Test that the update job uses the script URL of the newest worker when the
 // job starts, rather than when it is scheduled.
-TEST_F(ServiceWorkerUpdateJobTest, Update_ScriptUrlChanged) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_ScriptUrlChanged) {
   const GURL old_script("https://www.example.com/service_worker.js");
   const GURL new_script("https://www.example.com/new_worker.js");
 
   // Create a registration with an active version.
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
   auto* initial_client =
       helper_->AddNewPendingInstanceClient<FakeEmbeddedWorkerInstanceClient>(
           helper_.get());
@@ -2123,11 +2146,12 @@
 
 // Test that update fails if the incumbent worker was evicted
 // during the update job (this can happen on disk cache failure).
-TEST_F(ServiceWorkerUpdateJobTest, Update_EvictedIncumbent) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_EvictedIncumbent) {
   const GURL kNewVersionOrigin("https://newversion/");
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      update_helper_->SetupInitialRegistration(kNewVersionOrigin);
+      update_helper_->SetupInitialRegistration(
+          kNewVersionOrigin, GetTestStorageKey(kNewVersionOrigin));
   ASSERT_TRUE(registration.get());
   update_helper_->state_change_log_.clear();
 
@@ -2161,7 +2185,7 @@
   EXPECT_TRUE(registration->is_uninstalled());
 }
 
-TEST_F(ServiceWorkerUpdateJobTest, Update_UninstallingRegistration) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_UninstallingRegistration) {
   const GURL scope("https://www.example.com/one/");
 
   // Create a registration with a controllee and queue an unregister to force
@@ -2174,7 +2198,7 @@
 
   base::RunLoop run_loop;
   job_coordinator()->Unregister(
-      scope, blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
+      scope, GetTestStorageKey(scope),
       /*is_immediate=*/false,
       SaveUnregistration(blink::ServiceWorkerStatusCode::kOk,
                          run_loop.QuitClosure()));
@@ -2193,14 +2217,13 @@
   EXPECT_EQ(nullptr, registration->installing_version());
 }
 
-TEST_F(ServiceWorkerUpdateJobTest, RegisterMultipleTimesWhileUninstalling) {
+TEST_P(ServiceWorkerUpdateJobTest, RegisterMultipleTimesWhileUninstalling) {
   GURL script1("https://www.example.com/service_worker.js?first");
   GURL script2("https://www.example.com/service_worker.js?second");
   GURL script3("https://www.example.com/service_worker.js?third");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   auto* initial_client =
       helper_->AddNewPendingInstanceClient<FakeEmbeddedWorkerInstanceClient>(
@@ -2259,12 +2282,11 @@
 
 // Test that activation doesn't complete if it's triggered by removing a
 // controllee and starting the worker failed due to shutdown.
-TEST_F(ServiceWorkerUpdateJobTest, ActivateCancelsOnShutdown) {
+TEST_P(ServiceWorkerUpdateJobTest, ActivateCancelsOnShutdown) {
   GURL script("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   auto* initial_client =
       helper_->AddNewPendingInstanceClient<FakeEmbeddedWorkerInstanceClient>(
@@ -2342,7 +2364,7 @@
 }
 
 // Update job should handle the COEP header appropriately.
-TEST_F(ServiceWorkerUpdateJobTest, Update_CrossOriginEmbedderPolicyValue) {
+TEST_P(ServiceWorkerUpdateJobTest, Update_CrossOriginEmbedderPolicyValue) {
   const GURL kNewVersionOrigin("https://newversion/");
   const char kHeadersWithRequireCorp[] = R"(HTTP/1.1 200 OK
 Content-Type: application/javascript
@@ -2359,7 +2381,8 @@
   const base::Time kYesterday = kToday - base::Days(1) - base::Hours(1);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      update_helper_->SetupInitialRegistration(kNewVersionOrigin);
+      update_helper_->SetupInitialRegistration(
+          kNewVersionOrigin, GetTestStorageKey(kNewVersionOrigin));
   ASSERT_TRUE(registration.get());
   // COEP is populated here because the worker's script is loaded as a part of
   // the start worker sequence before registration and the response header is
@@ -2435,11 +2458,10 @@
 };
 
 // Test that the job queue doesn't get stuck by bad workers.
-TEST_F(ServiceWorkerJobTest, TimeoutBadJobs) {
+TEST_P(ServiceWorkerJobTest, TimeoutBadJobs) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-  const blink::StorageKey key =
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(options.scope));
+  const blink::StorageKey key = GetTestStorageKey(options.scope);
 
   // Make a job that gets stuck due to a worker stalled in starting.
   base::RunLoop loop1;
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 8f23374d..9d10939 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -80,7 +80,14 @@
   return base::Time::Now() - base::Days(1) - base::Seconds(1);
 }
 
-class ServiceWorkerVersionTest : public testing::Test {
+enum class StorageKeyTestCase {
+  kFirstParty,
+  kThirdParty,
+};
+
+class ServiceWorkerVersionTest
+    : public testing::Test,
+      public testing::WithParamInterface<StorageKeyTestCase> {
  public:
   ServiceWorkerVersionTest(const ServiceWorkerVersionTest&) = delete;
   ServiceWorkerVersionTest& operator=(const ServiceWorkerVersionTest&) = delete;
@@ -109,8 +116,7 @@
     blink::mojom::ServiceWorkerRegistrationOptions options;
     options.scope = scope_;
     registration_ = CreateNewServiceWorkerRegistration(
-        helper_->context()->registry(), options,
-        blink::StorageKey::CreateFirstParty(url::Origin::Create(scope_)));
+        helper_->context()->registry(), options, GetTestStorageKey(scope_));
     version_ = CreateNewServiceWorkerVersion(
         helper_->context()->registry(), registration_.get(),
         GURL("https://www.example.com/test/service_worker.js"),
@@ -219,6 +225,26 @@
     return remote_endpoint;
   }
 
+  bool UseFirstPartyStorageKey() {
+    return GetParam() == StorageKeyTestCase::kFirstParty;
+  }
+
+  blink::StorageKey GetTestStorageKey(const GURL& scope_url) {
+    auto scope_origin = url::Origin::Create(scope_url);
+    if (UseFirstPartyStorageKey()) {
+      return blink::StorageKey::CreateFirstParty(std::move(scope_origin));
+    } else {
+      // For simplicity create a third-party storage key by setting the ancestor
+      // chain bit to kCrossSite.
+      auto storage_key = blink::StorageKey::Create(
+          scope_origin, net::SchemefulSite(scope_origin),
+          blink::mojom::AncestorChainBit::kCrossSite,
+          /*third_party_partitioning_allowed=*/true);
+      EXPECT_TRUE(storage_key.IsThirdPartyContext());
+      return storage_key;
+    }
+  }
+
   BrowserTaskEnvironment task_environment_;
   std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
   scoped_refptr<ServiceWorkerRegistration> registration_;
@@ -233,6 +259,20 @@
   base::SimpleTestTickClock tick_clock_;
 };
 
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ServiceWorkerVersionTest,
+    testing::ValuesIn({StorageKeyTestCase::kFirstParty,
+                       StorageKeyTestCase::kThirdParty}),
+    [](const testing::TestParamInfo<StorageKeyTestCase>& info) {
+      switch (info.param) {
+        case (StorageKeyTestCase::kFirstParty):
+          return "FirstPartyStorageKey";
+        case (StorageKeyTestCase::kThirdParty):
+          return "ThirdPartyStorageKey";
+      }
+    });
+
 // An instance client that breaks the Mojo connection upon receiving the
 // Start() message.
 class FailStartInstanceClient : public FakeEmbeddedWorkerInstanceClient {
@@ -248,7 +288,7 @@
   }
 };
 
-TEST_F(ServiceWorkerVersionTest, ConcurrentStartAndStop) {
+TEST_P(ServiceWorkerVersionTest, ConcurrentStartAndStop) {
   // Call StartWorker() multiple times.
   absl::optional<blink::ServiceWorkerStatusCode> status1;
   absl::optional<blink::ServiceWorkerStatusCode> status2;
@@ -357,7 +397,7 @@
   }
 }
 
-TEST_F(ServiceWorkerVersionTest, DispatchEventToStoppedWorker) {
+TEST_P(ServiceWorkerVersionTest, DispatchEventToStoppedWorker) {
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status());
 
   // Dispatch an event without starting the worker.
@@ -390,7 +430,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, StartUnregisteredButStillLiveWorker) {
+TEST_P(ServiceWorkerVersionTest, StartUnregisteredButStillLiveWorker) {
   // Start the worker.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
             StartServiceWorker(version_.get()));
@@ -424,7 +464,7 @@
       version_->version_id()));
 }
 
-TEST_F(ServiceWorkerVersionTest, InstallAndWaitCompletion) {
+TEST_P(ServiceWorkerVersionTest, InstallAndWaitCompletion) {
   version_->SetStatus(ServiceWorkerVersion::INSTALLING);
 
   // Wait for the completion.
@@ -442,7 +482,7 @@
       version_->version_id()));
 }
 
-TEST_F(ServiceWorkerVersionTest, ActivateAndWaitCompletion) {
+TEST_P(ServiceWorkerVersionTest, ActivateAndWaitCompletion) {
   // TODO(mek): This test (and the one above for the install event) made more
   // sense back when ServiceWorkerVersion was responsible for updating the
   // status. Now a better version of this test should probably be added to
@@ -465,7 +505,7 @@
       version_->version_id()));
 }
 
-TEST_F(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) {
+TEST_P(ServiceWorkerVersionTest, RepeatedlyObserveStatusChanges) {
   EXPECT_EQ(ServiceWorkerVersion::NEW, version_->status());
 
   // Repeatedly observe status changes (the callback re-registers itself).
@@ -488,7 +528,7 @@
   ASSERT_EQ(ServiceWorkerVersion::REDUNDANT, statuses[4]);
 }
 
-TEST_F(ServiceWorkerVersionTest, Doom) {
+TEST_P(ServiceWorkerVersionTest, Doom) {
   // Add a controllee.
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
@@ -525,7 +565,7 @@
   EXPECT_TRUE(version_->main_script_load_params_.is_null());
 }
 
-TEST_F(ServiceWorkerVersionTest, SetDevToolsAttached) {
+TEST_P(ServiceWorkerVersionTest, SetDevToolsAttached) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   version_->StartWorker(
@@ -562,7 +602,7 @@
 // if devtools is detached (after it is attached).
 //
 // Regression test for crbug.com/1152255#c144
-TEST_F(ServiceWorkerVersionTest, DevToolsAttachThenDetach) {
+TEST_P(ServiceWorkerVersionTest, DevToolsAttachThenDetach) {
   SetupTestTickClock();
   absl::optional<blink::ServiceWorkerStatusCode> status;
 
@@ -646,7 +686,7 @@
                               false /* expect_running */);
 }
 
-TEST_F(ServiceWorkerVersionTest, RequestTerminationWithDevToolsAttached) {
+TEST_P(ServiceWorkerVersionTest, RequestTerminationWithDevToolsAttached) {
   auto* service_worker =
       helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get());
 
@@ -676,7 +716,7 @@
 }
 
 // Test that update isn't triggered for a non-stale worker.
-TEST_F(ServiceWorkerVersionTest, StaleUpdate_FreshWorker) {
+TEST_P(ServiceWorkerVersionTest, StaleUpdate_FreshWorker) {
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
   registration_->set_last_update_check(base::Time::Now());
@@ -687,7 +727,7 @@
 }
 
 // Test that update isn't triggered for a non-active worker.
-TEST_F(ServiceWorkerVersionTest, StaleUpdate_NonActiveWorker) {
+TEST_P(ServiceWorkerVersionTest, StaleUpdate_NonActiveWorker) {
   version_->SetStatus(ServiceWorkerVersion::INSTALLING);
   registration_->SetInstallingVersion(version_);
   registration_->set_last_update_check(GetYesterday());
@@ -698,7 +738,7 @@
 }
 
 // Test that staleness is detected when starting a worker.
-TEST_F(ServiceWorkerVersionTest, StaleUpdate_StartWorker) {
+TEST_P(ServiceWorkerVersionTest, StaleUpdate_StartWorker) {
   // Starting the worker marks it as stale.
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
@@ -714,7 +754,7 @@
 }
 
 // Test that staleness is detected on a running worker.
-TEST_F(ServiceWorkerVersionTest, StaleUpdate_RunningWorker) {
+TEST_P(ServiceWorkerVersionTest, StaleUpdate_RunningWorker) {
   // Start a fresh worker.
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
@@ -739,7 +779,7 @@
 }
 
 // Test that a stream of events doesn't restart the timer.
-TEST_F(ServiceWorkerVersionTest, StaleUpdate_DoNotDeferTimer) {
+TEST_P(ServiceWorkerVersionTest, StaleUpdate_DoNotDeferTimer) {
   // Make a stale worker.
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetActiveVersion(version_);
@@ -772,7 +812,7 @@
   EXPECT_EQ(run_time, version_->update_timer_.desired_run_time());
 }
 
-TEST_F(ServiceWorkerVersionTest, StartRequestWithNullContext) {
+TEST_P(ServiceWorkerVersionTest, StartRequestWithNullContext) {
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
             StartServiceWorker(version_.get()));
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
@@ -784,7 +824,7 @@
 
 // Tests the delay mechanism for self-updating service workers, to prevent
 // them from running forever (see https://crbug.com/805496).
-TEST_F(ServiceWorkerVersionTest, ResetUpdateDelay) {
+TEST_P(ServiceWorkerVersionTest, ResetUpdateDelay) {
   const base::TimeDelta kMinute = base::Minutes(1);
   const base::TimeDelta kNoDelay = base::TimeDelta();
 
@@ -818,7 +858,7 @@
   EXPECT_EQ(kNoDelay, registration_->self_update_delay());
 }
 
-TEST_F(ServiceWorkerVersionTest, UpdateCachedMetadata) {
+TEST_P(ServiceWorkerVersionTest, UpdateCachedMetadata) {
   CachedMetadataUpdateListener listener;
   version_->AddObserver(&listener);
   ASSERT_EQ(0, listener.updated_count);
@@ -842,7 +882,7 @@
   version_->RemoveObserver(&listener);
 }
 
-TEST_F(ServiceWorkerVersionTest, RestartWorker) {
+TEST_P(ServiceWorkerVersionTest, RestartWorker) {
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
             StartServiceWorker(version_.get()));
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
@@ -911,7 +951,7 @@
   base::OnceClosure quit_closure_;
 };
 
-TEST_F(ServiceWorkerVersionTest, RequestTimeout) {
+TEST_P(ServiceWorkerVersionTest, RequestTimeout) {
   auto* client = helper_->AddNewPendingInstanceClient<
       DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get());
   auto* worker =
@@ -966,7 +1006,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, RequestNowTimeout) {
+TEST_P(ServiceWorkerVersionTest, RequestNowTimeout) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   auto* service_worker =
@@ -998,7 +1038,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, RequestNowTimeoutKill) {
+TEST_P(ServiceWorkerVersionTest, RequestNowTimeoutKill) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
@@ -1024,7 +1064,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, RequestCustomizedTimeout) {
+TEST_P(ServiceWorkerVersionTest, RequestCustomizedTimeout) {
   absl::optional<blink::ServiceWorkerStatusCode> first_status;
   absl::optional<blink::ServiceWorkerStatusCode> second_status;
   base::RunLoop first_run_loop;
@@ -1088,7 +1128,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, MixedRequestTimeouts) {
+TEST_P(ServiceWorkerVersionTest, MixedRequestTimeouts) {
   absl::optional<blink::ServiceWorkerStatusCode> sync_status;
   absl::optional<blink::ServiceWorkerStatusCode> fetch_status;
   base::RunLoop sync_run_loop;
@@ -1138,7 +1178,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kStopped, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, FailToStart_RendererCrash) {
+TEST_P(ServiceWorkerVersionTest, FailToStart_RendererCrash) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   auto* client = helper_->AddNewPendingInstanceClient<
@@ -1166,7 +1206,7 @@
       version_->version_id()));
 }
 
-TEST_F(ServiceWorkerVersionTest, FailToStart_Timeout) {
+TEST_P(ServiceWorkerVersionTest, FailToStart_Timeout) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
 
@@ -1201,7 +1241,7 @@
 
 // Test that a service worker stalled in stopping will timeout and not get in a
 // sticky error state.
-TEST_F(ServiceWorkerVersionTest, StallInStopping_DetachThenStart) {
+TEST_P(ServiceWorkerVersionTest, StallInStopping_DetachThenStart) {
   // Start a worker.
   auto* client = helper_->AddNewPendingInstanceClient<
       DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get());
@@ -1242,7 +1282,7 @@
 
 // Test that a service worker stalled in stopping with a start worker
 // request queued up will timeout and restart.
-TEST_F(ServiceWorkerVersionTest, StallInStopping_DetachThenRestart) {
+TEST_P(ServiceWorkerVersionTest, StallInStopping_DetachThenRestart) {
   // Start a worker.
   auto* client = helper_->AddNewPendingInstanceClient<
       DelayedFakeEmbeddedWorkerInstanceClient>(helper_.get());
@@ -1273,7 +1313,7 @@
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk, start_status.value());
 }
 
-TEST_F(ServiceWorkerVersionTest, RendererCrashDuringEvent) {
+TEST_P(ServiceWorkerVersionTest, RendererCrashDuringEvent) {
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
 
   auto* client =
@@ -1307,7 +1347,7 @@
   EXPECT_FALSE(version_->FinishRequest(request_id, /*was_handled=*/true));
 }
 
-TEST_F(ServiceWorkerVersionTest, PingController) {
+TEST_P(ServiceWorkerVersionTest, PingController) {
   // Start starting an worker. Ping should not be active.
   version_->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN,
                         base::DoNothing());
@@ -1324,15 +1364,14 @@
 }
 
 // Test starting a service worker from a disallowed origin.
-TEST_F(ServiceWorkerVersionTest, BadOrigin) {
+TEST_P(ServiceWorkerVersionTest, BadOrigin) {
   // An https URL would have been allowed but http is not trustworthy.
   const GURL scope("http://www.example.com/test/");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = scope;
 
   auto registration = CreateNewServiceWorkerRegistration(
-      helper_->context()->registry(), options,
-      blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)));
+      helper_->context()->registry(), options, GetTestStorageKey(scope));
   auto version = CreateNewServiceWorkerVersion(
       helper_->context()->registry(), registration_.get(),
       GURL("http://www.example.com/test/service_worker.js"),
@@ -1341,7 +1380,7 @@
             StartServiceWorker(version.get()));
 }
 
-TEST_F(ServiceWorkerVersionTest,
+TEST_P(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByControllee) {
   // Start the worker before we have a controllee.
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1371,7 +1410,7 @@
       helper_->mock_render_process_host()->foreground_service_worker_count());
 }
 
-TEST_F(ServiceWorkerVersionTest,
+TEST_P(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountNotUpdatedBySameProcessControllee) {
   // Start the worker before we have a controllee.
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1390,7 +1429,7 @@
       helper_->mock_render_process_host()->foreground_service_worker_count());
 }
 
-TEST_F(ServiceWorkerVersionTest,
+TEST_P(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByControlleeProcessIdChange) {
   // Start the worker before we have a controllee.
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1450,7 +1489,7 @@
       helper_->mock_render_process_host()->foreground_service_worker_count());
 }
 
-TEST_F(ServiceWorkerVersionTest,
+TEST_P(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByWorkerStatus) {
   // Add a controllee in a different process from the service worker.
   auto remote_endpoint = ActivateWithControllee(/*in_different_process=*/true);
@@ -1479,7 +1518,7 @@
       helper_->mock_render_process_host()->foreground_service_worker_count());
 }
 
-TEST_F(ServiceWorkerVersionTest,
+TEST_P(ServiceWorkerVersionTest,
        ForegroundServiceWorkerCountUpdatedByControlleeForegroundStateChange) {
   // Start the worker before we have a controllee.
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1548,7 +1587,21 @@
   }
 };
 
-TEST_F(ServiceWorkerVersionNoFetchHandlerTest,
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ServiceWorkerVersionNoFetchHandlerTest,
+    testing::ValuesIn({StorageKeyTestCase::kFirstParty,
+                       StorageKeyTestCase::kThirdParty}),
+    [](const testing::TestParamInfo<StorageKeyTestCase>& info) {
+      switch (info.param) {
+        case (StorageKeyTestCase::kFirstParty):
+          return "FirstPartyStorageKey";
+        case (StorageKeyTestCase::kThirdParty):
+          return "ThirdPartyStorageKey";
+      }
+    });
+
+TEST_P(ServiceWorkerVersionNoFetchHandlerTest,
        ForegroundServiceWorkerCountNotUpdated) {
   // Start the worker before we have a controllee.
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1568,7 +1621,7 @@
       helper_->mock_render_process_host()->foreground_service_worker_count());
 }
 
-TEST_F(ServiceWorkerVersionTest, FailToStart_UseNewRendererProcess) {
+TEST_P(ServiceWorkerVersionTest, FailToStart_UseNewRendererProcess) {
   ServiceWorkerContextCore* context = helper_->context();
   int64_t id = version_->version_id();
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
@@ -1617,7 +1670,7 @@
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(ServiceWorkerVersionTest, FailToStart_RestartStalledWorker) {
+TEST_P(ServiceWorkerVersionTest, FailToStart_RestartStalledWorker) {
   absl::optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   // Stall in starting.
@@ -1646,7 +1699,7 @@
       version_->version_id()));
 }
 
-TEST_F(ServiceWorkerVersionTest, InstalledFetchEventHandlerExists) {
+TEST_P(ServiceWorkerVersionTest, InstalledFetchEventHandlerExists) {
   auto* service_worker =
       helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get());
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -1656,7 +1709,7 @@
             service_worker->fetch_handler_existence());
 }
 
-TEST_F(ServiceWorkerVersionNoFetchHandlerTest,
+TEST_P(ServiceWorkerVersionNoFetchHandlerTest,
        InstalledFetchEventHandlerDoesNotExist) {
   auto* service_worker =
       helper_->AddNewPendingServiceWorker<FakeServiceWorker>(helper_.get());
@@ -1697,7 +1750,7 @@
   base::RepeatingClosure add_message_to_console_callback_;
 };
 
-TEST_F(ServiceWorkerVersionTest, AddMessageToConsole) {
+TEST_P(ServiceWorkerVersionTest, AddMessageToConsole) {
   auto* service_worker =
       helper_->AddNewPendingServiceWorker<StoreMessageServiceWorker>(
           helper_.get());
@@ -1727,7 +1780,7 @@
 
 // Test that writing metadata aborts gracefully when a remote connection to
 // the Storage Service is disconnected.
-TEST_F(ServiceWorkerVersionTest, WriteMetadata_RemoteStorageDisconnection) {
+TEST_P(ServiceWorkerVersionTest, WriteMetadata_RemoteStorageDisconnection) {
   const std::string kMetadata("Test metadata");
 
   net::TestCompletionCallback completion;
@@ -1741,7 +1794,7 @@
 }
 
 // Test that writing metadata aborts gracefully when the storage is disabled.
-TEST_F(ServiceWorkerVersionTest, WriteMetadata_StorageDisabled) {
+TEST_P(ServiceWorkerVersionTest, WriteMetadata_StorageDisabled) {
   const std::string kMetadata("Test metadata");
 
   base::RunLoop loop;
@@ -1757,7 +1810,7 @@
 }
 
 // Test that writing metadata twice at the same time finishes successfully.
-TEST_F(ServiceWorkerVersionTest, WriteMetadata_MultipleWrites) {
+TEST_P(ServiceWorkerVersionTest, WriteMetadata_MultipleWrites) {
   const std::string kMetadata("Test metadata");
 
   net::TestCompletionCallback completion1;
@@ -1776,7 +1829,7 @@
 // Tests that adding pending external requests with different
 // ServiceWorkerExternalRequestTimeoutType is handled correctly within
 // ServiceWorkerVersion::pending_external_requests_.
-TEST_F(ServiceWorkerVersionTest, PendingExternalRequest) {
+TEST_P(ServiceWorkerVersionTest, PendingExternalRequest) {
   using TimeoutType = ServiceWorkerExternalRequestTimeoutType;
   using Result = ServiceWorkerExternalRequestResult;
   auto get_pending_request_size = [&]() -> size_t {
@@ -1817,7 +1870,7 @@
 }
 
 // Tests worker lifetime with ServiceWorkerVersion::StartExternalRequest.
-TEST_F(ServiceWorkerVersionTest, WorkerLifetimeWithExternalRequest) {
+TEST_P(ServiceWorkerVersionTest, WorkerLifetimeWithExternalRequest) {
   SetupTestTickClock();
   absl::optional<blink::ServiceWorkerStatusCode> status;
 
@@ -1889,7 +1942,7 @@
 // stopped by an external request with "default" timeout.
 //
 // Regression test for https://crbug.com/1189678
-TEST_F(ServiceWorkerVersionTest,
+TEST_P(ServiceWorkerVersionTest,
        DefaultTimeoutRequestDoesNotAffectMaxTimeoutRequest) {
   SetupTestTickClock();
   absl::optional<blink::ServiceWorkerStatusCode> status;
@@ -1931,7 +1984,7 @@
   EXPECT_EQ(blink::EmbeddedWorkerStatus::kRunning, version_->running_status());
 }
 
-TEST_F(ServiceWorkerVersionTest, SetResources) {
+TEST_P(ServiceWorkerVersionTest, SetResources) {
   // Create a new version
   scoped_refptr<ServiceWorkerVersion> version = CreateNewServiceWorkerVersion(
       helper_->context()->registry(), registration_.get(),
@@ -1967,7 +2020,21 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-TEST_F(ServiceWorkerVersionStaticRouterTest, SetRouterEvaluator) {
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ServiceWorkerVersionStaticRouterTest,
+    testing::ValuesIn({StorageKeyTestCase::kFirstParty,
+                       StorageKeyTestCase::kThirdParty}),
+    [](const testing::TestParamInfo<StorageKeyTestCase>& info) {
+      switch (info.param) {
+        case (StorageKeyTestCase::kFirstParty):
+          return "FirstPartyStorageKey";
+        case (StorageKeyTestCase::kThirdParty):
+          return "ThirdPartyStorageKey";
+      }
+    });
+
+TEST_P(ServiceWorkerVersionStaticRouterTest, SetRouterEvaluator) {
   // Create a new version
   scoped_refptr<ServiceWorkerVersion> version = CreateNewServiceWorkerVersion(
       helper_->context()->registry(), registration_.get(),
@@ -2065,14 +2132,14 @@
   bool has_hid_event_handlers_;
 };
 
-TEST_F(ServiceWorkerVersionTest, HasHidEventHandler) {
+TEST_P(ServiceWorkerVersionTest, HasHidEventHandler) {
   helper_->AddNewPendingInstanceClient<HidEventHandlerClient>(
       helper_.get(), /*has_hid_event_handlers*/ true);
   StartServiceWorker(version_.get());
   EXPECT_TRUE(version_->has_hid_event_handlers());
 }
 
-TEST_F(ServiceWorkerVersionTest, NoHidEventHandler) {
+TEST_P(ServiceWorkerVersionTest, NoHidEventHandler) {
   helper_->AddNewPendingInstanceClient<HidEventHandlerClient>(
       helper_.get(), /*has_hid_event_handlers*/ false);
   StartServiceWorker(version_.get());
@@ -2104,14 +2171,14 @@
   bool has_usb_event_handlers_;
 };
 
-TEST_F(ServiceWorkerVersionTest, HasUsbEventHandler) {
+TEST_P(ServiceWorkerVersionTest, HasUsbEventHandler) {
   helper_->AddNewPendingInstanceClient<UsbEventHandlerClient>(
       helper_.get(), /*has_usb_event_handlers*/ true);
   StartServiceWorker(version_.get());
   EXPECT_TRUE(version_->has_usb_event_handlers());
 }
 
-TEST_F(ServiceWorkerVersionTest, NoUsbEventHandler) {
+TEST_P(ServiceWorkerVersionTest, NoUsbEventHandler) {
   helper_->AddNewPendingInstanceClient<UsbEventHandlerClient>(
       helper_.get(), /*has_usb_event_handlers*/ false);
   StartServiceWorker(version_.get());
diff --git a/device/vr/public/cpp/features.cc b/device/vr/public/cpp/features.cc
index 7a14070..cbdf9f6e 100644
--- a/device/vr/public/cpp/features.cc
+++ b/device/vr/public/cpp/features.cc
@@ -47,12 +47,6 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 #endif
 
-#if BUILDFLAG(ENABLE_CARDBOARD)
-// Controls WebXR support for the Cardboard SDK Runtime. Note that enabling
-// this will also disable the GVR runtime.
-BASE_FEATURE(kEnableCardboard, "Cardboard", base::FEATURE_ENABLED_BY_DEFAULT);
-#endif  // ENABLE_CARDBOARD
-
 #if BUILDFLAG(ENABLE_OPENXR)
 // Controls WebXR support for the OpenXR Runtime.
 BASE_FEATURE(kOpenXR,
diff --git a/device/vr/public/cpp/features.h b/device/vr/public/cpp/features.h
index 3a99a278..8c84c212 100644
--- a/device/vr/public/cpp/features.h
+++ b/device/vr/public/cpp/features.h
@@ -21,10 +21,6 @@
 COMPONENT_EXPORT(VR_FEATURES) BASE_DECLARE_FEATURE(kWebXrSharedBuffers);
 #endif
 
-#if BUILDFLAG(ENABLE_CARDBOARD)
-COMPONENT_EXPORT(VR_FEATURES) BASE_DECLARE_FEATURE(kEnableCardboard);
-#endif  // ENABLE_CARDBOARD
-
 #if BUILDFLAG(ENABLE_OPENXR)
 COMPONENT_EXPORT(VR_FEATURES) BASE_DECLARE_FEATURE(kOpenXR);
 COMPONENT_EXPORT(VR_FEATURES)
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 468ba7e..14ce881f 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1916,7 +1916,6 @@
   ACCESSIBILITY_PRIVATE_GETDISPLAYBOUNDS = 1854,
   AUTOFILLPRIVATE_SERVERIBANLINKCLICKED = 1855,
   AUTOTESTPRIVATE_CLEARALLOWEDPREF = 1856,
-  DEVELOPERPRIVATE_DISMISSSAFETYHUBEXTENSIONSMENUNOTIFICATION = 1857,
   // Last entry: Add new entries above, then run:
   // tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/fuchsia_web/webengine/web_engine_integration_tests.shard.test-cml b/fuchsia_web/webengine/web_engine_integration_tests.shard.test-cml
index b791c4f..3a32b3d6 100644
--- a/fuchsia_web/webengine/web_engine_integration_tests.shard.test-cml
+++ b/fuchsia_web/webengine/web_engine_integration_tests.shard.test-cml
@@ -55,6 +55,13 @@
       subdir: "web_engine",
     },
     {
+      directory: "tzdata-icu",
+      from: "parent",
+      rights: [ "r*" ],
+      to: "#realm_builder",
+    },
+
+    {
       protocol: [
         "fuchsia.logger.LogSink",
         "fuchsia.scheduler.ProfileProvider",
diff --git a/fuchsia_web/webengine/web_instance-common.shard.cml b/fuchsia_web/webengine/web_instance-common.shard.cml
index 8bfefe3..28231f3 100644
--- a/fuchsia_web/webengine/web_instance-common.shard.cml
+++ b/fuchsia_web/webengine/web_instance-common.shard.cml
@@ -64,6 +64,16 @@
       availability: "optional",
     },
 
+    // Holds ICU time zone data files.
+    // See:
+    // https://fuchsia.dev/fuchsia-src/concepts/process/namespaces?typical_directory_structure
+    {
+      directory: "tzdata-icu",
+      rights: [ "r*" ],
+      path: "/config/tzdata/icu",
+      availability: "optional",
+    },
+
     // Temporary directory specified by WebInstanceHost.set_tmp_dir.
     {
       directory: "tmp",
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 28553a1..b588707 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -94,6 +94,7 @@
 #if BUILDFLAG(ENABLE_VULKAN)
 #include "components/viz/common/gpu/vulkan_context_provider.h"
 #include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_util.h"
 #endif  // BUILDFLAG(ENABLE_VULKAN)
 
 #if BUILDFLAG(IS_WIN)
@@ -1305,6 +1306,24 @@
     caps.supports_yuv_readback = true;
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (shared_context_state_->GrContextIsGL()) {
+    PopulateDRMCapabilities(&caps, feature_info());
+  }
+#if BUILDFLAG(ENABLE_VULKAN)
+  else if (shared_context_state_->GrContextIsVulkan()) {
+    auto* device_queue =
+        shared_context_state_->vk_context_provider()->GetDeviceQueue();
+    caps.drm_device_id = device_queue->drm_device_id();
+    gpu::PopulateVkDrmFormatsAndModifiers(device_queue,
+                                          caps.drm_formats_and_modifiers);
+  }
+#endif  // BUILDFLAG(ENABLE_VULKAN)
+  else {
+    NOTREACHED();
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   return caps;
 }
 
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index f047b7e..28adae6b 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -1109,10 +1109,15 @@
 
   const base::FeatureParam<std::string> enable_by_device_name(
       &features::kVulkan, "enable_by_device_name", "");
+  const base::FeatureParam<std::string> disable_by_renderer(
+      &features::kVulkan, "disable_by_gl_renderer", "");
+  const base::FeatureParam<std::string> disable_by_driver(
+      &features::kVulkan, "disable_by_gl_driver", "");
   if (!use_swiftshader && !forced_native &&
-      !CheckVulkanCompabilities(
+      !CheckVulkanCompatibilities(
           vulkan_implementation_->GetVulkanInstance()->vulkan_info(), gpu_info_,
-          enable_by_device_name.Get())) {
+          enable_by_device_name.Get(), disable_by_renderer.Get(),
+          disable_by_driver.Get())) {
     vulkan_implementation_.reset();
     return false;
   }
diff --git a/gpu/vulkan/BUILD.gn b/gpu/vulkan/BUILD.gn
index 35f90b1..5d3a178 100644
--- a/gpu/vulkan/BUILD.gn
+++ b/gpu/vulkan/BUILD.gn
@@ -148,7 +148,6 @@
 
     if (is_android) {
       sources += [ "vulkan_image_android.cc" ]
-      deps += [ "//gpu/config:config" ]
     }
 
     if (is_fuchsia) {
diff --git a/gpu/vulkan/vulkan_util.cc b/gpu/vulkan/vulkan_util.cc
index 6733205..c30c6b5 100644
--- a/gpu/vulkan/vulkan_util.cc
+++ b/gpu/vulkan/vulkan_util.cc
@@ -22,7 +22,6 @@
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/build_info.h"
-#include "gpu/config/gpu_finch_features.h"  //nogncheck
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -45,8 +44,6 @@
 
 namespace {
 
-#if BUILDFLAG(IS_ANDROID)
-
 bool IsDeviceBlocked(base::StringPiece field, base::StringPiece block_list) {
   auto disable_patterns = base::SplitString(
       block_list, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
@@ -58,6 +55,8 @@
   return false;
 }
 
+#if BUILDFLAG(IS_ANDROID)
+
 int GetEMUIVersion() {
   const auto* build_info = base::android::BuildInfo::GetInstance();
   base::StringPiece manufacturer(build_info->manufacturer());
@@ -409,9 +408,11 @@
   return vkQueuePresentKHR(queue, pPresentInfo);
 }
 
-bool CheckVulkanCompabilities(const VulkanInfo& vulkan_info,
-                              const GPUInfo& gpu_info,
-                              std::string enable_by_device_name) {
+bool CheckVulkanCompatibilities(const VulkanInfo& vulkan_info,
+                                const GPUInfo& gpu_info,
+                                const std::string& enable_by_device_name,
+                                const std::string& disable_by_renderer,
+                                const std::string& disable_by_driver) {
 // Android uses AHB and SyncFD for interop. They are imported into GL with other
 // API.
 #if !BUILDFLAG(IS_ANDROID)
@@ -425,8 +426,16 @@
   constexpr char kMemoryObjectExtension[] = "GL_EXT_memory_object_fd";
   constexpr char kSemaphoreExtension[] = "GL_EXT_semaphore_fd";
 #endif
+  if (IsDeviceBlocked(gpu_info.gl_renderer, disable_by_renderer)) {
+    return false;
+  }
+
+  if (IsDeviceBlocked(gpu_info.gpu.driver_version, disable_by_driver)) {
+    return false;
+  }
+
   // If Chrome and ANGLE share the same VkQueue, they can share vulkan
-  // resource without those extensions. 
+  // resource without those extensions.
   if (!base::FeatureList::IsEnabled(features::kVulkanFromANGLE)) {
     // If both Vulkan and GL are using native GPU (non swiftshader), check
     // necessary extensions for GL and Vulkan interop.
@@ -462,17 +471,11 @@
       return true;
   }
 
-  const base::FeatureParam<std::string> disable_patterns(
-      &features::kVulkan, "disable_by_gl_renderer", "");
-
-  if (IsDeviceBlocked(gpu_info.gl_renderer, disable_patterns.Get())) {
+  if (IsDeviceBlocked(gpu_info.gl_renderer, disable_by_renderer)) {
     return false;
   }
 
-  const base::FeatureParam<std::string> disable_driver_patterns(
-      &features::kVulkan, "disable_by_gl_driver", "");
-  if (IsDeviceBlocked(gpu_info.gpu.driver_version,
-                      disable_driver_patterns.Get())) {
+  if (IsDeviceBlocked(gpu_info.gpu.driver_version, disable_by_driver)) {
     return false;
   }
 
diff --git a/gpu/vulkan/vulkan_util.h b/gpu/vulkan/vulkan_util.h
index f9a56d4f9..0a599887 100644
--- a/gpu/vulkan/vulkan_util.h
+++ b/gpu/vulkan/vulkan_util.h
@@ -110,9 +110,11 @@
 VulkanQueuePresentKHRHook(VkQueue queue, const VkPresentInfoKHR* pPresentInfo);
 
 COMPONENT_EXPORT(VULKAN)
-bool CheckVulkanCompabilities(const VulkanInfo& vulkan_info,
-                              const GPUInfo& gpu_info,
-                              std::string enable_by_device_name);
+bool CheckVulkanCompatibilities(const VulkanInfo& vulkan_info,
+                                const GPUInfo& gpu_info,
+                                const std::string& enable_by_device_name,
+                                const std::string& disable_by_renderer,
+                                const std::string& disable_by_driver);
 
 COMPONENT_EXPORT(VULKAN)
 VkImageLayout GLImageLayoutToVkImageLayout(uint32_t layout);
diff --git "a/infra/config/generated/builders/ci/Win Builder \050dbg\051/properties.json" "b/infra/config/generated/builders/ci/Win Builder \050dbg\051/properties.json"
index 52ccb5a0..9618338 100644
--- "a/infra/config/generated/builders/ci/Win Builder \050dbg\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Win Builder \050dbg\051/properties.json"
@@ -42,6 +42,10 @@
         {
           "builder": "win_chromium_compile_dbg_ng",
           "group": "tryserver.chromium.win"
+        },
+        {
+          "builder": "win_chromium_compile_siso_dbg_ng",
+          "group": "tryserver.chromium.win"
         }
       ]
     }
diff --git a/infra/config/generated/builders/ci/fuchsia-official/gn-args.json b/infra/config/generated/builders/ci/fuchsia-official/gn-args.json
deleted file mode 100644
index 07d8da3..0000000
--- a/infra/config/generated/builders/ci/fuchsia-official/gn-args.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "gn_args": {
-    "is_official_build": true,
-    "target_os": "fuchsia",
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/fuchsia-official/properties.json b/infra/config/generated/builders/ci/fuchsia-official/properties.json
deleted file mode 100644
index a8d1172..0000000
--- a/infra/config/generated/builders/ci/fuchsia-official/properties.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/ci/fuchsia-official/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "fuchsia-official",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "fuchsia"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "fuchsia_x64",
-                  "checkout_pgo_profiles"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "fuchsia-official",
-          "project": "chromium"
-        }
-      ],
-      "mirroring_builder_group_and_names": [
-        {
-          "builder": "fuchsia-official",
-          "group": "tryserver.chromium"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-trusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/fuchsia-official/shadow-properties.json b/infra/config/generated/builders/ci/fuchsia-official/shadow-properties.json
deleted file mode 100644
index 2997dff..0000000
--- a/infra/config/generated/builders/ci/fuchsia-official/shadow-properties.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 7757203..e7ecef9 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -3,7 +3,6 @@
     "android-archive-rel": "ci/android-archive-rel/gn-args.json",
     "android-arm64-archive-rel": "ci/android-arm64-archive-rel/gn-args.json",
     "android-official": "ci/android-official/gn-args.json",
-    "fuchsia-official": "ci/fuchsia-official/gn-args.json",
     "lacros-arm-archive-rel": "ci/lacros-arm-archive-rel/gn-args.json",
     "lacros-arm64-archive-rel": "ci/lacros-arm64-archive-rel/gn-args.json",
     "lacros64-archive-rel": "ci/lacros64-archive-rel/gn-args.json",
@@ -498,7 +497,6 @@
   },
   "tryserver.chromium": {
     "android-official": "try/android-official/gn-args.json",
-    "fuchsia-official": "try/fuchsia-official/gn-args.json",
     "linux-official": "try/linux-official/gn-args.json",
     "mac-official": "try/mac-official/gn-args.json",
     "win-official": "try/win-official/gn-args.json",
@@ -871,6 +869,7 @@
     "win32-clobber-rel": "try/win32-clobber-rel/gn-args.json",
     "win_chromium_compile_dbg_ng": "try/win_chromium_compile_dbg_ng/gn-args.json",
     "win_chromium_compile_rel_ng": "try/win_chromium_compile_rel_ng/gn-args.json",
+    "win_chromium_compile_siso_dbg_ng": "try/win_chromium_compile_siso_dbg_ng/gn-args.json",
     "win_chromium_x64_rel_ng": "try/win_chromium_x64_rel_ng/gn-args.json"
   }
 }
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android-pie-arm64-dbg/gn-args.json b/infra/config/generated/builders/try/android-pie-arm64-dbg/gn-args.json
index 24e6409..4bd2489 100644
--- a/infra/config/generated/builders/try/android-pie-arm64-dbg/gn-args.json
+++ b/infra/config/generated/builders/try/android-pie-arm64-dbg/gn-args.json
@@ -2,10 +2,11 @@
   "gn_args": {
     "debuggable_apks": false,
     "ffmpeg_branding": "Chrome",
-    "is_component_build": true,
+    "is_component_build": false,
     "is_debug": true,
     "proprietary_codecs": true,
     "symbol_level": 1,
+    "system_webview_package_name": "com.google.android.webview",
     "target_cpu": "arm64",
     "target_os": "android",
     "use_remoteexec": true
diff --git a/infra/config/generated/builders/try/fuchsia-official/gn-args.json b/infra/config/generated/builders/try/fuchsia-official/gn-args.json
deleted file mode 100644
index 6ed3b6d..0000000
--- a/infra/config/generated/builders/try/fuchsia-official/gn-args.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "gn_args": {
-    "dcheck_always_on": true,
-    "is_official_build": true,
-    "symbol_level": 1,
-    "target_os": "fuchsia",
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/fuchsia-official/properties.json b/infra/config/generated/builders/try/fuchsia-official/properties.json
deleted file mode 100644
index 2c69ad9a..0000000
--- a/infra/config/generated/builders/try/fuchsia-official/properties.json
+++ /dev/null
@@ -1,61 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/try/fuchsia-official/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "fuchsia-official",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "builder_group": "chromium",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "config": "chromium",
-                "target_bits": 64,
-                "target_platform": "fuchsia"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "fuchsia_x64",
-                  "checkout_pgo_profiles"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "fuchsia-official",
-          "project": "chromium"
-        }
-      ]
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/gn-args.json b/infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/gn-args.json
new file mode 100644
index 0000000..f555781
--- /dev/null
+++ b/infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/gn-args.json
@@ -0,0 +1,11 @@
+{
+  "gn_args": {
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": true,
+    "is_debug": true,
+    "proprietary_codecs": true,
+    "symbol_level": 0,
+    "target_cpu": "x86",
+    "use_remoteexec": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/properties.json b/infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/properties.json
new file mode 100644
index 0000000..ef15d075
--- /dev/null
+++ b/infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/properties.json
@@ -0,0 +1,73 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Win Builder (dbg)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-win-archive",
+              "builder_group": "chromium.win",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Debug",
+                "config": "chromium",
+                "target_bits": 32
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Win Builder (dbg)",
+          "project": "chromium"
+        }
+      ],
+      "is_compile_only": true
+    }
+  },
+  "$build/flakiness": {
+    "check_for_flakiness": true,
+    "check_for_flakiness_with_resultdb": true
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 500,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-untrusted"
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.win",
+  "cq": "required",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index dbd83f4..f8ebf87 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -670,3 +670,6 @@
   * [`//sandbox/win/.+`](https://cs.chromium.org/chromium/src/sandbox/win/)
   * [`//sandbox/policy/win/.+`](https://cs.chromium.org/chromium/src/sandbox/policy/win/)
 
+* [win_chromium_compile_siso_dbg_ng](https://ci.chromium.org/p/chromium/builders/try/win_chromium_compile_siso_dbg_ng) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""win_chromium_compile_siso_dbg_ng""))
+  * Experiment percentage: 10.0
+
diff --git a/infra/config/generated/cq-usage/mega_cq_bots.txt b/infra/config/generated/cq-usage/mega_cq_bots.txt
index 4c3b4be..5a68f75 100644
--- a/infra/config/generated/cq-usage/mega_cq_bots.txt
+++ b/infra/config/generated/cq-usage/mega_cq_bots.txt
@@ -163,4 +163,5 @@
 chromium/try/win32-official
 chromium/try/win_chromium_compile_dbg_ng
 chromium/try/win_chromium_compile_rel_ng
+chromium/try/win_chromium_compile_siso_dbg_ng
 chromium/try/win_chromium_x64_rel_ng
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index 8e0b2f57..554d9922 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -6053,26 +6053,6 @@
           }
         ]
       },
-      "fuchsia-official": {
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
       "fuchsia-x64-cast-receiver-rel": {
         "contact_team_email": "chrome-fuchsia-engprod@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 8b194cb7..8c90b29 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -2482,10 +2482,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/fuchsia-official"
-        includable_only: true
-      }
-      builders {
         name: "chromium/try/fuchsia-x64-accessibility-rel"
         location_filters {
           gerrit_host_regexp: ".*"
@@ -5156,6 +5152,30 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/win_chromium_compile_siso_dbg_ng"
+        cancel_stale: NO
+        experiment_percentage: 10
+        location_filters {
+          gerrit_host_regexp: ".*"
+          gerrit_project_regexp: ".*"
+          path_regexp: "docs/.+"
+          exclude: true
+        }
+        location_filters {
+          gerrit_host_regexp: ".*"
+          gerrit_project_regexp: ".*"
+          path_regexp: "infra/config/.+"
+          exclude: true
+        }
+        location_filters {
+          gerrit_host_regexp: ".*"
+          gerrit_project_regexp: ".*"
+          path_regexp: "infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/.+"
+        }
+        mode_allowlist: "DRY_RUN"
+        mode_allowlist: "FULL_RUN"
+      }
+      builders {
         name: "chromium/try/win_chromium_x64_rel_ng"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 0707a381..f227c2d 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -26430,7 +26430,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/win_chromium_compile_dbg_ng\">win_chromium_compile_dbg_ng</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/win_chromium_compile_dbg_ng\">win_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/win_chromium_compile_siso_dbg_ng\">win_chromium_compile_siso_dbg_ng</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -38116,97 +38116,6 @@
       contact_team_email: "chrome-fuchsia-engprod@google.com"
     }
     builders {
-      name: "fuchsia-official"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builder:fuchsia-official"
-      dimensions: "cores:32"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/fuchsia-official/properties.json",'
-        '    "shadow_properties_file": "infra/config/generated/builders/ci/fuchsia-official/shadow-properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/fuchsia-official\">fuchsia-official</a></li></ul>"
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "builder:"
-        dimensions: "builderless:1"
-        dimensions: "pool:luci.chromium.try"
-      }
-    }
-    builders {
       name: "fuchsia-x64-accessibility-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -75390,101 +75299,6 @@
       contact_team_email: "chrome-fuchsia-engprod@google.com"
     }
     builders {
-      name: "fuchsia-official"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:32"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/fuchsia-official/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      experiments {
-        key: "swarming.prpc.cli"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/fuchsia-official\">fuchsia-official</a></li></ul>"
-    }
-    builders {
       name: "fuchsia-x64-accessibility-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -103368,6 +103182,103 @@
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win Builder\">Win Builder</a></li></ul>"
     }
     builders {
+      name: "win_chromium_compile_siso_dbg_ng"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:win_chromium_compile_siso_dbg_ng"
+      dimensions: "cores:16"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows-10"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/win_chromium_compile_siso_dbg_ng/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.win",'
+        '  "cq": "required",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      experiments {
+        key: "swarming.prpc.cli"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "This builder shadows win_chromium_compile_dbg_ng builder to compare between Siso builds and Ninja builds.<br/>\nThis builder should be removed after migrating win_chromium_compile_dbg_ng from Ninja to Siso. b/277863839\n<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win Builder (dbg)\">Win Builder (dbg)</a></li></ul>"
+      contact_team_email: "chrome-build-team@google.com"
+    }
+    builders {
       name: "win_chromium_x64_rel_ng"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index aaa5568..18854a5 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -455,11 +455,6 @@
     short_name: "lnx"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-official"
-    category: "chromium|fuchsia"
-    short_name: "off"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/lacros-arm-archive-rel"
     category: "chromium|lacros"
     short_name: "arm"
@@ -2203,11 +2198,6 @@
     short_name: "det"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-official"
-    category: "gardener|ci|x64"
-    short_name: "off"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/fuchsia-x64-rel"
     category: "gardener|ci|x64"
     short_name: "rel"
@@ -3379,6 +3369,9 @@
     name: "buildbucket/luci.chromium.try/win_chromium_compile_dbg_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win_chromium_compile_siso_dbg_ng"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win_optional_gpu_tests_rel"
   }
   builder_view_only: true
@@ -4127,11 +4120,6 @@
     short_name: "lnx"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/fuchsia-official"
-    category: "fuchsia"
-    short_name: "off"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/lacros-arm-archive-rel"
     category: "lacros"
     short_name: "arm"
@@ -17687,9 +17675,6 @@
     name: "buildbucket/luci.chromium.try/fuchsia-fyi-x64-dbg-persistent-emulator"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/fuchsia-official"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-x64-accessibility-rel"
   }
   builders {
@@ -18560,6 +18545,9 @@
     name: "buildbucket/luci.chromium.try/win_chromium_compile_rel_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win_chromium_compile_siso_dbg_ng"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win_chromium_x64_rel_ng"
   }
   builders {
@@ -18669,9 +18657,6 @@
     name: "buildbucket/luci.chromium.try/android-official"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/fuchsia-official"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-official"
   }
   builders {
@@ -19996,6 +19981,9 @@
     name: "buildbucket/luci.chromium.try/win_chromium_compile_rel_ng"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/win_chromium_compile_siso_dbg_ng"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/win_chromium_x64_rel_ng"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 9ce5f21e..d21de317d 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4240,15 +4240,6 @@
   }
 }
 job {
-  id: "fuchsia-official"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "fuchsia-official"
-  }
-}
-job {
   id: "fuchsia-x64-accessibility-rel"
   realm: "ci"
   buildbucket {
@@ -6498,7 +6489,6 @@
   triggers: "fuchsia-fyi-arm64-dbg"
   triggers: "fuchsia-fyi-x64-asan"
   triggers: "fuchsia-fyi-x64-dbg"
-  triggers: "fuchsia-official"
   triggers: "fuchsia-x64-accessibility-rel"
   triggers: "fuchsia-x64-cast-receiver-rel"
   triggers: "fuchsia-x64-dbg"
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 7caa3f0..0eb43b4 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -369,16 +369,16 @@
   },
   'LACROS_VERSION_SKEW_STABLE': {
     'identifier': 'Lacros version skew testing ash stable',
-    'description': 'Run with ash-chrome version 119.0.6045.209',
+    'description': 'Run with ash-chrome version 119.0.6045.212',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v119.0.6045.209',
-          'revision': 'version:119.0.6045.209',
+          'location': 'lacros_version_skew_tests_v119.0.6045.212',
+          'revision': 'version:119.0.6045.212',
         },
       ],
     },
diff --git a/infra/config/lib/builder_health_indicators.star b/infra/config/lib/builder_health_indicators.star
index 673deb2..97e5cc8e 100644
--- a/infra/config/lib/builder_health_indicators.star
+++ b/infra/config/lib/builder_health_indicators.star
@@ -355,7 +355,6 @@
         "chromeos-octopus-rel",
         "fuchsia-angle-builder",
         "fuchsia-code-coverage",
-        "fuchsia-official",
         "fuchsia-x64-accessibility-rel",
         "ios-angle-builder",
         "ios-asan",
@@ -630,7 +629,6 @@
         "fuchsia-deterministic-dbg",
         "fuchsia-fyi-arm64-dbg",
         "fuchsia-fyi-x64-dbg",
-        "fuchsia-official",
         "fuchsia-x64-accessibility-rel",
         "fuchsia-x64-cast-receiver-rel",
         "fuchsia-x64-cast-receiver-rel-compilator",
diff --git a/infra/config/lib/description_exceptions.star b/infra/config/lib/description_exceptions.star
index 86db931..a1653ce 100644
--- a/infra/config/lib/description_exceptions.star
+++ b/infra/config/lib/description_exceptions.star
@@ -356,7 +356,6 @@
         "fuchsia-fyi-x64-asan",
         "fuchsia-fyi-x64-dbg",
         "fuchsia-fyi-x64-dbg-persistent-emulator",
-        "fuchsia-official",
         "fuchsia-x64-accessibility-rel",
         "fuchsia-x64-cast-receiver-rel",
         "fuchsia-x64-dbg",
@@ -730,7 +729,6 @@
         "fuchsia-fyi-x64-asan",
         "fuchsia-fyi-x64-dbg",
         "fuchsia-fyi-x64-dbg-persistent-emulator",
-        "fuchsia-official",
         "fuchsia-x64-accessibility-rel",
         "fuchsia-x64-cast-receiver-rel",
         "fuchsia-x64-cast-receiver-rel-compilator",
diff --git a/infra/config/subprojects/chromium/ci/chromium.star b/infra/config/subprojects/chromium/ci/chromium.star
index 81c9bce..e32ede6 100644
--- a/infra/config/subprojects/chromium/ci/chromium.star
+++ b/infra/config/subprojects/chromium/ci/chromium.star
@@ -201,54 +201,6 @@
 )
 
 ci.builder(
-    name = "fuchsia-official",
-    branch_selector = branches.selector.FUCHSIA_BRANCHES,
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = [
-                "fuchsia_x64",
-                "checkout_pgo_profiles",
-            ],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = [
-                "mb",
-            ],
-            target_bits = 64,
-            target_platform = builder_config.target_platform.FUCHSIA,
-        ),
-    ),
-    gn_args = gn_args.config(
-        configs = [
-            "official_optimize",
-            "reclient",
-            "fuchsia",
-        ],
-    ),
-    builderless = False,
-    cores = 32,
-    sheriff_rotations = args.ignore_default(None),
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia",
-            short_name = "off",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.selector.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "gardener|ci|x64",
-            short_name = "off",
-        ),
-    ],
-    # TODO: Change this back down to something reasonable once these builders
-    # have populated their cached by getting through the compile step
-    execution_timeout = 10 * time.hour,
-    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CI,
-)
-
-ci.builder(
     name = "linux-chromeos-archive-rel",
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index ba8032c..760c41a 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -651,10 +651,7 @@
     ],
     gn_args = gn_args.config(
         configs = [
-            "android_builder",
-            "debug_try_builder",
-            "reclient",
-            "arm64",
+            "ci/Android arm64 Builder (dbg)",
         ],
     ),
     builderless = False,
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.star b/infra/config/subprojects/chromium/try/tryserver.chromium.star
index 7c850ef..b0c00db0 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.star
@@ -48,22 +48,6 @@
 )
 
 try_.builder(
-    name = "fuchsia-official",
-    branch_selector = branches.selector.FUCHSIA_BRANCHES,
-    mirrors = [
-        "ci/fuchsia-official",
-    ],
-    gn_args = gn_args.config(
-        configs = [
-            "ci/fuchsia-official",
-            "minimal_symbols",
-            "dcheck_always_on",
-        ],
-    ),
-    ssd = True,
-)
-
-try_.builder(
     name = "linux-official",
     branch_selector = branches.selector.LINUX_BRANCHES,
     mirrors = [
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
index f9a17e41..e373f7f9 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -187,6 +187,31 @@
 )
 
 try_.builder(
+    name = "win_chromium_compile_siso_dbg_ng",
+    description_html = """\
+This builder shadows win_chromium_compile_dbg_ng builder to compare between Siso builds and Ninja builds.<br/>
+This builder should be removed after migrating win_chromium_compile_dbg_ng from Ninja to Siso. b/277863839
+""",
+    mirrors = builder_config.copy_from("try/win_chromium_compile_dbg_ng"),
+    try_settings = builder_config.try_settings(
+        include_all_triggered_testers = True,
+        is_compile_only = True,
+    ),
+    gn_args = "try/win_chromium_compile_dbg_ng",
+    builderless = False,
+    cores = 16,
+    ssd = True,
+    contact_team_email = "chrome-build-team@google.com",
+    main_list_view = "try",
+    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
+    siso_enabled = True,
+    tryjob = try_.job(
+        cancel_stale = False,
+        experiment_percentage = 10,
+    ),
+)
+
+try_.builder(
     name = "win_chromium_compile_rel_ng",
     branch_selector = branches.selector.WINDOWS_BRANCHES,
     mirrors = [
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index b8fee8b..1364644 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -49,16 +49,16 @@
   },
   "LACROS_VERSION_SKEW_STABLE": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 119.0.6045.209",
+    "description": "Run with ash-chrome version 119.0.6045.212",
     "identifier": "Lacros version skew testing ash stable",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v119.0.6045.209",
-          "revision": "version:119.0.6045.209"
+          "location": "lacros_version_skew_tests_v119.0.6045.212",
+          "revision": "version:119.0.6045.212"
         }
       ]
     }
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 532b1ec..e6c2a5fd 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -688,7 +688,7 @@
 
   bundle_deps = [
     "//ios/chrome/app/resources",
-    "//ios/chrome/browser/resources:settings_resources",
+    "//ios/chrome/app/resources:settings_resources",
   ]
   if (!is_chrome_branded || ios_chrome_app_variants == []) {
     assert(ios_application_icons_target != "",
diff --git a/ios/chrome/app/resources/BUILD.gn b/ios/chrome/app/resources/BUILD.gn
index 4324102a..b2cafd0 100644
--- a/ios/chrome/app/resources/BUILD.gn
+++ b/ios/chrome/app/resources/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/apple/tweak_info_plist.gni")
 import("//build/config/chrome_build.gni")
 import("//build/config/features.gni")
 import("//build/config/ios/asset_catalog.gni")
@@ -9,6 +10,7 @@
 import("//build/config/locales.gni")
 import("//ios/build/chrome_build.gni")
 import("//ios/chrome/app/resources/ios_chrome_repack.gni")
+import("//ios/chrome/features.gni")
 import("//ios/chrome/tools/strings/generate_localizable_strings.gni")
 import("//ios/public/provider/chrome/browser/build_config.gni")
 import("//tools/grit/grit_rule.gni")
@@ -201,3 +203,37 @@
 group("quick_action_icons") {
   public_deps = [ ":quick_action_incognito" ]
 }
+
+tweak_info_plist("experimental_info_plist") {
+  info_plists = [ "Settings.bundle/Experimental.plist" ]
+  if (ios_enable_sandbox_dump) {
+    info_plists += [ "Settings.bundle/ExperimentalSandboxDump.plist" ]
+  }
+  info_plists += [ "Settings.bundle/ExperimentalFlags.plist" ]
+}
+
+bundle_data("settings_experimental_feed_refresh_plist") {
+  sources = [ "Settings.bundle/ExperimentalFeedRefresh.plist" ]
+  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/{{source_file_part}}" ]
+}
+
+bundle_data("settings_experimental_synctypeslist_plist") {
+  sources = [ "Settings.bundle/ExperimentalSyncTypesListDisabled.plist" ]
+  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/{{source_file_part}}" ]
+}
+
+bundle_data("settings_resources_experimental_plist") {
+  public_deps = [
+    ":experimental_info_plist",
+    ":settings_experimental_feed_refresh_plist",
+    ":settings_experimental_synctypeslist_plist",
+  ]
+  sources = get_target_outputs(":experimental_info_plist")
+  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/Experimental.plist" ]
+}
+
+bundle_data("settings_resources") {
+  public_deps = [ ":settings_resources_experimental_plist" ]
+  sources = [ "Settings.bundle/Root.plist" ]
+  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/{{source_file_part}}" ]
+}
diff --git a/ios/chrome/browser/resources/OWNERS b/ios/chrome/app/resources/OWNERS
similarity index 87%
rename from ios/chrome/browser/resources/OWNERS
rename to ios/chrome/app/resources/OWNERS
index ee5db2f..70c692d 100644
--- a/ios/chrome/browser/resources/OWNERS
+++ b/ios/chrome/app/resources/OWNERS
@@ -1,3 +1,2 @@
 # All committers can modify non-stable Experimental Settings.
-per-file *=*
 per-file Settings.bundle/*=*
diff --git a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist b/ios/chrome/app/resources/Settings.bundle/Experimental.plist
similarity index 100%
rename from ios/chrome/browser/resources/Settings.bundle/Experimental.plist
rename to ios/chrome/app/resources/Settings.bundle/Experimental.plist
diff --git a/ios/chrome/browser/resources/Settings.bundle/ExperimentalFeedRefresh.plist b/ios/chrome/app/resources/Settings.bundle/ExperimentalFeedRefresh.plist
similarity index 100%
rename from ios/chrome/browser/resources/Settings.bundle/ExperimentalFeedRefresh.plist
rename to ios/chrome/app/resources/Settings.bundle/ExperimentalFeedRefresh.plist
diff --git a/ios/chrome/browser/resources/Settings.bundle/ExperimentalFlags.plist b/ios/chrome/app/resources/Settings.bundle/ExperimentalFlags.plist
similarity index 100%
rename from ios/chrome/browser/resources/Settings.bundle/ExperimentalFlags.plist
rename to ios/chrome/app/resources/Settings.bundle/ExperimentalFlags.plist
diff --git a/ios/chrome/browser/resources/Settings.bundle/ExperimentalSandboxDump.plist b/ios/chrome/app/resources/Settings.bundle/ExperimentalSandboxDump.plist
similarity index 100%
rename from ios/chrome/browser/resources/Settings.bundle/ExperimentalSandboxDump.plist
rename to ios/chrome/app/resources/Settings.bundle/ExperimentalSandboxDump.plist
diff --git a/ios/chrome/browser/resources/Settings.bundle/ExperimentalSyncTypesListDisabled.plist b/ios/chrome/app/resources/Settings.bundle/ExperimentalSyncTypesListDisabled.plist
similarity index 100%
rename from ios/chrome/browser/resources/Settings.bundle/ExperimentalSyncTypesListDisabled.plist
rename to ios/chrome/app/resources/Settings.bundle/ExperimentalSyncTypesListDisabled.plist
diff --git a/ios/chrome/browser/resources/Settings.bundle/Root.plist b/ios/chrome/app/resources/Settings.bundle/Root.plist
similarity index 100%
rename from ios/chrome/browser/resources/Settings.bundle/Root.plist
rename to ios/chrome/app/resources/Settings.bundle/Root.plist
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 0615d08..4502813 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -3388,7 +3388,10 @@
         Collapse list
       </message>
       <message name="IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION" desc="Description for the Set Up List item that encourages the user to enable Chrome content notifications.">
-        Keep up with news, sports, and more based on your interest
+        Keep up with news, sports, and more based on your interests
+      </message>
+      <message name="IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_SHORT_DESCRIPTION" desc="Short description for the Set Up List item that encourages the user to enable Chrome content notifications.">
+        Keep up with news, sports, and more.
       </message>
       <message name="IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_TITLE" desc="Title for the Set Up List item that encourages the user to enable Chrome content notifications.">
         Get Content Notifications
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION.png.sha1
index 362dc0eb..7eb0d762 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION.png.sha1
@@ -1 +1 @@
-044fd24dd1ffecadbd8838e44fe3d5d3f7295458
\ No newline at end of file
+fc42566a2b7b4ad909ee53be77a6d2c2d825d8a6
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_SHORT_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_SHORT_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..2ef65ffa
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_SHORT_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+f9bbf508cc6b6f52260cd9668d9d507d1a179dbd
\ No newline at end of file
diff --git a/ios/chrome/browser/resources/BUILD.gn b/ios/chrome/browser/resources/BUILD.gn
deleted file mode 100644
index bc1def22..0000000
--- a/ios/chrome/browser/resources/BUILD.gn
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2022 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/apple/tweak_info_plist.gni")
-import("//ios/chrome/features.gni")
-
-tweak_info_plist("experimental_info_plist") {
-  info_plists = [ "Settings.bundle/Experimental.plist" ]
-  if (ios_enable_sandbox_dump) {
-    info_plists += [ "Settings.bundle/ExperimentalSandboxDump.plist" ]
-  }
-  info_plists += [ "Settings.bundle/ExperimentalFlags.plist" ]
-}
-
-bundle_data("settings_experimental_feed_refresh_plist") {
-  sources = [ "Settings.bundle/ExperimentalFeedRefresh.plist" ]
-  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/{{source_file_part}}" ]
-}
-
-bundle_data("settings_experimental_synctypeslist_plist") {
-  sources = [ "Settings.bundle/ExperimentalSyncTypesListDisabled.plist" ]
-  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/{{source_file_part}}" ]
-}
-
-bundle_data("settings_resources_experimental_plist") {
-  public_deps = [
-    ":experimental_info_plist",
-    ":settings_experimental_feed_refresh_plist",
-    ":settings_experimental_synctypeslist_plist",
-  ]
-  sources = get_target_outputs(":experimental_info_plist")
-  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/Experimental.plist" ]
-}
-
-bundle_data("settings_resources") {
-  public_deps = [ ":settings_resources_experimental_plist" ]
-  sources = [ "Settings.bundle/Root.plist" ]
-  outputs = [ "{{bundle_resources_dir}}/Settings.bundle/{{source_file_part}}" ]
-}
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 1d9a223f..3bfd1c9 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -414,6 +414,7 @@
     "//build:branding_buildflags",
     "//components/feed/core/shared_prefs:feed_shared_prefs",
     "//components/feed/core/v2/public/ios:feed_ios_public",
+    "//components/segmentation_platform/public",
     "//components/signin/internal/identity_manager",
     "//components/strings",
     "//components/sync/base:features",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
index fb7cb34..076bc5b 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
@@ -11,6 +11,8 @@
 #import "base/strings/sys_string_conversions.h"
 #import "base/strings/utf_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#import "components/segmentation_platform/public/constants.h"
+#import "components/segmentation_platform/public/features.h"
 #import "components/strings/grit/components_strings.h"
 #import "components/sync/base/features.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
@@ -178,28 +180,18 @@
       [self isRunningTest:@selector(testMagicStackEditButton)] ||
       [self isRunningTest:@selector
             (testMagicStackCompactedSetUpListCompleteAllItems)]) {
-    if ([self
-            isRunningTest:@selector(testMagicStackSetUpListCompleteAllItems)]) {
-      config.additional_args.push_back(
-          "--enable-features=" + std::string(kMagicStack.name) + "<" +
-          std::string(kMagicStack.name));
-      config.additional_args.push_back(
-          "--force-fieldtrials=" + std::string(kMagicStack.name) + "/Test");
-      config.additional_args.push_back(
-          "--force-fieldtrial-params=" + std::string(kMagicStack.name) +
-          ".Test:" + std::string(kSetUpListCompactedTimeThresholdDays) + "/" +
-          "3");
-    } else {
-      config.additional_args.push_back(
-          "--enable-features=" + std::string(kMagicStack.name) + "<" +
-          std::string(kMagicStack.name));
-      config.additional_args.push_back(
-          "--force-fieldtrials=" + std::string(kMagicStack.name) + "/Test");
-      config.additional_args.push_back(
-          "--force-fieldtrial-params=" + std::string(kMagicStack.name) +
-          ".Test:" + std::string(kSetUpListCompactedTimeThresholdDays) + "/" +
-          "0");
+    std::string enable_magic_stack_segmentation_arg =
+        "--enable-features=" +
+        std::string(segmentation_platform::features::
+                        kSegmentationPlatformIosModuleRanker.name) +
+        ":" + segmentation_platform::kDefaultModelEnabledParam + "/true" + "," +
+        kMagicStack.name;
+    if ([self isRunningTest:@selector
+              (testMagicStackCompactedSetUpListCompleteAllItems)]) {
+      enable_magic_stack_segmentation_arg +=
+          ":" + std::string(kSetUpListCompactedTimeThresholdDays) + "/" + "0";
     }
+    config.additional_args.push_back(enable_magic_stack_segmentation_arg);
   } else {
     config.features_disabled.push_back(kMagicStack);
   }
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 31387b1..453e555 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -622,14 +622,16 @@
         [self insertModuleIntoMagicStack:setUpListModule];
       }
     }
-    if (_magicStackRankReceived && shouldShowCompactedSetUpListModule) {
+    if (shouldShowCompactedSetUpListModule) {
       MultiRowContainerView* multiRowContainer = [[MultiRowContainerView alloc]
           initWithViews:_compactedSetUpListViews];
       _setUpListCompactedModule = [[MagicStackModuleContainer alloc]
           initWithContentView:multiRowContainer
                          type:ContentSuggestionsModuleType::kCompactedSetUpList
                      delegate:self];
-      [self insertModuleIntoMagicStack:_setUpListCompactedModule];
+      if (_magicStackRankReceived) {
+        [self insertModuleIntoMagicStack:_setUpListCompactedModule];
+      }
     }
   } else {
     SetUpListView* setUpListView =
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm
index 8380e9c..76ec426 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.mm
@@ -94,8 +94,7 @@
           syncString,
           IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_SHORT_DESCRIPTION,
           IDS_IOS_SET_UP_LIST_AUTOFILL_SHORT_DESCRIPTION,
-          // TODO(b/310713830): add short strings when they are finalized.
-          IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION,
+          IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_SHORT_DESCRIPTION,
           UIFontTextStyleFootnote,
           UIFontTextStyleCaption2,
           kCompactTextSpacing,
@@ -112,7 +111,6 @@
           syncString,
           IDS_IOS_SET_UP_LIST_DEFAULT_BROWSER_MAGIC_STACK_DESCRIPTION,
           IDS_IOS_SET_UP_LIST_AUTOFILL_MAGIC_STACK_DESCRIPTION,
-          // TODO(b/310713830): add magic stack strings when they are finalized.
           IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION,
           UIFontTextStyleSubheadline,
           UIFontTextStyleFootnote,
diff --git a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_show_more_item_view.mm b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_show_more_item_view.mm
index 6fff717..f5b41d9 100644
--- a/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_show_more_item_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_show_more_item_view.mm
@@ -210,7 +210,6 @@
       return l10n_util::GetNSString(
           IDS_IOS_SET_UP_LIST_AUTOFILL_SEE_MORE_DESCRIPTION);
     case SetUpListItemType::kContentNotification:
-      // TODO(b/310713830): add see more strings when they are finalized.
       return l10n_util::GetNSString(
           IDS_IOS_SET_UP_LIST_CONTENT_NOTIFICATION_DESCRIPTION);
     case SetUpListItemType::kAllSet:
diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src
index 920d43f..4069625 160000
--- a/net/third_party/quiche/src
+++ b/net/third_party/quiche/src
@@ -1 +1 @@
-Subproject commit 920d43f256464487a350cefa02763ce325490f2f
+Subproject commit 4069625b5260ab0df30f733e17d6ab7d1703cbce
diff --git a/services/on_device_model/ml/on_device_model_executor.cc b/services/on_device_model/ml/on_device_model_executor.cc
index 804aeded..200d398 100644
--- a/services/on_device_model/ml/on_device_model_executor.cc
+++ b/services/on_device_model/ml/on_device_model_executor.cc
@@ -29,11 +29,11 @@
 
 const base::FeatureParam<double> kTemperature{
     &optimization_guide::features::kOptimizationGuideOnDeviceModel,
-    "on_device_model_temperature", 1.0};
+    "on_device_model_temperature", 0.8};
 
 const base::FeatureParam<int> kTopK{
     &optimization_guide::features::kOptimizationGuideOnDeviceModel,
-    "on_device_model_topk", 1};
+    "on_device_model_topk", 3};
 
 // Helper to bind object methods as weak task-posting callback functions.
 template <typename R, typename C, typename... Args>
diff --git a/services/webnn/dml/command_recorder.cc b/services/webnn/dml/command_recorder.cc
index 4618388a..f3255a1 100644
--- a/services/webnn/dml/command_recorder.cc
+++ b/services/webnn/dml/command_recorder.cc
@@ -257,6 +257,9 @@
     command_resources_.push_back(persistent_resource);
   }
 
+  // DirectML may remove the device if invalid bindings are provided.
+  RETURN_IF_FAILED(dml_device_->GetDeviceRemovedReason());
+
   command_recorder_->RecordDispatch(command_list_.Get(), initializer.Get(),
                                     binding_table.Get());
 
@@ -369,6 +372,9 @@
       base::checked_cast<uint32_t>(output_bindings.size()),
       output_bindings.data());
 
+  // DirectML may remove the device if invalid bindings are provided.
+  RETURN_IF_FAILED(dml_device_->GetDeviceRemovedReason());
+
   // The output resources should be kept alive until the operator has been
   // executed on the GPU.
   for (size_t i = 0; i < output_bindings.size(); ++i) {
diff --git a/services/webnn/dml/graph_impl.cc b/services/webnn/dml/graph_impl.cc
index ad3b24133..16d417fe 100644
--- a/services/webnn/dml/graph_impl.cc
+++ b/services/webnn/dml/graph_impl.cc
@@ -1486,9 +1486,16 @@
                                 IdToNodeOutputMap& id_to_node_output_map) {
   const NodeOutput* input =
       GetNodeOutputForOperand(id_to_node_output_map, reshape->input_operand_id);
+
+  // Ensure the output tensor description having the
+  // `DML_TENSOR_FLAG_OWNED_BY_DML` flag if its corresponding node is a constant
+  // graph input.
   uint64_t output_id = reshape->output_operand_id;
+  const OperandPtr& output_operand = id_to_operand_map.at(output_id);
+  const auto& input_tensor_desc = input->GetTensorDesc();
   auto output_tensor_desc =
-      CreateOutputTensorDesc(id_to_operand_map, output_id);
+      TensorDesc(input_tensor_desc.GetDataType(), input_tensor_desc.GetFlags(),
+                 output_operand->dimensions);
 
   const Node& input_node = input->GetNode();
 
@@ -1730,6 +1737,104 @@
   return base::ok();
 }
 
+base::expected<void, mojom::ErrorPtr> CreateOperatorNodeForLayerNormalization(
+    const IdToOperandMap& id_to_operand_map,
+    const mojom::LayerNormalizationPtr& layer_normalization,
+    GraphBuilder& graph_builder,
+    IdToNodeOutputMap& id_to_node_output_map) {
+  const NodeOutput* input = GetNodeOutputForOperand(
+      id_to_node_output_map, layer_normalization->input_operand_id);
+  const auto& input_tensor_desc = input->GetTensorDesc();
+  size_t input_rank = input_tensor_desc.GetDimensions().size();
+
+  const NodeOutput* scale =
+      layer_normalization->scale_operand_id.has_value()
+          ? GetNodeOutputForOperand(
+                id_to_node_output_map,
+                layer_normalization->scale_operand_id.value())
+          : nullptr;
+  const NodeOutput* bias =
+      layer_normalization->bias_operand_id.has_value()
+          ? GetNodeOutputForOperand(
+                id_to_node_output_map,
+                layer_normalization->bias_operand_id.value())
+          : nullptr;
+
+  // `scale` and `bias` should be both given or not given when DML_FEATURE_LEVEL
+  // is less than DML_FEATURE_LEVEL_5_2.
+  // https://learn.microsoft.com/en-us/windows/win32/api/directml/ns-directml-dml_mean_variance_normalization1_operator_desc
+  if ((scale && !bias) || (!scale && bias)) {
+    return base::unexpected(
+        CreateError(mojom::Error::Code::kNotSupportedError,
+                    "The scale and bias of layerNormalization must be both "
+                    "given or not given."));
+  }
+
+  const auto& axes = layer_normalization->axes;
+  if (!base::MakeCheckedNum(axes.size()).IsValid<uint32_t>()) {
+    return base::unexpected(
+        CreateError(mojom::Error::Code::kUnknownError,
+                    "The axes rank of layerNormalization is too large."));
+  }
+
+  std::vector<const NodeOutput*> inputs = {input};
+  absl::optional<TensorDesc> scale_tensor_desc;
+  absl::optional<TensorDesc> bias_tensor_desc;
+
+  if (scale) {
+    inputs.push_back(scale);
+    scale_tensor_desc = scale->GetTensorDesc();
+    // The scale tensor should have the same rank as the input tensor required
+    // by DML_MEAN_VARIANCE_NORMALIZATION1_OPERATOR_DESC.
+    scale_tensor_desc->MakeBroadcastCompatible(input_rank, axes);
+  }
+  if (bias) {
+    inputs.push_back(bias);
+    bias_tensor_desc = bias->GetTensorDesc();
+    // The bias tensor should have the same rank as the input tensor required by
+    // DML_MEAN_VARIANCE_NORMALIZATION1_OPERATOR_DESC.
+    bias_tensor_desc->MakeBroadcastCompatible(input_rank, axes);
+  }
+
+  uint64_t output_id = layer_normalization->output_operand_id;
+  const auto output_tensor_desc =
+      CreateOutputTensorDesc(id_to_operand_map, output_id);
+
+  DML_MEAN_VARIANCE_NORMALIZATION1_OPERATOR_DESC
+  layer_normalization_operator_desc{
+      .InputTensor = &input_tensor_desc.GetDMLTensorDesc(),
+      .ScaleTensor = scale_tensor_desc.has_value()
+                         ? &scale_tensor_desc->GetDMLTensorDesc()
+                         : nullptr,
+      .BiasTensor = bias_tensor_desc.has_value()
+                        ? &bias_tensor_desc->GetDMLTensorDesc()
+                        : nullptr,
+      .OutputTensor = &output_tensor_desc.GetDMLTensorDesc(),
+      .AxisCount = base::checked_cast<uint32_t>(axes.size()),
+      .Axes = axes.data(),
+      // The layer normalization includes variance.
+      .NormalizeVariance = true,
+      .Epsilon = layer_normalization->epsilon,
+      .FusedActivation = nullptr};
+
+  const OperatorNode* layer_normalization_node =
+      graph_builder.CreateOperatorNode(
+          DML_OPERATOR_MEAN_VARIANCE_NORMALIZATION1,
+          &layer_normalization_operator_desc, inputs);
+  if (!layer_normalization_node) {
+    return base::unexpected(
+        CreateError(mojom::Error::Code::kUnknownError,
+                    "Failed to create layerNormalization operator."));
+  }
+
+  const NodeOutput* output = graph_builder.CreateNodeOutput(
+      layer_normalization_node, std::move(output_tensor_desc));
+  // The output id must be unique in the map.
+  CHECK(id_to_node_output_map.try_emplace(output_id, output).second);
+
+  return base::ok();
+}
+
 base::expected<void, mojom::ErrorPtr> CreateOperatorNodeForLeakyRelu(
     const IdToOperandMap& id_to_operand_map,
     const mojom::LeakyReluPtr& leaky_relu,
@@ -2434,6 +2539,12 @@
                                       graph_builder, id_to_node_output_map);
         break;
       }
+      case Operation::Tag::kLayerNormalization: {
+        create_operator_result = CreateOperatorNodeForLayerNormalization(
+            id_to_operand_map, operation->get_layer_normalization(),
+            graph_builder, id_to_node_output_map);
+        break;
+      }
       case Operation::Tag::kLeakyRelu: {
         create_operator_result = CreateOperatorNodeForLeakyRelu(
             id_to_operand_map, operation->get_leaky_relu(), graph_builder,
diff --git a/services/webnn/dml/graph_impl_test.cc b/services/webnn/dml/graph_impl_test.cc
index 1481ef7..6e9cc1f3 100644
--- a/services/webnn/dml/graph_impl_test.cc
+++ b/services/webnn/dml/graph_impl_test.cc
@@ -72,6 +72,7 @@
   // The dml::GraphImpl should be built successfully.
   base::RunLoop run_loop_create_graph;
   was_callback_called = false;
+  bool was_create_graph_error = true;
   webnn_context_remote.set_disconnect_handler(
       base::BindOnce([](base::RunLoop* run_loop) { run_loop->Quit(); },
                      &run_loop_create_graph));
@@ -79,14 +80,19 @@
       std::move(graph_info),
       base::BindLambdaForTesting(
           [&](mojom::CreateGraphResultPtr create_graph_result) {
-            webnn_graph_remote.Bind(
-                std::move(create_graph_result->get_graph_remote()));
+            if (!create_graph_result->is_error()) {
+              webnn_graph_remote.Bind(
+                  std::move(create_graph_result->get_graph_remote()));
+              was_create_graph_error = false;
+            }
             was_callback_called = true;
             run_loop_create_graph.Quit();
           }));
   run_loop_create_graph.Run();
+  EXPECT_TRUE(was_callback_called);
+
   if (expectation == BuildAndComputeExpectation::kCreateGraphFailure) {
-    EXPECT_FALSE(was_callback_called);
+    EXPECT_TRUE(was_create_graph_error);
     EXPECT_FALSE(webnn_graph_remote.is_bound());
     EXPECT_TRUE(webnn_context_remote.is_bound());
     webnn_graph_remote.reset();
@@ -95,7 +101,7 @@
     base::RunLoop().RunUntilIdle();
     return;
   }
-  EXPECT_TRUE(was_callback_called);
+  EXPECT_FALSE(was_create_graph_error);
   EXPECT_TRUE(webnn_graph_remote.is_bound());
 
   // The dml::GraphImpl should compute successfully.
@@ -4057,6 +4063,197 @@
 }
 
 template <typename T>
+struct LayerNormalizationTester {
+  OperandInfo<T> input;
+  absl::optional<OperandInfo<T>> scale;
+  absl::optional<OperandInfo<T>> bias;
+  struct LayerNormalizationAttributes {
+    absl::optional<uint64_t> scale_operand_id;
+    absl::optional<uint64_t> bias_operand_id;
+    std::vector<uint32_t> axes;
+    float epsilon = 1e-5;
+  };
+  LayerNormalizationAttributes attributes;
+  OperandInfo<T> output;
+
+  void Test(BuildAndComputeExpectation expectation =
+                BuildAndComputeExpectation::kSuccess) {
+    // Build the graph with mojo type.
+    GraphInfoBuilder builder;
+    uint64_t input_operand_id =
+        builder.BuildInput("input", input.dimensions, input.type);
+    uint64_t output_operand_id =
+        builder.BuildOutput("output", output.dimensions, output.type);
+    if (scale.has_value()) {
+      attributes.scale_operand_id =
+          builder.BuildInput("scale", scale->dimensions, scale->type);
+    }
+    if (bias.has_value()) {
+      attributes.bias_operand_id =
+          builder.BuildInput("bias", bias->dimensions, bias->type);
+    }
+
+    builder.BuildLayerNormalization(input_operand_id, output_operand_id,
+                                    std::move(attributes));
+
+    base::flat_map<std::string, mojo_base::BigBuffer> named_inputs;
+    named_inputs.insert({"input", VectorToBigBuffer(input.values)});
+    if (scale.has_value()) {
+      named_inputs.insert({"scale", VectorToBigBuffer(scale->values)});
+    }
+    if (bias.has_value()) {
+      named_inputs.insert({"bias", VectorToBigBuffer(bias->values)});
+    }
+    base::flat_map<std::string, mojo_base::BigBuffer> named_outputs;
+
+    BuildAndCompute(builder.CloneGraphInfo(), std::move(named_inputs),
+                    named_outputs, expectation);
+
+    if (expectation == BuildAndComputeExpectation::kSuccess) {
+      VerifyFloatDataIsEqual(
+          GetFloatOutputData(std::move(named_outputs["output"]), output.type),
+          output.values);
+    }
+  }
+};
+
+// Test building and computing a DML graph with single operator
+// layerNormalization.
+TEST_F(WebNNGraphDMLImplTest, BuildSingleOperatorLayerNormalization) {
+  // DML_MEAN_VARIANCE_NORMALIZATION1_OPERATOR_DESC support for 1~8 dimension
+  // counts was introduced in DML_FEATURE_LEVEL_3_1.
+  SKIP_TEST_IF(!adapter_->IsDMLFeatureLevelSupported(DML_FEATURE_LEVEL_3_1));
+  {
+    // Test layerNormalization with a scalar input with default scale and bias.
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {},
+                  .values = {5}},
+        .attributes = {.axes = {}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {},
+                   .values = {0}}}
+        .Test();
+  }
+  {
+    // Test layerNormalization with 1-D input with axes = [0] and default scale
+    // and bias.
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {5},
+                  .values = {0, 1, 2, 3, 4}},
+        .attributes = {.axes = {0}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {5},
+                   .values = {-1.4142100268524473, -0.7071050134262237, 0,
+                              0.7071050134262237, 1.4142100268524473}}}
+        .Test();
+  }
+  {
+    // Test layerNormalization with 4-D input with axes = [1, 2, 3] and default
+    // scale and bias.
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {1, 2, 1, 3},
+                  .values = {-1, 0, 1, 2, 3, 4}},
+        .attributes = {.axes = {1, 2, 3}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {1, 2, 1, 3},
+                   .values = {-1.4638475999719223, -0.8783085599831534,
+                              -0.29276951999438444, 0.29276951999438444,
+                              0.8783085599831534, 1.4638475999719223}}}
+        .Test();
+  }
+  {
+    // Test layerNormalization with 4-D input with axes = [2, 3].
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {1, 2, 1, 3},
+                  .values = {-1, 0, 1, 2, 3, 4}},
+        .scale = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                    .dimensions = {1, 3},
+                                    .values = {0.5, 1, -0.5}},
+        .bias = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                   .dimensions = {1, 3},
+                                   .values = {0.1, 0.2, 0.3}},
+        .attributes = {.axes = {2, 3}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {1, 2, 1, 3},
+                   .values = {-0.5123678429541951, 0.2, -0.3123678429541951,
+                              -0.5123678429541951, 0.2, -0.3123678429541951}}}
+        .Test();
+  }
+  {
+    // Test layerNormalization with 3-D input with axes = [0, 1, 2] and default
+    // scale and bias.
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {2, 2, 2},
+                  .values = {-4, -3, -2, -1, 1, 2, 3, 4}},
+        .attributes = {.axes = {0, 1, 2}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {2, 2, 2},
+                   .values = {-1.4605925129524255, -1.0954443847143192,
+                              -0.7302962564762128, -0.3651481282381064,
+                              0.3651481282381064, 0.7302962564762128,
+                              1.0954443847143192, 1.4605925129524255}}}
+        .Test();
+  }
+  {
+    // Test layerNormalization with 6-D input with permuted axes = [4, 1, 2].
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {1, 2, 1, 3, 2, 1},
+                  .values = {-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}},
+        .scale = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                    .dimensions = {2, 2, 1},
+                                    .values = {0.5, 0, 1, -0.5}},
+        .bias = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                   .dimensions = {2, 2, 1},
+                                   .values = {0.1, 0.2, 0.3, 0.4}},
+        .attributes = {.axes = {4, 1, 2}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {1, 2, 1, 3, 2, 1},
+                   .values = {-0.47539614454389156, -0.5219944922055593,
+                              -0.47539614454389156, -0.5219944922055593,
+                              -0.47539614454389156, -0.5219944922055593, 0.2,
+                              -0.17539614454389152, 0.2, -0.17539614454389152,
+                              0.2, -0.17539614454389152}}}
+        .Test();
+  }
+  {
+    // Test graph creation failure with given scale only.
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {1},
+                  .values = {5}},
+        .scale = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                    .dimensions = {1},
+                                    .values = {0.5}},
+        .attributes = {.axes = {0}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {1},
+                   .values = {0}}}
+        .Test(BuildAndComputeExpectation::kCreateGraphFailure);
+  }
+  {
+    // Test graph creation failure with given bias only.
+    LayerNormalizationTester<float>{
+        .input = {.type = mojom::Operand::DataType::kFloat32,
+                  .dimensions = {1},
+                  .values = {5}},
+        .bias = OperandInfo<float>{.type = mojom::Operand::DataType::kFloat32,
+                                   .dimensions = {1},
+                                   .values = {0.5}},
+        .attributes = {.axes = {0}},
+        .output = {.type = mojom::Operand::DataType::kFloat32,
+                   .dimensions = {1},
+                   .values = {0}}}
+        .Test(BuildAndComputeExpectation::kCreateGraphFailure);
+  }
+}
+
+template <typename T>
 struct MatmulTester {
   OperandInfo<T> input_a;
   OperandInfo<T> input_b;
@@ -4266,6 +4463,90 @@
             std::vector<float>({30, 30, 70, 70}));
 }
 
+// Test building and computing a DML graph whose gemm operator takes a reshaped
+// constant operand c in the following topology:
+//                        [constant_c]
+//                         |
+//     [input_a] [input_b] reshape
+//             \    |     /
+//                 gemm
+// This test case could reproduce the issue of ResNetV2 50 model of WebNN image
+// classification sample:
+// https://bugs.chromium.org/p/chromium/issues/detail?id=1509747
+TEST_F(WebNNGraphDMLImplTest, BuildGemmWithReshapedConstantOperand) {
+  // DML_GEMM_OPERATOR_DESC support for 2 dimensions was introduced in
+  // DML_FEATURE_LEVEL_4_0.
+  SKIP_TEST_IF(!adapter_->IsDMLFeatureLevelSupported(DML_FEATURE_LEVEL_4_0));
+  // Build the mojom graph info.
+  GraphInfoBuilder builder;
+  uint64_t input_a_operand_id =
+      builder.BuildInput("input_a", {2, 2}, mojom::Operand::DataType::kFloat32);
+  uint64_t input_b_operand_id =
+      builder.BuildInput("input_b", {2, 2}, mojom::Operand::DataType::kFloat32);
+  std::vector<float> constant_data = {1, 1};
+  uint64_t constant_c_operand_id =
+      builder.BuildConstant({2}, mojom::Operand::DataType::kFloat32,
+                            base::as_bytes(base::make_span(constant_data)));
+  // Reshape constant_c from [2] to [1, 2] and use it as operand c for gemm.
+  uint64_t reshape_operand_id = builder.BuildIntermediateOperand(
+      {1, 2}, mojom::Operand::DataType::kFloat32);
+  builder.BuildReshape(constant_c_operand_id, reshape_operand_id);
+  GemmAttributes gemm_attributes;
+  gemm_attributes.c_operand_id = reshape_operand_id;
+  uint64_t output_operand_id =
+      builder.BuildOutput("output", {2, 2}, mojom::Operand::DataType::kFloat32);
+  builder.BuildGemm(input_a_operand_id, input_b_operand_id, output_operand_id,
+                    gemm_attributes);
+
+  base::flat_map<std::string, mojo_base::BigBuffer> named_inputs;
+  std::vector<float> input_data = {1, 2, 3, 4};
+  named_inputs.insert({"input_a", VectorToBigBuffer(input_data)});
+  named_inputs.insert({"input_b", VectorToBigBuffer(input_data)});
+  base::flat_map<std::string, mojo_base::BigBuffer> named_outputs;
+
+  BuildAndCompute(builder.CloneGraphInfo(), std::move(named_inputs),
+                  named_outputs);
+
+  EXPECT_EQ(BigBufferToVector<float>(std::move(named_outputs["output"])),
+            std::vector<float>({8, 11, 16, 23}));
+}
+
+// Test building a DML graph whose add operator takes a reshaped
+// constant operand b in the following topology:
+//              [constant_b]
+//                 |
+//    [input_a]  reshape
+//           \    /
+//            add
+TEST_F(WebNNGraphDMLImplTest, BuildAddWithReshapedConstantOperand) {
+  // Build the mojom graph info.
+  GraphInfoBuilder builder;
+  uint64_t input_a_operand_id = builder.BuildInput(
+      "input_a", {1, 1, 2, 2}, mojom::Operand::DataType::kFloat32);
+  std::vector<float> constant_data = {1, 1};
+  uint64_t constant_b_operand_id =
+      builder.BuildConstant({2}, mojom::Operand::DataType::kFloat32,
+                            base::as_bytes(base::make_span(constant_data)));
+  // Reshape constant_b from [2] to [1, 2] and use it as operand b for add.
+  uint64_t reshape_operand_id = builder.BuildIntermediateOperand(
+      {1, 2}, mojom::Operand::DataType::kFloat32);
+  builder.BuildReshape(constant_b_operand_id, reshape_operand_id);
+  uint64_t output_operand_id = builder.BuildOutput(
+      "output", {1, 1, 2, 2}, mojom::Operand::DataType::kFloat32);
+  builder.BuildElementWiseBinary(mojom::ElementWiseBinary::Kind::kAdd,
+                                 input_a_operand_id, reshape_operand_id,
+                                 output_operand_id);
+
+  base::flat_map<std::string, mojo_base::BigBuffer> named_inputs;
+  std::vector<float> input_data = {1, 1, 1, 1};
+  named_inputs.insert({"input_a", VectorToBigBuffer(input_data)});
+  base::flat_map<std::string, mojo_base::BigBuffer> named_outputs;
+  BuildAndCompute(builder.CloneGraphInfo(), std::move(named_inputs),
+                  named_outputs);
+  EXPECT_EQ(BigBufferToVector<float>(std::move(named_outputs["output"])),
+            std::vector<float>({2, 2, 2, 2}));
+}
+
 // Test building a DML graph in the following topology.
 //    [input_a] [input_b]
 //           \    /
diff --git a/services/webnn/webnn_graph_impl_unittest.cc b/services/webnn/webnn_graph_impl_unittest.cc
index 5980634..58a30f2 100644
--- a/services/webnn/webnn_graph_impl_unittest.cc
+++ b/services/webnn/webnn_graph_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <limits>
 
+#include "base/containers/contains.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
@@ -1933,8 +1934,7 @@
     const bool expected =
         (inputDataType == outputDataType ||
          kOperatorsWithDissimilarDatatypeSupport.contains(kind)) &&
-        std::find(operator_trait.second.begin(), operator_trait.second.end(),
-                  inputDataType) != operator_trait.second.end();
+        base::Contains(operator_trait.second, inputDataType);
 
     ElementWiseUnaryTester{
         .kind = kind,
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 8e2eef0..206eb45 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -6251,9 +6251,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6263,8 +6263,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -6401,9 +6401,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6413,8 +6413,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 76683d3..1592f11 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -20651,9 +20651,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20663,8 +20663,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -20801,9 +20801,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20813,8 +20813,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index d233607..441debb 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -43490,9 +43490,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -43501,8 +43501,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -43640,9 +43640,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -43651,8 +43651,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -44964,9 +44964,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -44975,8 +44975,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -45114,9 +45114,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -45125,8 +45125,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -45822,9 +45822,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -45833,8 +45833,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.json b/testing/buildbot/chromium.json
index 63ea5fd..de4cdf8 100644
--- a/testing/buildbot/chromium.json
+++ b/testing/buildbot/chromium.json
@@ -16,11 +16,6 @@
       "all"
     ]
   },
-  "fuchsia-official": {
-    "additional_compile_targets": [
-      "all"
-    ]
-  },
   "lacros-arm-archive-rel": {
     "additional_compile_targets": [
       "chrome"
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index c1a17925..86d4d7e 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -16377,12 +16377,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16392,8 +16392,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
@@ -16547,12 +16547,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 119.0.6045.209",
+        "description": "Run with ash-chrome version 119.0.6045.212",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16562,8 +16562,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v119.0.6045.209",
-              "revision": "version:119.0.6045.209"
+              "location": "lacros_version_skew_tests_v119.0.6045.212",
+              "revision": "version:119.0.6045.212"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/filters/release_ready/mac.updater_tests.release_blocker.filter b/testing/buildbot/filters/release_ready/mac.updater_tests.release_blocker.filter
index 34d2311d..460d3c3 100644
--- a/testing/buildbot/filters/release_ready/mac.updater_tests.release_blocker.filter
+++ b/testing/buildbot/filters/release_ready/mac.updater_tests.release_blocker.filter
@@ -117,6 +117,8 @@
 MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/1
 MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/2
 MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/3
+MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/4
+MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/5
 PersistedDataTest.MixedCase
 PersistedDataTest.RegistrationRequest
 PersistedDataTest.RegistrationRequestPartial
diff --git a/testing/buildbot/filters/release_ready/win.updater_tests.release_blocker.filter b/testing/buildbot/filters/release_ready/win.updater_tests.release_blocker.filter
index c7da1f4..26f8625 100644
--- a/testing/buildbot/filters/release_ready/win.updater_tests.release_blocker.filter
+++ b/testing/buildbot/filters/release_ready/win.updater_tests.release_blocker.filter
@@ -261,6 +261,8 @@
 MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/1
 MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/2
 MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/3
+MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/4
+MsiTagTestMsiWriteTagTestCases/MsiTagTestMsiWriteTagTest.TestCases/5
 PersistedDataTest.LastOSVersion
 PersistedDataTest.MixedCase
 PersistedDataTest.RegistrationRequest
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 7caa3f0..0eb43b4 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -369,16 +369,16 @@
   },
   'LACROS_VERSION_SKEW_STABLE': {
     'identifier': 'Lacros version skew testing ash stable',
-    'description': 'Run with ash-chrome version 119.0.6045.209',
+    'description': 'Run with ash-chrome version 119.0.6045.212',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.209/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v119.0.6045.212/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v119.0.6045.209',
-          'revision': 'version:119.0.6045.209',
+          'location': 'lacros_version_skew_tests_v119.0.6045.212',
+          'revision': 'version:119.0.6045.212',
         },
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 8790f251..b1169f5 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -395,11 +395,6 @@
           'all',
         ],
       },
-      'fuchsia-official': {
-        'additional_compile_targets': [
-          'all',
-        ],
-      },
       'lacros-arm-archive-rel': {
         'additional_compile_targets': [
           'chrome',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 4e069641..8d45ba5d 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5583,37 +5583,6 @@
             ]
         }
     ],
-    "DesktopLinkCapturingPWAExperiment": [
-        {
-            "platforms": [
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_DefaultOn",
-                    "params": {
-                        "link_capturing_guardrail_storage_duration": "60",
-                        "on_by_default": "true"
-                    },
-                    "enable_features": [
-                        "DesktopPWAsLinkCapturing"
-                    ]
-                },
-                {
-                    "name": "Enabled_DefaultOff",
-                    "params": {
-                        "link_capturing_guardrail_storage_duration": "60",
-                        "on_by_default": "false"
-                    },
-                    "enable_features": [
-                        "DesktopPWAsLinkCapturing"
-                    ]
-                }
-            ]
-        }
-    ],
     "DesktopNtpDriveCache": [
         {
             "platforms": [
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
index cb2919a..dbbe450 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -60,11 +60,16 @@
         com_google_errorprone_error_prone_annotation: new PropertyOverride(
             url: 'https://github.com/google/error-prone/tree/master/annotation',
             licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
+            licenseName: 'Apache 2.0',
+            description: 'ErrorProne Annotations.',),
         com_google_errorprone_error_prone_annotations: new PropertyOverride(
-            url: 'https://github.com/google/error-prone/tree/master/type_annotations',
+            url: 'https://github.com/google/error-prone/tree/master/annotations',
             licenseUrl: 'https://www.apache.org/licenses/LICENSE-2.0.txt',
-            licenseName: 'Apache 2.0'),
+            licenseName: 'Apache 2.0',
+            description: 'ErrorProne Annotations.',),
+        com_google_errorprone_error_prone_type_annotations: new PropertyOverride(
+            url: 'https://github.com/google/error-prone/tree/master/type_annotations',
+            description: 'ErrorProne Annotations.',),
         com_google_errorprone_error_prone_check_api: new PropertyOverride(
             url: 'https://github.com/google/error-prone/tree/master/check_api'),
         com_google_errorprone_error_prone_core: new PropertyOverride(
diff --git a/third_party/android_deps/fetch_all.py b/third_party/android_deps/fetch_all.py
index 864f3bb..8f385ae1 100755
--- a/third_party/android_deps/fetch_all.py
+++ b/third_party/android_deps/fetch_all.py
@@ -54,6 +54,8 @@
 _GRADLEW = os.path.join(_CHROMIUM_SRC, 'third_party', 'gradle_wrapper',
                         'gradlew')
 
+_JAVA_HOME = os.path.join(_CHROMIUM_SRC, 'third_party', 'jdk', 'current')
+
 # Git-controlled files needed by, but not updated by this tool.
 # Relative to _PRIMARY_ANDROID_DEPS_DIR.
 _PRIMARY_ANDROID_DEPS_FILES = [
@@ -141,7 +143,10 @@
   """
     logging.debug('Run %s', args)
     stdout = None if print_stdout else subprocess.PIPE
-    p = subprocess.Popen(args, stdout=stdout, cwd=cwd)
+    # Explicitly set JAVA_HOME since some bots do not have this already set.
+    env = os.environ.copy()
+    env['JAVA_HOME'] = _JAVA_HOME
+    p = subprocess.Popen(args, stdout=stdout, cwd=cwd, env=env)
     pout, _ = p.communicate()
     if p.returncode != 0:
         RaiseCommandException(args, p.returncode, None, pout)
@@ -162,7 +167,13 @@
     messages.
   """
     logging.debug('Run %s', args)
-    p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    # Explicitly set JAVA_HOME since some bots do not have this already set.
+    env = os.environ.copy()
+    env['JAVA_HOME'] = _JAVA_HOME
+    p = subprocess.Popen(args,
+                         stdout=subprocess.PIPE,
+                         stderr=subprocess.PIPE,
+                         env=env)
     pout, perr = p.communicate()
     if p.returncode != 0:
         RaiseCommandException(args, p.returncode, pout, perr)
diff --git a/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation/README.chromium b/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation/README.chromium
index 601315a..7edb228 100644
--- a/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation/README.chromium
+++ b/third_party/android_deps/libs/com_google_errorprone_error_prone_annotation/README.chromium
@@ -1,6 +1,6 @@
 Name: @BugPattern annotation
 Short Name: error_prone_annotation
-URL: https://errorprone.info/
+URL: https://github.com/google/error-prone/tree/master/annotation
 Version: 2.19.1
 License: Apache 2.0
 License File: LICENSE
@@ -9,7 +9,7 @@
 Shipped: no
 
 Description:
-
+ErrorProne Annotations.
 
 Local Modifications:
 No modifications.
diff --git a/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations/README.chromium b/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations/README.chromium
index fb92bf8..df0655db 100644
--- a/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations/README.chromium
+++ b/third_party/android_deps/libs/com_google_errorprone_error_prone_annotations/README.chromium
@@ -1,6 +1,6 @@
 Name: error-prone annotations
 Short Name: error_prone_annotations
-URL: https://github.com/google/error-prone/tree/master/annotation
+URL: https://github.com/google/error-prone/tree/master/annotations
 Version: 2.23.0
 License: Apache 2.0
 License File: LICENSE
@@ -9,7 +9,7 @@
 Shipped: yes
 
 Description:
-
+ErrorProne Annotations.
 
 Local Modifications:
 No modifications.
diff --git a/third_party/android_deps/libs/com_google_errorprone_error_prone_type_annotations/README.chromium b/third_party/android_deps/libs/com_google_errorprone_error_prone_type_annotations/README.chromium
index 3f3f51c..4215e45f 100644
--- a/third_party/android_deps/libs/com_google_errorprone_error_prone_type_annotations/README.chromium
+++ b/third_party/android_deps/libs/com_google_errorprone_error_prone_type_annotations/README.chromium
@@ -9,7 +9,7 @@
 Shipped: no
 
 Description:
-
+ErrorProne Annotations.
 
 Local Modifications:
 No modifications.
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index b57f0eb..a6ed7ad1 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1490,6 +1490,7 @@
   sources += rebase_path(blink_core_tests_fullscreen, "", "fullscreen")
   sources += rebase_path(blink_core_tests_highlight, "", "highlight")
   sources += rebase_path(blink_core_tests_imagebitmap, "", "imagebitmap")
+  sources += rebase_path(blink_core_tests_input, "", "input")
   sources += rebase_path(blink_core_tests_inspector, "", "inspector")
   sources += rebase_path(blink_core_tests_intersection_observer,
                          "",
@@ -1621,6 +1622,10 @@
     "css/css_test_helpers.cc",
     "css/css_test_helpers.h",
     "css/mock_css_paint_image_generator.h",
+    "editing/testing/editing_test_base.cc",
+    "editing/testing/editing_test_base.h",
+    "editing/testing/selection_sample.cc",
+    "editing/testing/selection_sample.h",
     "frame/frame_test_helpers.cc",
     "frame/frame_test_helpers.h",
     "html/media/html_media_test_helper.cc",
diff --git a/third_party/blink/renderer/core/editing/build.gni b/third_party/blink/renderer/core/editing/build.gni
index 38c0334..16bc33b3 100644
--- a/third_party/blink/renderer/core/editing/build.gni
+++ b/third_party/blink/renderer/core/editing/build.gni
@@ -451,11 +451,7 @@
   "state_machines/state_machine_util_test.cc",
   "suggestion/text_suggestion_controller_test.cc",
   "surrounding_text_test.cc",
-  "testing/editing_test_base.cc",
-  "testing/editing_test_base.h",
   "testing/editing_test_base_test.cc",
-  "testing/selection_sample.cc",
-  "testing/selection_sample.h",
   "testing/selection_sample_test.cc",
   "text_offset_mapping_test.cc",
   "visible_position_test.cc",
diff --git a/third_party/blink/renderer/core/fetch/body.cc b/third_party/blink/renderer/core/fetch/body.cc
index db164b7..9b802e5 100644
--- a/third_party/blink/renderer/core/fetch/body.cc
+++ b/third_party/blink/renderer/core/fetch/body.cc
@@ -10,7 +10,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/task/single_thread_task_runner.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
@@ -19,7 +18,6 @@
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/html/forms/form_data.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
-#include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/core/url/url_search_params.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -98,10 +96,7 @@
 
 class BodyBlobConsumer final : public BodyConsumerBase {
  public:
-  explicit BodyBlobConsumer(ScriptPromiseResolver* resolver)
-      : BodyConsumerBase(resolver) {}
-  BodyBlobConsumer(const BodyBlobConsumer&) = delete;
-  BodyBlobConsumer& operator=(const BodyBlobConsumer&) = delete;
+  using BodyConsumerBase::BodyConsumerBase;
 
   void DidFetchDataLoadedBlobHandle(
       scoped_refptr<BlobDataHandle> blob_data_handle) override {
@@ -112,10 +107,7 @@
 
 class BodyArrayBufferConsumer final : public BodyConsumerBase {
  public:
-  explicit BodyArrayBufferConsumer(ScriptPromiseResolver* resolver)
-      : BodyConsumerBase(resolver) {}
-  BodyArrayBufferConsumer(const BodyArrayBufferConsumer&) = delete;
-  BodyArrayBufferConsumer& operator=(const BodyArrayBufferConsumer&) = delete;
+  using BodyConsumerBase::BodyConsumerBase;
 
   void DidFetchDataLoadedArrayBuffer(DOMArrayBuffer* array_buffer) override {
     ResolveLater(WrapPersistent(array_buffer));
@@ -124,34 +116,28 @@
 
 class BodyFormDataConsumer final : public BodyConsumerBase {
  public:
-  explicit BodyFormDataConsumer(ScriptPromiseResolver* resolver)
-      : BodyConsumerBase(resolver) {}
-  BodyFormDataConsumer(const BodyFormDataConsumer&) = delete;
-  BodyFormDataConsumer& operator=(const BodyFormDataConsumer&) = delete;
+  using BodyConsumerBase::BodyConsumerBase;
 
-  void DidFetchDataLoadedFormData(FormData* formData) override {
-    ResolveLater(WrapPersistent(formData));
+  void DidFetchDataLoadedFormData(FormData* form_data) override {
+    ResolveLater(WrapPersistent(form_data));
   }
 
   void DidFetchDataLoadedString(const String& string) override {
-    auto* formData = MakeGarbageCollected<FormData>();
+    auto* form_data = MakeGarbageCollected<FormData>();
     // URLSearchParams::Create() returns an on-heap object, but it can be
     // garbage collected, so making it a persistent variable on the stack
     // mitigates use-after-free scenarios. See crbug.com/1497997.
     Persistent<URLSearchParams> search_params = URLSearchParams::Create(string);
-    for (const auto& pair : search_params->Params()) {
-      formData->append(pair.first, pair.second);
+    for (const auto& [name, value] : search_params->Params()) {
+      form_data->append(name, value);
     }
-    DidFetchDataLoadedFormData(formData);
+    DidFetchDataLoadedFormData(form_data);
   }
 };
 
 class BodyTextConsumer final : public BodyConsumerBase {
  public:
-  explicit BodyTextConsumer(ScriptPromiseResolver* resolver)
-      : BodyConsumerBase(resolver) {}
-  BodyTextConsumer(const BodyTextConsumer&) = delete;
-  BodyTextConsumer& operator=(const BodyTextConsumer&) = delete;
+  using BodyConsumerBase::BodyConsumerBase;
 
   void DidFetchDataLoadedString(const String& string) override {
     ResolveLater(string);
@@ -160,10 +146,7 @@
 
 class BodyJsonConsumer final : public BodyConsumerBase {
  public:
-  explicit BodyJsonConsumer(ScriptPromiseResolver* resolver)
-      : BodyConsumerBase(resolver) {}
-  BodyJsonConsumer(const BodyJsonConsumer&) = delete;
-  BodyJsonConsumer& operator=(const BodyJsonConsumer&) = delete;
+  using BodyConsumerBase::BodyConsumerBase;
 
   void DidFetchDataLoadedString(const String& string) override {
     if (!Resolver()->GetExecutionContext() ||
@@ -184,13 +167,19 @@
   }
 };
 
+FetchDataLoader* CreateLoaderAsStringWithUTF8Decode() {
+  return FetchDataLoader::CreateLoaderAsString(
+      TextResourceDecoderOptions::CreateUTF8Decode());
+}
+
 }  // namespace
 
-ScriptPromise Body::arrayBuffer(ScriptState* script_state,
-                                ExceptionState& exception_state) {
+ScriptPromiseResolver* Body::PrepareToLoadBody(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   RejectInvalidConsumption(exception_state);
   if (exception_state.HadException())
-    return ScriptPromise();
+    return nullptr;
 
   // When the main thread sends a V8::TerminateExecution() signal to a worker
   // thread, any V8 API on the worker thread starts returning an empty
@@ -199,188 +188,139 @@
   // gone (which means that the V8::TerminateExecution() signal has been sent
   // to this worker thread).
   if (!ExecutionContext::From(script_state))
-    return ScriptPromise();
+    return nullptr;
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
+  return MakeGarbageCollected<ScriptPromiseResolver>(
       script_state, exception_state.GetContext());
+}
+
+// `Consumer` must be a subclass of BodyConsumerBase which takes a
+// ScriptPromiseResolver* as its constructor argument. `create_loader` should
+// take no arguments and return a FetchDataLoader*. `on_no_body` should
+// take a ScriptPromiseResolver* object and resolve or reject it, returning
+// nothing.
+template <class Consumer,
+          typename CreateLoaderFunction,
+          typename OnNoBodyFunction>
+ScriptPromise Body::LoadAndConvertBody(ScriptState* script_state,
+                                       CreateLoaderFunction create_loader,
+                                       OnNoBodyFunction on_no_body,
+                                       ExceptionState& exception_state) {
+  auto* resolver = PrepareToLoadBody(script_state, exception_state);
+  if (!resolver) {
+    return ScriptPromise();
+  }
+
   ScriptPromise promise = resolver->Promise();
-  if (BodyBuffer()) {
-    BodyBuffer()->StartLoading(
-        FetchDataLoader::CreateLoaderAsArrayBuffer(),
-        MakeGarbageCollected<BodyArrayBufferConsumer>(resolver),
-        exception_state);
+  if (auto* body_buffer = BodyBuffer()) {
+    body_buffer->StartLoading(create_loader(),
+                              MakeGarbageCollected<Consumer>(resolver),
+                              exception_state);
     if (exception_state.HadException()) {
       // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
       resolver->Resolve();
       return ScriptPromise();
     }
   } else {
-    resolver->Resolve(DOMArrayBuffer::Create(size_t{0}, size_t{0}));
+    on_no_body(resolver);
   }
   return promise;
 }
 
+ScriptPromise Body::arrayBuffer(ScriptState* script_state,
+                                ExceptionState& exception_state) {
+  auto on_no_body = [](ScriptPromiseResolver* resolver) {
+    resolver->Resolve(DOMArrayBuffer::Create(size_t{0}, size_t{0}));
+  };
+
+  return LoadAndConvertBody<BodyArrayBufferConsumer>(
+      script_state, &FetchDataLoader::CreateLoaderAsArrayBuffer, on_no_body,
+      exception_state);
+}
+
 ScriptPromise Body::blob(ScriptState* script_state,
                          ExceptionState& exception_state) {
-  RejectInvalidConsumption(exception_state);
-  if (exception_state.HadException())
-    return ScriptPromise();
-
-  // See above comment.
-  if (!ExecutionContext::From(script_state))
-    return ScriptPromise();
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
-  if (BodyBuffer()) {
+  auto create_loader = [this, script_state]() {
     ExecutionContext* context = ExecutionContext::From(script_state);
-    BodyBuffer()->StartLoading(
-        FetchDataLoader::CreateLoaderAsBlobHandle(
-            MimeType(), context->GetTaskRunner(TaskType::kNetworking)),
-        MakeGarbageCollected<BodyBlobConsumer>(resolver), exception_state);
-    if (exception_state.HadException()) {
-      // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
-      resolver->Resolve();
-      return ScriptPromise();
-    }
-  } else {
+    return FetchDataLoader::CreateLoaderAsBlobHandle(
+        MimeType(), context->GetTaskRunner(TaskType::kNetworking));
+  };
+  auto on_no_body = [this](ScriptPromiseResolver* resolver) {
     auto blob_data = std::make_unique<BlobData>();
     blob_data->SetContentType(MimeType());
     resolver->Resolve(MakeGarbageCollected<Blob>(
         BlobDataHandle::Create(std::move(blob_data), 0)));
-  }
-  return promise;
+  };
+
+  return LoadAndConvertBody<BodyBlobConsumer>(script_state, create_loader,
+                                              on_no_body, exception_state);
 }
 
 ScriptPromise Body::formData(ScriptState* script_state,
                              ExceptionState& exception_state) {
-  RejectInvalidConsumption(exception_state);
-  if (exception_state.HadException())
-    return ScriptPromise();
-
-  // See above comment.
-  if (!ExecutionContext::From(script_state))
-    return ScriptPromise();
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  const ParsedContentType parsedTypeWithParameters(ContentType());
-  const String parsedType = parsedTypeWithParameters.MimeType().LowerASCII();
-  ScriptPromise promise = resolver->Promise();
-  if (parsedType == "multipart/form-data") {
+  auto on_no_body_reject = [script_state](ScriptPromiseResolver* resolver) {
+    resolver->Reject(V8ThrowException::CreateTypeError(
+        script_state->GetIsolate(), "Invalid MIME type"));
+  };
+  const ParsedContentType parsed_type_with_parameters(ContentType());
+  const String parsed_type =
+      parsed_type_with_parameters.MimeType().LowerASCII();
+  if (parsed_type == "multipart/form-data") {
     const String boundary =
-        parsedTypeWithParameters.ParameterValueForName("boundary");
-    auto* body_buffer = BodyBuffer();
-    if (body_buffer && !boundary.empty()) {
-      body_buffer->StartLoading(
-          FetchDataLoader::CreateLoaderAsFormData(boundary),
-          MakeGarbageCollected<BodyFormDataConsumer>(resolver),
-          exception_state);
-      if (exception_state.HadException()) {
-        // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
-        resolver->Resolve();
-        return ScriptPromise();
-      }
-      return promise;
+        parsed_type_with_parameters.ParameterValueForName("boundary");
+    if (!boundary.empty()) {
+      auto create_loader = [&boundary]() {
+        return FetchDataLoader::CreateLoaderAsFormData(boundary);
+      };
+      return LoadAndConvertBody<BodyFormDataConsumer>(
+          script_state, create_loader, on_no_body_reject, exception_state);
     }
-  } else if (parsedType == "application/x-www-form-urlencoded") {
-    if (BodyBuffer()) {
-      // According to https://fetch.spec.whatwg.org/#concept-body-package-data
-      // application/x-www-form-urlencoded FormData bytes are parsed using
-      // https://url.spec.whatwg.org/#concept-urlencoded-parser
-      // which does not decode BOM.
-      BodyBuffer()->StartLoading(
-          FetchDataLoader::CreateLoaderAsString(
-              TextResourceDecoderOptions::CreateUTF8DecodeWithoutBOM()),
-          MakeGarbageCollected<BodyFormDataConsumer>(resolver),
-          exception_state);
-      if (exception_state.HadException()) {
-        // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
-        resolver->Resolve();
-        return ScriptPromise();
-      }
-    } else {
+    auto* resolver = PrepareToLoadBody(script_state, exception_state);
+    if (!resolver) {
+      return ScriptPromise();
+    }
+    on_no_body_reject(resolver);
+    return resolver->Promise();
+  } else if (parsed_type == "application/x-www-form-urlencoded") {
+    auto on_no_body_resolve = [](ScriptPromiseResolver* resolver) {
       resolver->Resolve(MakeGarbageCollected<FormData>());
-    }
-    return promise;
+    };
+    // According to https://fetch.spec.whatwg.org/#concept-body-package-data
+    // application/x-www-form-urlencoded FormData bytes are parsed using
+    // https://url.spec.whatwg.org/#concept-urlencoded-parser
+    // which does not decode BOM.
+    auto create_loader = []() {
+      return FetchDataLoader::CreateLoaderAsString(
+          TextResourceDecoderOptions::CreateUTF8DecodeWithoutBOM());
+    };
+    return LoadAndConvertBody<BodyFormDataConsumer>(
+        script_state, create_loader, on_no_body_resolve, exception_state);
   } else {
-    if (BodyBuffer()) {
-      BodyBuffer()->StartLoading(
-          FetchDataLoader::CreateLoaderAsFailure(),
-          MakeGarbageCollected<BodyFormDataConsumer>(resolver),
-          exception_state);
-      if (exception_state.HadException()) {
-        // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
-        resolver->Resolve();
-        return ScriptPromise();
-      }
-      return promise;
-    }
+    return LoadAndConvertBody<BodyFormDataConsumer>(
+        script_state, &FetchDataLoader::CreateLoaderAsFailure,
+        on_no_body_reject, exception_state);
   }
-
-  resolver->Reject(V8ThrowException::CreateTypeError(script_state->GetIsolate(),
-                                                     "Invalid MIME type"));
-  return promise;
 }
 
 ScriptPromise Body::json(ScriptState* script_state,
                          ExceptionState& exception_state) {
-  RejectInvalidConsumption(exception_state);
-  if (exception_state.HadException())
-    return ScriptPromise();
-
-  // See above comment.
-  if (!ExecutionContext::From(script_state))
-    return ScriptPromise();
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
-  if (BodyBuffer()) {
-    BodyBuffer()->StartLoading(
-        FetchDataLoader::CreateLoaderAsString(
-            TextResourceDecoderOptions::CreateUTF8Decode()),
-        MakeGarbageCollected<BodyJsonConsumer>(resolver), exception_state);
-    if (exception_state.HadException()) {
-      // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
-      resolver->Resolve();
-      return ScriptPromise();
-    }
-  } else {
+  auto on_no_body = [script_state](ScriptPromiseResolver* resolver) {
     resolver->Reject(V8ThrowException::CreateSyntaxError(
         script_state->GetIsolate(), "Unexpected end of input"));
-  }
-  return promise;
+  };
+  return LoadAndConvertBody<BodyJsonConsumer>(
+      script_state, &CreateLoaderAsStringWithUTF8Decode, on_no_body,
+      exception_state);
 }
 
 ScriptPromise Body::text(ScriptState* script_state,
                          ExceptionState& exception_state) {
-  RejectInvalidConsumption(exception_state);
-  if (exception_state.HadException())
-    return ScriptPromise();
-
-  // See above comment.
-  if (!ExecutionContext::From(script_state))
-    return ScriptPromise();
-
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(
-      script_state, exception_state.GetContext());
-  ScriptPromise promise = resolver->Promise();
-  if (BodyBuffer()) {
-    BodyBuffer()->StartLoading(
-        FetchDataLoader::CreateLoaderAsString(
-            TextResourceDecoderOptions::CreateUTF8Decode()),
-        MakeGarbageCollected<BodyTextConsumer>(resolver), exception_state);
-    if (exception_state.HadException()) {
-      // Need to resolve the ScriptPromiseResolver to avoid a DCHECK().
-      resolver->Resolve();
-      return ScriptPromise();
-    }
-  } else {
+  auto on_no_body = [](ScriptPromiseResolver* resolver) {
     resolver->Resolve(String());
-  }
-  return promise;
+  };
+  return LoadAndConvertBody<BodyTextConsumer>(
+      script_state, &CreateLoaderAsStringWithUTF8Decode, on_no_body,
+      exception_state);
 }
 
 ReadableStream* Body::body() {
@@ -393,9 +333,11 @@
     }
   }
 
-  if (!BodyBuffer())
-    return nullptr;
-  return BodyBuffer()->Stream();
+  if (auto* body_buffer = BodyBuffer()) {
+    return body_buffer->Stream();
+  }
+
+  return nullptr;
 }
 
 bool Body::IsBodyUsed() const {
diff --git a/third_party/blink/renderer/core/fetch/body.h b/third_party/blink/renderer/core/fetch/body.h
index 9174b57..94558aa 100644
--- a/third_party/blink/renderer/core/fetch/body.h
+++ b/third_party/blink/renderer/core/fetch/body.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_H_
 
-#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
@@ -19,6 +18,7 @@
 class ExceptionState;
 class ExecutionContext;
 class ReadableStream;
+class ScriptPromiseResolver;
 class ScriptState;
 
 // This class represents Body mix-in defined in the fetch spec
@@ -64,6 +64,23 @@
   // an exception if consumption cannot proceed. The caller must check
   // |exception_state| on return.
   void RejectInvalidConsumption(ExceptionState& exception_state) const;
+
+  // The parts of LoadAndConvertBody() that do not depend on the template
+  // parameters are split into this method to reduce binary size. Returns a
+  // freshly-created ScriptPromiseResolver* on success, or nullptr on error. On
+  // error, LoadAndConvertBody() must not continue.
+  ScriptPromiseResolver* PrepareToLoadBody(ScriptState*, ExceptionState&);
+
+  // Common implementation for body-reading accessors. To maximise performance
+  // at the cost of code size, this is templated on the types of the lambdas
+  // that are passed in.
+  template <class Consumer,
+            typename CreateLoaderFunction,
+            typename OnNoBodyFunction>
+  ScriptPromise LoadAndConvertBody(ScriptState*,
+                                   CreateLoaderFunction,
+                                   OnNoBodyFunction,
+                                   ExceptionState&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/input/ime_on_focus_test.cc b/third_party/blink/renderer/core/input/ime_on_focus_test.cc
index 6f19a1b..31f1225a 100644
--- a/third_party/blink/renderer/core/input/ime_on_focus_test.cc
+++ b/third_party/blink/renderer/core/input/ime_on_focus_test.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -39,6 +40,8 @@
                          const AtomicString& focus_element = g_null_atom,
                          String frame = "");
 
+  test::TaskEnvironment task_environment_;
+
   String base_url_;
   frame_test_helpers::WebViewHelper web_view_helper_;
   Persistent<Document> document_;
diff --git a/third_party/blink/renderer/core/input/touch_action_test.cc b/third_party/blink/renderer/core/input/touch_action_test.cc
index b7d2566..516393b 100644
--- a/third_party/blink/renderer/core/input/touch_action_test.cc
+++ b/third_party/blink/renderer/core/input/touch_action_test.cc
@@ -55,6 +55,7 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/testing/url_loader_mock_factory.h"
 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
@@ -144,6 +145,8 @@
   WebViewImpl* SetupTest(String file);
   void RunTestOnTree(ContainerNode* root, WebView*);
 
+  test::TaskEnvironment task_environment_;
+
   String base_url_;
   frame_test_helpers::WebViewHelper web_view_helper_;
 };
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 027cedb..5130d312 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -1017,6 +1017,7 @@
     "//third_party/blink/renderer/modules/peerconnection:test_support",
     "//third_party/blink/renderer/modules/storage:unit_tests",
     "//third_party/blink/renderer/modules/webgpu:unit_tests",
+    "//third_party/blink/renderer/modules/webtransport:unit_tests",
     "//third_party/blink/renderer/platform",
     "//third_party/blink/renderer/platform:test_support",
     "//third_party/blink/renderer/platform/wtf",
diff --git a/third_party/blink/renderer/modules/ml/BUILD.gn b/third_party/blink/renderer/modules/ml/BUILD.gn
index 93aba60..beb9ebd2 100644
--- a/third_party/blink/renderer/modules/ml/BUILD.gn
+++ b/third_party/blink/renderer/modules/ml/BUILD.gn
@@ -27,6 +27,8 @@
     "ml_model.h",
     "ml_model_loader.cc",
     "ml_model_loader.h",
+    "ml_trace.cc",
+    "ml_trace.h",
     "navigator_ml.cc",
     "navigator_ml.h",
     "webnn/ml_activation.cc",
@@ -98,6 +100,7 @@
     "ml_model_loader_test.cc",
     "ml_model_loader_test_util.cc",
     "ml_model_loader_test_util.h",
+    "ml_trace_unittest.cc",
     "webnn/ml_graph_builder_test.cc",
     "webnn/ml_graph_builder_test.h",
     "webnn/ml_graph_builder_utils.cc",
diff --git a/third_party/blink/renderer/modules/ml/DEPS b/third_party/blink/renderer/modules/ml/DEPS
index 81326f0..0afcbf7 100644
--- a/third_party/blink/renderer/modules/ml/DEPS
+++ b/third_party/blink/renderer/modules/ml/DEPS
@@ -3,4 +3,14 @@
   "+components/ml/mojom",
   "+components/ml/webnn",
   "+services/webnn/public/mojom",
-]
\ No newline at end of file
+]
+
+specific_include_rules = {
+  # Additional allowed list of include for ml trace unit test
+  # we need them for parsing the trace.
+  "ml_trace_unittest.cc" : [
+    "+base/json/json_reader.h",
+    "+base/memory/ref_counted_memory.h",
+    "+base/strings/stringprintf.h",
+  ],
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/ml/ml.cc b/third_party/blink/renderer/modules/ml/ml.cc
index e696efe37..7cd67ab 100644
--- a/third_party/blink/renderer/modules/ml/ml.cc
+++ b/third_party/blink/renderer/modules/ml/ml.cc
@@ -72,6 +72,7 @@
 ScriptPromise ML::createContext(ScriptState* script_state,
                                 MLContextOptions* options,
                                 ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("ML::createContext");
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Invalid script state");
@@ -102,6 +103,7 @@
 MLContext* ML::createContextSync(ScriptState* script_state,
                                  MLContextOptions* options,
                                  ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("ML::createContextSync");
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Invalid script state");
diff --git a/third_party/blink/renderer/modules/ml/ml_context.cc b/third_party/blink/renderer/modules/ml/ml_context.cc
index 06759513..badda38a 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.cc
+++ b/third_party/blink/renderer/modules/ml/ml_context.cc
@@ -91,6 +91,7 @@
                                  const MLNamedArrayBufferViews& inputs,
                                  const MLNamedArrayBufferViews& outputs,
                                  ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("MLContext::compute");
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Invalid script state");
@@ -106,7 +107,8 @@
         DOMExceptionCode::kDataError,
         "The graph isn't built within this context."));
   } else {
-    graph->ComputeAsync(inputs, outputs, resolver, exception_state);
+    graph->ComputeAsync(std::move(scoped_trace), inputs, outputs, resolver,
+                        exception_state);
   }
 
   return promise;
@@ -116,6 +118,7 @@
                             const MLNamedArrayBufferViews& inputs,
                             const MLNamedArrayBufferViews& outputs,
                             ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("MLContext::computeSync");
   if (graph->Context() != this) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kDataError,
@@ -125,9 +128,10 @@
   graph->ComputeSync(inputs, outputs, exception_state);
 }
 
-void MLContext::CreateAsync(ScriptPromiseResolver* resolver,
+void MLContext::CreateAsync(ScopedMLTrace scoped_trace,
+                            ScriptPromiseResolver* resolver,
                             MLContextOptions* options) {
-  CreateAsyncImpl(resolver, options);
+  CreateAsyncImpl(std::move(scoped_trace), resolver, options);
 }
 
 MLContext* MLContext::CreateSync(ScriptState* script_state,
@@ -136,7 +140,8 @@
   return CreateSyncImpl(script_state, options, exception_state);
 }
 
-void MLContext::CreateAsyncImpl(ScriptPromiseResolver* resolver,
+void MLContext::CreateAsyncImpl(ScopedMLTrace scoped_trace,
+                                ScriptPromiseResolver* resolver,
                                 MLContextOptions* options) {
   // TODO(crbug.com/1273291): Remove when async creation gets implemented for
   // all context types.
diff --git a/third_party/blink/renderer/modules/ml/ml_context.h b/third_party/blink/renderer/modules/ml/ml_context.h
index 18c2c460..5e56bd7 100644
--- a/third_party/blink/renderer/modules/ml/ml_context.h
+++ b/third_party/blink/renderer/modules/ml/ml_context.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_model_format.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_power_preference.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -74,11 +75,14 @@
   // Create and initialize a MLContext object. Resolve the promise with
   // this concrete object if the underlying context gets created
   // successfully.
-  void CreateAsync(ScriptPromiseResolver* resolver, MLContextOptions* options);
+  void CreateAsync(ScopedMLTrace scoped_trace,
+                   ScriptPromiseResolver* resolver,
+                   MLContextOptions* options);
 
   // An MLContext backend should implement this method to create and initialize
   // a platform specific context asynchronously.
-  virtual void CreateAsyncImpl(ScriptPromiseResolver* resolver,
+  virtual void CreateAsyncImpl(ScopedMLTrace scoped_trace,
+                               ScriptPromiseResolver* resolver,
                                MLContextOptions* options);
 
   // CreateSync() has the similar function as CreateAsync(). The difference is
diff --git a/third_party/blink/renderer/modules/ml/ml_trace.cc b/third_party/blink/renderer/modules/ml/ml_trace.cc
new file mode 100644
index 0000000..a547d8d
--- /dev/null
+++ b/third_party/blink/renderer/modules/ml/ml_trace.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_id_helper.h"
+
+namespace blink {
+
+constexpr char kWebNNTraceCategory[] = "webnn";
+
+// Reset the |id_| so the moved `ScopedMLTrace` object won't end the trace
+// prematurely on destruction.
+ScopedMLTrace::ScopedMLTrace(ScopedMLTrace&& other)
+    : name_(other.name_),
+      id_(std::exchange(other.id_, absl::nullopt)),
+      step_(std::move(other.step_)) {}
+
+ScopedMLTrace::~ScopedMLTrace() {
+  if (id_.has_value()) {
+    TRACE_EVENT_NESTABLE_ASYNC_END0(kWebNNTraceCategory, name_,
+                                    TRACE_ID_LOCAL(id_.value()));
+  }
+}
+
+ScopedMLTrace& ScopedMLTrace::operator=(ScopedMLTrace&& other) {
+  if (this != &other) {
+    name_ = other.name_;
+    id_ = std::exchange(other.id_, absl::nullopt);
+    step_ = std::move(other.step_);
+  }
+  return *this;
+}
+
+void ScopedMLTrace::AddStep(const char* step_name) {
+  // Calling AddStep() after move is not allowed.
+  CHECK(id_.has_value());
+  step_.reset();
+  step_ = base::WrapUnique(new ScopedMLTrace(step_name, id_.value()));
+}
+
+ScopedMLTrace::ScopedMLTrace(const char* name)
+    : ScopedMLTrace(name, base::trace_event::GetNextGlobalTraceId()) {}
+
+ScopedMLTrace::ScopedMLTrace(const char* name, uint64_t id)
+    : name_(name), id_(id) {
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(kWebNNTraceCategory, name_,
+                                    TRACE_ID_LOCAL(id_.value()));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/ml_trace.h b/third_party/blink/renderer/modules/ml/ml_trace.h
new file mode 100644
index 0000000..dc2f3f00
--- /dev/null
+++ b/third_party/blink/renderer/modules/ml/ml_trace.h
@@ -0,0 +1,63 @@
+// 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 THIRD_PARTY_BLINK_RENDERER_MODULES_ML_ML_TRACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_ML_ML_TRACE_H_
+
+#include <memory>
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+
+namespace blink {
+
+// The trace starts when an object of this class is created, and ends when
+// the object goes out scope.
+// You should 'std::move' this object when binding callbacks. The trace ends
+// if the callback is destroyed (even if it's not run).
+//
+// Methods in this class is safe to be called from any threads.
+class MODULES_EXPORT ScopedMLTrace {
+ public:
+  // Create a ScopedAsyncTrace instance.
+  //
+  // Important note: Use literal strings only. See trace_event_common.h.
+  explicit ScopedMLTrace(const char* name);
+  ScopedMLTrace(ScopedMLTrace&& other);
+  ScopedMLTrace& operator=(ScopedMLTrace&& other);
+  ScopedMLTrace(const ScopedMLTrace&) = delete;
+  ScopedMLTrace& operator=(const ScopedMLTrace&) = delete;
+  ~ScopedMLTrace();
+
+  // Starts a nested sub-trace in the current trace. The next AddStep() call
+  // will mark the end of the previous sub-trace.
+  void AddStep(const char* step_name);
+
+ private:
+  ScopedMLTrace(const char* name, uint64_t id);
+
+  const char* name_;
+
+  // The trace ID.
+  //
+  // An 'absl::nullopt' means the trace has been transferred to another
+  // 'ScopedMLTrace' object, and stops 'this''s destruction from ending the
+  // trace.
+  absl::optional<uint64_t> id_;
+  std::unique_ptr<ScopedMLTrace> step_;
+};
+
+}  // namespace blink
+
+namespace WTF {
+
+template <>
+struct CrossThreadCopier<blink::ScopedMLTrace>
+    : public WTF::CrossThreadCopierByValuePassThrough<blink::ScopedMLTrace> {};
+
+}  // namespace WTF
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_ML_ML_TRACE_H_
diff --git a/third_party/blink/renderer/modules/ml/ml_trace_unittest.cc b/third_party/blink/renderer/modules/ml/ml_trace_unittest.cc
new file mode 100644
index 0000000..a98e0a89
--- /dev/null
+++ b/third_party/blink/renderer/modules/ml/ml_trace_unittest.cc
@@ -0,0 +1,366 @@
+// 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 "third_party/blink/renderer/modules/ml/ml_trace.h"
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/task/bind_post_task.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/trace_event/trace_buffer.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_log.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+
+namespace blink {
+
+class ScopedMLTraceTest : public testing::Test {
+ public:
+  ScopedMLTraceTest() = default;
+
+  ~ScopedMLTraceTest() override = default;
+
+  void SetUp() override {
+    test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
+  }
+
+  void TearDown() override { base::trace_event::TraceLog::ResetForTesting(); }
+
+ protected:
+  void StartTracing(const std::string& filter) {
+    base::trace_event::TraceLog::GetInstance()->SetEnabled(
+        base::trace_event::TraceConfig(filter,
+                                       base::trace_event::RECORD_UNTIL_FULL),
+        base::trace_event::TraceLog::RECORDING_MODE);
+  }
+
+  static void TraceDataCb(
+      base::OnceClosure quit_closure,
+      base::trace_event::TraceResultBuffer::SimpleOutput* json_output,
+      const scoped_refptr<base::RefCountedString>& json_events_str,
+      bool has_more_events) {
+    base::trace_event::TraceResultBuffer trace_buffer;
+    trace_buffer.SetOutputCallback(json_output->GetCallback());
+    trace_buffer.Start();
+    trace_buffer.AddFragment(json_events_str->data());
+    trace_buffer.Finish();
+    if (!has_more_events) {
+      std::move(quit_closure).Run();
+    }
+  }
+
+  // End tracing, return tracing data in a map of event
+  // name->(begin_event_counts, end_event_counts)
+  std::map<std::string, std::pair<int, int>> EndTracing() {
+    std::map<std::string, std::pair<int, int>> event_counts;
+    base::trace_event::TraceResultBuffer::SimpleOutput json_data;
+    base::trace_event::TraceLog::GetInstance()->SetDisabled();
+    base::RunLoop run_loop;
+    base::trace_event::TraceLog::GetInstance()->Flush(base::BindRepeating(
+        &ScopedMLTraceTest::TraceDataCb, run_loop.QuitClosure(), &json_data));
+    run_loop.Run();
+
+    auto parsed_json =
+        base::JSONReader::ReadAndReturnValueWithError(json_data.json_output);
+    CHECK(parsed_json.has_value())
+        << "JSON parsing failed (" << parsed_json.error().message
+        << ") JSON data:" << std::endl
+        << json_data.json_output;
+
+    CHECK(parsed_json->is_list());
+    for (const base::Value& entry : parsed_json->GetList()) {
+      const auto& dict = entry.GetDict();
+      const std::string* name = dict.FindString("name");
+      CHECK(name);
+      const std::string* trace_type = dict.FindString("ph");
+      CHECK(trace_type);
+      // Count both the "BEGIN" and "END" traces.
+      if (*trace_type != "E" && *trace_type != "e") {
+        ((event_counts)[*name].first)++;
+      } else {
+        ((event_counts)[*name].second)++;
+      }
+    }
+    return event_counts;
+  }
+
+  // The task runner we use for posting tasks.
+  scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
+};
+
+TEST_F(ScopedMLTraceTest, SingleScopeWithoutStep) {
+  {
+    // Check the behavior without move. Both begin/end event should be seen.
+    StartTracing("webnn");
+    { ScopedMLTrace scoped_trace1("Method1"); }
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+  }
+
+  {
+    // Check the behavior with move assign. Both begin/end event should be seen.
+    StartTracing("webnn");
+    {
+      ScopedMLTrace scoped_trace1("Method1");
+      ScopedMLTrace scoped_trace2 = std::move(scoped_trace1);
+    }
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+  }
+
+  {
+    // Check the behavior with move ctor, similar as move assign.
+    StartTracing("webnn");
+    {
+      ScopedMLTrace scoped_trace1("Method1");
+      ScopedMLTrace scoped_trace2(std::move(scoped_trace1));
+    }
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+  }
+
+  {
+    // Move should not trigger an immediate end event.
+    StartTracing("webnn");
+    {
+      ScopedMLTrace scoped_trace1("Method1");
+      ScopedMLTrace scoped_trace2 = std::move(scoped_trace1);
+      auto event_counts = EndTracing();
+      auto [method_begins, method_ends] = event_counts.at("Method1");
+      EXPECT_EQ(1, method_begins);
+      EXPECT_EQ(0, method_ends);
+    }
+  }
+}
+
+// Both main trace and sub-trace should have pairing begin/end.
+TEST_F(ScopedMLTraceTest, SingleScopeWithStep) {
+  StartTracing("webnn");
+  {
+    ScopedMLTrace scoped_trace1("Method1");
+    scoped_trace1.AddStep("Step1");
+    ScopedMLTrace scoped_trace2 = std::move(scoped_trace1);
+  }
+  auto event_counts = EndTracing();
+
+  auto [method_begins, method_ends] = event_counts.at("Method1");
+  auto [step_begins, step_ends] = event_counts.at("Step1");
+  EXPECT_EQ(1, method_begins);
+  EXPECT_EQ(1, method_ends);
+  EXPECT_EQ(1, step_begins);
+  EXPECT_EQ(1, step_ends);
+}
+
+// Multiple steps should results in multiple begin/end pairs.
+TEST_F(ScopedMLTraceTest, MultipleAddSteps) {
+  StartTracing("webnn");
+  {
+    ScopedMLTrace scoped_trace1("Method1");
+    scoped_trace1.AddStep("Step1");
+    scoped_trace1.AddStep("Step2");
+    ScopedMLTrace scoped_trace2(std::move(scoped_trace1));
+    scoped_trace2.AddStep("Step3");
+  }
+  auto event_counts = EndTracing();
+
+  auto [method1_begins, method1_ends] = event_counts.at("Method1");
+  auto [step1_begins, step1_ends] = event_counts.at("Step1");
+  auto [step2_begins, step2_ends] = event_counts.at("Step2");
+  auto [step3_begins, step3_ends] = event_counts.at("Step3");
+  EXPECT_EQ(1, method1_begins);
+  EXPECT_EQ(1, method1_ends);
+  EXPECT_EQ(1, step1_begins);
+  EXPECT_EQ(1, step1_ends);
+  EXPECT_EQ(1, step2_begins);
+  EXPECT_EQ(1, step2_ends);
+  EXPECT_EQ(1, step3_begins);
+  EXPECT_EQ(1, step3_ends);
+}
+
+// Nesting top-level traces should have pairing begin/end.
+TEST_F(ScopedMLTraceTest, MultipleNestedTraces) {
+  StartTracing("webnn");
+  {
+    ScopedMLTrace scoped_trace1("Method1");
+    { ScopedMLTrace scoped_trace2("Method2"); }
+  }
+  auto event_counts = EndTracing();
+
+  auto [method1_begins, method1_ends] = event_counts.at("Method1");
+  auto [method2_begins, method2_ends] = event_counts.at("Method2");
+  EXPECT_EQ(1, method1_begins);
+  EXPECT_EQ(1, method1_ends);
+  EXPECT_EQ(1, method2_begins);
+  EXPECT_EQ(1, method2_ends);
+}
+
+// Trace handle should be passed correct across function boundaries.
+TEST_F(ScopedMLTraceTest, PassScopedTraceToFunc) {
+  {
+    // Pass to another function that does not add extra step.
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace1("Method1");
+    ([](ScopedMLTrace trace) {})(std::move(scoped_trace1));
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    method_ends = event_counts["Method1"].second;
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+  }
+
+  {
+    // Pass to another function call that adds extra step.
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace2("Method1");
+    ([](ScopedMLTrace trace) { trace.AddStep("Step1"); })(
+        std::move(scoped_trace2));
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    auto [step_begins, step_ends] = event_counts.at("Step1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+    EXPECT_EQ(1, step_begins);
+    EXPECT_EQ(1, step_ends);
+  }
+}
+
+// Trace handle should be passed correctly by posting tasks.
+TEST_F(ScopedMLTraceTest, WorksWithPostCrossThreadTask) {
+  {
+    // Post to another thread that does not add extra step.
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace1("Method1");
+    PostCrossThreadTask(*test_task_runner_, FROM_HERE,
+                        CrossThreadBindOnce([](ScopedMLTrace trace) {},
+                                            std::move(scoped_trace1)));
+    test_task_runner_->RunUntilIdle();
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+  }
+
+  {
+    // Post to another thread that adds extra step.
+    base::trace_event::TraceLog::ResetForTesting();
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace2("Method1");
+    PostCrossThreadTask(
+        *test_task_runner_, FROM_HERE,
+        CrossThreadBindOnce([](ScopedMLTrace trace) { trace.AddStep("Step1"); },
+                            std::move(scoped_trace2)));
+    test_task_runner_->RunUntilIdle();
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    auto [step_begins, step_ends] = event_counts.at("Step1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+    EXPECT_EQ(1, step_begins);
+    EXPECT_EQ(1, step_ends);
+  }
+
+  {
+    // Add step first, and post to another thread without adding step.
+    base::trace_event::TraceLog::ResetForTesting();
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace3("Method1");
+    scoped_trace3.AddStep("Step1");
+    PostCrossThreadTask(*test_task_runner_, FROM_HERE,
+                        CrossThreadBindOnce([](ScopedMLTrace trace) {},
+                                            std::move(scoped_trace3)));
+    test_task_runner_->RunUntilIdle();
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    auto [step_begins, step_ends] = event_counts.at("Step1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+    EXPECT_EQ(1, step_begins);
+    EXPECT_EQ(1, step_ends);
+  }
+
+  {
+    // Add step first, and post to another thread that adds step.
+    base::trace_event::TraceLog::ResetForTesting();
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace4("Method1");
+    scoped_trace4.AddStep("Step1");
+    PostCrossThreadTask(
+        *test_task_runner_, FROM_HERE,
+        CrossThreadBindOnce([](ScopedMLTrace trace) { trace.AddStep("Step2"); },
+                            std::move(scoped_trace4)));
+    test_task_runner_->RunUntilIdle();
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    auto [step_begins, step_ends] = event_counts.at("Step1");
+    auto [step_in_task_begins, step_in_task_ends] = event_counts["Step2"];
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+    EXPECT_EQ(1, step_begins);
+    EXPECT_EQ(1, step_ends);
+    EXPECT_EQ(1, step_in_task_begins);
+    EXPECT_EQ(1, step_in_task_ends);
+  }
+}
+
+TEST_F(ScopedMLTraceTest, WorksWithBindOnce) {
+  {
+    // Invoke BindOnce without adding extra step.
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace1("Method1");
+    test_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce([](ScopedMLTrace trace) {}, std::move(scoped_trace1)));
+    test_task_runner_->RunUntilIdle();
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+  }
+
+  {
+    // Invoke BindOnce and add extra step.
+    StartTracing("webnn");
+    ScopedMLTrace scoped_trace2("Method1");
+    test_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce([](ScopedMLTrace trace) { trace.AddStep("Step1"); },
+                       std::move(scoped_trace2)));
+    test_task_runner_->RunUntilIdle();
+    auto event_counts = EndTracing();
+
+    auto [method_begins, method_ends] = event_counts.at("Method1");
+    auto [step_begins, step_ends] = event_counts.at("Step1");
+    EXPECT_EQ(1, method_begins);
+    EXPECT_EQ(1, method_ends);
+    EXPECT_EQ(1, step_begins);
+    EXPECT_EQ(1, step_ends);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.cc b/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.cc
index 888d01b..21e3351 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.cc
@@ -34,6 +34,7 @@
 void MLContextMojo::ValidateAndCreateAsync(ScriptPromiseResolver* resolver,
                                            MLContextOptions* options,
                                            ML* ml) {
+  ScopedMLTrace scoped_trace("MLContextMojo::ValidateAndCreateAsync");
   CHECK_EQ(options->deviceType(), V8MLDeviceType::Enum::kGpu);
   // TODO(crbug.com/1273291): Remove unsupported options (ex. model_format)
   // once the context gets implemented for non-mojo too.
@@ -41,7 +42,7 @@
       options->devicePreference(), options->deviceType(),
       options->powerPreference(), options->modelFormat(), options->numThreads(),
       ml);
-  context->CreateAsync(resolver, options);
+  context->CreateAsync(std::move(scoped_trace), resolver, options);
 }
 
 // static
@@ -57,7 +58,8 @@
   return context->CreateSync(script_state, options, exception_state);
 }
 
-void MLContextMojo::CreateAsyncImpl(ScriptPromiseResolver* resolver,
+void MLContextMojo::CreateAsyncImpl(ScopedMLTrace scoped_trace,
+                                    ScriptPromiseResolver* resolver,
                                     MLContextOptions* options) {
   auto options_mojo = webnn::mojom::blink::CreateContextOptions::New();
   options_mojo->power_preference =
@@ -65,7 +67,7 @@
   GetML()->CreateWebNNContext(
       std::move(options_mojo),
       WTF::BindOnce(&MLContextMojo::OnCreateWebNNContext, WrapPersistent(this),
-                    WrapPersistent(resolver)));
+                    std::move(scoped_trace), WrapPersistent(resolver)));
 }
 
 MLContext* MLContextMojo::CreateSyncImpl(ScriptState* script_state,
@@ -140,6 +142,7 @@
 }
 
 void MLContextMojo::OnCreateWebNNContext(
+    ScopedMLTrace scoped_trace,
     ScriptPromiseResolver* resolver,
     blink_mojom::CreateContextResultPtr result) {
   if (result->is_error()) {
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.h b/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.h
index 4b1720f..52811b5 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.h
@@ -8,6 +8,7 @@
 #include "services/webnn/public/mojom/webnn_context_provider.mojom-blink.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom-blink.h"
 #include "third_party/blink/renderer/modules/ml/ml_context.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 
 namespace blink {
@@ -61,7 +62,8 @@
   // Create `WebNNContext` message pipe with `ML` mojo interface, then
   // create the context with the hardware accelerated OS machine
   // learning API in the WebNN Service.
-  void CreateAsyncImpl(ScriptPromiseResolver* resolver,
+  void CreateAsyncImpl(ScopedMLTrace scoped_trace,
+                       ScriptPromiseResolver* resolver,
                        MLContextOptions* options) override;
 
   // Create `WebNNContext` message pipe with `ML` mojo interface, then
@@ -75,7 +77,8 @@
   // The callback of creating `WebNNContext` mojo interface from WebNN Service.
   // Return `CreateContextResult::kNotSupported` on non-supported input
   // configuration.
-  void OnCreateWebNNContext(ScriptPromiseResolver* resolver,
+  void OnCreateWebNNContext(ScopedMLTrace scoped_trace,
+                            ScriptPromiseResolver* resolver,
                             webnn::mojom::blink::CreateContextResultPtr result);
 
   // The `WebNNContext` is a initialized context that can be used by the
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc
index 629b389..ec7cc83 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph.cc
@@ -93,7 +93,8 @@
   return output_resources_info_;
 }
 
-void MLGraph::ComputeAsync(const MLNamedArrayBufferViews& inputs,
+void MLGraph::ComputeAsync(ScopedMLTrace scoped_trace,
+                           const MLNamedArrayBufferViews& inputs,
                            const MLNamedArrayBufferViews& outputs,
                            ScriptPromiseResolver* resolver,
                            ExceptionState& exception_state) {
@@ -116,7 +117,8 @@
   }
 
   // Call ComputeAsyncImpl() implemented by an MLGraph backend.
-  ComputeAsyncImpl(inputs, outputs, resolver, exception_state);
+  ComputeAsyncImpl(std::move(scoped_trace), inputs, outputs, resolver,
+                   exception_state);
 }
 
 void MLGraph::ComputeSync(const MLNamedArrayBufferViews& inputs,
@@ -124,7 +126,7 @@
                           ExceptionState& exception_state) {
   // The MLGraph object should be initialized before computing.
   DCHECK(resources_info_initialized_);
-
+  ScopedMLTrace scoped_trace("MLGraph::ComputeSync");
   // Validate the input and output MLNamedArrayBufferViews.
   String error_message;
   if (!ValidateNamedArrayBufferViews(inputs, input_resources_info_,
@@ -144,7 +146,8 @@
   ComputeSyncImpl(inputs, outputs, exception_state);
 }
 
-void MLGraph::BuildAsync(const MLNamedOperands& named_outputs,
+void MLGraph::BuildAsync(ScopedMLTrace scoped_trace,
+                         const MLNamedOperands& named_outputs,
                          ScriptPromiseResolver* resolver) {
   String error_message;
   if (!ValidateAndInitializeResourcesInfo(named_outputs, error_message)) {
@@ -152,12 +155,13 @@
         DOMExceptionCode::kDataError, error_message));
     return;
   }
-  BuildAsyncImpl(named_outputs, resolver);
+  BuildAsyncImpl(std::move(scoped_trace), named_outputs, resolver);
 }
 
 MLGraph* MLGraph::BuildSync(ScriptState* script_state,
                             const MLNamedOperands& named_outputs,
                             ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("MLGraph::BuildSync");
   String error_message;
   if (!ValidateAndInitializeResourcesInfo(named_outputs, error_message)) {
     exception_state.ThrowDOMException(DOMExceptionCode::kDataError,
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph.h
index 8544706..b0f3081 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ML_WEBNN_ML_GRAPH_H_
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_operand_descriptor.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -56,7 +57,8 @@
   // ones to ComputeAsyncImpl() implemented by an MLGraph backend that binds the
   // array buffer views and executes the compiled platform graph. This method is
   // called by MLContext to implement MLContext.compute() method.
-  void ComputeAsync(const MLNamedArrayBufferViews& inputs,
+  void ComputeAsync(ScopedMLTrace scoped_trace,
+                    const MLNamedArrayBufferViews& inputs,
                     const MLNamedArrayBufferViews& outputs,
                     ScriptPromiseResolver* resolver,
                     ExceptionState& exception_state);
@@ -80,7 +82,8 @@
   // the input and output resources info. If there are no errors, it calls
   // BuildAsyncImpl() implemented by an MLGraph backend that builds the platform
   // specific graph.
-  void BuildAsync(const MLNamedOperands& named_outputs,
+  void BuildAsync(ScopedMLTrace scoped_trace,
+                  const MLNamedOperands& named_outputs,
                   ScriptPromiseResolver* resolver);
 
   // An MLGraph backend should implement this method to build and compile a
@@ -89,7 +92,8 @@
   // main thread. Once the platform graph is compiled, the resolver should be
   // resolved with a concrete MLGraph object. Otherwise, the resolver should be
   // rejected with a DOMException accordingly.
-  virtual void BuildAsyncImpl(const MLNamedOperands& outputs,
+  virtual void BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                              const MLNamedOperands& outputs,
                               ScriptPromiseResolver* resolver) = 0;
 
   // BuildSync() has the similar function as BuildAsync() and should also be
@@ -121,7 +125,8 @@
   // the resolver will be resolved with an MLComputeResult that contains the
   // input and output buffers. Otherwise, the resolver will be rejected with a
   // DOMException accordingly.
-  virtual void ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+  virtual void ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                                const MLNamedArrayBufferViews& inputs,
                                 const MLNamedArrayBufferViews& outputs,
                                 ScriptPromiseResolver* resolver,
                                 ExceptionState& exception_state) = 0;
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
index a037bf42..0299d26 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc
@@ -1680,6 +1680,7 @@
 ScriptPromise MLGraphBuilder::build(ScriptState* script_state,
                                     const MLNamedOperands& named_outputs,
                                     ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("MLGraphBuilder::build");
   if (!script_state->ContextIsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Invalid script state");
@@ -1698,7 +1699,8 @@
 
 #if BUILDFLAG(BUILD_WEBNN_WITH_XNNPACK)
   if (ml_context_->GetDeviceType() == V8MLDeviceType::Enum::kCpu) {
-    MLGraphXnnpack::ValidateAndBuildAsync(ml_context_, named_outputs, resolver);
+    MLGraphXnnpack::ValidateAndBuildAsync(std::move(scoped_trace), ml_context_,
+                                          named_outputs, resolver);
     return promise;
   }
 #endif
@@ -1706,7 +1708,8 @@
 #if BUILDFLAG(BUILD_WEBNN_ON_CROS)
   // On ChromeOS, ML model inferencing is off-loaded to ModelLoader service.
   if (ml_context_->GetDeviceType() == V8MLDeviceType::Enum::kCpu) {
-    MLGraphCrOS::ValidateAndBuildAsync(ml_context_, named_outputs, resolver);
+    MLGraphCrOS::ValidateAndBuildAsync(std::move(scoped_trace), ml_context_,
+                                       named_outputs, resolver);
     return promise;
   }
 #endif
@@ -1718,8 +1721,8 @@
     // GetInterface() method before creating `WebNNGraph` message pipe.
     MLContextMojo* ml_context_mojo =
         static_cast<MLContextMojo*>(ml_context_.Get());
-    MLGraphMojo::ValidateAndBuildAsync(ml_context_mojo, named_outputs,
-                                       resolver);
+    MLGraphMojo::ValidateAndBuildAsync(std::move(scoped_trace), ml_context_mojo,
+                                       named_outputs, resolver);
     return promise;
   }
 #endif
@@ -1732,6 +1735,7 @@
 MLGraph* MLGraphBuilder::buildSync(ScriptState* script_state,
                                    const MLNamedOperands& named_outputs,
                                    ExceptionState& exception_state) {
+  ScopedMLTrace scoped_trace("MLGraphBuilder::buildSync");
   if (g_backend_for_testing) {
     return g_backend_for_testing->BuildGraphSyncImpl(
         script_state, ml_context_, named_outputs, exception_state);
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc
index 88bda23..cabd7dc 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_builder_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ml_operand_descriptor.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_activation.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.h"
@@ -6546,7 +6547,7 @@
                                     const MLNamedOperands& named_outputs,
                                     ScriptPromiseResolver* resolver) {
     auto* graph = MakeGarbageCollected<FakeMLGraphBackend>(context);
-    graph->BuildAsync(named_outputs, resolver);
+    graph->BuildAsync(ScopedMLTrace("BuildAsync"), named_outputs, resolver);
   }
 
   // Create and build a FakeMLGraphBackend object synchronously.
@@ -6567,7 +6568,8 @@
  private:
   // Resolve the promise with this FakeMLGraphBackend object for testing the
   // input and output resources info.
-  void BuildAsyncImpl(const MLNamedOperands& named_outputs,
+  void BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                      const MLNamedOperands& named_outputs,
                       ScriptPromiseResolver* resolver) override {
     resolver->Resolve(this);
   }
@@ -6582,7 +6584,8 @@
 
   // Resolve the promise for testing the validation of inputs and outputs in
   // MLGraph::ComputeAsync().
-  void ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+  void ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                        const MLNamedArrayBufferViews& inputs,
                         const MLNamedArrayBufferViews& outputs,
                         ScriptPromiseResolver* resolver,
                         ExceptionState& exception_state) override {
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.cc
index c7875d4..ee08ce3 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.cc
@@ -118,14 +118,16 @@
 }  // namespace
 
 // static
-void MLGraphCrOS::ValidateAndBuildAsync(MLContext* ml_context,
+void MLGraphCrOS::ValidateAndBuildAsync(ScopedMLTrace scoped_trace,
+                                        MLContext* ml_context,
                                         const MLNamedOperands& named_outputs,
                                         ScriptPromiseResolver* resolver) {
+  scoped_trace.AddStep("MLGraphCrOS::ValidateAndBuildAsync");
   auto* script_state = resolver->GetScriptState();
   auto* execution_context = ExecutionContext::From(script_state);
   auto* graph =
       MakeGarbageCollected<MLGraphCrOS>(execution_context, ml_context);
-  graph->BuildAsync(named_outputs, resolver);
+  graph->BuildAsync(std::move(scoped_trace), named_outputs, resolver);
 }
 
 MLGraphCrOS::MLGraphCrOS(ExecutionContext* execution_context,
@@ -139,7 +141,8 @@
   MLGraph::Trace(visitor);
 }
 
-void MLGraphCrOS::BuildAsyncImpl(const MLNamedOperands& outputs,
+void MLGraphCrOS::BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                                 const MLNamedOperands& outputs,
                                  ScriptPromiseResolver* resolver) {
   DOMArrayBuffer* buffer = nullptr;
   if (g_flatbuffer_for_testing) {
@@ -162,11 +165,12 @@
   ml_model_loader->Load(
       script_state, buffer,
       WTF::BindOnce(&MLGraphCrOS::OnRemoteModelLoad, WrapPersistent(this),
-                    WrapPersistent(execution_context),
+                    std::move(scoped_trace), WrapPersistent(execution_context),
                     WrapPersistent(resolver)));
 }
 
 void MLGraphCrOS::OnRemoteModelLoad(
+    ScopedMLTrace scoped_trace,
     ExecutionContext* execution_context,
     ScriptPromiseResolver* resolver,
     ml::model_loader::mojom::blink::LoadModelResult result,
@@ -219,7 +223,8 @@
   return nullptr;
 }
 
-void MLGraphCrOS::ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+void MLGraphCrOS::ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                                   const MLNamedArrayBufferViews& inputs,
                                    const MLNamedArrayBufferViews& outputs,
                                    ScriptPromiseResolver* resolver,
                                    ExceptionState& exception_state) {
@@ -255,11 +260,12 @@
   remote_model_->Compute(
       std::move(input_mojo),
       WTF::BindOnce(&MLGraphCrOS::OnComputeGraph, WrapPersistent(this),
-                    WrapPersistent(resolver), std::move(inputs_info),
-                    std::move(outputs_info)));
+                    std::move(scoped_trace), WrapPersistent(resolver),
+                    std::move(inputs_info), std::move(outputs_info)));
 }
 
 void MLGraphCrOS::OnComputeGraph(
+    ScopedMLTrace scoped_trace,
     ScriptPromiseResolver* resolver,
     std::unique_ptr<Vector<std::pair<String, ArrayBufferViewInfo>>> inputs_info,
     std::unique_ptr<Vector<std::pair<String, ArrayBufferViewInfo>>>
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.h
index 13bdb2ee..266bc4a 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_cros.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ML_WEBNN_ML_GRAPH_CROS_H_
 
 #include "components/ml/mojom/web_platform_model.mojom-blink.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_operand.h"
@@ -24,7 +25,8 @@
   // Create and build an MLGraphCrOS object. Resolve the promise with
   // this concrete object if the underlying TF-Lite model converted from WebNN
   // graph builds successfully.
-  static void ValidateAndBuildAsync(MLContext* context,
+  static void ValidateAndBuildAsync(ScopedMLTrace scoped_trace,
+                                    MLContext* context,
                                     const MLNamedOperands& named_outputs,
                                     ScriptPromiseResolver* resolver);
 
@@ -44,6 +46,7 @@
   // The callback of loading tflite model, it will bind the `Model` pending
   // remote if it's successful.
   void OnRemoteModelLoad(
+      ScopedMLTrace scoped_trace,
       ExecutionContext* execution_context,
       ScriptPromiseResolver* resolver,
       ml::model_loader::mojom::blink::LoadModelResult result,
@@ -53,7 +56,8 @@
   // Load a WebNN graph in `MLService` with `ModelLoader` message pipe, the
   // operations of WebNN need to be converted into a TF-Lite model in
   // FlatBuffers.
-  void BuildAsyncImpl(const MLNamedOperands& named_outputs,
+  void BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                      const MLNamedOperands& named_outputs,
                       ScriptPromiseResolver* resolver) override;
 
   // Load the converted model with synchronous call of `ModelLoader` interface.
@@ -62,15 +66,18 @@
                          ExceptionState& exception_state) override;
 
   // Compute the converted model with asynchronous call of `Model` interface.
-  void ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+  void ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                        const MLNamedArrayBufferViews& inputs,
                         const MLNamedArrayBufferViews& outputs,
                         ScriptPromiseResolver* resolver,
                         ExceptionState& exception_state) override;
+
   // Resolve the promise with an MLComputeResult that contains input and output
   // ArrayBufferViews. The `inputs_info` and `outputs_info` carry the backing
   // memory in `ArrayBufferContents` transferred from the original user supplied
   // `ArrayBufferView`s.
   void OnComputeGraph(
+      ScopedMLTrace scoped_trace,
       ScriptPromiseResolver* resolver,
       std::unique_ptr<Vector<std::pair<String, ArrayBufferViewInfo>>>
           inputs_info,
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.cc
index d9d8782..d2eb59c6 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.cc
@@ -119,12 +119,14 @@
 }  // namespace
 
 // static
-void MLGraphMojo::ValidateAndBuildAsync(MLContextMojo* context,
+void MLGraphMojo::ValidateAndBuildAsync(ScopedMLTrace scoped_trace,
+                                        MLContextMojo* context,
                                         const MLNamedOperands& named_outputs,
                                         ScriptPromiseResolver* resolver) {
   auto* graph =
       MakeGarbageCollected<MLGraphMojo>(resolver->GetScriptState(), context);
-  graph->BuildAsync(named_outputs, resolver);
+  scoped_trace.AddStep("MLGraphMojo::ValidateAndBuildAsync");
+  graph->BuildAsync(std::move(scoped_trace), named_outputs, resolver);
 }
 
 // static
@@ -149,7 +151,8 @@
   MLGraph::Trace(visitor);
 }
 
-void MLGraphMojo::BuildAsyncImpl(const MLNamedOperands& outputs,
+void MLGraphMojo::BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                                 const MLNamedOperands& outputs,
                                  ScriptPromiseResolver* resolver) {
   auto graph_info = BuildWebNNGraphInfo(outputs);
   if (!graph_info.has_value()) {
@@ -162,7 +165,7 @@
   ml_context_mojo_->CreateWebNNGraph(
       std::move(graph_info.value()),
       WTF::BindOnce(&MLGraphMojo::OnCreateWebNNGraph, WrapPersistent(this),
-                    WrapPersistent(resolver)));
+                    std::move(scoped_trace), WrapPersistent(resolver)));
 }
 
 MLGraph* MLGraphMojo::BuildSyncImpl(ScriptState* script_state,
@@ -199,7 +202,8 @@
   return this;
 }
 
-void MLGraphMojo::ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+void MLGraphMojo::ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                                   const MLNamedArrayBufferViews& inputs,
                                    const MLNamedArrayBufferViews& outputs,
                                    ScriptPromiseResolver* resolver,
                                    ExceptionState& exception_state) {
@@ -234,11 +238,12 @@
   remote_graph_->Compute(
       std::move(name_to_buffer_map),
       WTF::BindOnce(&MLGraphMojo::OnDidCompute, WrapPersistent(this),
-                    WrapPersistent(resolver), std::move(inputs_info),
-                    std::move(outputs_info)));
+                    std::move(scoped_trace), WrapPersistent(resolver),
+                    std::move(inputs_info), std::move(outputs_info)));
 }
 
 void MLGraphMojo::OnDidCompute(
+    ScopedMLTrace scoped_trace,
     ScriptPromiseResolver* resolver,
     std::unique_ptr<Vector<std::pair<String, ArrayBufferViewInfo>>> inputs_info,
     std::unique_ptr<Vector<std::pair<String, ArrayBufferViewInfo>>>
@@ -330,7 +335,8 @@
   }
 }
 
-void MLGraphMojo::OnCreateWebNNGraph(ScriptPromiseResolver* resolver,
+void MLGraphMojo::OnCreateWebNNGraph(ScopedMLTrace scoped_trace,
+                                     ScriptPromiseResolver* resolver,
                                      blink_mojom::CreateGraphResultPtr result) {
   // Handle error message and throw exception.
   if (result->is_error()) {
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.h
index c35df2e..10492cf9 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_mojo.h
@@ -7,6 +7,7 @@
 
 #include "services/webnn/public/mojom/webnn_context_provider.mojom-blink.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom-blink.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_context_mojo.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h"
@@ -24,7 +25,8 @@
   // Create and build an MLGraphMojo object. Resolve the promise with
   // this concrete object if the graph builds successfully out of renderer
   // process.
-  static void ValidateAndBuildAsync(MLContextMojo* context,
+  static void ValidateAndBuildAsync(ScopedMLTrace scoped_trace,
+                                    MLContextMojo* context,
                                     const MLNamedOperands& named_outputs,
                                     ScriptPromiseResolver* resolver);
 
@@ -43,20 +45,23 @@
   // Create `WebNNGraph` message pipe with `WebNNContext` mojo interface, then
   // build the computational graph with the hardware accelerated OS machine
   // learning API in the WebNN Service.
-  void BuildAsyncImpl(const MLNamedOperands& outputs,
+  void BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                      const MLNamedOperands& outputs,
                       ScriptPromiseResolver* resolver) override;
 
   MLGraph* BuildSyncImpl(ScriptState* script_state,
                          const MLNamedOperands& named_outputs,
                          ExceptionState& exception_state) override;
 
-  void ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+  void ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                        const MLNamedArrayBufferViews& inputs,
                         const MLNamedArrayBufferViews& outputs,
                         ScriptPromiseResolver* resolver,
                         ExceptionState& exception_state) override;
   // The callback of computing `WebNNGraph` by calling hardware accelerated OS
   // machine learning APIs.
   void OnDidCompute(
+      ScopedMLTrace scoped_trace,
       ScriptPromiseResolver* resolver,
       std::unique_ptr<Vector<std::pair<String, ArrayBufferViewInfo>>>
           inputs_info,
@@ -72,7 +77,8 @@
   // The callback of creating `WebNNGraph` mojo interface from WebNN Service.
   // The returned `CreateGraphResultPtr` contains a `pending_remote<WebNNGraph>`
   // if the graph was successfully created and an `Error` otherwise.
-  void OnCreateWebNNGraph(ScriptPromiseResolver* resolver,
+  void OnCreateWebNNGraph(ScopedMLTrace scoped_trace,
+                          ScriptPromiseResolver* resolver,
                           webnn::mojom::blink::CreateGraphResultPtr result);
 
   Member<MLContextMojo> ml_context_mojo_;
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_base.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_base.cc
index ecea2f5..6b42731e 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_base.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_test_base.cc
@@ -113,7 +113,8 @@
       auto* resolver =
           MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
       ScriptPromiseTester tester(scope.GetScriptState(), resolver->Promise());
-      graph->ComputeAsync(inputs, outputs, resolver, scope.GetExceptionState());
+      graph->ComputeAsync(ScopedMLTrace("ComputeAsync"), inputs, outputs,
+                          resolver, scope.GetExceptionState());
       tester.WaitUntilSettled();
       if (tester.IsFulfilled()) {
         // For `MLGraph::ComputeAsync()`, the input and output ArrayBufferViews
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
index 6bb59204..0c09730 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.cc
@@ -163,7 +163,7 @@
  public:
   static scoped_refptr<SharedXnnpackContext> GetInstance(
       String& error_message) {
-    TRACE_EVENT("blink", "SharedXnnpackContext::GetInstance");
+    ScopedMLTrace scoped_trace("SharedXnnpackContext::GetInstance");
     base::AutoLock auto_lock(SharedXnnpackContextLock());
     if (instance_ == nullptr) {
       // Initializes XNNPACK library. By passing nullptr to allocator argument,
@@ -256,7 +256,7 @@
       Vector<DataBufferPtr> static_data_buffers,
       uint32_t num_threads,
       String& error_message) {
-    TRACE_EVENT("blink", "XnnRuntimeWrapper::Create");
+    ScopedMLTrace scoped_trace("XnnRuntimeWrapper::Create");
     CHECK(xnn_context);
     CHECK(subgraph);
 
@@ -302,7 +302,7 @@
   xnn_status Invoke(XnnExternalValuesPtr external_values,
                     String& error_message) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    TRACE_EVENT("blink", "XnnRuntimeWrapper::Invoke");
+    ScopedMLTrace scoped_trace("XnnRuntimeWrapper::Invoke");
     CHECK(external_values);
 
     // Check if any data pointers of the provided `xnn_external_values` changed
@@ -1864,11 +1864,13 @@
 }  // namespace
 
 // static
-void MLGraphXnnpack::ValidateAndBuildAsync(MLContext* context,
+void MLGraphXnnpack::ValidateAndBuildAsync(ScopedMLTrace scoped_trace,
+                                           MLContext* context,
                                            const MLNamedOperands& named_outputs,
                                            ScriptPromiseResolver* resolver) {
+  scoped_trace.AddStep("MLGraphXnnpack::ValidateAndBuildAsync");
   auto* graph = MakeGarbageCollected<MLGraphXnnpack>(context);
-  graph->BuildAsync(named_outputs, resolver);
+  graph->BuildAsync(std::move(scoped_trace), named_outputs, resolver);
 }
 
 // static
@@ -1909,14 +1911,15 @@
   return xnn_runtime_wrapper_->GetXnnExternalValuesTesting();
 }
 
-void MLGraphXnnpack::BuildAsyncImpl(const MLNamedOperands& named_outputs,
+void MLGraphXnnpack::BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                                    const MLNamedOperands& named_outputs,
                                     ScriptPromiseResolver* resolver) {
   CHECK(IsMainThread());
   CHECK(!xnn_runtime_wrapper_);
   PostCrossThreadTask(
       *xnnpack_task_runner_, FROM_HERE,
       CrossThreadBindOnce(
-          &GetSharedXnnpackContextOnBackgroundThread,
+          &GetSharedXnnpackContextOnBackgroundThread, std::move(scoped_trace),
           MakeCrossThreadHandle(this),
           MakeCrossThreadHandle(
               MakeGarbageCollected<MLNamedOperands>(named_outputs)),
@@ -1925,6 +1928,7 @@
 
 // static
 void MLGraphXnnpack::GetSharedXnnpackContextOnBackgroundThread(
+    ScopedMLTrace scoped_trace,
     CrossThreadHandle<MLGraphXnnpack> graph,
     CrossThreadHandle<MLNamedOperands> named_outputs,
     CrossThreadHandle<ScriptPromiseResolver> resolver,
@@ -1938,13 +1942,14 @@
       CrossThreadBindOnce(
           &MLGraphXnnpack::OnDidGetSharedXnnpackContext,
           MakeUnwrappingCrossThreadHandle(std::move(graph)),
-          std::move(xnn_context),
+          std::move(scoped_trace), std::move(xnn_context),
           MakeUnwrappingCrossThreadHandle(std::move(named_outputs)),
           MakeUnwrappingCrossThreadHandle(std::move(resolver)),
           std::move(error_message)));
 }
 
 void MLGraphXnnpack::OnDidGetSharedXnnpackContext(
+    ScopedMLTrace scoped_trace,
     scoped_refptr<SharedXnnpackContext> xnn_context,
     MLNamedOperands* named_outputs,
     ScriptPromiseResolver* resolver,
@@ -1971,14 +1976,16 @@
   PostCrossThreadTask(
       *xnnpack_task_runner_, FROM_HERE,
       CrossThreadBindOnce(
-          &CreateXnnRuntimeOnBackgroundThread, std::move(subgraph),
-          std::move(xnn_context), std::move(static_data_buffers),
-          MakeCrossThreadHandle(this), ml_context_->GetNumThreads(),
-          MakeCrossThreadHandle(resolver), resolver_task_runner_));
+          &CreateXnnRuntimeOnBackgroundThread, std::move(scoped_trace),
+          std::move(subgraph), std::move(xnn_context),
+          std::move(static_data_buffers), MakeCrossThreadHandle(this),
+          ml_context_->GetNumThreads(), MakeCrossThreadHandle(resolver),
+          resolver_task_runner_));
 }
 
 // static
 void MLGraphXnnpack::CreateXnnRuntimeOnBackgroundThread(
+    ScopedMLTrace scoped_trace,
     XnnSubgraphPtr subgraph,
     scoped_refptr<SharedXnnpackContext> xnn_context,
     Vector<DataBufferPtr> static_data_buffers,
@@ -1995,12 +2002,14 @@
       *resolver_task_runner, FROM_HERE,
       CrossThreadBindOnce(&MLGraphXnnpack::OnDidCreateXnnRuntime,
                           MakeUnwrappingCrossThreadHandle(std::move(graph)),
+                          std::move(scoped_trace),
                           std::move(xnn_runtime_wrapper),
                           MakeUnwrappingCrossThreadHandle(std::move(resolver)),
                           std::move(error_message)));
 }
 
 void MLGraphXnnpack::OnDidCreateXnnRuntime(
+    ScopedMLTrace scoped_trace,
     scoped_refptr<XnnRuntimeWrapper> xnn_runtime_wrapper,
     ScriptPromiseResolver* resolver,
     String error_message) {
@@ -2048,10 +2057,13 @@
   return this;
 }
 
-void MLGraphXnnpack::ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+void MLGraphXnnpack::ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                                      const MLNamedArrayBufferViews& inputs,
                                       const MLNamedArrayBufferViews& outputs,
                                       ScriptPromiseResolver* resolver,
                                       ExceptionState& exception_state) {
+  scoped_trace.AddStep("MLGraphXnnpack::TransferNamedArrayBufferViews");
+
   // `MLNamedArrayBufferViews` objects should be accessed on the thread owning
   // the heap before transferring.
   auto external_values = CreateExternalValues(inputs, outputs);
@@ -2066,6 +2078,7 @@
         "Invalid inputs: " + exception_state.Message()));
     return;
   }
+
   auto outputs_info = TransferNamedArrayBufferViews(
       resolver->GetScriptState()->GetIsolate(), outputs, exception_state);
   if (!outputs_info) {
@@ -2079,15 +2092,17 @@
   // re-creation in `OnDidCompute()`.
   PostCrossThreadTask(
       *xnnpack_task_runner_, FROM_HERE,
-      CrossThreadBindOnce(&ComputeOnBackgroundThread, xnn_runtime_wrapper_,
-                          std::move(external_values), std::move(inputs_info),
-                          std::move(outputs_info), MakeCrossThreadHandle(this),
+      CrossThreadBindOnce(&ComputeOnBackgroundThread, std::move(scoped_trace),
+                          xnn_runtime_wrapper_, std::move(external_values),
+                          std::move(inputs_info), std::move(outputs_info),
+                          MakeCrossThreadHandle(this),
                           MakeCrossThreadHandle(resolver),
                           resolver_task_runner_));
 }
 
 // static
 void MLGraphXnnpack::ComputeOnBackgroundThread(
+    ScopedMLTrace scoped_trace,
     scoped_refptr<XnnRuntimeWrapper> xnn_runtime_wrapper,
     XnnExternalValuesPtr external_values,
     NamedArrayBufferViewsInfoPtr inputs_info,
@@ -2096,6 +2111,8 @@
     CrossThreadHandle<ScriptPromiseResolver> resolver,
     scoped_refptr<base::SequencedTaskRunner> resolver_task_runner) {
   CHECK(!IsMainThread());
+  scoped_trace.AddStep("MLGraphXnnpack::ComputeOnBackgroundThread");
+
   String error_message;
   xnn_status status =
       xnn_runtime_wrapper->Invoke(std::move(external_values), error_message);
@@ -2103,13 +2120,14 @@
       *resolver_task_runner, FROM_HERE,
       CrossThreadBindOnce(&MLGraphXnnpack::OnDidCompute,
                           MakeUnwrappingCrossThreadHandle(std::move(graph)),
-                          status, std::move(inputs_info),
-                          std::move(outputs_info),
+                          std::move(scoped_trace), status,
+                          std::move(inputs_info), std::move(outputs_info),
                           MakeUnwrappingCrossThreadHandle(std::move(resolver)),
                           std::move(error_message)));
 }
 
-void MLGraphXnnpack::OnDidCompute(xnn_status status,
+void MLGraphXnnpack::OnDidCompute(ScopedMLTrace scoped_trace,
+                                  xnn_status status,
                                   NamedArrayBufferViewsInfoPtr inputs_info,
                                   NamedArrayBufferViewsInfoPtr outputs_info,
                                   ScriptPromiseResolver* resolver,
@@ -2145,7 +2163,7 @@
     XnnSubgraphPtr& out_subgraph,
     Vector<DataBufferPtr>& out_static_data_buffers,
     String& error_message) {
-  TRACE_EVENT("blink", "MLGraphXnnpack::CreateXnnSubgraph");
+  ScopedMLTrace scoped_trace("MLGraphXnnpack::CreateXnnSubgraph");
 
   // The number of external value IDs that is reserved by XNNPACK Subgraph. Set
   // its value to the number of graph input and output resources.
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.h b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.h
index 962d060..e3614cc 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.h
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ML_WEBNN_ML_GRAPH_XNNPACK_H_
 
 #include "base/task/sequenced_task_runner.h"
+#include "third_party/blink/renderer/modules/ml/ml_trace.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph.h"
 #include "third_party/blink/renderer/modules/ml/webnn/ml_graph_utils.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -39,7 +40,8 @@
   // Create and build an MLGraphXnnpack object. Resolve the promise with
   // this concrete object if the underlying XNNPACK subgraph builds
   // successfully.
-  static void ValidateAndBuildAsync(MLContext* context,
+  static void ValidateAndBuildAsync(ScopedMLTrace scoped_trace,
+                                    MLContext* context,
                                     const MLNamedOperands& named_outputs,
                                     ScriptPromiseResolver* resolver);
 
@@ -88,22 +90,26 @@
   //          post task ------------> `CreateXnnRuntimeOnBackgroundThread()`
   //                                                  |
   //   `OnDidCreateXnnRuntime()  <-----------------post task
-  void BuildAsyncImpl(const MLNamedOperands& named_outputs,
+  void BuildAsyncImpl(ScopedMLTrace scoped_trace,
+                      const MLNamedOperands& named_outputs,
                       ScriptPromiseResolver* resolver) override;
 
   static void GetSharedXnnpackContextOnBackgroundThread(
+      ScopedMLTrace scoped_trace,
       CrossThreadHandle<MLGraphXnnpack> graph,
       CrossThreadHandle<MLNamedOperands> named_outputs,
       CrossThreadHandle<ScriptPromiseResolver> resolver,
       scoped_refptr<base::SequencedTaskRunner> resolver_task_runner);
 
   void OnDidGetSharedXnnpackContext(
+      ScopedMLTrace scoped_trace,
       scoped_refptr<SharedXnnpackContext> xnn_context,
       MLNamedOperands* named_outputs,
       ScriptPromiseResolver* resolver,
       String error_message = String());
 
   static void CreateXnnRuntimeOnBackgroundThread(
+      ScopedMLTrace scoped_trace,
       XnnSubgraphPtr subgraph,
       scoped_refptr<SharedXnnpackContext> xnn_context,
       Vector<DataBufferPtr> static_data_buffers,
@@ -113,6 +119,7 @@
       scoped_refptr<base::SequencedTaskRunner> resolver_task_runner);
 
   void OnDidCreateXnnRuntime(
+      ScopedMLTrace scoped_trace,
       scoped_refptr<XnnRuntimeWrapper> xnn_runtime_wrapper,
       ScriptPromiseResolver* resolver,
       String error_message = String());
@@ -132,7 +139,8 @@
   // `ArrayBufferView` while the background thread is accessing them. And it
   // would also avoid accessing the heap-allocated `ArrayBufferView` in the
   // background thread.
-  void ComputeAsyncImpl(const MLNamedArrayBufferViews& inputs,
+  void ComputeAsyncImpl(ScopedMLTrace scoped_trace,
+                        const MLNamedArrayBufferViews& inputs,
                         const MLNamedArrayBufferViews& outputs,
                         ScriptPromiseResolver* resolver,
                         ExceptionState& exception_state) override;
@@ -146,6 +154,7 @@
   // passed forward to `OnDidCompute()` which is called on the thread
   // owning these GC objects.
   static void ComputeOnBackgroundThread(
+      ScopedMLTrace scoped_trace,
       scoped_refptr<XnnRuntimeWrapper> xnn_runtime_wrapper,
       XnnExternalValuesPtr external_values,
       NamedArrayBufferViewsInfoPtr inputs_info,
@@ -160,7 +169,8 @@
   // from the `inputs_info` and `outputs_info` that carry the backing memory in
   // `ArrayBufferContents` transferred from the original user supplied
   // `ArrayBufferView`s.
-  void OnDidCompute(xnn_status status,
+  void OnDidCompute(ScopedMLTrace scoped_trace,
+                    xnn_status status,
                     NamedArrayBufferViewsInfoPtr inputs_info,
                     NamedArrayBufferViewsInfoPtr outputs_info,
                     ScriptPromiseResolver* resolver,
diff --git a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc
index 3fb14639..5ab4c57 100644
--- a/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc
+++ b/third_party/blink/renderer/modules/ml/webnn/ml_graph_xnnpack_test.cc
@@ -568,7 +568,8 @@
     auto* resolver =
         MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
     ScriptPromiseTester tester(scope.GetScriptState(), resolver->Promise());
-    graph->ComputeAsync({{"a", a_buffer_view}, {"b", b_buffer_view}},
+    graph->ComputeAsync(ScopedMLTrace("ComputeAsync"),
+                        {{"a", a_buffer_view}, {"b", b_buffer_view}},
                         {{"output", output_buffer_view}}, resolver,
                         scope.GetExceptionState());
     tester.WaitUntilSettled();
@@ -595,7 +596,8 @@
     auto* resolver =
         MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
     ScriptPromiseTester tester(scope.GetScriptState(), resolver->Promise());
-    graph->ComputeAsync({{"a", a_buffer_view}, {"b", b_buffer_view}},
+    graph->ComputeAsync(ScopedMLTrace("ComputeAsync"),
+                        {{"a", a_buffer_view}, {"b", b_buffer_view}},
                         {{"output", output_buffer_view}}, resolver,
                         scope.GetExceptionState());
     tester.WaitUntilSettled();
@@ -622,7 +624,8 @@
     auto* resolver =
         MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
     ScriptPromiseTester tester(scope.GetScriptState(), resolver->Promise());
-    graph->ComputeAsync({{"a", a_buffer_view}, {"b", b_buffer_view}},
+    graph->ComputeAsync(ScopedMLTrace("ComputeAsync"),
+                        {{"a", a_buffer_view}, {"b", b_buffer_view}},
                         {{"output", output_buffer_view}}, resolver,
                         scope.GetExceptionState());
     tester.WaitUntilSettled();
@@ -648,7 +651,8 @@
     auto* resolver =
         MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
     ScriptPromiseTester tester(scope.GetScriptState(), resolver->Promise());
-    graph->ComputeAsync({{"a", a_buffer_view}, {"b", b_buffer_view}},
+    graph->ComputeAsync(ScopedMLTrace("ComputeAsync"),
+                        {{"a", a_buffer_view}, {"b", b_buffer_view}},
                         {{"output", output_buffer_view}}, resolver,
                         scope.GetExceptionState());
     EXPECT_EQ(a_buffer_view->IsDetached(), true);
@@ -689,7 +693,8 @@
     auto* resolver =
         MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
     ScriptPromiseTester tester(scope.GetScriptState(), resolver->Promise());
-    graph->ComputeAsync({{"a", a_buffer_view}, {"b", b_buffer_view}},
+    graph->ComputeAsync(ScopedMLTrace("ComputeAsync"),
+                        {{"a", a_buffer_view}, {"b", b_buffer_view}},
                         {{"output", output_buffer_view}}, resolver,
                         scope.GetExceptionState());
     tester.WaitUntilSettled();
diff --git a/third_party/blink/renderer/modules/webtransport/bidirectional_stream_test.cc b/third_party/blink/renderer/modules/webtransport/bidirectional_stream_test.cc
index 3062593..0393fff 100644
--- a/third_party/blink/renderer/modules/webtransport/bidirectional_stream_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/bidirectional_stream_test.cc
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/modules/webtransport/test_utils.h"
 #include "third_party/blink/renderer/modules/webtransport/web_transport.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -276,6 +277,7 @@
 }
 
 TEST(BidirectionalStreamTest, CreateLocallyAndWrite) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -286,6 +288,7 @@
 }
 
 TEST(BidirectionalStreamTest, CreateRemotelyAndWrite) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -321,6 +324,7 @@
 }
 
 TEST(BidirectionalStreamTest, CreateLocallyAndRead) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -331,6 +335,7 @@
 }
 
 TEST(BidirectionalStreamTest, CreateRemotelyAndRead) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -341,6 +346,7 @@
 }
 
 TEST(BidirectionalStreamTest, IncomingStreamCleanClose) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -371,6 +377,7 @@
 }
 
 TEST(BidirectionalStreamTest, OutgoingStreamCleanClose) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -399,6 +406,7 @@
 }
 
 TEST(BidirectionalStreamTest, CloseWebTransport) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -412,6 +420,7 @@
 }
 
 TEST(BidirectionalStreamTest, RemoteDropWebTransport) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -427,6 +436,7 @@
 }
 
 TEST(BidirectionalStreamTest, WriteAfterCancellingIncoming) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -447,6 +457,7 @@
 }
 
 TEST(BidirectionalStreamTest, WriteAfterIncomingClosed) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -466,6 +477,7 @@
 }
 
 TEST(BidirectionalStreamTest, ReadAfterClosingOutgoing) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -487,6 +499,7 @@
 }
 
 TEST(BidirectionalStreamTest, ReadAfterAbortingOutgoing) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
@@ -504,6 +517,7 @@
 }
 
 TEST(BidirectionalStreamTest, ReadAfterOutgoingAborted) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   ScopedWebTransport scoped_web_transport(scope);
   auto* bidirectional_stream =
diff --git a/third_party/blink/renderer/modules/webtransport/datagram_duplex_stream_test.cc b/third_party/blink/renderer/modules/webtransport/datagram_duplex_stream_test.cc
index 8246f15..f02d5b6 100644
--- a/third_party/blink/renderer/modules/webtransport/datagram_duplex_stream_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/datagram_duplex_stream_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/modules/webtransport/test_utils.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -152,6 +153,7 @@
 };
 
 TEST(DatagramDuplexStreamTest, Defaults) {
+  test::TaskEnvironment task_environment;
   ScopedDatagramDuplexStream scope;
   auto* duplex = scope.Duplex();
   EXPECT_FALSE(duplex->incomingMaxAge().has_value());
@@ -161,6 +163,7 @@
 }
 
 TEST(DatagramDuplexStreamTest, SetIncomingMaxAge) {
+  test::TaskEnvironment task_environment;
   ScopedDatagramDuplexStream scope;
   auto* duplex = scope.Duplex();
 
@@ -179,6 +182,7 @@
 }
 
 TEST(DatagramDuplexStreamTest, SetOutgoingMaxAge) {
+  test::TaskEnvironment task_environment;
   ScopedDatagramDuplexStream scope;
   auto* duplex = scope.Duplex();
   auto* stub = scope.Stub();
@@ -226,6 +230,7 @@
 }
 
 TEST(DatagramDuplexStreamTest, SetIncomingHighWaterMark) {
+  test::TaskEnvironment task_environment;
   ScopedDatagramDuplexStream scope;
   auto* duplex = scope.Duplex();
 
@@ -240,6 +245,7 @@
 }
 
 TEST(DatagramDuplexStreamTest, SetOutgoingHighWaterMark) {
+  test::TaskEnvironment task_environment;
   ScopedDatagramDuplexStream scope;
   auto* duplex = scope.Duplex();
 
@@ -254,6 +260,7 @@
 }
 
 TEST(DatagramDuplexStreamTest, InitialMaxDatagramSize) {
+  test::TaskEnvironment task_environment;
   ScopedDatagramDuplexStream scope;
   auto* duplex = scope.Duplex();
 
diff --git a/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc b/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc
index c5fa586..45f0a58c 100644
--- a/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/incoming_stream_test.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
 #include "third_party/blink/renderer/modules/webtransport/web_transport_error.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "v8/include/v8.h"
 
@@ -139,6 +140,7 @@
   }
 
   base::MockOnceCallback<void(absl::optional<uint8_t>)> mock_on_abort_;
+  test::TaskEnvironment task_environment_;
   mojo::ScopedDataPipeProducerHandle data_pipe_producer_;
   mojo::ScopedDataPipeConsumerHandle data_pipe_consumer_;
 };
diff --git a/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc b/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc
index d30b4247..781069a1 100644
--- a/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/outgoing_stream_test.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "v8/include/v8.h"
 
@@ -120,6 +121,7 @@
 };
 
 TEST(OutgoingStreamTest, Create) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   auto* outgoing_stream = stream_creator.Create(scope);
@@ -129,6 +131,7 @@
 }
 
 TEST(OutgoingStreamTest, WriteArrayBuffer) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   auto* outgoing_stream = stream_creator.Create(scope);
@@ -148,6 +151,7 @@
 }
 
 TEST(OutgoingStreamTest, WriteArrayBufferView) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   auto* outgoing_stream = stream_creator.Create(scope);
@@ -173,6 +177,7 @@
 }
 
 TEST(OutgoingStreamTest, AsyncWrite) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   // Set a large pipe capacity, so any platform-specific excess is dwarfed in
@@ -232,6 +237,7 @@
 
 // Writing immediately followed by closing should not lose data.
 TEST(OutgoingStreamTest, WriteThenClose) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
 
@@ -271,6 +277,7 @@
 }
 
 TEST(OutgoingStreamTest, DataPipeClosed) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
 
@@ -315,6 +322,7 @@
 }
 
 TEST(OutgoingStreamTest, DataPipeClosedDuringAsyncWrite) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
 
@@ -365,6 +373,7 @@
 }
 
 TEST(OutgoingStreamTest, Abort) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   ScriptState* script_state = scope.GetScriptState();
@@ -383,6 +392,7 @@
 }
 
 TEST(OutgoingStreamTest, AbortWithWebTransportError) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   ScriptState* script_state = scope.GetScriptState();
@@ -405,6 +415,7 @@
 }
 
 TEST(OutgoingStreamTest, AbortWithWebTransportErrorWithCode) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   ScriptState* script_state = scope.GetScriptState();
@@ -426,6 +437,7 @@
 }
 
 TEST(OutgoingStreamTest, CloseAndConnectionError) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   StreamCreator stream_creator;
   ScriptState* script_state = scope.GetScriptState();
diff --git a/third_party/blink/renderer/modules/webtransport/web_transport_error_test.cc b/third_party/blink/renderer/modules/webtransport/web_transport_error_test.cc
index bef3d29..d8e83b66 100644
--- a/third_party/blink/renderer/modules/webtransport/web_transport_error_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/web_transport_error_test.cc
@@ -10,10 +10,12 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_web_transport_error_init.h"
 #include "third_party/blink/renderer/modules/webtransport/web_transport_error.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 
 namespace blink {
 
 TEST(WebTransportErrorTest, DefaultConstruct) {
+  test::TaskEnvironment task_environment;
   auto* error = WebTransportError::Create(WebTransportErrorInit::Create());
 
   EXPECT_EQ(error->code(), 0);
@@ -23,6 +25,7 @@
 }
 
 TEST(WebTransportErrorTest, ConstructWithStreamErrorCode) {
+  test::TaskEnvironment task_environment;
   auto* init = WebTransportErrorInit::Create();
   init->setStreamErrorCode(11);
   auto* error = WebTransportError::Create(init);
@@ -32,6 +35,7 @@
 }
 
 TEST(WebTransportErrorTest, ConstructWithMessage) {
+  test::TaskEnvironment task_environment;
   auto* init = WebTransportErrorInit::Create();
   init->setMessage("wow");
   auto* error = WebTransportError::Create(init);
@@ -40,6 +44,7 @@
 }
 
 TEST(WebTransportErrorTest, InternalCreate) {
+  test::TaskEnvironment task_environment;
   V8TestingScope scope;
   auto* isolate = scope.GetIsolate();
   auto context = scope.GetContext();
diff --git a/third_party/blink/renderer/modules/webtransport/web_transport_test.cc b/third_party/blink/renderer/modules/webtransport/web_transport_test.cc
index da7ecdd..d43cdd74 100644
--- a/third_party/blink/renderer/modules/webtransport/web_transport_test.cc
+++ b/third_party/blink/renderer/modules/webtransport/web_transport_test.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/testing/task_environment.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -332,6 +333,7 @@
       pending_unidirectional_accept_callbacks_;
   WTF::Deque<AcceptBidirectionalStreamCallback>
       pending_bidirectional_accept_callbacks_;
+  test::TaskEnvironment task_environment_;
   WebTransportConnector connector_;
   std::unique_ptr<MockWebTransport> mock_web_transport_;
   mojo::Remote<network::mojom::blink::WebTransportClient> client_remote_;
diff --git a/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.cc b/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.cc
index 7fcdee6b..7ce6b1c0 100644
--- a/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.cc
+++ b/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.cc
@@ -110,13 +110,22 @@
 void HlsDataSourceProviderImpl::OnDataSourceReady(
     absl::optional<media::hls::types::ByteRange> range,
     ReadCb callback,
-    std::unique_ptr<media::CrossOriginDataSource> data_source) {
+    std::unique_ptr<media::DataSource> data_source) {
   auto stream_id = stream_id_generator_.GenerateNextId();
   auto it = data_source_map_.try_emplace(stream_id, std::move(data_source));
-  it.first->second->Initialize(
-      base::BindPostTaskToCurrentDefault(base::BindOnce(
-          &HlsDataSourceProviderImpl::DataSourceInitialized,
-          weak_factory_.GetWeakPtr(), stream_id, range, std::move(callback))));
+  // The factory may return a CrossOriginDataSource, which must be initialized.
+  // Other implementations of DataSource may not even have an Initialization
+  // method. In these cases, the factory should provide instances which are
+  // ready to use.
+  // TODO(crbug/1266991): Unify the `Initialize` method across DataSource
+  // implementations, and remove the check for CrossOriginDataSource here.
+  if (auto* cross_origin = it.first->second->GetAsCrossOriginDataSource()) {
+    cross_origin->Initialize(base::BindPostTaskToCurrentDefault(base::BindOnce(
+        &HlsDataSourceProviderImpl::DataSourceInitialized,
+        weak_factory_.GetWeakPtr(), stream_id, range, std::move(callback))));
+  } else {
+    DataSourceInitialized(stream_id, range, std::move(callback), true);
+  }
 }
 
 void HlsDataSourceProviderImpl::ReadFromUrl(
diff --git a/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.h b/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.h
index 17c4c35..9412c1b 100644
--- a/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.h
+++ b/third_party/blink/renderer/platform/media/hls_data_source_provider_impl.h
@@ -30,7 +30,7 @@
   class DataSourceFactory {
    public:
     using DataSourceCb =
-        base::OnceCallback<void(std::unique_ptr<media::CrossOriginDataSource>)>;
+        base::OnceCallback<void(std::unique_ptr<media::DataSource>)>;
     virtual ~DataSourceFactory() = 0;
     virtual void CreateDataSource(GURL uri, DataSourceCb cb) = 0;
   };
@@ -49,10 +49,9 @@
   void AbortPendingReads(base::OnceClosure cb) override;
 
  private:
-  void OnDataSourceReady(
-      absl::optional<media::hls::types::ByteRange> range,
-      ReadCb callback,
-      std::unique_ptr<media::CrossOriginDataSource> data_source);
+  void OnDataSourceReady(absl::optional<media::hls::types::ByteRange> range,
+                         ReadCb callback,
+                         std::unique_ptr<media::DataSource> data_source);
   void OnStreamReleased(media::HlsDataSourceStream::StreamId stream_id);
   void DataSourceInitialized(media::HlsDataSourceStream::StreamId stream_id,
                              absl::optional<media::hls::types::ByteRange> range,
@@ -64,7 +63,7 @@
   media::HlsDataSourceStream::StreamId::Generator stream_id_generator_;
 
   base::flat_map<media::HlsDataSourceStream::StreamId,
-                 std::unique_ptr<media::CrossOriginDataSource>>
+                 std::unique_ptr<media::DataSource>>
       data_source_map_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/third_party/blink/renderer/platform/media/hls_data_source_provider_impl_unittest.cc b/third_party/blink/renderer/platform/media/hls_data_source_provider_impl_unittest.cc
index 5ddd1b1..7878ad7 100644
--- a/third_party/blink/renderer/platform/media/hls_data_source_provider_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/media/hls_data_source_provider_impl_unittest.cc
@@ -77,10 +77,7 @@
               (double playback_rate),
               (override));
   MOCK_METHOD(void, OnMediaIsPlaying, (), (override));
-  MOCK_METHOD(CrossOriginDataSource*,
-              GetAsCrossOriginDataSource,
-              (),
-              (override));
+  CrossOriginDataSource* GetAsCrossOriginDataSource() override { return this; }
 };
 
 class MockDataSourceFactory
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4c4bd9e..3df6600 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -667,7 +667,7 @@
     },
     {
       name: "ClipboardWellFormedHtmlSanitizationWrite",
-      status: "experimental",
+      status: "stable",
     },
     {
       // https://drafts.fxtf.org/css-masking/#typedef-geometry-box
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/001-expected.txt b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/001-expected.txt
deleted file mode 100644
index 464a7c9..0000000
--- a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/001-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] document.open in unload
-  assert_equals: expected "0123456789" but got "012389"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/support/001-1.html b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/support/001-1.html
index 72f41ae3e..2a9cab4 100644
--- a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/support/001-1.html
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/unloading-documents/support/001-1.html
@@ -1,7 +1,7 @@
 <!DOCTYPE HTML>
 <script>
  t = opener.t;
- do_test = t.step(function () {
+ do_test = t.step_func(function () {
    localStorage.test6564729 += '4';
    var d = document;
    var e = document.open(); // has no effect (ignore-opens-during-unload > 0)
diff --git a/third_party/blink/web_tests/fast/scroll-snap/snap-to-target-on-layout-change.html b/third_party/blink/web_tests/fast/scroll-snap/snap-to-target-on-layout-change.html
index 3118798..bbf63606 100644
--- a/third_party/blink/web_tests/fast/scroll-snap/snap-to-target-on-layout-change.html
+++ b/third_party/blink/web_tests/fast/scroll-snap/snap-to-target-on-layout-change.html
@@ -2,6 +2,9 @@
 <script src="../../resources/gesture-util.js"></script>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/testdriver.js"></script>
+<script src="../../resources/testdriver-actions.js"></script>
+<script src="../../resources/testdriver-vendor.js"></script>
 <style>
 #scroller {
   overflow: scroll;
@@ -58,43 +61,12 @@
   assert_equals(scroller.scrollTop, 0);
 }
 
-function scrollTop() {
-  return scroller.scrollTop;
-}
-
-function wheelScroll(delta, x, y, direction) {
-  // A mouse wheel scroll comes from mouse input with imprecise delta.
-  return smoothScroll(delta, x ,y , GestureSourceType.MOUSE_INPUT, direction,
-                      SPEED_INSTANT, false /* is precise delta */);
-}
-
-function keyPress(key) {
-  return new Promise((resolve, reject) => {
-    if (window.eventSender) {
-      eventSender.keyDown(key);
-      resolve();
-    }
-    else {
-      reject('This test requires window.eventSender');
-    }
-  })
-}
-
-function touchScroll(delta, x, y, direction) {
-  return smoothScroll(delta, x, y, GestureSourceType.TOUCH_INPUT, direction,
-                      SPEED_INSTANT);
-}
-
 promise_test (async t => {
   t.add_cleanup(cleanup);
 
   await mouseClickOn(10, 10);
-  const scrollPromise = waitForScrollEvent(scroller);
-  await wheelScroll(250 /* pixels to scroll */, 50, 50, 'down');
-  await scrollPromise;
-  // Use time based wait since overshooting the snap target position.
-  await waitForAnimationEndTimeBased(scrollTop);
-  assert_equals(scrollTop(), 400);
+  await wheelScroll(50, 50, 0, 250 /* pixels to scroll */, scroller);
+  assert_equals(scroller.scrollTop, 400);
 
   target_area.style.setProperty('top', '600px');
   assert_equals(scroller.scrollTop, 800);
@@ -105,8 +77,8 @@
   t.add_cleanup(cleanup);
 
   await mouseClickOn(10, 10);
-  await keyPress('ArrowDown');
-  await waitForScrollEnd(scroller, scrollTop, 400);
+  await keyboardScroll("ArrowDown", scroller);
+  assert_equals(scroller.scrollTop, 400);
 
   target_area.style.setProperty('top', '600px');
   assert_equals(scroller.scrollTop, 800);
@@ -119,11 +91,7 @@
     t.add_cleanup(cleanup);
 
     await mouseClickOn(10, 10);
-    const scrollPromise = waitForScrollEvent(scroller);
-    await touchScroll(250 /* delta */, 50, 50, 'down');
-    await scrollPromise;
-    // Use time based wait since overshooting the scroll snap position.
-    await waitForAnimationEndTimeBased(scrollTop);
+    await touchScroll(50, 250, 0, -250 /* delta */, scroller);
     assert_equals(scroller.scrollTop, 400);
 
     target_area.style.setProperty('top', '600px');
diff --git a/third_party/blink/web_tests/platform/fuchsia/compositing/geometry/bounds-ignores-hidden-expected.txt b/third_party/blink/web_tests/platform/fuchsia/compositing/geometry/bounds-ignores-hidden-expected.txt
index 94efc9b..aca93b6 100644
--- a/third_party/blink/web_tests/platform/fuchsia/compositing/geometry/bounds-ignores-hidden-expected.txt
+++ b/third_party/blink/web_tests/platform/fuchsia/compositing/geometry/bounds-ignores-hidden-expected.txt
@@ -7,7 +7,7 @@
       "backgroundColor": "#FFFFFF"
     },
     {
-      "name": "LayoutImage (positioned) IMG",
+      "name": "LayoutImage (positioned, block) IMG",
       "position": [10, 10],
       "bounds": [50, 50],
       "contentsOpaque": true,
diff --git a/third_party/catapult b/third_party/catapult
index 5725e8f..b142d41 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 5725e8f3792e1a4a2463cb598704d27d9116e006
+Subproject commit b142d415308e1f3ace94f0f39807bf0198ee004f
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index d87a929..1197cfe1 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: N/A
-Revision: 7049d966b5b24bcd4ce0a27312a366592019f1e8
+Revision: 9f896f2581c076ec028935b8d9703551eb73a796
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
@@ -46,8 +46,6 @@
    (crashpad/util/BUILD.gn, crashpad/util/linux/memory_map_test.cc)
  - CloseMultipleNowOrOnExec has been updated to invoke the new
    base::subtle::ResetFDOwnership() API
- - FALLTHROUGH macro has been replaced with C++17 attribute [[fallthrough]]
- - Qualified calls to bit_cast<>() with base::.
  - MultiprocessExec.MultiprocessExec is disabled entirely on the Mac.
    https://crbug.com/1341377
  - SystemSnapshotLinux.Basic is disabled on Android: https://crbug.com/1362091
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index 80c1aab..9559f70a 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -47,7 +47,7 @@
       '9719c1e1e676814c456b55f5f070eabad6709d31',
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      '9e21183c1ea369398d6f6ddd302c8db580bd19c4',
+      'ac3e7323953425b2b48af2536f5a6f778dcd0f4c',
   'crashpad/third_party/libfuzzer/src':
       Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
       'fda403cf93ecb8792cb1d061564d89a6553ca020',
diff --git a/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm b/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
index f68963a..0268b3a 100644
--- a/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
+++ b/third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
@@ -43,7 +43,6 @@
 #include <type_traits>
 #include <typeinfo>
 
-#include "base/bit_cast.h"
 #include "base/format_macros.h"
 #include "base/logging.h"
 #include "base/memory/free_deleter.h"
diff --git a/third_party/crashpad/crashpad/util/linux/memory_map.cc b/third_party/crashpad/crashpad/util/linux/memory_map.cc
index 58de835e..58383573 100644
--- a/third_party/crashpad/crashpad/util/linux/memory_map.cc
+++ b/third_party/crashpad/crashpad/util/linux/memory_map.cc
@@ -18,7 +18,6 @@
 #include <string.h>
 #include <sys/sysmacros.h>
 
-#include "base/bit_cast.h"
 #include "base/check_op.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
diff --git a/third_party/crashpad/crashpad/util/misc/clock_mac.cc b/third_party/crashpad/crashpad/util/misc/clock_mac.cc
index 81e53170..2b4dccd0 100644
--- a/third_party/crashpad/crashpad/util/misc/clock_mac.cc
+++ b/third_party/crashpad/crashpad/util/misc/clock_mac.cc
@@ -14,32 +14,12 @@
 
 #include "util/misc/clock.h"
 
-#include <mach/mach_time.h>
-
-#include "base/apple/mach_logging.h"
-
-namespace {
-
-mach_timebase_info_data_t* TimebaseInternal() {
-  mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t;
-  kern_return_t kr = mach_timebase_info(timebase_info);
-  MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
-  return timebase_info;
-}
-
-mach_timebase_info_data_t* Timebase() {
-  static mach_timebase_info_data_t* timebase_info = TimebaseInternal();
-  return timebase_info;
-}
-
-}  // namespace
+#include <time.h>
 
 namespace crashpad {
 
 uint64_t ClockMonotonicNanoseconds() {
-  uint64_t absolute_time = mach_absolute_time();
-  mach_timebase_info_data_t* timebase_info = Timebase();
-  return absolute_time * timebase_info->numer / timebase_info->denom;
+  return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
 }
 
 }  // namespace crashpad
diff --git a/third_party/cros-components/src b/third_party/cros-components/src
index 4d5c5a6..b1b1b0b 160000
--- a/third_party/cros-components/src
+++ b/third_party/cros-components/src
@@ -1 +1 @@
-Subproject commit 4d5c5a69d68bd814b9857800c594f5895cd1204e
+Subproject commit b1b1b0b6ff08289f3f5d960947aa12db68775d41
diff --git a/third_party/depot_tools b/third_party/depot_tools
index d9dab49..18bb70a 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit d9dab49dc651e5c5104a3793fb5113cc474dc673
+Subproject commit 18bb70aed6953810172bda05512ea42d1f39ef81
diff --git a/third_party/libc++/src b/third_party/libc++/src
index a88e6d6..1f70899 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit a88e6d6daa3e091f662c8a198b5d3a0d3a723705
+Subproject commit 1f70899ab6d9d2c3c455fa7adb3f3cbd7bfdf215
diff --git a/third_party/re2/src b/third_party/re2/src
index 71857b0..9d0b5bf 160000
--- a/third_party/re2/src
+++ b/third_party/re2/src
@@ -1 +1 @@
-Subproject commit 71857b0112ab6d9e1478f0212951c38c4a0ee1c8
+Subproject commit 9d0b5bf57cf0dc5388568127bcf079812d3f989e
diff --git a/third_party/skia b/third_party/skia
index e7e8a52..3a3475d 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit e7e8a521abc8803b1f54642997eb592485627081
+Subproject commit 3a3475d12f220b795821eabae517ab7cb6f12c9f
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 763e46e5..902f91b0 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -16572,8 +16572,6 @@
   <int value="1854" label="ACCESSIBILITY_PRIVATE_GETDISPLAYBOUNDS"/>
   <int value="1855" label="AUTOFILLPRIVATE_SERVERIBANLINKCLICKED"/>
   <int value="1856" label="AUTOTESTPRIVATE_CLEARALLOWEDPREF"/>
-  <int value="1857"
-      label="DEVELOPERPRIVATE_DISMISSSAFETYHUBEXTENSIONSMENUNOTIFICATION"/>
 </enum>
 
 <enum name="ExtensionInProgressRequestState">
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 8ee3038..bdcc19e0 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -4985,7 +4985,7 @@
 </histogram>
 
 <histogram name="Ash.Overview.Scroll.PresentationTime.MaxLatency.TabletMode"
-    units="ms" expires_after="2024-01-10">
+    units="ms" expires_after="2025-01-10">
   <owner>sammiequon@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
@@ -4995,7 +4995,7 @@
 </histogram>
 
 <histogram name="Ash.Overview.Scroll.PresentationTime.TabletMode" units="ms"
-    expires_after="2024-01-10">
+    expires_after="2025-01-10">
   <owner>sammiequon@chromium.org</owner>
   <owner>tclaiborne@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index da5cb44d..95f591c 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -2626,7 +2626,9 @@
 </histogram>
 
 <histogram name="Conversions.AggregatableReport.CreateReportStatus4"
-    enum="ConversionCreateAggregatableReportStatus" expires_after="2024-05-12">
+    enum="ConversionCreateAggregatableReportStatus" expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>linnan@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -2635,6 +2637,11 @@
     and why. Recorded once for each trigger event processed by the attribution
     storage layer. `kNotRegistered` will be logged if the source or trigger
     doesn't declare aggregatable data.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -2737,7 +2744,9 @@
 </histogram>
 
 <histogram name="Conversions.AggregatableReport.ReportSendOutcome2"
-    enum="ConversionReportSendOutcome" expires_after="2024-05-10">
+    enum="ConversionReportSendOutcome" expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>linnan@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -2745,6 +2754,11 @@
     Records the high level request status of an aggregatable report. Recorded
     once per aggregatable report. For aggregatable report that has been retried,
     only the last retry will be counted.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -2884,7 +2898,9 @@
 </histogram>
 
 <histogram name="Conversions.CreateReportStatus9"
-    enum="ConversionStorageCreateReportStatus" expires_after="2024-05-12">
+    enum="ConversionStorageCreateReportStatus" expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>tquintanilla@chromium.org</owner>
   <owner>apaseltiner@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -2892,6 +2908,11 @@
     Measures how often event-level triggers are stored successfully or rejected
     and why. Recorded once for each conversion event processed by the
     attribution storage layer.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -3196,7 +3217,9 @@
 </histogram>
 
 <histogram name="Conversions.OsRegistrationResult.{Type}"
-    enum="ConversionOsRegistrationResult" expires_after="2024-05-12">
+    enum="ConversionOsRegistrationResult" expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>linnan@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -3205,6 +3228,11 @@
     with Android or failed and why. Only recorded on Android when the
     AttributionReportingCrossAppWeb feature is enabled. Recorded for each OS
     registration processed by the attribution manager layer.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
   <token key="Type">
     <variant name="Source"/>
@@ -3289,7 +3317,9 @@
 </histogram>
 
 <histogram name="Conversions.ReportSendOutcome3"
-    enum="ConversionReportSendOutcome" expires_after="2024-05-12">
+    enum="ConversionReportSendOutcome" expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>linnan@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -3297,6 +3327,11 @@
     Records the high level request status of a conversion report. Recorded once
     per conversion report. For conversion report that has been retried, only the
     last retry will be counted.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -3464,7 +3499,9 @@
 </histogram>
 
 <histogram name="Conversions.SourceStoredStatus8"
-    enum="ConversionStorageSourceStatus" expires_after="2024-05-19">
+    enum="ConversionStorageSourceStatus" expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>tquintanilla@chromium.org</owner>
   <owner>anthonygarant@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -3472,6 +3509,11 @@
     Measures how often sources are stored successfully or rejected and why.
     Recorded once for each source event processed by the attribution storage
     layer.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index ba107d7e..b9ec19b3 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -492,13 +492,20 @@
 
 <histogram name="PrivacySandbox.AggregationService.ReportAssembler.Status"
     enum="PrivacySandboxAggregationServiceReportAssemblerStatus"
-    expires_after="2024-04-28">
+    expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>alexmt@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
   <summary>
     Records the high level status of each aggregatable report assembly request.
     Recorded for reports requested from any API (e.g. Attribution Reporting
     API).
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -516,7 +523,9 @@
 
 <histogram name="PrivacySandbox.AggregationService.ReportSender.Status"
     enum="PrivacySandboxAggregationServiceReportSenderStatus"
-    expires_after="2024-04-28">
+    expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>alexmt@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
   <summary>
@@ -526,6 +535,11 @@
 
     Recorded for every aggregatable report sent. It might retry in case of
     failure but only the final status will be counted for each report.
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -1025,7 +1039,9 @@
 
 <histogram name="PrivacySandbox.PrivateAggregation.Manager.RequestResult"
     enum="PrivacySandboxPrivateAggregationManagerRequestResult"
-    expires_after="2024-05-12">
+    expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>alexmt@chromium.org</owner>
   <owner>linnan@chromium.org</owner>
   <owner>measurement-api-dev+metrics@google.com</owner>
@@ -1035,6 +1051,11 @@
     cleared due to budget denial. Also breaks out the case of a report that
     started with no contributions. Recorded for each individual report request
     (but not each individual histogram contribution).
+
+    This is a critical histogram monitored by alerts to
+    measurement-api-dev+alerts@google.com. Please make sure future versions of
+    this histogram are properly set up for alerting, but prefer not to version
+    the histogram if possible.
   </summary>
 </histogram>
 
@@ -1343,7 +1364,9 @@
 </histogram>
 
 <histogram name="PrivacySandbox.{ApiType}" enum="PrivacySandboxApiAllowed"
-    expires_after="2024-04-28">
+    expires_after="never">
+<!-- expires-never: critical system health metric for monitoring -->
+
   <owner>linnan@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
   <owner>sauski@chromium.org</owner>
@@ -1351,6 +1374,11 @@
     Whether or not the {ApiType} operation was allowed by Privacy Sandbox
     settings and why. Recorded at the time settings are checked when performing
     the operation.
+
+    This is a critical histogram monitored by alerts. Please make sure future
+    versions of this histogram are properly set up for alerting. The attribution
+    reporting and private aggregation variants are monitored by
+    measurement-api-dev+alerts@google.com.
   </summary>
   <token key="ApiType">
     <variant name="IsAttributionReportingAllowed"
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index be39b76..33d4209 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -225,20 +225,6 @@
   </summary>
 </histogram>
 
-<histogram name="Tab.NewTabOnload{TabNewTabOnload}" units="ms"
-    expires_after="2021-03-07">
-  <owner>carlosk@chromium.org</owner>
-  <owner>harringtond@chromium.org</owner>
-  <summary>
-    The time for the new tab page to load. Only recorded on Android. Recorded
-    only once per tab, i.e. excluding back/forward navigations.
-    {TabNewTabOnload}
-  </summary>
-  <token key="TabNewTabOnload">
-    <variant name=""/>
-  </token>
-</histogram>
-
 <histogram name="Tab.PageLoadInPortrait" enum="DeviceOrientation"
     expires_after="M85">
   <owner>marq@chromium.org</owner>
diff --git a/tools/rust/package_rust.py b/tools/rust/package_rust.py
index fe4a0b9..e2dd4e4 100755
--- a/tools/rust/package_rust.py
+++ b/tools/rust/package_rust.py
@@ -26,18 +26,23 @@
 RUST_TOOLCHAIN_PACKAGE_NAME = f'rust-toolchain-{PACKAGE_VERSION}.tar.xz'
 
 
+# TODO(https://crbug.com/1329611): Use this function (after integrating Crubit
+# into Chromium; this work is on hold right now - see also
+# https://crbug.com/1510943#c2).
 def BuildCrubit(build_mac_arm):
     with open(os.path.join(THIRD_PARTY_DIR, BUILDLOG_NAME), 'w') as log:
         build_cmd = [sys.executable, os.path.join(THIS_DIR, 'build_crubit.py')]
         if build_mac_arm:
             build_cmd.append('--build-mac-arm')
-        # TODO(lukasza): Default to `fail_hard` once we actually depend on the
-        # build step (i.e. once we start packaging Crubit).
+        # TODO(https://crbug.com/1329611): Default to `fail_hard` once we
+        # actually depend on the build step (i.e. once we start packaging
+        # Crubit).
         TeeCmd(build_cmd, log, fail_hard=False)
 
-    # TODO(lukasza): Rename this function to BuildAndInstallCrubit and actually
-    # install Crubit binaries into RUST_TOOLCHAIN_OUT_DIR/bin (once we gain
-    # confidence that Crubit continues to build uneventfully on the bots).
+    # TODO(https://crbug.com/1329611): Rename this function to
+    # BuildAndInstallCrubit and actually install Crubit binaries into
+    # RUST_TOOLCHAIN_OUT_DIR/bin (once we gain confidence that Crubit continues
+    # to build uneventfully on the bots).
 
 
 def main():
@@ -95,8 +100,6 @@
             build_cmd.append('--build-mac-arm')
         TeeCmd(build_cmd, log)
 
-    BuildCrubit(args.build_mac_arm)
-
     # Strip everything in bin/ to reduce the package size.
     bin_dir_path = os.path.join(RUST_TOOLCHAIN_OUT_DIR, 'bin')
     if sys.platform != 'win32' and os.path.exists(bin_dir_path):
diff --git a/tools/typescript/definitions/developer_private.d.ts b/tools/typescript/definitions/developer_private.d.ts
index e2354706..863b1ea 100644
--- a/tools/typescript/definitions/developer_private.d.ts
+++ b/tools/typescript/definitions/developer_private.d.ts
@@ -488,7 +488,6 @@
           Promise<MatchingExtensionInfo[]>;
       export function updateSiteAccess(
           site: string, updates: ExtensionSiteAccessUpdate[]): Promise<void>;
-      export function dismissSafetyHubExtensionsMenuNotification(): void;
 
       export const onItemStateChanged: ChromeEvent<(data: EventData) => void>;
       export const onProfileStateChanged:
diff --git a/ui/file_manager/file_manager/foreground/js/directory_model.ts b/ui/file_manager/file_manager/foreground/js/directory_model.ts
index 5f04acae..de7b297 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_model.ts
+++ b/ui/file_manager/file_manager/foreground/js/directory_model.ts
@@ -1254,6 +1254,7 @@
           volumeChanged: (previousVolumeInfo !== currentVolumeInfo),
         },
       });
+      await currentVolumeInfo?.resolveDisplayRoot();
       this.dispatchEvent(event);
       if (previousDirEntry) {
         // If we changed from a directory to another directory always clear
diff --git a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
deleted file mode 100644
index db8445e..0000000
--- a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
+++ /dev/null
@@ -1,314 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assert} from 'chrome://resources/ash/common/assert.js';
-
-import {getKeyModifiers} from '../../common/js/dom_utils.js';
-import {getTreeItemEntry, isSameEntry} from '../../common/js/entry_utils.js';
-import {isNewDirectoryTreeEnabled} from '../../common/js/flags.js';
-import {DirectoryTreeContainer} from '../../containers/directory_tree_container.js';
-import {readSubDirectoriesForRenamedEntry} from '../../state/ducks/all_entries.js';
-import {getStore} from '../../state/store.js';
-import {XfTree} from '../../widgets/xf_tree.js';
-import {XfTreeItem} from '../../widgets/xf_tree_item.js';
-import {isTreeItem} from '../../widgets/xf_tree_util.js';
-
-import {DirectoryModel} from './directory_model.js';
-import {renameEntry, validateEntryName} from './file_rename.js';
-import {DirectoryItem, DirectoryTree} from './ui/directory_tree.js';
-import {FilesAlertDialog} from './ui/files_alert_dialog.js';
-
-/**
- * Naming controller for directory tree.
- */
-export class DirectoryTreeNamingController {
-  /**
-   * @param {!DirectoryModel} directoryModel
-   * @param {!DirectoryTree|!XfTree} directoryTree
-   * @param {DirectoryTreeContainer|null} directoryTreeContainer
-   * @param {!FilesAlertDialog} alertDialog
-   */
-  constructor(
-      directoryModel, directoryTree, directoryTreeContainer, alertDialog) {
-    /** @private @const @type {!DirectoryModel} */
-    this.directoryModel_ = directoryModel;
-
-    /** @private @const @type {!DirectoryTree|!XfTree} */
-    this.directoryTree_ = directoryTree;
-
-    /** @private @const @type {DirectoryTreeContainer|null} */
-    this.directoryTreeContainer_ = directoryTreeContainer;
-
-    /** @private @const @type {!FilesAlertDialog} */
-    this.alertDialog_ = alertDialog;
-
-    /** @private @type {?(DirectoryItem|XfTreeItem)} */
-    this.currentDirectoryItem_ = null;
-
-    /** @private @type {boolean} */
-    this.editing_ = false;
-
-    /**
-     * Whether the entry being renamed is a root of a removable
-     * partition/volume.
-     * @private @type {boolean}
-     */
-    this.isRemovableRoot_ = false;
-
-    /** @private @type {?import("../../externs/volume_info.js").VolumeInfo} */
-    this.volumeInfo_ = null;
-
-    /** @private @const @type {!HTMLInputElement} */
-    this.inputElement_ = /** @type {!HTMLInputElement} */
-        (document.createElement('input'));
-    this.inputElement_.type = 'text';
-    this.inputElement_.spellcheck = false;
-    this.inputElement_.addEventListener('keydown', this.onKeyDown_.bind(this));
-    this.inputElement_.addEventListener('blur', this.commitRename_.bind(this));
-    this.inputElement_.addEventListener('click', event => {
-      // Stop propagation of click event to prevent it being captured by
-      // directory item and current directory is changed to editing item.
-      event.stopPropagation();
-    });
-    // These events propagation needs to be stopped otherwise ripple will show
-    // on the tree item when the input is clicked.
-    // Note: 'up/down' are events from <paper-ripple> component.
-    const suppressedEvents = ['mouseup', 'mousedown', 'up', 'down'];
-    suppressedEvents.forEach(event => {
-      this.inputElement_.addEventListener(event, event => {
-        event.stopPropagation();
-      });
-    });
-  }
-
-  /**
-   * Returns input element.
-   * @return {!HTMLInputElement}
-   */
-  getInputElement() {
-    return this.inputElement_;
-  }
-
-  /**
-   * Returns the '.label' class element child of this.currentDirectoryItem_.
-   * @private
-   * @return {!HTMLElement}
-   */
-  getLabelElement_() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    const element = this.currentDirectoryItem_.firstElementChild;
-    // @ts-ignore: error TS18047: 'element' is possibly 'null'.
-    const label = element.querySelector('.label');
-    return /** @type {!HTMLElement} */ (assert(label));
-  }
-
-  /**
-   * Attaches naming controller to specified directory item and start rename.
-   * @param {!(DirectoryItem|XfTreeItem)} directoryItem An html element of a
-   *     node of the target.
-   * @param {boolean} isRemovableRoot Indicates whether the target is a
-   *     removable volume root or not.
-   * @param {import("../../externs/volume_info.js").VolumeInfo|null} volumeInfo
-   *     A volume information about the target entry. |volumeInfo| can be null
-   *     if method is invoked on a folder that is in the tree view and is not
-   *     root of an external drive.
-   */
-  attachAndStart(directoryItem, isRemovableRoot, volumeInfo) {
-    this.isRemovableRoot_ = isRemovableRoot;
-    this.volumeInfo_ = this.isRemovableRoot_ ? assert(volumeInfo) : null;
-
-    if (this.currentDirectoryItem_) {
-      return;
-    }
-
-    this.currentDirectoryItem_ = directoryItem;
-    // @ts-ignore: error TS2345: Argument of type 'boolean' is not assignable to
-    // parameter of type 'string'.
-    this.currentDirectoryItem_.setAttribute('renaming', true);
-
-    if (isTreeItem(directoryItem)) {  // XfTreeItem instance
-      this.inputElement_.slot = 'rename';
-      this.currentDirectoryItem_.appendChild(this.inputElement_);
-    } else {  // DirectoryItem instance
-      const renameInputElementPlaceholder =
-          // @ts-ignore: error TS2531: Object is possibly 'null'.
-          this.currentDirectoryItem_.firstElementChild.getElementsByClassName(
-              'rename-placeholder');
-
-      if (this.isRemovableRoot_ && renameInputElementPlaceholder.length === 1) {
-        // @ts-ignore: error TS2532: Object is possibly 'undefined'.
-        renameInputElementPlaceholder[0].appendChild(this.inputElement_);
-      } else {
-        const label = this.getLabelElement_();
-        label.insertAdjacentElement('afterend', this.inputElement_);
-      }
-    }
-
-    this.inputElement_.value = this.currentDirectoryItem_.label;
-    this.inputElement_.select();
-    this.inputElement_.focus();
-
-    this.editing_ = true;
-  }
-
-  /**
-   * Commits rename.
-   * @private
-   */
-  async commitRename_() {
-    // @ts-ignore: error TS2551: Property 'contextMenu' does not exist on type
-    // 'HTMLInputElement'. Did you mean 'oncontextmenu'?
-    const contextMenu = this.inputElement_.contextMenu;
-    if (!this.editing_ || (contextMenu && !contextMenu.hidden)) {
-      return;
-    }
-    this.editing_ = false;
-
-    const entry = /** @type {DirectoryEntry} */ (
-        assert(getTreeItemEntry(this.currentDirectoryItem_)));
-    const newName = this.inputElement_.value;
-
-    // If new name is the same as current name or empty, do nothing.
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    if (newName === this.currentDirectoryItem_.label || newName.length == 0) {
-      this.detach_();
-      return;
-    }
-
-    try {
-      await validateEntryName(
-          entry, newName,
-          this.directoryModel_.getFileFilter().isHiddenFilesVisible(),
-          this.volumeInfo_, this.isRemovableRoot_);
-      await this.performRename_(entry, newName);
-    } catch (error) {
-      // @ts-ignore: error TS18046: 'error' is of type 'unknown'.
-      await this.alertDialog_.showAsync(/** @type {string} */ (error.message));
-      this.editing_ = true;
-    }
-  }
-
-  /**
-   * Performs rename operation.
-   * @param {!DirectoryEntry} entry
-   * @param {string} newName Validated name.
-   * @private
-   */
-  async performRename_(entry, newName) {
-    const renamingCurrentDirectory =
-        isSameEntry(entry, this.directoryModel_.getCurrentDirEntry());
-    if (renamingCurrentDirectory) {
-      this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
-          true /* ignore */);
-    }
-
-    // TODO(yawano): Rename might take time on some volumes. Optimistically show
-    // new name in the UI before actual rename is completed.
-    try {
-      const newEntry = await renameEntry(
-          entry, newName, this.volumeInfo_, this.isRemovableRoot_);
-
-      // Put the new name in the .label element before detaching the
-      // <input> to prevent showing the old name.
-      if (isNewDirectoryTreeEnabled()) {
-        // @ts-ignore: error TS2531: Object is possibly 'null'.
-        this.currentDirectoryItem_.label = newName;
-      } else {
-        this.getLabelElement_().textContent = newName;
-        if (window.IN_TEST) {
-          // @ts-ignore: error TS2531: Object is possibly 'null'.
-          this.currentDirectoryItem_.setAttribute('entry-label', newName);
-        }
-      }
-
-      // We currently don't have promises/callbacks for when removableRoots are
-      // successfully renamed, so we can't update their subdirectories or
-      // update the current directory to them at this point.
-      if (this.isRemovableRoot_) {
-        return;
-      }
-
-      if (isNewDirectoryTreeEnabled() && this.directoryTreeContainer_) {
-        getStore().dispatch(readSubDirectoriesForRenamedEntry(newEntry));
-        this.directoryTreeContainer_.focusItemWithKeyWhenRendered(
-            newEntry.toURL());
-      } else {
-        // @ts-ignore: error TS2339: Property 'entry' does not exist on type
-        // 'XfTreeItem | DirectoryItem'.
-        this.currentDirectoryItem_.entry = newEntry;
-        // @ts-ignore: error TS2339: Property 'updateSubDirectories' does not
-        // exist on type 'XfTreeItem | DirectoryItem'.
-        this.currentDirectoryItem_.updateSubDirectories(/* recursive= */ true);
-      }
-
-      // If renamed directory was current directory, change it to new one.
-      if (renamingCurrentDirectory) {
-        this.directoryModel_.changeDirectoryEntry(
-            /** @type {!DirectoryEntry} */ (newEntry),
-            this.directoryModel_.setIgnoringCurrentDirectoryDeletion.bind(
-                this.directoryModel_, /* ignore= */ false));
-      }
-    } catch (error) {
-      this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
-          /* ignore= */ false);
-
-      // @ts-ignore: error TS18046: 'error' is of type 'unknown'.
-      this.alertDialog_.show(/** @type {string} */ (error.message));
-    } finally {
-      this.detach_();
-    }
-  }
-
-  /**
-   * Cancels rename.
-   * @private
-   */
-  cancelRename_() {
-    if (!this.editing_) {
-      return;
-    }
-
-    this.editing_ = false;
-    this.detach_();
-  }
-
-  /**
-   * Detaches controller from current directory item.
-   * @private
-   */
-  detach_() {
-    assert(!!this.currentDirectoryItem_);
-
-    this.inputElement_.remove();
-
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.currentDirectoryItem_.removeAttribute('renaming');
-    this.currentDirectoryItem_ = null;
-
-    // Restore focus to directory tree.
-    this.directoryTree_.focus();
-  }
-
-  /**
-   * Handles keydown event.
-   * @param {!Event} event
-   * @private
-   */
-  onKeyDown_(event) {
-    event.stopPropagation();
-
-    // @ts-ignore: error TS2339: Property 'key' does not exist on type 'Event'.
-    switch (getKeyModifiers(event) + event.key) {
-      case 'Escape':
-        this.cancelRename_();
-        event.preventDefault();
-        break;
-
-      case 'Enter':
-        this.commitRename_();
-        event.preventDefault();
-        break;
-    }
-  }
-}
diff --git a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.ts b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.ts
new file mode 100644
index 0000000..87e2fd2
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.ts
@@ -0,0 +1,271 @@
+// Copyright 2015 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assert} from 'chrome://resources/js/assert.js';
+
+import {getKeyModifiers} from '../../common/js/dom_utils.js';
+import {getTreeItemEntry, isSameEntry} from '../../common/js/entry_utils.js';
+import {isNewDirectoryTreeEnabled} from '../../common/js/flags.js';
+import {DirectoryTreeContainer} from '../../containers/directory_tree_container.js';
+import type {VolumeInfo} from '../../externs/volume_info.js';
+import {readSubDirectoriesForRenamedEntry} from '../../state/ducks/all_entries.js';
+import {getStore} from '../../state/store.js';
+import {XfTree} from '../../widgets/xf_tree.js';
+import {XfTreeItem} from '../../widgets/xf_tree_item.js';
+import {isTreeItem} from '../../widgets/xf_tree_util.js';
+
+import {DirectoryModel} from './directory_model.js';
+import {renameEntry, validateEntryName} from './file_rename.js';
+import {WithContextMenu} from './ui/context_menu_handler.js';
+import {DirectoryItem, DirectoryTree, SubDirectoryItem} from './ui/directory_tree.js';
+import {FilesAlertDialog} from './ui/files_alert_dialog.js';
+
+/**
+ * Naming controller for directory tree.
+ */
+export class DirectoryTreeNamingController {
+  private currentDirectoryItem_: DirectoryItem|XfTreeItem|null = null;
+  private editing_ = false;
+  /**
+   * Whether the entry being renamed is a root of a removable partition/volume.
+   */
+  private isRemovableRoot_: boolean = false;
+  private volumeInfo_: VolumeInfo|null = null;
+  private readonly inputElement_: HTMLInputElement&WithContextMenu;
+
+  constructor(
+      private readonly directoryModel_: DirectoryModel,
+      private readonly directoryTree_: DirectoryTree|XfTree,
+      private readonly directoryTreeContainer_: DirectoryTreeContainer|null,
+      private readonly alertDialog_: FilesAlertDialog) {
+    this.inputElement_ =
+        document.createElement('input') as HTMLInputElement & WithContextMenu;
+    this.inputElement_.type = 'text';
+    this.inputElement_.spellcheck = false;
+    this.inputElement_.addEventListener('keydown', this.onKeyDown_.bind(this));
+    this.inputElement_.addEventListener('blur', this.commitRename_.bind(this));
+    this.inputElement_.addEventListener('click', event => {
+      // Stop propagation of click event to prevent it being captured by
+      // directory item and current directory is changed to editing item.
+      event.stopPropagation();
+    });
+    // These events propagation needs to be stopped otherwise ripple will show
+    // on the tree item when the input is clicked.
+    // Note: 'up/down' are events from <paper-ripple> component.
+    const suppressedEvents = ['mouseup', 'mousedown', 'up', 'down'];
+    suppressedEvents.forEach(event => {
+      this.inputElement_.addEventListener(event, event => {
+        event.stopPropagation();
+      });
+    });
+  }
+
+  /**
+   * Returns input element.
+   */
+  getInputElement(): HTMLInputElement&WithContextMenu {
+    return this.inputElement_;
+  }
+
+  /**
+   * Returns the '.label' class element child of this.currentDirectoryItem_.
+   */
+  private getLabelElement_(): HTMLElement {
+    const element = this.currentDirectoryItem_!.firstElementChild!;
+    const label = element.querySelector('.label');
+    assert(label);
+    return label as HTMLElement;
+  }
+
+  /**
+   * Attaches naming controller to specified directory item and start rename.
+   * @param directoryItem An html element of a node of the target.
+   * @param isRemovableRoot Indicates whether the target is a removable volume
+   *     root or not.
+   * @param volumeInfo A volume information about the target entry. |volumeInfo|
+   *     can be null if method is invoked on a folder that is in the tree view
+   *     and is not root of an external drive.
+   */
+  attachAndStart(
+      directoryItem: (DirectoryItem|XfTreeItem), isRemovableRoot: boolean,
+      volumeInfo: VolumeInfo|null) {
+    this.isRemovableRoot_ = isRemovableRoot;
+    if (this.isRemovableRoot_) {
+      assert(volumeInfo);
+      this.volumeInfo_ = volumeInfo;
+    } else {
+      this.volumeInfo_ = null;
+    }
+
+    if (this.currentDirectoryItem_) {
+      return;
+    }
+
+    this.currentDirectoryItem_ = directoryItem;
+    this.currentDirectoryItem_.setAttribute('renaming', 'true');
+
+    if (isTreeItem(directoryItem)) {  // XfTreeItem instance
+      this.inputElement_.slot = 'rename';
+      this.currentDirectoryItem_.appendChild(this.inputElement_);
+    } else {  // DirectoryItem instance
+      const renameInputElementPlaceholder =
+          this.currentDirectoryItem_.firstElementChild!.getElementsByClassName(
+              'rename-placeholder');
+
+      if (this.isRemovableRoot_ && renameInputElementPlaceholder.length === 1) {
+        renameInputElementPlaceholder[0]!.appendChild(this.inputElement_);
+      } else {
+        const label = this.getLabelElement_();
+        label.insertAdjacentElement('afterend', this.inputElement_);
+      }
+    }
+
+    this.inputElement_.value = this.currentDirectoryItem_.label;
+    this.inputElement_.select();
+    this.inputElement_.focus();
+
+    this.editing_ = true;
+  }
+
+  /**
+   * Commits rename.
+   */
+  private async commitRename_() {
+    const contextMenu = this.inputElement_.contextMenu;
+    if (!this.editing_ || (contextMenu && !contextMenu.hidden)) {
+      return;
+    }
+    this.editing_ = false;
+
+    const entry =
+        getTreeItemEntry(this.currentDirectoryItem_) as DirectoryEntry;
+    assert(entry);
+    const newName = this.inputElement_.value;
+
+    // If new name is the same as current name or empty, do nothing.
+    if (newName === this.currentDirectoryItem_!.label || newName.length == 0) {
+      this.detach_();
+      return;
+    }
+
+    try {
+      await validateEntryName(
+          entry, newName,
+          this.directoryModel_.getFileFilter().isHiddenFilesVisible(),
+          this.volumeInfo_, this.isRemovableRoot_);
+      await this.performRename_(entry, newName);
+    } catch (error) {
+      await this.alertDialog_.showAsync((error as Error).message);
+      this.editing_ = true;
+    }
+  }
+
+  /**
+   * Performs rename operation.
+   * @param newName Validated name.
+   */
+  private async performRename_(entry: DirectoryEntry, newName: string) {
+    const renamingCurrentDirectory =
+        isSameEntry(entry, this.directoryModel_.getCurrentDirEntry());
+    if (renamingCurrentDirectory) {
+      this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
+          true /* ignore */);
+    }
+
+    // TODO(yawano): Rename might take time on some volumes. Optimistically show
+    // new name in the UI before actual rename is completed.
+    try {
+      const newEntry = await renameEntry(
+                           entry, newName, this.volumeInfo_,
+                           this.isRemovableRoot_) as DirectoryEntry;
+
+      // Put the new name in the .label element before detaching the <input> to
+      // prevent showing the old name.
+      if (isNewDirectoryTreeEnabled()) {
+        this.currentDirectoryItem_!.label = newName;
+      } else {
+        this.getLabelElement_().textContent = newName;
+        if (window.IN_TEST) {
+          this.currentDirectoryItem_!.setAttribute('entry-label', newName);
+        }
+      }
+
+      // We currently don't have promises/callbacks for when removableRoots are
+      // successfully renamed, so we can't update their subdirectories or update
+      // the current directory to them at this point.
+      if (this.isRemovableRoot_) {
+        return;
+      }
+
+      if (isNewDirectoryTreeEnabled() && this.directoryTreeContainer_) {
+        getStore().dispatch(readSubDirectoriesForRenamedEntry(newEntry));
+        this.directoryTreeContainer_.focusItemWithKeyWhenRendered(
+            newEntry.toURL());
+      } else {
+        assert(!isTreeItem(this.currentDirectoryItem_!));
+        assert(this.currentDirectoryItem_ instanceof SubDirectoryItem);
+        this.currentDirectoryItem_!.entry = newEntry;
+        this.currentDirectoryItem_!.updateSubDirectories(/* recursive= */ true);
+      }
+
+      // If renamed directory was current directory, change it to new one.
+      if (renamingCurrentDirectory) {
+        this.directoryModel_.changeDirectoryEntry(
+            newEntry,
+            this.directoryModel_.setIgnoringCurrentDirectoryDeletion.bind(
+                this.directoryModel_, /* ignore= */ false));
+      }
+    } catch (error) {
+      this.directoryModel_.setIgnoringCurrentDirectoryDeletion(
+          /* ignore= */ false);
+
+      this.alertDialog_.show((error as Error).message);
+    } finally {
+      this.detach_();
+    }
+  }
+
+  private cancelRename_() {
+    if (!this.editing_) {
+      return;
+    }
+
+    this.editing_ = false;
+    this.detach_();
+  }
+
+  /**
+   * Detaches controller from current directory item.
+   */
+  private detach_() {
+    assert(!!this.currentDirectoryItem_);
+
+    this.inputElement_.remove();
+
+    this.currentDirectoryItem_.removeAttribute('renaming');
+    this.currentDirectoryItem_ = null;
+
+    // Restore focus to directory tree.
+    this.directoryTree_.focus();
+  }
+
+  /**
+   * Handles keydown event.
+   */
+  private onKeyDown_(event: KeyboardEvent) {
+    event.stopPropagation();
+
+    switch (getKeyModifiers(event) + event.key) {
+      case 'Escape':
+        this.cancelRename_();
+        event.preventDefault();
+        break;
+
+      case 'Enter':
+        this.commitRename_();
+        event.preventDefault();
+        break;
+    }
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.ts b/ui/file_manager/file_manager/foreground/js/file_manager_commands.ts
index ccfeb1a..96e8c30 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.ts
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.ts
@@ -37,7 +37,7 @@
 import {MenuCommandsForUma, recordMenuItemSelected} from './command_handler.js';
 import {canExecuteVisibleOnDriveInNormalAppModeOnly, containsNonInteractiveEntry, currentVolumeIsInteractive, getCommandEntries, getCommandEntry, getElementVolumeInfo, getEventEntry, getOnlyOneSelectedDirectory, getParentEntry, getSharesheetLaunchSource, hasCapability, isDriveEntries, isFromSelectionMenu, isOnlyMyDriveEntries, isOnTrashRoot, isRootEntry, shouldIgnoreEvents, shouldShowMenuItemsForEntry} from './file_manager_commands_util.js';
 import {PasteWithDestDirectoryEvent} from './file_transfer_controller.js';
-import {HoldingSpaceUtil} from './holding_space_util.js';
+import {getAllowedVolumeTypes, maybeStoreTimeOfFirstPin} from './holding_space_util.js';
 import {PathComponent} from './path_component.js';
 import {type CanExecuteEvent, Command, type CommandEvent} from './ui/command.js';
 import type {DirectoryItem, DirectoryTree} from './ui/directory_tree.js';
@@ -1419,7 +1419,7 @@
     }
 
     // Filter out entries from unsupported volumes.
-    const allowedVolumeTypes = HoldingSpaceUtil.getAllowedVolumeTypes();
+    const allowedVolumeTypes = getAllowedVolumeTypes();
     const entries =
         fileManager.selectionHandler.selection.entries.filter(entry => {
           const volumeInfo = fileManager.volumeManager.getVolumeInfo(entry);
@@ -1431,7 +1431,7 @@
         entries.map(unwrapEntry) as Entry[], this.addsItems_, () => {});
 
     if (this.addsItems_) {
-      HoldingSpaceUtil.maybeStoreTimeOfFirstPin();
+      maybeStoreTimeOfFirstPin();
     }
 
     recordMenuItemSelected(
@@ -1442,7 +1442,7 @@
   override canExecute(event: CanExecuteEvent, fileManager: CommandHandlerDeps) {
     const command = event.command;
 
-    const allowedVolumeTypes = HoldingSpaceUtil.getAllowedVolumeTypes();
+    const allowedVolumeTypes = getAllowedVolumeTypes();
     const currentRootType = fileManager.directoryModel.getCurrentRootType();
     if (!isRecentRootType(currentRootType)) {
       const volumeInfo = fileManager.directoryModel.getCurrentVolumeInfo();
diff --git a/ui/file_manager/file_manager/foreground/js/holding_space_util.js b/ui/file_manager/file_manager/foreground/js/holding_space_util.js
deleted file mode 100644
index 85df33c0..0000000
--- a/ui/file_manager/file_manager/foreground/js/holding_space_util.js
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Utility methods for the holding space feature.
- */
-
-import {recordValue} from '../../common/js/metrics.js';
-import {storage} from '../../common/js/storage.js';
-import {VolumeType} from '../../common/js/volume_manager_types.js';
-
-export class HoldingSpaceUtil {
-  /**
-   * Returns the key in localStorage to store the time (in milliseconds) of the
-   * first pin to holding space.
-   * @return {string}
-   * @private
-   */
-  static get TIME_OF_FIRST_PIN_KEY_() {
-    return 'holdingSpaceTimeOfFirstPin';
-  }
-
-  /**
-   * Returns the key in localStorage to store the time (in milliseconds) of the
-   * first showing of the holding space welcome banner.
-   * @constructor
-   * @private
-   */
-  static get TIME_OF_FIRST_WELCOME_BANNER_SHOW_KEY_() {
-    return 'holdingSpaceTimeOfFirstWelcomeBannerShow';
-  }
-
-  /**
-   * Returns the volume types for which the holding space feature is allowed.
-   * @return {!Array<?VolumeType>}
-   */
-  static getAllowedVolumeTypes() {
-    return [
-      VolumeType.ANDROID_FILES,
-      VolumeType.CROSTINI,
-      VolumeType.GUEST_OS,
-      VolumeType.DRIVE,
-      VolumeType.DOWNLOADS,
-    ];
-  }
-
-  /**
-   * Returns a promise which resolves to the time (in milliseconds) of the first
-   * pin to holding space. If no pin has occurred, resolves to `undefined`.
-   * @return {Promise<?number>}
-   * @private
-   */
-  static getTimeOfFirstPin_() {
-    return new Promise(resolve => {
-      const key = HoldingSpaceUtil.TIME_OF_FIRST_PIN_KEY_;
-      storage.local.get(key, values => {
-        // @ts-ignore: error TS7053: Element implicitly has an 'any' type
-        // because expression of type 'string' can't be used to index type
-        // 'Object'.
-        resolve(values[key]);
-      });
-    });
-  }
-
-  /**
-   * Returns a promise which resolves to the time (in milliseconds) of the first
-   * showing of the holding space welcome banner. If no showing has occurred,
-   * resolves to `undefined`.
-   * @return {Promise<?number>}
-   * @private
-   */
-  static getTimeOfFirstWelcomeBannerShow_() {
-    return new Promise(resolve => {
-      const key = HoldingSpaceUtil.TIME_OF_FIRST_WELCOME_BANNER_SHOW_KEY_;
-      storage.local.get(key, values => {
-        // @ts-ignore: error TS7053: Element implicitly has an 'any' type
-        // because expression of type 'string' can't be used to index type
-        // 'Object'.
-        resolve(values[key]);
-      });
-    });
-  }
-
-  /**
-   * If not previously stored, stores now (in milliseconds) as the time of the
-   * first pin to holding space.
-   */
-  static async maybeStoreTimeOfFirstPin() {
-    const now = Date.now();
-
-    // Time of first pin should only be stored once.
-    if (await HoldingSpaceUtil.getTimeOfFirstPin_()) {
-      return;
-    }
-
-    // Store time of first pin.
-    const values = {};
-    // @ts-ignore: error TS7053: Element implicitly has an 'any' type because
-    // expression of type 'string' can't be used to index type '{}'.
-    values[HoldingSpaceUtil.TIME_OF_FIRST_PIN_KEY_] = now;
-    storage.local.set(values);
-
-    // Record a metric of the interval from the first time the holding space
-    // welcome banner was shown to the time of the first pin to holding space.
-    // If the welcome banner was not shown prior to the first pin, record zero.
-    const timeOfFirstWelcomeBannerShow =
-        await HoldingSpaceUtil.getTimeOfFirstWelcomeBannerShow_() || now;
-    // We trim the max value to be 2^31 - 1, which is the maximum integer value
-    // that histograms can record.
-    const timeFromFirstWelcomeBannerShowToFirstPin =
-        Math.min(2 ** 31 - 1, now - timeOfFirstWelcomeBannerShow);
-
-    // The histogram will use min values of 1 second and max of 1 day. Note
-    // that it's permissible to record values smaller/larger than the min/max
-    // and they will fall into the histogram's underflow/overflow bucket
-    // respectively.
-    const oneSecondInMillis = 1000;
-    const oneDayInMillis = 24 * 60 * 60 * 1000;
-    recordValue(
-        /*name=*/ 'HoldingSpace.TimeFromFirstWelcomeBannerShowToFirstPin',
-        chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG,
-        /*min=*/ oneSecondInMillis,
-        /*max=*/ oneDayInMillis,
-        /*buckets=*/ 50,
-        /*value=*/ timeFromFirstWelcomeBannerShowToFirstPin);
-  }
-
-  /**
-   * If not previously stored, stores now (in milliseconds) as the time of the
-   * first showing of the holding space welcome banner.
-   */
-  static async maybeStoreTimeOfFirstWelcomeBannerShow() {
-    const now = Date.now();
-
-    // Time of first show should only be stored once.
-    if (await HoldingSpaceUtil.getTimeOfFirstWelcomeBannerShow_()) {
-      return;
-    }
-
-    // Store time of first show.
-    const values = {};
-    // @ts-ignore: error TS7053: Element implicitly has an 'any' type because
-    // expression of type 'string' can't be used to index type '{}'.
-    values[HoldingSpaceUtil.TIME_OF_FIRST_WELCOME_BANNER_SHOW_KEY_] = now;
-    storage.local.set(values);
-  }
-}
diff --git a/ui/file_manager/file_manager/foreground/js/holding_space_util.ts b/ui/file_manager/file_manager/foreground/js/holding_space_util.ts
new file mode 100644
index 0000000..ce5bb59
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/holding_space_util.ts
@@ -0,0 +1,115 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Utility methods for the holding space feature.
+ */
+
+import {recordValue} from '../../common/js/metrics.js';
+import {storage} from '../../common/js/storage.js';
+import {VolumeType} from '../../common/js/volume_manager_types.js';
+
+/**
+ * Key in localStorage to store the time (in milliseconds) of the first pin to
+ * holding space.
+ */
+const TIME_OF_FIRST_PIN_KEY = 'holdingSpaceTimeOfFirstPin';
+
+/**
+ * Key in localStorage to store the time (in milliseconds) of the first showing
+ * of the holding space welcome banner.
+ */
+const TIME_OF_FIRST_WELCOME_BANNER_SHOW_KEY =
+    'holdingSpaceTimeOfFirstWelcomeBannerShow';
+
+
+/** Gets the volume types for which the holding space feature is allowed. */
+export function getAllowedVolumeTypes(): VolumeType[] {
+  return [
+    VolumeType.ANDROID_FILES,
+    VolumeType.CROSTINI,
+    VolumeType.GUEST_OS,
+    VolumeType.DRIVE,
+    VolumeType.DOWNLOADS,
+  ];
+}
+
+/**
+ * Returns a promise which resolves to the time (in milliseconds) of the first
+ * pin to holding space. If no pin has occurred, resolves to `undefined`.
+ */
+function getTimeOfFirstPin(): Promise<number|undefined> {
+  const key = TIME_OF_FIRST_PIN_KEY;
+  return new Promise(
+      resolve => storage.local.get(
+          key, (values: Record<string, any>) => resolve(values[key])));
+}
+
+/**
+ * Returns a promise which resolves to the time (in milliseconds) of the first
+ * showing of the holding space welcome banner. If no showing has occurred,
+ * resolves to `undefined`.
+ */
+function getTimeOfFirstWelcomeBannerShow(): Promise<number|undefined> {
+  const key = TIME_OF_FIRST_WELCOME_BANNER_SHOW_KEY;
+  return new Promise(
+      resolve => storage.local.get(
+          key, (values: Record<string, any>) => resolve(values[key])));
+}
+
+/**
+ * If not previously stored, stores now (in milliseconds) as the time of the
+ * first pin to holding space.
+ */
+export async function maybeStoreTimeOfFirstPin() {
+  const now = Date.now();
+
+  // Time of first pin should only be stored once.
+  if (await getTimeOfFirstPin()) {
+    return;
+  }
+
+  // Store time of first pin.
+  storage.local.set({[TIME_OF_FIRST_PIN_KEY]: now});
+
+  // Record a metric of the interval from the first time the holding space
+  // welcome banner was shown to the time of the first pin to holding space.
+  // If the welcome banner was not shown prior to the first pin, record zero.
+  const timeOfFirstWelcomeBannerShow =
+      await getTimeOfFirstWelcomeBannerShow() || now;
+  // We trim the max value to be 2^31 - 1, which is the maximum integer value
+  // that histograms can record.
+  const timeFromFirstWelcomeBannerShowToFirstPin =
+      Math.min(2 ** 31 - 1, now - timeOfFirstWelcomeBannerShow);
+
+  // The histogram will use min values of 1 second and max of 1 day. Note
+  // that it's permissible to record values smaller/larger than the min/max
+  // and they will fall into the histogram's underflow/overflow bucket
+  // respectively.
+  const oneSecondInMillis = 1000;
+  const oneDayInMillis = 24 * 60 * 60 * 1000;
+  recordValue(
+      /*name=*/ 'HoldingSpace.TimeFromFirstWelcomeBannerShowToFirstPin',
+      chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG,
+      /*min=*/ oneSecondInMillis,
+      /*max=*/ oneDayInMillis,
+      /*buckets=*/ 50,
+      /*value=*/ timeFromFirstWelcomeBannerShowToFirstPin);
+}
+
+/**
+ * If not previously stored, stores now (in milliseconds) as the time of the
+ * first showing of the holding space welcome banner.
+ */
+export async function maybeStoreTimeOfFirstWelcomeBannerShow() {
+  const now = Date.now();
+
+  // Time of first show should only be stored once.
+  if (await getTimeOfFirstWelcomeBannerShow()) {
+    return;
+  }
+
+  // Store time of first show.
+  storage.local.set({[TIME_OF_FIRST_WELCOME_BANNER_SHOW_KEY]: now});
+}
diff --git a/ui/file_manager/file_manager/foreground/js/metadata_box_controller.ts b/ui/file_manager/file_manager/foreground/js/metadata_box_controller.ts
index bec79dbd..9160a34 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata_box_controller.ts
+++ b/ui/file_manager/file_manager/foreground/js/metadata_box_controller.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {isDirectoryEntry, isSameEntry, unwrapEntry} from '../../common/js/entry_utils.js';
+import {isDirectoryEntry, isNativeEntry, isSameEntry, unwrapEntry} from '../../common/js/entry_utils.js';
 import {getType} from '../../common/js/file_type.js';
 import {strf} from '../../common/js/translations.js';
 import {TrashEntry} from '../../common/js/trash.js';
@@ -230,6 +230,13 @@
       return;
     }
     const directoryEntry = unwrapEntry(entry);
+    if (!isNativeEntry(directoryEntry)) {
+      const typeName = ('typeName' in directoryEntry) ?
+          directoryEntry.typeName :
+          'no typeName';
+      console.warn('Supplied directory is not a native type:', typeName);
+      return;
+    }
 
     if (this.metadataBox.size === '') {
       this.metadataBox.size = ' ';  // Provide a dummy size value.
diff --git a/ui/file_manager/file_manager/foreground/js/naming_controller.ts b/ui/file_manager/file_manager/foreground/js/naming_controller.ts
index 2c8c990..dd8e8b0 100644
--- a/ui/file_manager/file_manager/foreground/js/naming_controller.ts
+++ b/ui/file_manager/file_manager/foreground/js/naming_controller.ts
@@ -16,21 +16,20 @@
 import type {DirectoryModel} from './directory_model.js';
 import {renameEntry, validateEntryName, validateFileName} from './file_rename.js';
 import type {FileSelectionHandler} from './file_selection.js';
+import type {WithContextMenu} from './ui/context_menu_handler.js';
 import type {ConfirmDialog} from './ui/dialogs.js';
 import {FilesAlertDialog} from './ui/files_alert_dialog.js';
 import {type ListContainer, ListType} from './ui/list_container.js';
 import type {ListItem} from './ui/list_item.js';
 import type {ListSelectionModel} from './ui/list_selection_model.js';
 import type {ListSingleSelectionModel} from './ui/list_single_selection_model.js';
-import type {Menu} from './ui/menu.js';
 
 
 // TODO(b/289003444): Fix this by using proper custom element.
 type XfRenameInput = HTMLInputElement&{
   currentEntry: Entry | FilesAppEntry | null,
   validation: boolean,
-  contextMenu: Menu,
-};
+}&WithContextMenu;
 
 /**
  * Controller to handle naming.
diff --git a/ui/file_manager/file_manager/foreground/js/selection_menu_controller.js b/ui/file_manager/file_manager/foreground/js/selection_menu_controller.js
deleted file mode 100644
index 0c402f78..0000000
--- a/ui/file_manager/file_manager/foreground/js/selection_menu_controller.js
+++ /dev/null
@@ -1,54 +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.
-
-import {Menu} from './ui/menu.js';
-import {MultiMenuButton} from './ui/multi_menu_button.js';
-
-export class SelectionMenuController {
-  /**
-   * @param {!MultiMenuButton} selectionMenuButton
-   * @param {!Menu} menu
-   */
-  constructor(selectionMenuButton, menu) {
-    /**
-     * @type {!Menu}
-     * @const
-     */
-    this.menu_ = menu;
-
-    selectionMenuButton.addEventListener(
-        'menushow', this.onShowMenu_.bind(this));
-    selectionMenuButton.addEventListener(
-        'menuhide', this.onHideMenu_.bind(this));
-  }
-
-  /**
-   * @private
-   */
-  onShowMenu_() {
-    // @ts-ignore: error TS2339: Property 'classList' does not exist on type
-    // 'Menu'.
-    this.menu_.classList.toggle('toolbar-menu', true);
-    // crbug.com 752035 focus still on button, get rid of the tooltip
-    // @ts-ignore: error TS2304: Cannot find name 'FilesTooltip'.
-    /** @type {!FilesTooltip} */ (document.querySelector('files-tooltip'))
-        .hideTooltip();
-  }
-
-  /**
-   * @private
-   */
-  onHideMenu_() {
-    // If menu is animating to close, then do not remove 'toolbar-menu' yet, it
-    // will be removed at the end of FilesMenuItem.setMenuAsAnimating_ to avoid
-    // flicker.  See crbug.com/862926.
-    // @ts-ignore: error TS2339: Property 'classList' does not exist on type
-    // 'Menu'.
-    if (!this.menu_.classList.contains('animating')) {
-      // @ts-ignore: error TS2339: Property 'classList' does not exist on type
-      // 'Menu'.
-      this.menu_.classList.toggle('toolbar-menu', false);
-    }
-  }
-}
diff --git a/ui/file_manager/file_manager/foreground/js/selection_menu_controller.ts b/ui/file_manager/file_manager/foreground/js/selection_menu_controller.ts
new file mode 100644
index 0000000..951f09f
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/selection_menu_controller.ts
@@ -0,0 +1,31 @@
+// 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.
+
+import {Menu} from './ui/menu.js';
+import {MultiMenuButton} from './ui/multi_menu_button.js';
+
+export class SelectionMenuController {
+  constructor(
+      selectionMenuButton: MultiMenuButton, private readonly menu_: Menu) {
+    selectionMenuButton.addEventListener(
+        'menushow', this.onShowMenu_.bind(this));
+    selectionMenuButton.addEventListener(
+        'menuhide', this.onHideMenu_.bind(this));
+  }
+
+  private onShowMenu_() {
+    this.menu_.classList.toggle('toolbar-menu', true);
+    // crbug.com 752035 focus still on button, get rid of the tooltip
+    document.querySelector('files-tooltip')?.hideTooltip();
+  }
+
+  private onHideMenu_() {
+    // If menu is animating to close, then do not remove 'toolbar-menu' yet, it
+    // will be removed at the end of FilesMenuItem.setMenuAsAnimating_ to avoid
+    // flicker.  See crbug.com/862926.
+    if (!this.menu_.classList.contains('animating')) {
+      this.menu_.classList.toggle('toolbar-menu', false);
+    }
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.ts b/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.ts
index d124d88..ed11dab9 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.ts
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners/holding_space_welcome_banner.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {RootType, VolumeType} from '../../../../common/js/volume_manager_types.js';
-import {HoldingSpaceUtil} from '../../holding_space_util.js';
+import {getAllowedVolumeTypes, maybeStoreTimeOfFirstWelcomeBannerShow} from '../../holding_space_util.js';
 
 import {EducationalBanner} from './educational_banner.js';
 import {getTemplate} from './holding_space_welcome_banner.html.js';
@@ -34,23 +34,22 @@
    * at HoldingSpaceUtil.
    */
   override allowedVolumes() {
-    return HoldingSpaceUtil.getAllowedVolumeTypes().map(
-        (type: VolumeType|null) => {
-          if (type === VolumeType.DRIVE) {
-            return {
-              type: VolumeType.DRIVE,
-              root: RootType.DRIVE,
-            };
-          }
-          return {type: type!};
-        });
+    return getAllowedVolumeTypes().map((type: VolumeType|null) => {
+      if (type === VolumeType.DRIVE) {
+        return {
+          type: VolumeType.DRIVE,
+          root: RootType.DRIVE,
+        };
+      }
+      return {type: type!};
+    });
   }
 
   /**
    * Store the time the banner was first shown.
    */
   override onShow() {
-    HoldingSpaceUtil.maybeStoreTimeOfFirstWelcomeBannerShow();
+    maybeStoreTimeOfFirstWelcomeBannerShow();
   }
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js b/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js
deleted file mode 100644
index b368a49..0000000
--- a/ui/file_manager/file_manager/foreground/js/ui/commandbutton.js
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2012 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview This implements a common button control, bound to command.
- */
-
-import {assert} from 'chrome://resources/ash/common/assert.js';
-import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
-
-import {crInjectTypeAndInit} from '../../../common/js/cr_ui.js';
-
-import {Command} from './command.js';
-
-/**
- * Creates a new button element.
- */
-export class CommandButton extends CrButtonElement {
-  constructor() {
-    super();
-
-    /**
-     * Associated command.
-     * @private @type {?Command}
-     */
-    this.command_ = null;
-  }
-
-  initialize() {
-    let commandId;
-    if ((commandId = this.getAttribute('command'))) {
-      this.setCommand(commandId);
-    }
-
-    this.addEventListener('click', this.handleClick_.bind(this));
-  }
-
-  /**
-   * Returns associated command.
-   * @return {?Command} associated command.
-   */
-  getCommand() {
-    return this.command_;
-  }
-
-  /**
-   * Associates command with this button.
-   * @param {string|Command} command Command id, or command object to
-   * associate with this button.
-   */
-  setCommand(command) {
-    if (this.command_) {
-      this.command_.removeEventListener(
-          'labelChange',
-          // @ts-ignore: error TS2352: Conversion of type 'this' to type
-          // 'EventListener' may be a mistake because neither type sufficiently
-          // overlaps with the other. If this was intentional, convert the
-          // expression to 'unknown' first.
-          /** @type {EventListener} */ (this));
-      this.command_.removeEventListener(
-          'disabledChange',
-          // @ts-ignore: error TS2352: Conversion of type 'this' to type
-          // 'EventListener' may be a mistake because neither type sufficiently
-          // overlaps with the other. If this was intentional, convert the
-          // expression to 'unknown' first.
-          /** @type {EventListener} */ (this));
-      this.command_.removeEventListener(
-          'hiddenChange',
-          // @ts-ignore: error TS2352: Conversion of type 'this' to type
-          // 'EventListener' may be a mistake because neither type sufficiently
-          // overlaps with the other. If this was intentional, convert the
-          // expression to 'unknown' first.
-          /** @type {EventListener} */ (this));
-    }
-
-    if (typeof command == 'string') {
-      assert(command[0] == '#');
-      command = /** @type {!Command} */
-          (this.ownerDocument.body.querySelector(command));
-      crInjectTypeAndInit(command, Command);
-    }
-
-    this.command_ = command;
-    if (command) {
-      if (command.id) {
-        this.setAttribute('command', '#' + command.id);
-      }
-
-      this.setLabel(command.label);
-      this.disabled = command.disabled;
-      this.hidden = command.hidden;
-
-      this.command_.addEventListener(
-          'labelChange',
-          // @ts-ignore: error TS2352: Conversion of type 'this' to type
-          // 'EventListener' may be a mistake because neither type sufficiently
-          // overlaps with the other. If this was intentional, convert the
-          // expression to 'unknown' first.
-          /** @type {EventListener} */ (this));
-      this.command_.addEventListener(
-          'disabledChange',
-          // @ts-ignore: error TS2352: Conversion of type 'this' to type
-          // 'EventListener' may be a mistake because neither type sufficiently
-          // overlaps with the other. If this was intentional, convert the
-          // expression to 'unknown' first.
-          /** @type {EventListener} */ (this));
-      this.command_.addEventListener(
-          'hiddenChange',
-          // @ts-ignore: error TS2352: Conversion of type 'this' to type
-          // 'EventListener' may be a mistake because neither type sufficiently
-          // overlaps with the other. If this was intentional, convert the
-          // expression to 'unknown' first.
-          /** @type {EventListener} */ (this));
-    }
-  }
-
-  /**
-   * Returns button label
-   * @return {string} Button label.
-   */
-  getLabel() {
-    return this.command_ ? this.command_.label : '';
-  }
-
-  /**
-   * Sets button label.
-   * @param {string} label New button label.
-   */
-  setLabel(label) {
-    // Swap the textContent with current label only when this button doesn't
-    // have any elements as children.
-    //
-    // TODO(fukino): If a user customize the button content, it becomes the
-    // user's responsibility to update the content on command label's change.
-    // Updating the label in customized button content should be done
-    // automatically by specifying an element which should be synced with the
-    // command label using class name or polymer's template binding.
-    if (!this.firstElementChild) {
-      this.textContent = label;
-    }
-  }
-
-  /**
-   * Handles click event and dispatches associated command.
-   * @param {Event} _e The mouseup event object.
-   * @private
-   */
-  handleClick_(_e) {
-    if (!this.disabled && this.command_) {
-      this.command_.execute(this);
-    }
-  }
-
-  /**
-   * Handles changes to the associated command.
-   * @param {Event} e The event object.
-   */
-  handleEvent(e) {
-    switch (e.type) {
-      case 'disabledChange':
-        // @ts-ignore: error TS2531: Object is possibly 'null'.
-        this.disabled = this.command_.disabled;
-        break;
-      case 'hiddenChange':
-        // @ts-ignore: error TS2531: Object is possibly 'null'.
-        this.hidden = this.command_.hidden;
-        break;
-      case 'labelChange':
-        // @ts-ignore: error TS2531: Object is possibly 'null'.
-        this.setLabel(this.command_.label);
-        break;
-    }
-  }
-}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/commandbutton.ts b/ui/file_manager/file_manager/foreground/js/ui/commandbutton.ts
new file mode 100644
index 0000000..0fc093c
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/ui/commandbutton.ts
@@ -0,0 +1,128 @@
+// Copyright 2012 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview This implements a common button control, bound to command.
+ */
+
+import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import {assert} from 'chrome://resources/js/assert.js';
+
+import {crInjectTypeAndInit} from '../../../common/js/cr_ui.js';
+
+import {Command} from './command.js';
+
+/**
+ * Creates a new button element.
+ */
+export class CommandButton extends CrButtonElement {
+  /**
+   * Associated command.
+   */
+  private command_: null|Command = null;
+
+  initialize() {
+    const commandId = this.getAttribute('command');
+    if (commandId) {
+      this.setCommand(commandId);
+    }
+
+    this.addEventListener('click', this.handleClick_.bind(this));
+  }
+
+  /**
+   * Returns associated command.
+   */
+  getCommand(): null|Command {
+    return this.command_;
+  }
+
+  /**
+   * Associates command with this button.
+   * @param command Command id, or command object to associate with this button.
+   */
+  setCommand(command: string|Command) {
+    if (this.command_) {
+      this.command_.removeEventListener('labelChange', this);
+      this.command_.removeEventListener('disabledChange', this);
+      this.command_.removeEventListener('hiddenChange', this);
+    }
+
+    if (typeof command == 'string') {
+      assert(command[0] == '#');
+      command = this.ownerDocument.body.querySelector<Command>(command)!;
+      assert(command);
+      crInjectTypeAndInit(command, Command);
+    }
+
+    this.command_ = command;
+    if (command) {
+      if (command.id) {
+        this.setAttribute('command', '#' + command.id);
+      }
+
+      this.setLabel(command.label);
+      this.disabled = command.disabled;
+      this.hidden = command.hidden;
+
+      this.command_.addEventListener('labelChange', this);
+      this.command_.addEventListener('disabledChange', this);
+      this.command_.addEventListener('hiddenChange', this);
+    }
+  }
+
+  /**
+   * Returns button label
+   */
+  getLabel(): string {
+    return this.command_ ? this.command_.label : '';
+  }
+
+  /**
+   * Sets button label.
+   */
+  setLabel(label: string) {
+    // Swap the textContent with current label only when this button doesn't
+    // have any elements as children.
+    //
+    // TODO(fukino): If a user customize the button content, it becomes the
+    // user's responsibility to update the content on command label's change.
+    // Updating the label in customized button content should be done
+    // automatically by specifying an element which should be synced with the
+    // command label using class name or polymer's template binding.
+    if (!this.firstElementChild) {
+      this.textContent = label;
+    }
+  }
+
+  /**
+   * Handles click event and dispatches associated command.
+   * @param _e The mouseup event object.
+   */
+  private handleClick_(_e: Event) {
+    if (!this.disabled && this.command_) {
+      this.command_.execute(this);
+    }
+  }
+
+  /**
+   * Handles changes to the associated command.
+   */
+  handleEvent(e: Event) {
+    switch (e.type) {
+      case 'disabledChange':
+        assert(this.command_);
+        this.disabled = this.command_.disabled;
+        break;
+      case 'hiddenChange':
+        assert(this.command_);
+        this.hidden = this.command_.hidden;
+        break;
+      case 'labelChange':
+        assert(this.command_);
+        this.setLabel(this.command_.label);
+        break;
+    }
+  }
+}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/context_menu_handler.js b/ui/file_manager/file_manager/foreground/js/ui/context_menu_handler.js
index 1a44f4a..c0d4ff9 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/context_menu_handler.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/context_menu_handler.js
@@ -389,6 +389,18 @@
 }
 
 /**
+ * Use this interface to define an element that also might have a context menu
+ * attached, e.g. HTMLInputElement & WithContextMenu.
+ * @interface
+ */
+export class WithContextMenu {
+  constructor() {
+    /** @type {Menu|undefined} */
+    this.contextMenu = undefined;
+  }
+}
+
+/**
  * The singleton context menu handler.
  * @type {!ContextMenuHandler}
  */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_menu.js b/ui/file_manager/file_manager/foreground/js/ui/files_menu.js
deleted file mode 100644
index ff44303..0000000
--- a/ui/file_manager/file_manager/foreground/js/ui/files_menu.js
+++ /dev/null
@@ -1,329 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {assertInstanceof} from 'chrome://resources/ash/common/assert.js';
-
-import {Menu} from './menu.js';
-import {MenuItem} from './menu_item.js';
-
-/**
- * Menu item with ripple animation.
- */
-export class FilesMenuItem extends MenuItem {
-  constructor() {
-    super();
-
-    /** @private @type {boolean} */
-    this.animating_ = false;
-
-    /** @private @type {(boolean|undefined)} */
-    this.hidden_ = undefined;
-
-    /** @private @type {?HTMLElement} */
-    this.label_ = null;
-
-    /** @private @type {?HTMLElement} */
-    this.iconStart_ = null;
-
-    /** @private @type {?HTMLElement} */
-    this.iconManaged_ = null;
-
-    /** @private @type {?HTMLElement} */
-    this.iconEnd_ = null;
-
-    /** @private @type {?HTMLElement} */
-    this.ripple_ = null;
-
-    /** @public @type {?chrome.fileManagerPrivate.FileTaskDescriptor} */
-    this.descriptor = null;
-
-    throw new Error('Designed to decorate elements');
-  }
-
-  /**
-   * @override
-   */
-  initialize() {
-    this.animating_ = false;
-
-    // Custom menu item can have sophisticated content (elements).
-    if (!this.children.length) {
-      this.label_ =
-          assertInstanceof(document.createElement('span'), HTMLElement);
-      this.label_.textContent = this.textContent;
-
-      this.iconStart_ =
-          assertInstanceof(document.createElement('div'), HTMLElement);
-      this.iconStart_.classList.add('icon', 'start');
-
-      this.iconManaged_ =
-          assertInstanceof(document.createElement('div'), HTMLElement);
-      this.iconManaged_.classList.add('icon', 'managed');
-
-      this.iconEnd_ =
-          assertInstanceof(document.createElement('div'), HTMLElement);
-      this.iconEnd_.classList.add('icon', 'end');
-      /**
-       * This is hidden by default because most of the menu items require
-       * neither the end icon nor the managed icon, so the component that
-       * plans to use either end icon should explicitly make it visible.
-       */
-      this.setIconEndHidden(true);
-      this.toggleManagedIcon(/*visible=*/ false);
-
-      // Override with standard menu item elements.
-      this.textContent = '';
-      this.appendChild(this.iconStart_);
-      this.appendChild(this.label_);
-      this.appendChild(this.iconManaged_);
-      this.appendChild(this.iconEnd_);
-    }
-
-    this.ripple_ =
-        assertInstanceof(document.createElement('paper-ripple'), HTMLElement);
-    this.appendChild(this.ripple_);
-
-    this.addEventListener('activate', this.onActivated_.bind(this));
-  }
-
-  /**
-   * Handles activate event.
-   * @param {Event} event
-   * @private
-   */
-  onActivated_(event) {
-    // Perform ripple animation if it's activated by keyboard.
-    // @ts-ignore: error TS2339: Property 'originalEvent' does not exist on type
-    // 'Event'.
-    if (event.originalEvent instanceof KeyboardEvent) {
-      // @ts-ignore: error TS2339: Property 'simulatedRipple' does not exist on
-      // type 'HTMLElement'.
-      this.ripple_.simulatedRipple();
-    }
-
-    // Perform fade out animation.
-    // @ts-ignore: error TS2345: Argument of type '(arg0?: Object | undefined)
-    // => Element' is not assignable to parameter of type 'new (...arg1: any[])
-    // => any'.
-    const menu = assertInstanceof(this.parentNode, Menu);
-    // If activation was on a menu-item that hosts a sub-menu, don't animate
-    // @ts-ignore: error TS2339: Property 'getAttribute' does not exist on type
-    // 'EventTarget'.
-    const subMenuId = event.target.getAttribute('sub-menu');
-    if (subMenuId !== null) {
-      if (document.querySelector(subMenuId) !== null) {
-        return;
-      }
-    }
-    this.setMenuAsAnimating_(menu, true /* animating */);
-
-    const player = menu.animate(
-        [
-          {
-            opacity: 1,
-            offset: 0,
-          },
-          {
-            opacity: 0,
-            offset: 1,
-          },
-        ],
-        300);
-
-    player.addEventListener(
-        'finish',
-        this.setMenuAsAnimating_.bind(this, menu, false /* not animating */));
-  }
-
-  /**
-   * Sets menu as animating.
-   * @param {!Menu} menu
-   * @param {boolean} value True to set it as animating.
-   * @private
-   */
-  setMenuAsAnimating_(menu, value) {
-    menu.classList.toggle('animating', value);
-
-    for (let i = 0; i < menu.menuItems.length; i++) {
-      const menuItem = menu.menuItems[i];
-      if (menuItem instanceof FilesMenuItem) {
-        menuItem.setAnimating_(value);
-      }
-    }
-
-    if (!value) {
-      menu.classList.remove('toolbar-menu');
-    }
-  }
-
-  /**
-   * Sets the menu item as animating.
-   * @param {boolean} value True to set this as animating.
-   * @private
-   */
-  setAnimating_(value) {
-    this.animating_ = value;
-
-    if (this.animating_) {
-      return;
-    }
-
-    // Update hidden property if there is a pending change.
-    if (this.hidden_ !== undefined) {
-      this.hidden = this.hidden_;
-      this.hidden_ = undefined;
-    }
-  }
-
-  /**
-   * @return {boolean}
-   * @override
-   */
-  get hidden() {
-    if (this.hidden_ !== undefined) {
-      return this.hidden_;
-    }
-
-
-    return Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'hidden')
-        ?.get?.call(this);
-  }
-
-  /**
-   * Overrides hidden property to block the change of hidden property while
-   * menu is animating.
-   * @param {boolean} value
-   * @override
-   */
-  set hidden(value) {
-    if (this.animating_) {
-      this.hidden_ = value;
-      return;
-    }
-
-    Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'hidden')
-        ?.set?.call(this, value);
-  }
-
-  /**
-   * @return {string}
-   * @override
-   */
-  get label() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    return this.label_.textContent;
-  }
-
-  /**
-   * @param {string} value
-   * @override
-   */
-  set label(value) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.label_.textContent = value;
-  }
-
-  /**
-   * @return {string}
-   */
-  get iconStartImage() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    return this.iconStart_.style.backgroundImage;
-  }
-
-  /**
-   * @param {string} value
-   */
-  set iconStartImage(value) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconStart_.setAttribute('style', 'background-image: ' + value);
-  }
-
-  /**
-   * @return {string}
-   */
-  get iconStartFileType() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    return this.iconStart_.getAttribute('file-type-icon');
-  }
-
-  /**
-   * @param {string} value
-   */
-  set iconStartFileType(value) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconStart_.setAttribute('file-type-icon', value);
-  }
-
-  /**
-   * Sets or removes the `is-managed` attribute.
-   * @param {boolean} isManaged
-   */
-  toggleIsManagedAttribute(isManaged) {
-    this.toggleAttribute('is-managed', isManaged);
-  }
-
-  /**
-   * Sets the `is-default` attribute.
-   */
-  setIsDefaultAttribute() {
-    this.toggleAttribute('is-default', true);
-  }
-
-  /**
-   * Toggles visibility of the `Managed by Policy` icon.
-   * @param {boolean} visible
-   */
-  toggleManagedIcon(visible) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconManaged_.toggleAttribute('hidden', !visible);
-    this.toggleIsManagedAttribute(visible);
-  }
-
-  /**
-   * @return {string}
-   */
-  get iconEndImage() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    return this.iconEnd_.style.backgroundImage;
-  }
-
-  /**
-   * @param {string} value
-   */
-  set iconEndImage(value) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconEnd_.setAttribute('style', 'background-image: ' + value);
-  }
-
-  /**
-   * @return {string}
-   */
-  get iconEndFileType() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    return this.iconEnd_.getAttribute('file-type-icon');
-  }
-
-  /**
-   * @param {string} value
-   */
-  set iconEndFileType(value) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconEnd_.setAttribute('file-type-icon', value);
-  }
-
-  removeIconEndFileType() {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconEnd_.removeAttribute('file-type-icon');
-  }
-
-  /**
-   *
-   * @param {boolean} isHidden
-   */
-  setIconEndHidden(isHidden) {
-    // @ts-ignore: error TS2531: Object is possibly 'null'.
-    this.iconEnd_.toggleAttribute('hidden', isHidden);
-  }
-}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_menu.ts b/ui/file_manager/file_manager/foreground/js/ui/files_menu.ts
new file mode 100644
index 0000000..1371d1c2
--- /dev/null
+++ b/ui/file_manager/file_manager/foreground/js/ui/files_menu.ts
@@ -0,0 +1,237 @@
+// Copyright 2015 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {assertInstanceof} from 'chrome://resources/js/assert.js';
+import type {PaperRippleElement} from 'chrome://resources/polymer/v3_0/paper-ripple/paper-ripple.js';
+
+import {Menu} from './menu.js';
+import {MenuItem, MenuItemActivationEvent} from './menu_item.js';
+
+/**
+ * Menu item with ripple animation.
+ */
+export class FilesMenuItem extends MenuItem {
+  private animating_: boolean = false;
+  private hidden_: boolean|undefined = undefined;
+  private label_: HTMLElement;
+  private iconStart_: HTMLElement;
+  private iconManaged_: HTMLElement;
+  private iconEnd_: HTMLElement;
+  private ripple_: PaperRippleElement;
+
+  descriptor: chrome.fileManagerPrivate.FileTaskDescriptor|null = null;
+
+  constructor() {
+    super();
+    throw new Error('Designed to decorate elements');
+  }
+
+  override initialize() {
+    this.animating_ = false;
+
+    // Custom menu item can have sophisticated content (elements).
+    if (!this.children.length) {
+      this.label_ = document.createElement('span');
+      this.label_.textContent = this.textContent;
+
+      this.iconStart_ = document.createElement('div');
+      this.iconStart_.classList.add('icon', 'start');
+
+      this.iconManaged_ = document.createElement('div');
+      this.iconManaged_.classList.add('icon', 'managed');
+
+      this.iconEnd_ = document.createElement('div');
+      this.iconEnd_.classList.add('icon', 'end');
+      /**
+       * This is hidden by default because most of the menu items require
+       * neither the end icon nor the managed icon, so the component that
+       * plans to use either end icon should explicitly make it visible.
+       */
+      this.setIconEndHidden(true);
+      this.toggleManagedIcon(/*visible=*/ false);
+
+      // Override with standard menu item elements.
+      this.textContent = '';
+      this.appendChild(this.iconStart_);
+      this.appendChild(this.label_);
+      this.appendChild(this.iconManaged_);
+      this.appendChild(this.iconEnd_);
+    }
+
+    this.ripple_ = document.createElement('paper-ripple');
+    this.appendChild(this.ripple_);
+
+    this.addEventListener('activate', this.onActivated_.bind(this));
+  }
+
+  /**
+   * Handles activate event.
+   */
+  private onActivated_(evt: Event) {
+    const event = evt as MenuItemActivationEvent;
+    // Perform ripple animation if it's activated by keyboard.
+    if (event.originalEvent instanceof KeyboardEvent) {
+      this.ripple_.simulatedRipple();
+    }
+
+    // Perform fade out animation.
+    const menu = this.parentNode;
+    assertInstanceof(menu, Menu);
+    // If activation was on a menu-item that hosts a sub-menu, don't animate
+    const subMenuId = (event.target as MenuItem).getAttribute('sub-menu');
+    if (subMenuId) {
+      if (document.querySelector(subMenuId) !== null) {
+        return;
+      }
+    }
+    this.setMenuAsAnimating_(menu, /*animating=*/ true);
+
+    const player = menu.animate(
+        [
+          {
+            opacity: 1,
+            offset: 0,
+          },
+          {
+            opacity: 0,
+            offset: 1,
+          },
+        ],
+        300);
+
+    player.addEventListener(
+        'finish',
+        this.setMenuAsAnimating_.bind(this, menu, /*animating=*/ false));
+  }
+
+  /**
+   * Sets menu as animating. Pass value equal to true to set it as animating.
+   */
+  private setMenuAsAnimating_(menu: Menu, value: boolean) {
+    menu.classList.toggle('animating', value);
+
+    for (let i = 0; i < menu.menuItems.length; i++) {
+      const menuItem = menu.menuItems[i];
+      if (menuItem instanceof FilesMenuItem) {
+        menuItem.setAnimating_(value);
+      }
+    }
+
+    if (!value) {
+      menu.classList.remove('toolbar-menu');
+    }
+  }
+
+  /**
+   * Sets the menu item as animating. Pass value set to true to set this as
+   * animating.
+   */
+  private setAnimating_(value: boolean) {
+    this.animating_ = value;
+
+    if (this.animating_) {
+      return;
+    }
+
+    // Update hidden property if there is a pending change.
+    if (this.hidden_ !== undefined) {
+      this.hidden = this.hidden_;
+      this.hidden_ = undefined;
+    }
+  }
+
+  override get hidden(): boolean {
+    if (this.hidden_ !== undefined) {
+      return this.hidden_;
+    }
+
+    return Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'hidden')
+        ?.get?.call(this);
+  }
+
+  /**
+   * Overrides hidden property to block the change of hidden property while
+   * menu is animating.
+   */
+  override set hidden(value: boolean) {
+    if (this.animating_) {
+      this.hidden_ = value;
+      return;
+    }
+
+    Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'hidden')
+        ?.set?.call(this, value);
+  }
+
+  override get label(): string {
+    return this.label_.textContent || '';
+  }
+
+  override set label(value: string) {
+    this.label_.textContent = value;
+  }
+
+  get iconStartImage(): string {
+    return this.iconStart_.style.backgroundImage;
+  }
+
+  set iconStartImage(value: string) {
+    this.iconStart_.setAttribute('style', 'background-image: ' + value);
+  }
+
+  get iconStartFileType(): string {
+    return this.iconStart_.getAttribute('file-type-icon') || '';
+  }
+
+  set iconStartFileType(value: string) {
+    this.iconStart_.setAttribute('file-type-icon', value);
+  }
+
+  /**
+   * Sets or removes the `is-managed` attribute.
+   */
+  toggleIsManagedAttribute(isManaged: boolean) {
+    this.toggleAttribute('is-managed', isManaged);
+  }
+
+  /**
+   * Sets the `is-default` attribute.
+   */
+  setIsDefaultAttribute() {
+    this.toggleAttribute('is-default', true);
+  }
+
+  /**
+   * Toggles visibility of the `Managed by Policy` icon.
+   */
+  toggleManagedIcon(visible: boolean) {
+    this.iconManaged_.toggleAttribute('hidden', !visible);
+    this.toggleIsManagedAttribute(visible);
+  }
+
+  get iconEndImage(): string {
+    return this.iconEnd_.style.backgroundImage;
+  }
+
+  set iconEndImage(value: string) {
+    this.iconEnd_.setAttribute('style', 'background-image: ' + value);
+  }
+
+  get iconEndFileType(): string {
+    return this.iconEnd_.getAttribute('file-type-icon') || '';
+  }
+
+  set iconEndFileType(value: string) {
+    this.iconEnd_.setAttribute('file-type-icon', value);
+  }
+
+  removeIconEndFileType() {
+    this.iconEnd_.removeAttribute('file-type-icon');
+  }
+
+  setIconEndHidden(isHidden: boolean) {
+    this.iconEnd_.toggleAttribute('hidden', isHidden);
+  }
+}
+
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni
index 1272238a..f941af1 100644
--- a/ui/file_manager/file_names.gni
+++ b/ui/file_manager/file_names.gni
@@ -32,17 +32,14 @@
   "file_manager/foreground/js/app_state_controller.js",
   "file_manager/foreground/js/constants.js",
   "file_manager/foreground/js/crostini_controller.js",
-  "file_manager/foreground/js/directory_tree_naming_controller.js",
   "file_manager/foreground/js/elements_importer.js",
   "file_manager/foreground/js/fake_file_selection_handler.js",
   "file_manager/foreground/js/file_manager.js",
   "file_manager/foreground/js/gear_menu_controller.js",
-  "file_manager/foreground/js/holding_space_util.js",
   "file_manager/foreground/js/metadata_update_controller.js",
   "file_manager/foreground/js/metrics_start.js",
   "file_manager/foreground/js/mock_navigation_list_model.js",
   "file_manager/foreground/js/navigation_list_model.js",
-  "file_manager/foreground/js/selection_menu_controller.js",
 
   # Metadata:
   "file_manager/foreground/js/metadata/metadata_dispatcher.js",
@@ -51,7 +48,6 @@
   "file_manager/foreground/js/ui/a11y_announce.js",
   "file_manager/foreground/js/ui/action_model_ui.js",
   "file_manager/foreground/js/ui/actions_submenu.js",
-  "file_manager/foreground/js/ui/commandbutton.js",
   "file_manager/foreground/js/ui/context_menu_handler.js",
   "file_manager/foreground/js/ui/default_task_dialog.js",
   "file_manager/foreground/js/ui/dialog_footer.js",
@@ -62,7 +58,6 @@
   "file_manager/foreground/js/ui/file_tap_handler.js",
   "file_manager/foreground/js/ui/files_alert_dialog.js",
   "file_manager/foreground/js/ui/files_confirm_dialog.js",
-  "file_manager/foreground/js/ui/files_menu.js",
   "file_manager/foreground/js/ui/gear_menu.js",
   "file_manager/foreground/js/ui/install_linux_package_dialog.js",
   "file_manager/foreground/js/ui/providers_menu.js",
@@ -187,9 +182,8 @@
   # Don't remove this.
   # These lines will be removed at the end of the TS migration.
   # Foreground UI.
-  "file_manager/foreground/js/deferred_elements.ts",
-  "file_manager/foreground/js/task_history.ts",
   "file_manager/foreground/js/ui/dialogs.ts",
+  "file_manager/foreground/js/ui/commandbutton.ts",
   "file_manager/foreground/js/ui/combobutton.ts",
   "file_manager/foreground/js/ui/command.ts",
   "file_manager/foreground/js/ui/file_grid.ts",
@@ -197,6 +191,7 @@
   "file_manager/foreground/js/ui/file_manager_ui.ts",
   "file_manager/foreground/js/ui/file_table.ts",
   "file_manager/foreground/js/ui/file_table_list.ts",
+  "file_manager/foreground/js/ui/files_menu.ts",
   "file_manager/foreground/js/ui/grid.ts",
   "file_manager/foreground/js/ui/import_crostini_image_dialog.ts",
   "file_manager/foreground/js/ui/list.ts",
@@ -253,19 +248,15 @@
   "file_manager/background/js/drive_sync_handler.ts",
   "file_manager/background/js/entry_location_impl.ts",
   "file_manager/background/js/file_manager_base.ts",
-  "file_manager/foreground/js/file_manager_commands.ts",
   "file_manager/background/js/file_operation_handler.ts",
-  "file_manager/foreground/js/folder_shortcuts_data_model.ts",
   "file_manager/background/js/metrics_start.ts",
   "file_manager/background/js/mock_crostini.ts",
   "file_manager/background/js/mock_progress_center.ts",
   "file_manager/background/js/mock_volume_manager.ts",
-  "file_manager/foreground/js/naming_controller.ts",
-  "file_manager/foreground/js/path_component.ts",
   "file_manager/background/js/progress_center.ts",
   "file_manager/background/js/runtime_loaded_test_util.ts",
-  "file_manager/background/js/test_util.ts",
   "file_manager/background/js/test_util_base.ts",
+  "file_manager/background/js/test_util.ts",
   "file_manager/background/js/volume_info_impl.ts",
   "file_manager/background/js/volume_info_list_impl.ts",
   "file_manager/background/js/volume_manager_factory.ts",
@@ -275,18 +266,21 @@
   # Don't remove this.
   # These lines will be removed at the end of the TS migration.
   # Foreground.
-  "file_manager/foreground/js/actions_model.ts",
   "file_manager/foreground/js/actions_controller.ts",
+  "file_manager/foreground/js/actions_model.ts",
   "file_manager/foreground/js/android_app_list_model.ts",
   "file_manager/foreground/js/banner_controller.ts",
   "file_manager/foreground/js/command_handler.ts",
   "file_manager/foreground/js/crossover_search_utils.ts",
+  "file_manager/foreground/js/deferred_elements.ts",
   "file_manager/foreground/js/dialog_action_controller.ts",
   "file_manager/foreground/js/directory_contents.ts",
   "file_manager/foreground/js/directory_model.ts",
+  "file_manager/foreground/js/directory_tree_naming_controller.ts",
   "file_manager/foreground/js/empty_folder_controller.ts",
   "file_manager/foreground/js/fake_android_app_list_model.ts",
   "file_manager/foreground/js/file_list_model.ts",
+  "file_manager/foreground/js/file_manager_commands.ts",
   "file_manager/foreground/js/file_manager_commands_util.ts",
   "file_manager/foreground/js/file_rename.ts",
   "file_manager/foreground/js/file_selection.ts",
@@ -294,10 +288,12 @@
   "file_manager/foreground/js/file_transfer_controller.ts",
   "file_manager/foreground/js/file_type_filters_controller.ts",
   "file_manager/foreground/js/file_watcher.ts",
+  "file_manager/foreground/js/folder_shortcuts_data_model.ts",
   "file_manager/foreground/js/guest_os_controller.ts",
+  "file_manager/foreground/js/holding_space_util.ts",
   "file_manager/foreground/js/last_modified_controller.ts",
-  "file_manager/foreground/js/list_thumbnail_loader.ts",
   "file_manager/foreground/js/launch_param.ts",
+  "file_manager/foreground/js/list_thumbnail_loader.ts",
   "file_manager/foreground/js/main.ts",
   "file_manager/foreground/js/main_window_component.ts",
   "file_manager/foreground/js/metadata_box_controller.ts",
@@ -305,15 +301,19 @@
   "file_manager/foreground/js/mock_directory_model.ts",
   "file_manager/foreground/js/mock_folder_shortcut_data_model.ts",
   "file_manager/foreground/js/mock_thumbnail_loader.ts",
+  "file_manager/foreground/js/naming_controller.ts",
   "file_manager/foreground/js/navigation_uma.ts",
+  "file_manager/foreground/js/path_component.ts",
   "file_manager/foreground/js/providers_model.ts",
   "file_manager/foreground/js/quick_view_controller.ts",
   "file_manager/foreground/js/quick_view_model.ts",
   "file_manager/foreground/js/quick_view_uma.ts",
   "file_manager/foreground/js/scan_controller.ts",
+  "file_manager/foreground/js/selection_menu_controller.ts",
   "file_manager/foreground/js/sort_menu_controller.ts",
   "file_manager/foreground/js/spinner_controller.ts",
   "file_manager/foreground/js/task_controller.ts",
+  "file_manager/foreground/js/task_history.ts",
   "file_manager/foreground/js/thumbnail_loader.ts",
   "file_manager/foreground/js/toolbar_controller.ts",
   "file_manager/foreground/js/uma_enums.gen.ts",
diff --git a/ui/webui/resources/cr_components/most_visited/most_visited.ts b/ui/webui/resources/cr_components/most_visited/most_visited.ts
index 966ea3e..33b8c62 100644
--- a/ui/webui/resources/cr_components/most_visited/most_visited.ts
+++ b/ui/webui/resources/cr_components/most_visited/most_visited.ts
@@ -330,6 +330,11 @@
     return skColor ? skColorToRgba(skColor) : 'inherit';
   }
 
+  // Adds "force-hover" class to the tile element positioned at `index`.
+  private enableForceHover_(index: number) {
+    this.tileElements_[index].classList.add('force-hover');
+  }
+
   private clearForceHover_() {
     const forceHover = this.shadowRoot!.querySelector('.force-hover');
     if (forceHover) {
@@ -425,7 +430,7 @@
    * is done in the DOM and the by the |reorderMostVisitedTile()| call. This is
    * done to prevent flicking between the time when the tiles are moved back to
    * their original positions (by removing position absolute) and when the
-   * tiles are updated via a |setMostVisitedTiles()| call.
+   * tiles are updated via the |setMostVisitedInfo| handler.
    *
    * |reordering_| is not set to false when the tiles are reordered. The callers
    * will need to set it to false. This is necessary to handle a mouse drag
@@ -436,19 +441,24 @@
       this.reordering_ = false;
       return;
     }
+
     this.dragOffset_ = null;
+
     const dragElement =
         this.shadowRoot!.querySelector<HTMLElement>('.tile.dragging');
     if (!dragElement) {
       this.reordering_ = false;
       return;
     }
+
+    dragElement.classList.remove('dragging');
+
+    this.tileElements_.forEach(el => resetTilePosition(el));
+    resetTilePosition(this.$.addShortcut);
+
     const dragIndex = (this.$.tiles.modelForElement(dragElement) as unknown as {
                         index: number,
                       }).index;
-    dragElement.classList.remove('dragging');
-    this.tileElements_.forEach(el => resetTilePosition(el));
-    resetTilePosition(this.$.addShortcut);
     const dropIndex = getHitIndex(this.tileRects_, x, y);
     if (dragIndex !== dropIndex && dropIndex > -1) {
       const [draggingTile] = this.tiles_.splice(dragIndex, 1);
@@ -675,7 +685,7 @@
       this.dragEnd_(e.x, e.y);
       const dropIndex = getHitIndex(this.tileRects_, e.x, e.y);
       if (dropIndex !== -1) {
-        this.tileElements_[dropIndex].classList.add('force-hover');
+        this.enableForceHover_(dropIndex);
       }
       this.addEventListener('pointermove', () => {
         this.clearForceHover_();
diff --git a/url/BUILD.gn b/url/BUILD.gn
index c525c16..0ac17c0 100644
--- a/url/BUILD.gn
+++ b/url/BUILD.gn
@@ -41,6 +41,7 @@
     "url_canon_ip.cc",
     "url_canon_ip.h",
     "url_canon_mailtourl.cc",
+    "url_canon_non_special_url.cc",
     "url_canon_path.cc",
     "url_canon_pathurl.cc",
     "url_canon_query.cc",
diff --git a/url/url_canon.h b/url/url_canon.h
index 913b368..71343b8 100644
--- a/url/url_canon.h
+++ b/url/url_canon.h
@@ -714,6 +714,22 @@
                              CanonOutput* output,
                              Parsed* new_parsed);
 
+// Use for non-special URLs.
+COMPONENT_EXPORT(URL)
+bool CanonicalizeNonSpecialURL(const char* spec,
+                               int spec_len,
+                               const Parsed& parsed,
+                               CharsetConverter* query_converter,
+                               CanonOutput& output,
+                               Parsed& new_parsed);
+COMPONENT_EXPORT(URL)
+bool CanonicalizeNonSpecialURL(const char16_t* spec,
+                               int spec_len,
+                               const Parsed& parsed,
+                               CharsetConverter* query_converter,
+                               CanonOutput& output,
+                               Parsed& new_parsed);
+
 // Use for file URLs.
 COMPONENT_EXPORT(URL)
 bool CanonicalizeFileURL(const char* spec,
diff --git a/url/url_canon_fileurl.cc b/url/url_canon_fileurl.cc
index 802fe42..a5af5fbe 100644
--- a/url/url_canon_fileurl.cc
+++ b/url/url_canon_fileurl.cc
@@ -127,6 +127,8 @@
                            CharsetConverter* query_converter,
                            CanonOutput* output,
                            Parsed* new_parsed) {
+  DCHECK(!parsed.has_opaque_path);
+
   // Things we don't set in file: URLs.
   new_parsed->username = Component();
   new_parsed->password = Component();
diff --git a/url/url_canon_non_special_url.cc b/url/url_canon_non_special_url.cc
new file mode 100644
index 0000000..eb567b4
--- /dev/null
+++ b/url/url_canon_non_special_url.cc
@@ -0,0 +1,132 @@
+// 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.
+
+// Functions to canonicalize non-special URLs.
+
+#include "url/url_canon.h"
+#include "url/url_canon_internal.h"
+
+namespace url {
+
+namespace {
+
+template <typename CHAR>
+bool DoCanonicalizeNonSpecialURL(const URLComponentSource<CHAR>& source,
+                                 const Parsed& parsed,
+                                 CharsetConverter* query_converter,
+                                 CanonOutput& output,
+                                 Parsed& new_parsed) {
+  // The implementation is similar to `DoCanonicalizeStandardURL()`, but there
+  // are many subtle differences. So we have a different function for
+  // canonicalizing non-special URLs.
+
+  DCHECK(!parsed.has_opaque_path);
+
+  // Scheme: this will append the colon.
+  bool success = CanonicalizeScheme(source.scheme, parsed.scheme, &output,
+                                    &new_parsed.scheme);
+  bool have_authority =
+      (parsed.username.is_valid() || parsed.password.is_valid() ||
+       parsed.host.is_valid() || parsed.port.is_valid());
+
+  // Non-special URL examples which should be carefully handled:
+  //
+  // | URL      | parsed.user   | parsed.host   | have_authority | Valid URL? |
+  // |----------+---------------+---------------+----------------+------------|
+  // | git:/a   | invalid       | invalid       | false          | valid      |
+  // | git://@/ | valid (empty) | invalid       | true           | invalid    |
+  // | git:///  | invalid       | valid (empty) | true           | valid      |
+
+  if (have_authority) {
+    // Only write the authority separators when we have a scheme.
+    if (parsed.scheme.is_valid()) {
+      output.push_back('/');
+      output.push_back('/');
+    }
+
+    // User info: the canonicalizer will handle the : and @.
+    success &= CanonicalizeUserInfo(source.username, parsed.username,
+                                    source.password, parsed.password, &output,
+                                    &new_parsed.username, &new_parsed.password);
+
+    // Host
+    if (parsed.host.is_valid()) {
+      success &= CanonicalizeNonSpecialHost(source.host, parsed.host, output,
+                                            new_parsed.host);
+    } else {
+      // URL is invalid if `have_authority` is true, but `parsed.host` is
+      // invalid. Example: "git://@/".
+      success = false;
+    }
+
+    // Port
+    success &= CanonicalizePort(source.port, parsed.port, PORT_UNSPECIFIED,
+                                &output, &new_parsed.port);
+  } else {
+    // No authority, clear the components.
+    new_parsed.host.reset();
+    new_parsed.username.reset();
+    new_parsed.password.reset();
+    new_parsed.port.reset();
+  }
+
+  // Path
+  if (parsed.path.is_valid()) {
+    success &=
+        CanonicalizePath(source.path, parsed.path, CanonMode::kNonSpecialURL,
+                         &output, &new_parsed.path);
+  } else {
+    new_parsed.path.reset();
+  }
+
+  // Query
+  CanonicalizeQuery(source.query, parsed.query, query_converter, &output,
+                    &new_parsed.query);
+
+  // Ref: ignore failure for this, since the page can probably still be loaded.
+  CanonicalizeRef(source.ref, parsed.ref, &output, &new_parsed.ref);
+
+  // Carry over the flag for potentially dangling markup:
+  if (parsed.potentially_dangling_markup) {
+    new_parsed.potentially_dangling_markup = true;
+  }
+
+  return success;
+}
+
+}  // namespace
+
+bool CanonicalizeNonSpecialURL(const char* spec,
+                               int spec_len,
+                               const Parsed& parsed,
+                               CharsetConverter* query_converter,
+                               CanonOutput& output,
+                               Parsed& new_parsed) {
+  // Carry over the flag.
+  new_parsed.has_opaque_path = parsed.has_opaque_path;
+
+  if (parsed.has_opaque_path) {
+    return CanonicalizePathURL(spec, spec_len, parsed, &output, &new_parsed);
+  }
+  return DoCanonicalizeNonSpecialURL(URLComponentSource(spec), parsed,
+                                     query_converter, output, new_parsed);
+}
+
+bool CanonicalizeNonSpecialURL(const char16_t* spec,
+                               int spec_len,
+                               const Parsed& parsed,
+                               CharsetConverter* query_converter,
+                               CanonOutput& output,
+                               Parsed& new_parsed) {
+  // Carry over the flag.
+  new_parsed.has_opaque_path = parsed.has_opaque_path;
+
+  if (parsed.has_opaque_path) {
+    return CanonicalizePathURL(spec, spec_len, parsed, &output, &new_parsed);
+  }
+  return DoCanonicalizeNonSpecialURL(URLComponentSource(spec), parsed,
+                                     query_converter, output, new_parsed);
+}
+
+}  // namespace url
diff --git a/url/url_canon_stdurl.cc b/url/url_canon_stdurl.cc
index 8096b56..304ca4c0 100644
--- a/url/url_canon_stdurl.cc
+++ b/url/url_canon_stdurl.cc
@@ -20,6 +20,8 @@
                                CharsetConverter* query_converter,
                                CanonOutput* output,
                                Parsed* new_parsed) {
+  DCHECK(!parsed.has_opaque_path);
+
   // Scheme: this will append the colon.
   bool success = CanonicalizeScheme(source.scheme, parsed.scheme,
                                     output, &new_parsed->scheme);
diff --git a/url/url_canon_unittest.cc b/url/url_canon_unittest.cc
index dff0f71..fd10ca0 100644
--- a/url/url_canon_unittest.cc
+++ b/url/url_canon_unittest.cc
@@ -1778,6 +1778,103 @@
   }
 }
 
+TEST(URLCanonTest, CanonicalizeNonSpecialURL) {
+  // The individual component canonicalize tests should have caught the cases
+  // for each of those components. Here, we just need to test that the various
+  // parts are included or excluded properly, and have the correct separators.
+  struct URLCase {
+    const std::string_view input;
+    const std::string_view expected;
+    bool expected_success;
+  } cases[] = {
+      // Basic cases.
+      {"git://host:80/path?a=b#ref", "git://host:80/path?a=b#ref", true},
+      {"git://host", "git://host", true},
+      {"git://host/", "git://host/", true},
+      {"git://HosT/", "git://HosT/", true},
+      {"git://..", "git://..", true},
+      {"git://../", "git://../", true},
+      {"git://../..", "git://../", true},
+
+      // Empty hosts.
+      {"git://", "git://", true},
+      {"git:///", "git:///", true},
+      {"git:////", "git:////", true},
+      {"git:///a", "git:///a", true},
+      {"git:///a/../b", "git:///b", true},
+      {"git:///..", "git:///", true},
+
+      // No hosts.
+      {"git:/", "git:/", true},
+      {"git:/a", "git:/a", true},
+      {"git:/a/../b", "git:/b", true},
+      {"git:/..", "git:/", true},
+      {"git:/../", "git:/", true},
+      {"git:/../..", "git:/", true},
+
+      // Users.
+      {"git://@host", "git://host", true},
+      {"git:// @host", "git://%20@host", true},
+      {"git://\\@host", "git://%5C@host", true},
+
+      // Paths.
+      {"git://host/path", "git://host/path", true},
+      {"git://host/p ath", "git://host/p%20ath", true},
+      {"git://host/a/../b", "git://host/b", true},
+      {"git://host/..", "git://host/", true},
+      {"git://host/../", "git://host/", true},
+      {"git://host/../..", "git://host/", true},
+      {"git://host/.", "git://host/", true},
+      {"git://host/./", "git://host/", true},
+      {"git://host/./.", "git://host/", true},
+      // Backslashes.
+      {"git://host/a\\..\\b", "git://host/a\\..\\b", true},
+
+      // IPv6.
+      {"git://[1:2:0:0:5:0:0:0]", "git://[1:2:0:0:5::]", true},
+      {"git://[1:2:0:0:5:0:0:0]/", "git://[1:2:0:0:5::]/", true},
+      {"git://[1:2:0:0:5:0:0:0]/path", "git://[1:2:0:0:5::]/path", true},
+
+      // IPv4 is unsupported.
+      {"git://127.00.0.1", "git://127.00.0.1", true},
+      {"git://127.1000.0.1", "git://127.1000.0.1", true},
+
+      // Invalid URLs.
+      {"git://@", "git://", false},
+      // Forbidden host code points.
+      {"git://<", "git://", false},
+      {"git:// /", "git:///", false},
+      // Backslashes cannot be used as host terminators.
+      {"git://host\\a/../b", "git://host/b", false},
+
+      // Opaque paths.
+      {"git:", "git:", true},
+      {"git:opaque", "git:opaque", true},
+      {"git:o p a q u e", "git:o p a q u e", true},
+      {"git: <", "git: <", true},
+      {"git:opaque/a/../b", "git:opaque/a/../b", true},
+      {"git:opaque\\a\\..\\b", "git:opaque\\a\\..\\b", true},
+      {"git:\\a", "git:\\a", true},
+      // Like URNs.
+      {"git:a:b:c:123", "git:a:b:c:123", true},
+  };
+
+  for (const auto& i : cases) {
+    SCOPED_TRACE(i.input);
+    Parsed parsed;
+    ParseNonSpecialURL(i.input.data(), i.input.size(), &parsed);
+    Parsed out_parsed;
+    std::string out_str;
+    StdStringCanonOutput output(&out_str);
+    bool success = CanonicalizeNonSpecialURL(
+        i.input.data(), i.input.size(), parsed,
+        /*query_converter=*/nullptr, output, out_parsed);
+    output.Complete();
+    EXPECT_EQ(success, i.expected_success);
+    EXPECT_EQ(out_str, i.expected);
+  }
+}
+
 // The codepath here is the same as for regular canonicalization, so we just
 // need to test that things are replaced or not correctly.
 TEST(URLCanonTest, ReplaceStandardURL) {
diff --git a/url/url_parse_unittest.cc b/url/url_parse_unittest.cc
index 6edc3f9..8d8c83e3 100644
--- a/url/url_parse_unittest.cc
+++ b/url/url_parse_unittest.cc
@@ -686,6 +686,10 @@
      nullptr},
     {"git://ho\\st/", "git", nullptr, nullptr, "ho\\st", -1, "/", nullptr,
      nullptr},
+    // Empty users
+    {"git://@host", "git", "", nullptr, "host", -1, nullptr, nullptr, nullptr},
+    // Empty user and invalid host. "git://@" is an invalid URL.
+    {"git://@", "git", "", nullptr, nullptr, -1, nullptr, nullptr, nullptr},
     // Empty host cases
     {"git://", "git", nullptr, nullptr, "", -1, nullptr, nullptr, nullptr},
     {"git:///", "git", nullptr, nullptr, "", -1, "/", nullptr, nullptr},