diff --git a/DEPS b/DEPS
index 46fa2bc..9f0aa209 100644
--- a/DEPS
+++ b/DEPS
@@ -300,15 +300,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'ee9a494402f066aee426dc42b75d30cc71a18dbd',
+  'src_internal_revision': 'c71002ce45140bc59f297a629123968b7368dc5b',
   # 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': '8a0152a423497497cc7425541947cf1bb3745a2e',
+  'skia_revision': 'e8a71c76b88f44e08806d7661d474718a81ad5dd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '6013b6402ff6de4db468323ac6b7c69de83218f2',
+  'v8_revision': '14e2ac94ae54e70c139e4f67f4bd53c67d47a6e9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -355,7 +355,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': '80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef',
+  'freetype_revision': '4e1c0e8fba456b2050c672c9c2c876987f03a3e6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -431,7 +431,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '9c2369afe0b478746070fb835cc7bd50467257e0',
+  'dawn_revision': '77ef59593a387a00da6deee0c86fe2a6b269f4e1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -812,12 +812,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '5e52430df499dcba461383ad4f7f5e0ef58b092e',
+    '442bc6eb90382a1125b1c3d518a479981edf92a5',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '109669807a0bdcd2c7cd195966b41aa181939cb1',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'eb898c4426b9ceb03d49cec72c238333dfc56687',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1242,13 +1242,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '37d1312fcd44558402bebc2966b6b98babca5d7b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0d627781be1c6318b9e593545a88c2f6455515e1',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '7b9cd2ab1fce7837fc5a40b816f3cd4f5ba5235a',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '3c9d29555824bae688ff1b5ccb819a4de211a1a5',
     'condition': 'checkout_src_internal',
   },
 
@@ -1256,7 +1256,7 @@
     Var('chromium_git') + '/chromium/dom-distiller/dist.git' + '@' + '199de96b345ada7c6e7e6ba3d2fa7a6911b8767d',
 
   'src/third_party/eigen3/src':
-    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + 'c18f94e3b017104284cd541e553472e62e85e526',
+    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '316eab8deb574d150f9cfc7f8b170156dc0cdd9f',
 
   'src/third_party/emoji-metadata/src': {
     'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '045f146fca682a836e01cd265171312bfb300e06',
@@ -1284,7 +1284,7 @@
     Var('chromium_git') + '/chromium/deps/flac.git' + '@' + '689da3a7ed50af7448c3f1961d1791c7c1d9c85c',
 
   'src/third_party/flatbuffers/src':
-    Var('chromium_git') + '/external/github.com/google/flatbuffers.git' + '@' + 'a56f9ec50e908362e20254fcef28e62a2f148d91',
+    Var('chromium_git') + '/external/github.com/google/flatbuffers.git' + '@' + '13fc75cb6b7b44793f3f5b4ba025ff403d012c9f',
 
   # Used for embedded builds. CrOS & Linux use the system version.
   'src/third_party/fontconfig/src': {
@@ -1408,7 +1408,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '41cdffd71c9948f63c7ad36e1fb0ff519aa7a37e',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'a2961dc659b4ae847a9c6120718cc2517ee57d9e',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '17665248776256842466ab820f5f1ae9637dbfd8',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1793,7 +1793,7 @@
   },
 
   'src/third_party/ruy/src':
-    Var('chromium_git') + '/external/github.com/google/ruy.git' + '@' + '72d107f88082aeca462ffbe91b3a3b0e02686a73',
+    Var('chromium_git') + '/external/github.com/google/ruy.git' + '@' + 'caa244343de289f913c505100e6a463d46c174de',
 
   'src/third_party/skia':
     Var('skia_git') + '/skia.git' + '@' +  Var('skia_revision'),
@@ -1847,7 +1847,7 @@
     Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c036420683f672d685e27415de0a5f5e85bdc23f',
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'be1a33f1566b40bd43ecfc53dbd8c3dfbc7f2aa1',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '5abeda2494b0eb551b5c4689e47008fe374684dc',
 
   'src/third_party/turbine': {
       'packages': [
@@ -1947,7 +1947,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/linux-amd64',
-          'version': 'UMqOqE8nlXwo9xj56g4W9ySvvqo2Tsw3B6tWZLo19L8C',
+          'version': '7Xu0Ohf2xgwAgLoFgq7LiZmi_Uk3x5gIX5Dta-tZg40C',
         },
       ],
       'dep_type': 'cipd',
@@ -1968,7 +1968,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-amd64',
-          'version': 'vjY1ew6KN-bIFZEk7IbwPCAb6xZxib9uqLXMwbi8JLQC',
+          'version': 'KYHtCI8pS_1-ZVhMhj0d7h-3H8ZdNtaGLYsYnDqRbnMC',
         },
       ],
       'dep_type': 'cipd',
@@ -1979,7 +1979,7 @@
       'packages': [
         {
           'package': 'skia/tools/goldctl/mac-arm64',
-          'version': 'twOZsJTmRSqlOwstPpWMhT_fXhiOaijMu24-UJLGK20C',
+          'version': 'h-5CLVE7g4EEE3-uXZ610OaDlIMoYEx3RLgGWHXRA9AC',
         },
       ],
       'dep_type': 'cipd',
@@ -4001,7 +4001,7 @@
 
   'src/chrome/app/theme/default_200_percent/google_chrome': {
       'url': Var('chrome_git') + '/chrome/theme/default_200_percent/google_chrome.git' + '@' +
-        '1d0c7e07b914acb41d288af6af24e4393dad4977',
+        '85212eec597265149121b67e1fc0b80e7a0cf696',
       'condition': 'checkout_src_internal',
   },
 
@@ -4194,7 +4194,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '2a32883844796c25615972296a26d0b1be2269f6',
+        'c0b142181922bd9f626aeec47b854af0ec97ce5f',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 9de745e6..2a7587b 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -657,6 +657,7 @@
     "//mojo/public/java:system_java",
     "//mojo/public/java/system:system_impl_java",
     "//net/android:net_java",
+    "//services/data_decoder/public/cpp/android:safe_json_java",
     "//services/network/public/mojom:mojom_java",
     "//services/network/public/mojom:url_loader_base_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 1214e35..dd0c598 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -235,6 +235,8 @@
                     "Enables the use of sec-ch-viewport-height client hint."),
             Flag.baseFeature(GpuFeatures.CANVAS_CONTEXT_LOST_IN_BACKGROUND,
                     "Free Canvas2D resources when the webview is in the background."),
+            Flag.baseFeature(GpuFeatures.USE_CLIENT_GMB_INTERFACE,
+                    "Uses the ClientGmbInetrface to create GpuMemoryBuffers for Renderers."),
             Flag.baseFeature(GpuFeatures.USE_GPU_SCHEDULER_DFS,
                     "Uses the new SchedulerDFS GPU job scheduler."),
             Flag.baseFeature(BlinkFeatures.AUTOFILL_SHADOW_DOM,
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index ce632f74..7780ea4 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -1549,6 +1549,10 @@
 const char kAshLoginSessionStartedIsFirstSession[] =
     "ash.Login.SessionStarted.IsFirstSession";
 
+// A boolean pref that controls whether input force respect ui gains is enabled.
+const char kInputForceRespectUiGainsEnabled[] =
+    "ash.input_force_respect_ui_gains_enabled";
+
 // NOTE: New prefs should start with the "ash." prefix. Existing prefs moved
 // into this file should not be renamed, since they may be synced.
 
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 997cdea..f33e856 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -68,6 +68,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kMultitaskMenuNudgeTabletLastShown[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kInputForceRespectUiGainsEnabled[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSamlPasswordModifiedTime[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSamlPasswordExpirationTime[];
diff --git a/ash/system/time/calendar_view.cc b/ash/system/time/calendar_view.cc
index c233be0..d1d7fe78 100644
--- a/ash/system/time/calendar_view.cc
+++ b/ash/system/time/calendar_view.cc
@@ -72,13 +72,15 @@
 constexpr int kMonthVerticalPadding = 10;
 constexpr int kLabelVerticalPadding = 10;
 constexpr int kLabelTextInBetweenPadding = 10;
-const int kWeekRowHorizontalPadding =
+constexpr int kLabelTextInBetweenPaddingJelly = 4;
+constexpr int kWeekRowHorizontalPadding =
     kContentHorizontalPadding - calendar_utils::kDateHorizontalPadding;
 const int kWeekRowHorizontalPaddingJelly =
     kContentHorizontalPadding - calendar_utils::kDateHorizontalPaddingJelly;
 constexpr int kExpandedCalendarPadding = 11;
 constexpr int kExpandedCalendarPaddingJelly = 10;
 constexpr int kChevronPadding = calendar_utils::kColumnSetPadding - 1;
+constexpr int kChevronPaddingJelly = 16;
 constexpr int kMonthHeaderLabelTopPadding = 14;
 constexpr int kMonthHeaderLabelBottomPadding = 2;
 constexpr int kEventListViewHorizontalOffset = 1;
@@ -185,7 +187,7 @@
     "Ash.CalendarView.SmoothScrollToTodaysDateCell.LabelView."
     "AnimationSmoothness";
 
-std::unique_ptr<views::Label> HeaderView(const std::u16string& month) {
+std::unique_ptr<views::Label> CreateHeaderView(const std::u16string& month) {
   return views::Builder<views::Label>(
              bubble_utils::CreateLabel(TypographyToken::kCrosDisplay7, month,
                                        cros_tokens::kCrosSysOnSurface))
@@ -195,6 +197,21 @@
       .Build();
 }
 
+std::unique_ptr<views::Label> CreateHeaderYearView(const std::u16string& year) {
+  const int label_padding = features::IsCalendarJellyEnabled()
+                                ? kLabelTextInBetweenPaddingJelly
+                                : kLabelTextInBetweenPadding;
+
+  return views::Builder<views::Label>(
+             bubble_utils::CreateLabel(TypographyToken::kCrosDisplay7, year,
+                                       cros_tokens::kCrosSysOnSurface))
+      .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_TO_HEAD)
+      .SetTextContext(CONTEXT_CALENDAR_LABEL)
+      .SetAutoColorReadabilityEnabled(false)
+      .SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(0, label_padding)))
+      .Build();
+}
+
 int GetExpandedCalendarPadding() {
   return features::IsCalendarJellyEnabled() ? kExpandedCalendarPaddingJelly
                                             : kExpandedCalendarPadding;
@@ -268,7 +285,7 @@
  public:
   MonthHeaderLabelView(LabelType type,
                        CalendarViewController* calendar_view_controller)
-      : month_label_(AddChildView(HeaderView(std::u16string()))) {
+      : month_label_(AddChildView(CreateHeaderView(std::u16string()))) {
     // The layer is required in animation.
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
@@ -381,21 +398,8 @@
 
 CalendarHeaderView::CalendarHeaderView(const std::u16string& month,
                                        const std::u16string& year)
-    : header_(AddChildView(HeaderView(month))),
-      header_year_(AddChildView(
-          views::Builder<views::Label>(
-              bubble_utils::CreateLabel(TypographyToken::kCrosDisplay7,
-                                        year,
-                                        cros_tokens::kCrosSysOnSurface))
-              .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_TO_HEAD)
-              .SetTextContext(CONTEXT_CALENDAR_LABEL)
-              .SetAutoColorReadabilityEnabled(false)
-              .SetBorder(views::CreateEmptyBorder(
-                  gfx::Insets::TLBR(0,
-                                    kLabelTextInBetweenPadding,
-                                    0,
-                                    kLabelTextInBetweenPadding)))
-              .Build())) {
+    : header_(AddChildView(CreateHeaderView(month))),
+      header_year_(AddChildView(CreateHeaderYearView(year))) {
   // The layer is required in animation.
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
@@ -507,8 +511,10 @@
   button_container_layout->set_main_axis_alignment(
       views::BoxLayout::MainAxisAlignment::kEnd);
   // Aligns button with the calendar dates in the `TableLayout`.
-  button_container_layout->set_between_child_spacing(horizontal_padding +
-                                                     kChevronPadding);
+  button_container_layout->set_between_child_spacing(
+      features::IsCalendarJellyEnabled()
+          ? kChevronPaddingJelly
+          : horizontal_padding + kChevronPadding);
 
   up_button_ = button_container->AddChildView(std::make_unique<IconButton>(
       base::BindRepeating(&CalendarView::OnMonthArrowButtonActivated,
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts
index 2352cc40..777f63e 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_selected_element.ts
@@ -272,41 +272,39 @@
         image?.descriptionTitle;
   }
 
-  private isCollectionPath_(path: string): boolean {
-    return path === Paths.COLLECTION_IMAGES ||
-        path === Paths.GOOGLE_PHOTOS_COLLECTION;
-  }
-
   private computeShowDailyRefreshButton_(
       path: string, collectionId: string, googlePhotosAlbumId: string|undefined,
       photosByAlbumId: Record<string, GooglePhotosPhoto[]|null|undefined>) {
-    if (!this.isCollectionPath_(path)) {
-      return false;
-    }
     // Special collection where daily refresh is disabled.
     if (collectionId ===
         loadTimeData.getString('timeOfDayWallpaperCollectionId')) {
       return false;
     }
-    const isNonEmptyGooglePhotosAlbum = !!googlePhotosAlbumId &&
-        !!photosByAlbumId &&
-        isNonEmptyArray(photosByAlbumId[googlePhotosAlbumId]);
-    return path === Paths.COLLECTION_IMAGES ||
-        (path === Paths.GOOGLE_PHOTOS_COLLECTION &&
-         isNonEmptyGooglePhotosAlbum);
+    switch (path) {
+      case Paths.COLLECTION_IMAGES:
+        return true;
+      case Paths.GOOGLE_PHOTOS_COLLECTION:
+        return !!googlePhotosAlbumId && !!photosByAlbumId &&
+            isNonEmptyArray(photosByAlbumId[googlePhotosAlbumId]);
+      default:
+        return false;
+    }
   }
 
   private computeShowRefreshButton_(
       path: string, collectionId: string|undefined,
       googlePhotosAlbumId: string|undefined,
       dailyRefreshState: DailyRefreshState|null) {
-    if (!this.isCollectionPath_(path)) {
-      return false;
+    switch (path) {
+      case Paths.COLLECTION_IMAGES:
+        return !!collectionId &&
+            this.isDailyRefreshId_(collectionId, dailyRefreshState);
+      case Paths.GOOGLE_PHOTOS_COLLECTION:
+        return !!googlePhotosAlbumId &&
+            this.isDailyRefreshId_(googlePhotosAlbumId, dailyRefreshState);
+      default:
+        return false;
     }
-    return (!collectionId && !googlePhotosAlbumId) ?
-        false :
-        this.isDailyRefreshId_(
-            collectionId! || googlePhotosAlbumId!, dailyRefreshState);
   }
 
   private getWallpaperSrc_(image: CurrentWallpaper|null): string|null {
diff --git a/ash/webui/print_management/resources/icons.html b/ash/webui/print_management/resources/icons.html
index 8ed9f91..f45aa69 100644
--- a/ash/webui/print_management/resources/icons.html
+++ b/ash/webui/print_management/resources/icons.html
@@ -45,4 +45,13 @@
       </g>
     </defs>
   </svg>
+  <svg id="empty-state" xmlns="http://www.w3.org/2000/svg" width="322" height="237" viewBox="0 0 322 237" fill="none">
+    <path d="M236.777 90.584L209.208 68.9842C205.778 66.3608 203.517 62.4502 202.924 58.1118C202.331 53.7735 203.453 49.3625 206.044 45.8485V45.8485C207.327 44.1076 208.932 42.6395 210.768 41.528C212.604 40.4166 214.635 39.6835 216.744 39.3707C218.853 39.0579 221 39.1715 223.062 39.705C225.123 40.2386 227.059 41.1816 228.758 42.4802L256.319 64.0787C259.751 66.7009 262.013 70.6112 262.608 74.9499C263.203 79.2886 262.082 83.7005 259.491 87.2157V87.2157C256.899 90.7296 253.05 93.0584 248.791 93.69C244.531 94.3216 240.21 93.2044 236.777 90.584V90.584Z" fill="var(--cros-sys-illo-color3)" />
+    <path d="M85.1662 84.2372L72.8191 98.9518C71.2617 100.808 71.5038 103.575 73.3599 105.132L87.9005 117.333C89.7565 118.891 92.5237 118.649 94.0811 116.793L106.428 102.078C107.986 100.222 107.743 97.4549 105.887 95.8975L91.3468 83.6965C89.4908 82.1391 86.7236 82.3811 85.1662 84.2372Z" fill="var(--cros-sys-illo-color5)" />
+    <path d="M78.7032 188.115C84.1071 186.925 89.4438 185.445 94.689 183.681C101.986 181.501 109.83 182.242 116.498 185.74C120.141 187.833 123.896 189.732 127.749 191.43C141.472 196.798 157.382 189.517 160.959 174.989C164.101 162.228 155.512 148.399 142.122 146.423C135.936 145.481 129.647 146.652 123.428 146.255C116.808 145.902 110.548 143.297 105.721 138.888C101.595 135.075 97.8122 130.889 93.1593 127.592C88.65 124.363 83.3803 122.294 77.8232 121.57C69.0434 120.448 60.1179 122.691 52.8967 127.833C45.6755 132.975 40.7124 140.622 39.0361 149.189C37.3598 157.757 39.0988 166.587 43.8928 173.85C48.6867 181.114 56.1678 186.253 64.7856 188.203C69.3679 189.128 74.109 189.098 78.7032 188.115V188.115Z" fill="var(--cros-sys-illo-color1-2)" />
+    <path d="M257.605 175.866C257.354 176.426 256.987 176.925 256.527 177.331C256.068 177.737 255.527 178.041 254.941 178.222C254.354 178.403 253.734 178.457 253.122 178.381C252.511 178.305 251.921 178.101 251.391 177.781C242.089 172.217 235.131 163.435 231.848 153.117C228.564 142.798 229.186 131.667 233.594 121.854C238.002 112.04 245.887 104.233 255.74 99.9269C265.593 95.6204 276.722 95.1167 286.998 98.5122C287.585 98.7057 288.126 99.0202 288.585 99.4355C289.045 99.8507 289.413 100.357 289.665 100.922C289.918 101.487 290.049 102.098 290.051 102.714C290.052 103.331 289.924 103.94 289.675 104.501L257.605 175.866Z" stroke="var(--cros-sys-illo-secondary)" stroke-width="3" stroke-miterlimit="10" />
+    <path d="M204.704 187.052C215.197 187.052 223.704 178.545 223.704 168.052C223.704 157.558 215.197 149.052 204.704 149.052C194.21 149.052 185.704 157.558 185.704 168.052C185.704 178.545 194.21 187.052 204.704 187.052Z" stroke="var(--cros-sys-illo-color4)" stroke-width="3" stroke-miterlimit="10" />
+    <circle cx="159" cy="96.0518" r="46" fill="var(--cros-sys-illo-secondary)" />
+    <path d="M152.325 106.303L153.411 103.123C153.938 101.577 154.568 100.313 155.298 99.3294C156.088 98.317 157.075 97.4462 158.261 96.717C159.507 95.9588 161.032 95.1731 162.839 94.36C164.112 93.8086 165.26 93.2637 166.282 92.7252C167.304 92.1867 168.165 91.5437 168.864 90.7962C169.623 90.0196 170.198 89.0572 170.59 87.909C171.299 85.8334 171.195 83.8994 170.279 82.1069C169.406 80.3296 167.733 79.0186 165.26 78.1739C163.671 77.6309 162.211 77.5021 160.88 77.7875C159.55 78.0728 158.38 78.6102 157.371 79.3997C156.361 80.1892 155.507 81.0314 154.806 81.9265L150.386 77.9757C151.339 76.7726 152.625 75.6089 154.241 74.4844C155.858 73.36 157.753 72.5771 159.926 72.1356C162.098 71.6941 164.487 71.9183 167.093 72.8083C169.787 73.7283 171.939 75.0798 173.549 76.8626C175.219 78.6163 176.281 80.6312 176.737 82.9072C177.251 85.1542 177.101 87.47 176.287 89.8548C175.653 91.7096 174.69 93.2299 173.398 94.4157C172.149 95.6167 170.836 96.5736 169.458 97.2866C168.096 97.9554 166.918 98.5149 165.926 98.965C164.712 99.4874 163.66 100.04 162.771 100.624C161.881 101.208 161.131 101.889 160.52 102.666C159.909 103.444 159.415 104.385 159.038 105.489L158.088 108.271L152.325 106.303ZM150.33 121.673C149.137 121.266 148.257 120.521 147.69 119.44C147.181 118.329 147.131 117.178 147.538 115.985C147.93 114.837 148.66 114.001 149.726 113.478C150.852 112.925 152.011 112.853 153.203 113.26C154.351 113.652 155.179 114.404 155.688 115.514C156.255 116.596 156.343 117.71 155.951 118.859C155.544 120.051 154.777 120.923 153.652 121.476C152.585 121.999 151.478 122.065 150.33 121.673Z" fill="var(--cros-sys-illo-base)" />
+  </svg>
 </iron-iconset-svg>
\ No newline at end of file
diff --git a/ash/webui/print_management/resources/printer_setup_info.html b/ash/webui/print_management/resources/printer_setup_info.html
index cee6bb4..c38e806 100644
--- a/ash/webui/print_management/resources/printer_setup_info.html
+++ b/ash/webui/print_management/resources/printer_setup_info.html
@@ -1,5 +1,15 @@
 <style include="print-management-shared print-management-fonts
     cros-color-overrides">
+  /* Remap illustration variables to jelly variables used in SVG. */
+  :host-context(body:not(.jelly-enabled)) {
+    --cros-sys-illo-base: var(--cros-illustration-color-base-color);
+    --cros-sys-illo-color1-2: var(--cros-illustration-color-1-shade-2);
+    --cros-sys-illo-color3: var(--cros-illustration-color-3);
+    --cros-sys-illo-color4: var(--cros-illustration-color-4);
+    --cros-sys-illo-color5: var(--cros-illustration-color-5);
+    --cros-sys-illo-secondary: var(--cros-illustration-secondary-color);
+  }
+
   .container {
     display: flex;
     flex-direction: column;
@@ -7,9 +17,15 @@
     text-align: center;
     width: 100%;
   }
+
+  iron-icon {
+    --iron-icon-height: 237px;
+    --iron-icon-width: 322px;
+    margin-inline: auto;
+  }
 </style>
 <div class="container">
-  <iron-icon icon="print-management:file-generic"></iron-icon>
+  <iron-icon icon="print-management:empty-state"></iron-icon>
   <h2 class="message-heading">[[i18n('emptyStateNoJobsMessage')]]</h2>
   <p class="message-detail">[[i18n('emptyStatePrinterSettingsMessage')]]</p>
   <cr-button class="action-button">
diff --git a/ash/webui/print_management/resources/printer_setup_info.ts b/ash/webui/print_management/resources/printer_setup_info.ts
index d34e0fe..5d20a68 100644
--- a/ash/webui/print_management/resources/printer_setup_info.ts
+++ b/ash/webui/print_management/resources/printer_setup_info.ts
@@ -7,6 +7,7 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './print_management_fonts.css.js';
 import './print_management_shared.css.js';
+import './icons.html.js';
 
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/base/cpu.h b/base/cpu.h
index 10450e9..2ac00e9 100644
--- a/base/cpu.h
+++ b/base/cpu.h
@@ -5,6 +5,7 @@
 #ifndef BASE_CPU_H_
 #define BASE_CPU_H_
 
+#include <cstdint>
 #include <string>
 
 #include "base/base_export.h"
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java b/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java
index b5be4e8d..8a1ca71 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java
@@ -6,6 +6,8 @@
 
 import android.os.Build;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.runners.model.FrameworkMethod;
 
 import org.chromium.base.Log;
@@ -20,12 +22,21 @@
  * Currently, this only includes checks against a few {@link android.os.Build} values.
  */
 public class DisableIfSkipCheck extends SkipCheck {
+    private static final String EXTRA_RUN_DISABLED_TEST =
+            "org.chromium.base.test.util.DisableIfSkipCheck.RunDisabledTest";
+
     private static final String TAG = "base_test";
 
     @Override
     public boolean shouldSkip(FrameworkMethod method) {
         if (method == null) return true;
 
+        String runDisabledTest =
+                InstrumentationRegistry.getArguments().getString(EXTRA_RUN_DISABLED_TEST);
+        if ("true".equals(runDisabledTest)) {
+            return false;
+        }
+
         List<DisableIf.Build> buildAnnotationList = gatherBuildAnnotations(method);
 
         for (DisableIf.Build v : buildAnnotationList) {
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py
index f520879..2be3384 100644
--- a/build/android/pylib/instrumentation/instrumentation_test_instance.py
+++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -672,6 +672,8 @@
     self._is_unit_test = False
     self._initializeUnitTestFlag(args)
 
+    self._run_disabled = args.run_disabled
+
   def _initializeApkAttributes(self, args, error_func):
     if args.apk_under_test:
       apk_under_test_path = args.apk_under_test
@@ -1119,6 +1121,9 @@
   def GetDataDependencies(self):
     return self._data_deps
 
+  def GetRunDisabledFlag(self):
+    return self._run_disabled
+
   def GetTests(self):
     if self._test_apk_incremental_install_json:
       # Would likely just be a matter of calling GetAllTestsFromApk on all
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 9dc4ef9..d246c55 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -97,6 +97,9 @@
 
 EXTRA_TRACE_FILE = ('org.chromium.base.test.BaseJUnit4ClassRunner.TraceFile')
 
+_EXTRA_RUN_DISABLED_TEST = (
+    'org.chromium.base.test.util.DisableIfSkipCheck.RunDisabledTest')
+
 _EXTRA_TEST_LIST = (
     'org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestList')
 
@@ -721,6 +724,9 @@
   def _RunTest(self, device, test):
     extras = {}
 
+    if self._test_instance.GetRunDisabledFlag():
+      extras[_EXTRA_RUN_DISABLED_TEST] = 'true'
+
     if self._test_instance.is_unit_test:
       extras[_EXTRA_TEST_IS_UNIT] = 'true'
 
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn
index ae371093f..ed39cc6 100644
--- a/build/config/clang/BUILD.gn
+++ b/build/config/clang/BUILD.gn
@@ -56,15 +56,6 @@
         "raw-ptr-exclude-path=base/containers/span.h",
       ]
     }
-
-    if (enable_check_raw_ref_fields) {
-      cflags += [
-        "-Xclang",
-        "-plugin-arg-find-bad-constructs",
-        "-Xclang",
-        "check-raw-ref-fields",
-      ]
-    }
   }
 }
 
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index 0ff60e9..0de9792 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -18,11 +18,6 @@
       build_with_chromium && !is_official_build &&
       ((is_linux && !is_castos) || (is_android && !is_cast_android))
 
-  # TODO(crbug.com/1446146): Merge with enable_check_raw_ptr_fields once both
-  # checks are activated on the same set of platforms.
-  enable_check_raw_ref_fields =
-      build_with_chromium && !is_official_build && is_linux && !is_castos
-
   clang_base_path = default_clang_base_path
 
   # Specifies whether or not bitcode should be embedded during compilation.
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index a6e3523..8a14258 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230605.1.1
+12.20230605.3.1
diff --git a/cc/paint/paint_filter.cc b/cc/paint/paint_filter.cc
index 0bfc45d..64ee0ed 100644
--- a/cc/paint/paint_filter.cc
+++ b/cc/paint/paint_filter.cc
@@ -24,7 +24,9 @@
 #include "third_party/skia/include/core/SkColorFilter.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkMatrix.h"
 #include "third_party/skia/include/core/SkScalar.h"
+#include "third_party/skia/include/core/SkShader.h"
 #include "third_party/skia/include/core/SkString.h"
 #include "third_party/skia/include/core/SkTileMode.h"
 #include "third_party/skia/include/effects/SkImageFilters.h"
@@ -998,14 +1000,12 @@
   sk_sp<SkShader> shader;
   switch (turbulence_type_) {
     case TurbulenceType::kTurbulence:
-      shader = SkPerlinNoiseShader::MakeTurbulence(
-          base_frequency_x_, base_frequency_y_, num_octaves_, seed_,
-          &tile_size_);
+      shader = SkShaders::MakeTurbulence(base_frequency_x_, base_frequency_y_,
+                                         num_octaves_, seed_, &tile_size_);
       break;
     case TurbulenceType::kFractalNoise:
-      shader = SkPerlinNoiseShader::MakeFractalNoise(
-          base_frequency_x_, base_frequency_y_, num_octaves_, seed_,
-          &tile_size_);
+      shader = SkShaders::MakeFractalNoise(base_frequency_x_, base_frequency_y_,
+                                           num_octaves_, seed_, &tile_size_);
       break;
   }
 
diff --git a/cc/raster/bitmap_raster_buffer_provider.cc b/cc/raster/bitmap_raster_buffer_provider.cc
index cc53c03..5f1c57d 100644
--- a/cc/raster/bitmap_raster_buffer_provider.cc
+++ b/cc/raster/bitmap_raster_buffer_provider.cc
@@ -8,25 +8,59 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <limits>
 #include <utility>
 
+#include "base/logging.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/shared_memory_mapping.h"
+#include "base/process/memory.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
-#include "cc/raster/raster_source.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "components/viz/common/resources/bitmap_allocation.h"
-#include "components/viz/common/resources/platform_color.h"
+#include "components/viz/common/resources/shared_image_format.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
+#include "ui/gfx/buffer_format_util.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace cc {
 namespace {
 
+base::UnsafeSharedMemoryRegion AllocateSharedMemory(
+    const gfx::Size& size,
+    viz::SharedImageFormat format) {
+  DCHECK(format.IsBitmapFormatSupported())
+      << "(format = " << format.ToString() << ")";
+
+  size_t bytes = 0;
+  if (!viz::ResourceSizes::MaybeSizeInBytes(size, format.resource_format(),
+                                            &bytes)) {
+    DLOG(ERROR) << "AllocateMappedBitmap with size that overflows";
+    size_t alloc_size = std::numeric_limits<int>::max();
+    base::TerminateBecauseOutOfMemory(alloc_size);
+  }
+
+  auto shared_memory = base::UnsafeSharedMemoryRegion::Create(bytes);
+  if (!shared_memory.IsValid()) {
+    DLOG(ERROR) << "Browser failed to allocate shared memory";
+    base::TerminateBecauseOutOfMemory(bytes);
+  }
+  return shared_memory;
+}
+
 class BitmapSoftwareBacking : public ResourcePool::SoftwareBacking {
  public:
   ~BitmapSoftwareBacking() override {
-    frame_sink->DidDeleteSharedBitmap(shared_bitmap_id);
+    if (frame_sink->shared_image_interface()) {
+      frame_sink->shared_image_interface()->DestroySharedImage(
+          gpu::SyncToken(), shared_bitmap_id);
+    } else {
+      frame_sink->DidDeleteSharedBitmap(shared_bitmap_id);
+    }
   }
 
   void OnMemoryDump(
@@ -40,6 +74,8 @@
 
   raw_ptr<LayerTreeFrameSink> frame_sink;
   base::WritableSharedMemoryMapping mapping;
+
+  base::UnsafeSharedMemoryRegion unsafe_region;
 };
 
 class BitmapRasterBufferImpl : public RasterBuffer {
@@ -117,13 +153,36 @@
   if (!resource.software_backing()) {
     auto backing = std::make_unique<BitmapSoftwareBacking>();
     backing->frame_sink = frame_sink_;
-    backing->shared_bitmap_id = viz::SharedBitmap::GenerateId();
-    base::MappedReadOnlyRegion shm =
-        viz::bitmap_allocation::AllocateSharedBitmap(
-            size, viz::SinglePlaneFormat::kRGBA_8888);
-    backing->mapping = std::move(shm.mapping);
-    frame_sink_->DidAllocateSharedBitmap(std::move(shm.region),
-                                         backing->shared_bitmap_id);
+
+    if (frame_sink_->shared_image_interface()) {
+      constexpr char kDebugLabel[] = "BitmapRasterBufferProvider";
+      backing->unsafe_region =
+          AllocateSharedMemory(size, viz::SinglePlaneFormat::kRGBA_8888);
+      backing->mapping = backing->unsafe_region.Map();
+
+      gfx::GpuMemoryBufferHandle handle;
+      handle.type = gfx::SHARED_MEMORY_BUFFER;
+      handle.offset = 0;
+      handle.stride = static_cast<int32_t>(gfx::RowSizeForBufferFormat(
+          size.width(), gfx::BufferFormat::RGBA_8888, 0));
+      handle.region = backing->unsafe_region.Duplicate();
+
+      backing->shared_bitmap_id =
+          frame_sink_->shared_image_interface()->CreateSharedImage(
+              viz::SinglePlaneFormat::kRGBA_8888, size, color_space,
+              kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
+              gpu::SHARED_IMAGE_USAGE_CPU_WRITE, kDebugLabel,
+              std::move(handle));
+
+    } else {
+      backing->shared_bitmap_id = viz::SharedBitmap::GenerateId();
+      base::MappedReadOnlyRegion shm =
+          viz::bitmap_allocation::AllocateSharedBitmap(
+              size, viz::SinglePlaneFormat::kRGBA_8888);
+      backing->mapping = std::move(shm.mapping);
+      frame_sink_->DidAllocateSharedBitmap(std::move(shm.region),
+                                           backing->shared_bitmap_id);
+    }
 
     resource.set_software_backing(std::move(backing));
   }
diff --git a/cc/trees/layer_tree_frame_sink.cc b/cc/trees/layer_tree_frame_sink.cc
index d479519..64d35197 100644
--- a/cc/trees/layer_tree_frame_sink.cc
+++ b/cc/trees/layer_tree_frame_sink.cc
@@ -7,11 +7,13 @@
 #include <stdint.h>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/trees/layer_tree_frame_sink_client.h"
+#include "components/viz/common/features.h"
 #include "components/viz/common/gpu/context_lost_observer.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -141,4 +143,11 @@
   client_->DidLoseLayerTreeFrameSink();
 }
 
+gpu::ClientSharedImageInterface* LayerTreeFrameSink::shared_image_interface()
+    const {
+  return base::FeatureList::IsEnabled(features::kSharedBitmapToSharedImage)
+             ? shared_image_interface_.get()
+             : nullptr;
+}
+
 }  // namespace cc
diff --git a/cc/trees/layer_tree_frame_sink.h b/cc/trees/layer_tree_frame_sink.h
index cc3826f..5a824ab 100644
--- a/cc/trees/layer_tree_frame_sink.h
+++ b/cc/trees/layer_tree_frame_sink.h
@@ -111,9 +111,7 @@
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager() const {
     return gpu_memory_buffer_manager_;
   }
-  gpu::ClientSharedImageInterface* shared_image_interface() const {
-    return shared_image_interface_.get();
-  }
+  gpu::ClientSharedImageInterface* shared_image_interface() const;
 
   // If supported, this sets the viz::LocalSurfaceId the LayerTreeFrameSink will
   // use to submit a CompositorFrame.
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 6280b9a9..970768ca 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1147,6 +1147,7 @@
       "//third_party/gif_player:gif_player_java",
       "//third_party/google-truth:google_truth_java",
       "//third_party/hamcrest:hamcrest_java",
+      "//ui/accessibility:ax_base_java",
       "//ui/android:ui_java",
       "//ui/android:ui_java_test_support",
       "//ui/android:ui_junit_test_support",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
index 7254a4f..12987a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
@@ -156,7 +156,7 @@
                 mandatoryReauthSwitch.setSummary(
                         R.string.autofill_settings_page_enable_payment_method_mandatory_reauth_sublabel);
                 mandatoryReauthSwitch.setChecked(
-                        PersonalDataManager.isAutofillPaymentMethodsMandatoryReauthEnabled());
+                        PersonalDataManager.isPaymentMethodsMandatoryReauthEnabled());
                 mandatoryReauthSwitch.setKey(PREF_MANDATORY_REAUTH);
                 mandatoryReauthSwitch.setOnPreferenceChangeListener(
                         this::onMandatoryReauthSwitchToggled);
@@ -190,7 +190,7 @@
             if (card.getIsLocal()) {
                 if (ChromeFeatureList.isEnabled(
                             ChromeFeatureList.AUTOFILL_ENABLE_PAYMENTS_MANDATORY_REAUTH)
-                        && PersonalDataManager.isAutofillPaymentMethodsMandatoryReauthEnabled()) {
+                        && PersonalDataManager.isPaymentMethodsMandatoryReauthEnabled()) {
                     // When mandatory reauth is enabled, we require additional authentication before
                     // user can view/edit local card.
                     card_pref.setOnPreferenceClickListener(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProvider.java
index 8c1775e9..243db23 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProvider.java
@@ -4,20 +4,15 @@
 
 package org.chromium.chrome.browser.messages;
 
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT;
-
-import android.os.Build;
 import android.text.format.DateUtils;
 
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.components.messages.MessageAutodismissDurationProvider;
 import org.chromium.components.messages.MessageIdentifier;
 import org.chromium.components.messages.MessagesMetrics;
+import org.chromium.ui.accessibility.AccessibilityState;
 
 /**
  * Implementation of {@link MessageAutodismissDurationProvider}.
@@ -56,16 +51,14 @@
         if (finchControlledDuration > 0) {
             nonA11yDuration = Math.max(finchControlledDuration, nonA11yDuration);
         }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
-                && ChromeAccessibilityUtil.get().isAccessibilityEnabled()) {
-            // crbug.com/1312548: To have a minimum duration even if the system has a default value.
-            return Math.max(mAutodismissDurationWithA11yMs,
-                    ChromeAccessibilityUtil.get().getRecommendedTimeoutMillis((int) nonA11yDuration,
-                            FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS | FLAG_CONTENT_TEXT));
-        }
-        return ChromeAccessibilityUtil.get().isAccessibilityEnabled()
-                ? Math.max(mAutodismissDurationWithA11yMs, nonA11yDuration)
-                : nonA11yDuration;
+
+        // If no a11y service that can perform gestures is enabled, use the set duration. Otherwise
+        // multiply the duration by the recommended multiplier and use that with a minimum of 30s.
+        return !AccessibilityState.isPerformGesturesEnabled()
+                ? nonA11yDuration
+                : Math.max(mAutodismissDurationWithA11yMs,
+                        (long) (AccessibilityState.getRecommendedTimeoutMultiplier()
+                                * nonA11yDuration));
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java
index e72ea15..0b8c92d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediator.java
@@ -249,7 +249,10 @@
 
     @VisibleForTesting
     boolean areBrowserControlsReady() {
+        if (mIsDestroyed) return false;
+        assert mActivityTabProvider != null;
         final Tab tab = mActivityTabProvider.get();
+        if (tab == null || tab.isDestroyed()) return false;
         return TabBrowserControlsConstraintsHelper.getConstraints(tab)
                 == BrowserControlsState.HIDDEN
                 || BrowserControlsUtils.areBrowserControlsFullyVisible(mBrowserControlsManager);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplIntegrationTest.java
index 7d7906b4..ee20323 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplIntegrationTest.java
@@ -8,7 +8,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Assert;
-import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -16,6 +16,7 @@
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
+import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.HistogramWatcher;
@@ -27,6 +28,7 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.share.ShareParams;
 import org.chromium.components.ui_metrics.CanonicalURLResult;
@@ -41,6 +43,7 @@
  * Integration tests for the Share Menu handling.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class ShareDelegateImplIntegrationTest {
     private static final String PAGE_WITH_HTTPS_CANONICAL_URL =
@@ -50,13 +53,13 @@
     private static final String PAGE_WITH_NO_CANONICAL_URL =
             "/chrome/test/data/android/share/link_share_no_canonical.html";
 
-    @Rule
-    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+    @ClassRule
+    public static ChromeTabbedActivityTestRule sActivityTestRule =
+            new ChromeTabbedActivityTestRule();
 
-    @Before
-    public void setUp() throws InterruptedException {
-        mActivityTestRule.startMainActivityOnBlankPage();
-    }
+    @Rule
+    public BlankCTATabInitialStateRule mInitialStateRule =
+            new BlankCTATabInitialStateRule(sActivityTestRule, false);
 
     @Test
     @SmallTest
@@ -104,7 +107,7 @@
     private void verifyShareUrl(
             String pageUrl, String expectedShareUrl, @CanonicalURLResult int expectedUrlResult)
             throws IllegalArgumentException, TimeoutException {
-        mActivityTestRule.loadUrl(pageUrl);
+        sActivityTestRule.loadUrl(pageUrl);
         var urlResultHistogram = HistogramWatcher.newSingleRecordWatcher(
                 ShareDelegateImpl.CANONICAL_URL_RESULT_HISTOGRAM, expectedUrlResult);
         ShareParams params = triggerShare();
@@ -129,14 +132,14 @@
                 }
             };
 
-            new ShareDelegateImpl(mActivityTestRule.getActivity()
+            new ShareDelegateImpl(sActivityTestRule.getActivity()
                                           .getRootUiCoordinatorForTesting()
                                           .getBottomSheetController(),
-                    mActivityTestRule.getActivity().getLifecycleDispatcher(),
-                    mActivityTestRule.getActivity().getActivityTabProvider(),
-                    mActivityTestRule.getActivity().getTabModelSelectorSupplier(),
+                    sActivityTestRule.getActivity().getLifecycleDispatcher(),
+                    sActivityTestRule.getActivity().getActivityTabProvider(),
+                    sActivityTestRule.getActivity().getTabModelSelectorSupplier(),
                     new ObservableSupplierImpl<>(), delegate, false)
-                    .share(mActivityTestRule.getActivity().getActivityTab(), false,
+                    .share(sActivityTestRule.getActivity().getActivityTab(), false,
                             /*shareOrigin=*/0);
         });
         helper.waitForCallback(0);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplTest.java
index 0a6f8fb..7acc233 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplTest.java
@@ -7,11 +7,13 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Assert;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.tab.MockTab;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.util.SadTabRule;
@@ -26,10 +28,11 @@
 /**
  * Tests (requiring native) of the ShareDelegateImpl.
  */
+@Batch(Batch.PER_CLASS)
 @RunWith(BaseJUnit4ClassRunner.class)
 public class ShareDelegateImplTest {
-    @Rule
-    public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
+    @ClassRule
+    public static final ChromeBrowserTestRule sBrowserTestRule = new ChromeBrowserTestRule();
 
     @Rule
     public final SadTabRule mSadTabRule = new SadTabRule();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProviderTest.java
index 13a6a97..bfd1079a 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageAutodismissDurationProviderTest.java
@@ -14,9 +14,9 @@
 import org.chromium.base.FeatureList.TestValues;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.components.messages.MessageIdentifier;
 import org.chromium.components.messages.MessagesMetrics;
+import org.chromium.ui.accessibility.AccessibilityState;
 
 /**
  * Unit tests for {@link ChromeMessageAutodismissDurationProvider}.
@@ -31,56 +31,60 @@
         mFeatureTestValues = new TestValues();
         mFeatureTestValues.addFeatureFlagOverride(
                 ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE, true);
-
+        AccessibilityState.setIsPerformGesturesEnabledForTesting(false);
         FeatureList.setTestValues(mFeatureTestValues);
     }
 
     @Test
     public void testDefaultNonA11yDuration() {
-        ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(false);
         ChromeMessageAutodismissDurationProvider provider =
                 new ChromeMessageAutodismissDurationProvider();
         provider.setDefaultAutodismissDurationMsForTesting(500);
         provider.setDefaultAutodismissDurationWithA11yMsForTesting(1000);
-        Assert.assertEquals("Provider should return default non-a11y duration if a11y is off", 500,
-                provider.get(MessageIdentifier.TEST_MESSAGE, 0));
+        Assert.assertEquals(
+                "Provider should return default non-a11y duration if no gesture performing "
+                        + "a11y services are running.",
+                500, provider.get(MessageIdentifier.TEST_MESSAGE, 0));
     }
 
     @Test
     public void testA11yDuration() {
-        ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true);
+        AccessibilityState.setIsPerformGesturesEnabledForTesting(true);
         ChromeMessageAutodismissDurationProvider provider =
                 new ChromeMessageAutodismissDurationProvider();
         provider.setDefaultAutodismissDurationMsForTesting(500);
         provider.setDefaultAutodismissDurationWithA11yMsForTesting(1000);
-        Assert.assertEquals("Provider should return default a11y duration if a11y is on", 1000,
-                provider.get(MessageIdentifier.TEST_MESSAGE, 0));
+        Assert.assertEquals(
+                "Provider should return default a11y duration if any gesture performing "
+                        + "a11y services are running.",
+                1000, provider.get(MessageIdentifier.TEST_MESSAGE, 0));
     }
 
     @Test
     public void testCustomDuration() {
-        ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(false);
         ChromeMessageAutodismissDurationProvider provider =
                 new ChromeMessageAutodismissDurationProvider();
         provider.setDefaultAutodismissDurationMsForTesting(500);
         provider.setDefaultAutodismissDurationWithA11yMsForTesting(1000);
-        Assert.assertEquals("Provider should return custom non-a11y duration if a11y is off", 1500,
-                provider.get(MessageIdentifier.TEST_MESSAGE, 1500));
+        Assert.assertEquals(
+                "Provider should return custom non-a11y duration if no gesture performing "
+                        + "a11y services are running.",
+                1500, provider.get(MessageIdentifier.TEST_MESSAGE, 1500));
         Assert.assertEquals(
                 "Provider should return default non-a11y duration if custom duration is too short",
                 500, provider.get(MessageIdentifier.TEST_MESSAGE, 250));
-        ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true);
-        Assert.assertEquals("Provider should return custom a11y duration if a11y is on", 1500,
-                provider.get(MessageIdentifier.TEST_MESSAGE, 1500));
+        AccessibilityState.setIsPerformGesturesEnabledForTesting(true);
+        Assert.assertEquals("Provider should return custom a11y duration if any gesture performing "
+                        + "a11y services are running.",
+                1500, provider.get(MessageIdentifier.TEST_MESSAGE, 1500));
         Assert.assertEquals(
-                "Provider should return default a11y duration if custom duration is too short",
+                "Provider should return default a11y duration if custom duration is too short "
+                        + "and any gesture performing a11y services are running.",
                 1000, provider.get(MessageIdentifier.TEST_MESSAGE, 250));
     }
 
     @Test
     public void testFeatureCustomDuration() {
-        ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(false);
-
         mFeatureTestValues.addFieldTrialParamOverride(
                 ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE,
                 ChromeMessageAutodismissDurationProvider
@@ -93,7 +97,9 @@
                 new ChromeMessageAutodismissDurationProvider();
         provider.setDefaultAutodismissDurationMsForTesting(500);
         provider.setDefaultAutodismissDurationWithA11yMsForTesting(1000);
-        Assert.assertEquals("Provider should return finch custom non-a11y duration if a11y is off",
+        Assert.assertEquals(
+                "Provider should return finch custom non-a11y duration if no gesture performing "
+                        + "a11y services are running.",
                 2000, provider.get(MessageIdentifier.TEST_MESSAGE, 1500));
 
         Assert.assertEquals(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediatorTest.java
index 0f6f173..e68d7480 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/messages/ChromeMessageQueueMediatorTest.java
@@ -28,6 +28,7 @@
 import org.robolectric.annotation.LooperMode;
 
 import org.chromium.base.Callback;
+import org.chromium.base.UserDataHost;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
@@ -43,6 +44,7 @@
 import org.chromium.chrome.browser.layouts.LayoutType;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.messages.ManagedMessageDispatcher;
 import org.chromium.ui.modaldialog.ModalDialogManager;
@@ -83,6 +85,9 @@
     private ActivityTabProvider mActivityTabProvider;
 
     @Mock
+    private Tab mTab;
+
+    @Mock
     private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
 
     @Mock
@@ -225,6 +230,11 @@
         visibilitySupplier.set(true);
         when(mBrowserControlsManager.getBrowserControlHiddenRatio()).thenReturn(0f);
 
+        when(mActivityTabProvider.get()).thenReturn(mTab);
+        when(mTab.isDestroyed()).thenReturn(false);
+        // Mock TabBrowserControlsConstraintsHelper to avoid NPE.
+        when(mTab.getUserDataHost()).thenReturn(new UserDataHost());
+
         mMediator.onRequestShowing(() -> {});
         Assert.assertTrue(mMediator.isReadyForShowing());
 
@@ -322,6 +332,7 @@
         mMediator.onAnimationEnd();
         verify(mMessageContainerCoordinator, times(1)).onAnimationEnd();
     }
+
     /**
      * Test the queue can be suspended and resumed correctly on omnibox focus events.
      */
@@ -342,4 +353,17 @@
         Assert.assertEquals("mUrlFocusToken should be invalidated.", TokenHolder.INVALID_TOKEN,
                 mMediator.getUrlFocusTokenForTesting());
     }
+
+    /**
+     * Test when tab is destroyed before {@link ChromeMessageQueueMediator#destroy()}.
+     */
+    @Test
+    public void testTabDestroyed() {
+        initMediator();
+        when(mActivityTabProvider.get()).thenReturn(mTab);
+        when(mTab.isDestroyed()).thenReturn(true);
+
+        // Expect no error.
+        mMediator.areBrowserControlsReady();
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java
index 812ce7f..975115e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/share/LensUtilsTest.java
@@ -20,6 +20,7 @@
 
 import org.chromium.base.FeatureList;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Batch;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 
@@ -30,6 +31,7 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
+@Batch(Batch.UNIT_TESTS)
 public class LensUtilsTest {
     @Before
     public void setUp() {
@@ -127,4 +129,4 @@
         Assert.assertTrue("Feature incorrectly disabled when Lens on tablet was enabled",
                 LensUtils.isGoogleLensFeatureEnabledOnTablet());
     }
-}
\ No newline at end of file
+}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 1ea7008..3324290 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1547,6 +1547,9 @@
         <message name="IDS_SHOW_PERFORMANCE" desc="The text label of the Performance menu item">
           Performance
         </message>
+        <message name="IDS_SHOW_TRANSLATE" desc="The text label of the Translate menu item">
+          Translate...
+        </message>
         <message name="IDS_SETTINGS" desc="The text label of the Settings menu item">
           Settin&amp;gs
         </message>
@@ -1597,6 +1600,9 @@
         <message name="IDS_SHOW_PERFORMANCE" desc="In Title Case: The text label of the Performance menu item">
           Performance
         </message>
+        <message name="IDS_SHOW_TRANSLATE" desc="In Title Case: The text label of the Translate menu item">
+          Translate
+        </message>
         <message name="IDS_SETTINGS" desc="In Title Case: The text label of the Settings menu item">
           &amp;Settings
         </message>
@@ -7830,6 +7836,12 @@
       <message name="IDS_BOOKMARKS_OPEN_BOOKMARK_LABEL" is_accessibility_with_no_ui="true" desc="Accessible label for a bookmarks side panel folder list item">
         Open <ph name="BOOKMARK_TITLE">$1<ex>New York Times</ex></ph>
       </message>
+      <message name="IDS_BOOKMARKS_SELECT_FOLDER_LABEL" is_accessibility_with_no_ui="true" desc="Accessible label for a bookmarks side panel folder list item in edit mode">
+        Select folder <ph name="FOLDER_TITLE">$1<ex>All Bookmarks</ex></ph>
+      </message>
+      <message name="IDS_BOOKMARKS_SELECT_BOOKMARK_LABEL" is_accessibility_with_no_ui="true" desc="Accessible label for a bookmarks side panel folder list item in edit mode">
+        Select <ph name="BOOKMARK_TITLE">$1<ex>New York Times</ex></ph>
+      </message>
       <message name="IDS_BOOKMARK_ACCESSIBLE_DESCRIPTION_PRICE_TRACKING" is_accessibility_with_no_ui="true" desc="Accessible description for price tracking data on a bookmarks side panel list item">
         Price tracking is enabled. Price is <ph name="CURRENT_PRICE">$1<ex>400</ex></ph>.
       </message>
@@ -13963,6 +13975,18 @@
       <message name="IDS_WEBAUTHN_CABLE_ACTIVATE_DEVICE_NAME_DESCRIPTION" desc="Contents of the dialog shown when the user tries to sign-in using a phone as a security key.">
         A notification was sent to <ph name="DEVICE_NAME">$1<ex>Ted's Pixel 6 Pro</ex></ph>
       </message>
+      <message name="IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE" desc="The title of a dialog that is shown when the computer is connecting to a phone or tablet.">
+        Connecting to your device
+      </message>
+      <message name="IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION" desc="The second line of a dialog that shows once the computer has successfully connected to the user's phone or tablet. That phone/tablet will be showing instructions for what to do next and the user should follow those instuctions.">
+        Follow the steps on your device
+      </message>
+      <message name="IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION" desc="The second line of a dialog that shows once the computer fails to connect to the user's phone or tablet.">
+        On both devices, check your internet connection and turn on Bluetooth. Then, try again.
+      </message>
+      <message name="IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE" desc="The text of a button on a dialog. The button is the user's only option at this point since the process has failed. The dialog contains instructions about how to try again and the button simply closes the dialog.">
+        Close
+      </message>
 
       <message name="IDS_WEBAUTHN_CABLEV2_SERVERLINK_TROUBLE" desc="This message is shown as a link below IDS_WEBAUTHN_CABLEV2_SERVERLINK_DESCRIPTION. The 'it' referenced here is the notification mentioned in that message.">
         Didn't get it?
diff --git a/chrome/app/generated_resources_grd/IDS_SHOW_TRANSLATE.png.sha1 b/chrome/app/generated_resources_grd/IDS_SHOW_TRANSLATE.png.sha1
new file mode 100644
index 0000000..bca76c08
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_SHOW_TRANSLATE.png.sha1
@@ -0,0 +1 @@
+02800922751e0a6977f53edd29e03ec218bc7d3c
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..25c9faa
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+4efee63dc4f1982ba125f9fd7cce0f5b47d67dbe
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE.png.sha1
new file mode 100644
index 0000000..77dd30e
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE.png.sha1
@@ -0,0 +1 @@
+0633498700fb67e099d452e407400e188451143e
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE.png.sha1
new file mode 100644
index 0000000..3d6f4cbc
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE.png.sha1
@@ -0,0 +1 @@
+c60507545ddcb3d41e92355640b217239f036c22
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..58712b11
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8094d90a7dbe9548aa5dd4bc47a964cc15e77eb8
\ No newline at end of file
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 4fe3b8c7e..9a595a5b 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -295,9 +295,6 @@
   <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING_DESCRIPTION" desc="Description for the memory saver mode setting">
     When on, Chromium frees up memory from inactive tabs. This gives active tabs and other apps more computer resources and keeps Chromium fast. Your inactive tabs automatically become active again when you go back to them.
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL" desc="Label for the memory saver mode to discard tabs based on a set of heuristics" translateable="false">
-    Chromium decides when a tab becomes inactive
-  </message>
   <message name="IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING_DESCRIPTION" desc="Description for the energy saver mode setting">
     When on, Chromium conserves battery power by limiting background activity and visual effects, such as smooth scrolling and video frame rates.
   </message>
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 1397730..2fd0bbe 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -288,9 +288,6 @@
   <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING_DESCRIPTION" desc="Description for the memory saver mode setting">
     When on, Chrome frees up memory from inactive tabs. This gives active tabs and other apps more computer resources and keeps Chrome fast. Your inactive tabs automatically become active again when you go back to them.
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL" desc="Label for the memory saver mode to discard tabs based on a set of heuristics" translateable="false">
-    Chrome decides when a tab becomes inactive
-  </message>
   <message name="IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING_DESCRIPTION" desc="Description for the energy saver mode setting">
     When on, Chrome conserves battery power by limiting background activity and visual effects, such as smooth scrolling and video frame rates.
   </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index c857088..6ca496e4 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1543,12 +1543,18 @@
   <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING" desc="Memory saver mode setting label. This mode allows the browser to save memory from background tabs">
     Memory Saver
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL" desc="Label for the memory saver mode to discard tabs on a timer" translateable="false">
-    Select when your tabs become inactive
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL" desc="Label for the memory saver mode to discard tabs based on a set of heuristics">
+    Free up memory based on usage
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL" desc="Accessibility label which will be read by screen readers when focus enters the memory saver mode radio group. When memory saver mode is enabled, this radio group will be expanded and allow the user to choose between different tab discarding policies." translateable="false">
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL" desc="Label for the memory saver mode to discard tabs on a timer">
+    Free up memory based on tab inactivity
+  </message>
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL" desc="Accessibility label which will be read by screen readers when focus enters the memory saver mode radio group. When memory saver mode is enabled, this radio group will be expanded and allow the user to choose between different tab discarding policies.">
     Memory saver options
   </message>
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_CHOOSE_DISCARD_TIME_ARIA_LABEL" desc="Accessibility label which will be read by screen readers when focus enters the dropdown menu in the second option of the memory saver radio group. The dropdown allows the user to select the time before a tab is discarded by memory saver.">
+    Choose time range
+  </message>
   <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_BUTTON_ARIA_LABEL" desc="Accessibility label which will be read by screen readers when focusing the tab discarding exceptions list add button. Sites added to the list will not be discarded by memory saver.">
     Add to the "always keep these sites active" list
   </message>
@@ -1561,14 +1567,14 @@
   <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADDITIONAL_SITES" desc="Label for button that expands the tab discarding exceptions list to show all entries. Sites added to the list will not be discarded by memory saver.">
     Additional sites
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS" desc="Label for the first tab of the tab discarding exception list add dialog, that allows users to pick sites from currently opened tabs to add to the list." translateable="false">
-    Add open tabs
+  <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS" desc="Label for the first tab of the tab discarding exception list add dialog, that allows users to pick sites from currently opened tabs to add to the list.">
+    Add current sites
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_MANUAL" desc="Label for the second tab of the tab discarding exception list add dialog, where users can manually input a site to be added to the list." translateable="false">
+  <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_MANUAL" desc="Label for the second tab of the tab discarding exception list add dialog, where users can manually input a site to be added to the list.">
     Add sites manually
   </message>
-  <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_HELP" desc="Label for button that expands the tab discarding exceptions list to show all entries. Sites added to the list will not be discarded by memory saver." translateable="false">
-    Sites you add remain active and won't be used to give active tabs and other apps more resources. <ph name="BEGIN_LINK">&lt;a href="$1" target="_blank"&gt;</ph>Learn more about site exclusion<ph name="END_LINK">&lt;/a&gt;</ph>
+  <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_HELP" desc="Label for button that expands the tab discarding exceptions list to show all entries. Sites added to the list will not be discarded by memory saver.">
+    Sites you add will always stay active and memory won't be freed up from them. <ph name="BEGIN_LINK">&lt;a href="$1" target="_blank"&gt;</ph>Learn more about site exclusion<ph name="END_LINK">&lt;/a&gt;</ph>
   </message>
   <message name="IDS_SETTINGS_BATTERY_PAGE_TITLE" desc="Title of the power section of the performance settings page">
     Power
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_CHOOSE_DISCARD_TIME_ARIA_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_CHOOSE_DISCARD_TIME_ARIA_LABEL.png.sha1
new file mode 100644
index 0000000..9e50aea
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_CHOOSE_DISCARD_TIME_ARIA_LABEL.png.sha1
@@ -0,0 +1 @@
+1fb9be597781f11259c0b99a0f082fe8c4d2694b
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL.png.sha1
new file mode 100644
index 0000000..9e50aea
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL.png.sha1
@@ -0,0 +1 @@
+1fb9be597781f11259c0b99a0f082fe8c4d2694b
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL.png.sha1
new file mode 100644
index 0000000..9e50aea
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL.png.sha1
@@ -0,0 +1 @@
+1fb9be597781f11259c0b99a0f082fe8c4d2694b
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL.png.sha1
new file mode 100644
index 0000000..9e50aea
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL.png.sha1
@@ -0,0 +1 @@
+1fb9be597781f11259c0b99a0f082fe8c4d2694b
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS.png.sha1
new file mode 100644
index 0000000..c5eed62
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_CURRENT_TABS.png.sha1
@@ -0,0 +1 @@
+753243fb2e8c69029a13020aa86675d596500548
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_HELP.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_HELP.png.sha1
new file mode 100644
index 0000000..c6888f5
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_HELP.png.sha1
@@ -0,0 +1 @@
+0e408a985d2ea88335dcdfbc1b489df202e628a2
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_MANUAL.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_MANUAL.png.sha1
new file mode 100644
index 0000000..c6888f5
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_DIALOG_MANUAL.png.sha1
@@ -0,0 +1 @@
+0e408a985d2ea88335dcdfbc1b489df202e628a2
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5c981e6..a25f7bc 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1896,7 +1896,7 @@
     # TODO(crbug.com/1335199): break this dep when favicon is in its own target
     "//chrome/browser/share",
     "//chrome/browser/ui",
-    "//chrome/browser/storage_access_api:permissions",
+    "//chrome/browser/storage_access_api",
     "//chrome/browser/top_level_storage_access_api:permissions",
     "//chrome/browser/safe_browsing",
     "//chrome/browser/safe_browsing:verdict_cache_manager_factory",
@@ -2018,7 +2018,7 @@
     "//chrome/browser/sharing:buildflags",
     "//chrome/browser/sharing/proto",
     "//chrome/browser/signin:identity_manager_provider",
-    "//chrome/browser/storage_access_api:permissions",
+    "//chrome/browser/storage_access_api",
     "//chrome/browser/thumbnail",
     "//chrome/browser/top_level_storage_access_api:permissions",
     "//chrome/browser/touch_to_fill",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e1f2fa7..8b1b71c0 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -202,6 +202,7 @@
 #include "services/tracing/public/cpp/tracing_features.h"
 #include "storage/browser/quota/quota_features.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/features_generated.h"
 #include "third_party/blink/public/common/forcedark/forcedark_switches.h"
 #include "third_party/blink/public/common/switches.h"
 #include "ui/accessibility/accessibility_features.h"
@@ -5447,6 +5448,9 @@
     {"mutation-events", flag_descriptions::kMutationEventsName,
      flag_descriptions::kMutationEventsDescription, kOsAll,
      FEATURE_VALUE_TYPE(blink::features::kMutationEvents)},
+    {"popover-attribute", flag_descriptions::kHTMLPopoverAttributeName,
+     flag_descriptions::kHTMLPopoverAttributeDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kHTMLPopoverAttribute)},
     {"fill-on-account-select", flag_descriptions::kFillOnAccountSelectName,
      flag_descriptions::kFillOnAccountSelectDescription, kOsAll,
      FEATURE_VALUE_TYPE(password_manager::features::kFillOnAccountSelect)},
@@ -7028,6 +7032,11 @@
     {"ignore-ui-gains", flag_descriptions::kIgnoreUiGainsName,
      flag_descriptions::kIgnoreUiGainsDescription, kOsCrOS | kOsLacros,
      FEATURE_VALUE_TYPE(media::kIgnoreUiGains)},
+    {"show-force-respect-ui-gains-toggle",
+     flag_descriptions::kShowForceRespectUiGainsToggleName,
+     flag_descriptions::kShowForceRespectUiGainsToggleDescription,
+     kOsCrOS | kOsLacros,
+     FEATURE_VALUE_TYPE(media::kShowForceRespectUiGainsToggle)},
 #endif
 
     {"enable-css-selector-fragment-anchor",
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
index f52f1c8..61854cf 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
@@ -285,6 +285,7 @@
 constexpr char kWebAppWindowHistogramName[] = "WebAppWindow";
 
 constexpr char kUsageTimeAppIdKey[] = "app_id";
+constexpr char kUsageTimeAppPublisherIdKey[] = "app_publisher_id";
 constexpr char kUsageTimeAppTypeKey[] = "app_type";
 constexpr char kUsageTimeDurationKey[] = "time";
 constexpr char kReportingUsageTimeDurationKey[] = "reporting_usage_time";
@@ -378,6 +379,10 @@
   }
 }
 
+AppPlatformMetrics::UsageTime::UsageTime() = default;
+
+AppPlatformMetrics::UsageTime::~UsageTime() = default;
+
 AppPlatformMetrics::UsageTime::UsageTime(const base::Value& value) {
   const base::Value::Dict* data_dict = value.GetIfDict();
   if (!data_dict) {
@@ -390,6 +395,12 @@
     return;
   }
 
+  const std::string* const app_publisher_id_value =
+      data_dict->FindString(kUsageTimeAppPublisherIdKey);
+  if (app_publisher_id_value) {
+    app_publisher_id = *app_publisher_id_value;
+  }
+
   const std::string* const app_type_value =
       data_dict->FindString(kUsageTimeAppTypeKey);
   if (!app_type_value) {
@@ -424,6 +435,7 @@
 base::Value::Dict AppPlatformMetrics::UsageTime::ConvertToDict() const {
   base::Value::Dict usage_time_dict;
   usage_time_dict.Set(kUsageTimeAppIdKey, app_id);
+  usage_time_dict.Set(kUsageTimeAppPublisherIdKey, app_publisher_id);
   usage_time_dict.Set(kUsageTimeAppTypeKey,
                       GetAppTypeHistogramName(app_type_name));
   usage_time_dict.Set(kUsageTimeDurationKey,
@@ -1243,7 +1255,7 @@
   }
 
   ScopedDictPrefUpdate usage_dict_pref(profile_->GetPrefs(), kAppUsageTime);
-  for (auto it : usage_time_per_two_hours_) {
+  for (const auto& it : usage_time_per_two_hours_) {
     const std::string& instance_id = it.first.ToString();
     auto* const usage_info = usage_dict_pref->FindDictByDottedPath(instance_id);
     if (!usage_info) {
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
index 9ca6ec30..41880cc 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
@@ -57,6 +57,7 @@
 extern const char kWebAppWindowHistogramName[];
 
 extern const char kUsageTimeAppIdKey[];
+extern const char kUsageTimeAppPublisherIdKey[];
 extern const char kUsageTimeAppTypeKey[];
 extern const char kUsageTimeDurationKey[];
 extern const char kReportingUsageTimeDurationKey[];
@@ -119,11 +120,22 @@
   // Usage time representation for the data that is persisted in the pref store.
   // Includes helpers for serialization/deserialization.
   struct UsageTime {
-    UsageTime() = default;
+    UsageTime();
     explicit UsageTime(const base::Value& value);
+    UsageTime(const UsageTime&) = delete;
+    UsageTime& operator=(const UsageTime&) = delete;
+    ~UsageTime();
+
     base::TimeDelta running_time;
     ukm::SourceId source_id = ukm::kInvalidSourceId;
     std::string app_id;
+
+    // App publisher id tracked for commercial insights reporting. This
+    // facilitates external components to report the publisher id that includes
+    // the package name for android apps, web app url for web apps, etc. which
+    // are public app identifiers. We use an empty string if there is no
+    // publisher id associated with the app.
+    std::string app_publisher_id;
     AppTypeName app_type_name = AppTypeName::kUnknown;
     bool window_is_closed = false;
 
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
index 2c5b3bf..f226720 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service_unittest.cc
@@ -81,6 +81,8 @@
 
 constexpr char kChromeAppId[] = "plfjlfohfjjpmmifkbcmalnmcebkklkh";
 constexpr char kExtensionId[] = "mhjfbmdgcfjbbpaeojofohoefgiehjai";
+constexpr char kAndroidAppId[] = "a";
+constexpr char kAndroidAppPublisherId[] = "com.google.A";
 
 constexpr apps::InstanceState kActiveInstanceState =
     static_cast<apps::InstanceState>(
@@ -250,7 +252,7 @@
     auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
     apps::AppRegistryCache& cache = proxy->AppRegistryCache();
 
-    AddApp(cache, /*app_id=*/"a", AppType::kArc, "com.google.A",
+    AddApp(cache, kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
            Readiness::kReady, InstallReason::kUser, InstallSource::kPlayStore,
            true /* should_notify_initialized */);
 
@@ -1943,11 +1945,11 @@
   VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kCrostini);
   VerifyAppLaunchPerAppTypeV2Histogram(1, AppTypeNameV2::kCrostini);
 
-  EXPECT_CALL(fake_arc_apps, Launch(/*app_id=*/"a", ui::EF_NONE,
+  EXPECT_CALL(fake_arc_apps, Launch(kAndroidAppId, ui::EF_NONE,
                                     LaunchSource::kFromChromeInternal, _))
       .Times(1);
-  proxy->Launch(
-      /*app_id=*/"a", ui::EF_NONE, LaunchSource::kFromChromeInternal, nullptr);
+  proxy->Launch(kAndroidAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
+                nullptr);
   VerifyAppsLaunchUkm("app://com.google.A", AppTypeName::kArc,
                       LaunchSource::kFromChromeInternal);
   VerifyAppLaunchPerAppTypeHistogram(1, AppTypeName::kArc);
@@ -2091,8 +2093,7 @@
   FakePublisher fake_standalone_browser_extension(
       proxy, AppType::kStandaloneBrowserExtension);
 
-  proxy->UninstallSilently(
-      /*app_id=*/"a", UninstallSource::kAppList);
+  proxy->UninstallSilently(kAndroidAppId, UninstallSource::kAppList);
   VerifyAppsUninstallUkm("app://com.google.A", AppTypeName::kArc,
                          UninstallSource::kAppList);
 
@@ -2116,23 +2117,22 @@
   window->Init(ui::LAYER_NOT_DRAWN);
 
   // Set the window active state.
-  static constexpr char kAppId[] = "a";
   const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
-  ModifyInstance(kInstanceId, kAppId, window.get(),
+  ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
                  ::apps::InstanceState::kActive);
   static constexpr base::TimeDelta kAppRunningDuration = base::Minutes(5);
   task_environment_.FastForwardBy(kAppRunningDuration);
 
   // Close app window to stop tracking further usage and verify usage info is
   // persisted in the pref store.
-  ModifyInstance(kInstanceId, kAppId, window.get(),
+  ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
                  ::apps::InstanceState::kDestroyed);
   const auto& usage_dict_pref = GetPrefService()->GetDict(kAppUsageTime);
   ASSERT_THAT(usage_dict_pref.size(), Eq(1UL));
   ASSERT_THAT(usage_dict_pref.Find(kInstanceId.ToString()), NotNull());
   EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
                    ->FindString(kUsageTimeAppIdKey),
-              StrEq(kAppId));
+              StrEq(kAndroidAppId));
   EXPECT_THAT(
       base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
                                  ->Find(kUsageTimeDurationKey)),
@@ -2175,23 +2175,22 @@
   window->Init(ui::LAYER_NOT_DRAWN);
 
   // Set the window active state.
-  static constexpr char kAppId[] = "a";
   const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
-  ModifyInstance(kInstanceId, kAppId, window.get(),
+  ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
                  ::apps::InstanceState::kActive);
   static constexpr base::TimeDelta kAppRunningDuration = base::Minutes(5);
   task_environment_.FastForwardBy(kAppRunningDuration);
 
   // Close app window to stop tracking further usage and verify usage info is
   // persisted in the pref store.
-  ModifyInstance(kInstanceId, kAppId, window.get(),
+  ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
                  ::apps::InstanceState::kDestroyed);
   const auto& usage_dict_pref = GetPrefService()->GetDict(kAppUsageTime);
   ASSERT_THAT(usage_dict_pref.size(), Eq(1UL));
   ASSERT_THAT(usage_dict_pref.Find(kInstanceId.ToString()), NotNull());
   EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
                    ->FindString(kUsageTimeAppIdKey),
-              StrEq(kAppId));
+              StrEq(kAndroidAppId));
   EXPECT_THAT(
       base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
                                  ->Find(kUsageTimeDurationKey)),
@@ -2204,6 +2203,8 @@
     usage_dict->FindDictByDottedPath(kInstanceId.ToString())
         ->Set(kReportingUsageTimeDurationKey,
               base::TimeDeltaToValue(kAppRunningDuration));
+    usage_dict->FindDictByDottedPath(kInstanceId.ToString())
+        ->Set(kUsageTimeAppPublisherIdKey, kAndroidAppPublisherId);
   }
 
   // Fast forward by two hours so it reports usage data and we can verify usage
@@ -2216,7 +2217,10 @@
   EXPECT_THAT(updated_usage_dict_pref.Find(kInstanceId.ToString()), NotNull());
   EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
                    ->FindString(kUsageTimeAppIdKey),
-              StrEq(kAppId));
+              StrEq(kAndroidAppId));
+  EXPECT_THAT(*usage_dict_pref.FindDict(kInstanceId.ToString())
+                   ->FindString(kUsageTimeAppPublisherIdKey),
+              StrEq(kAndroidAppPublisherId));
   EXPECT_THAT(
       base::ValueToTimeDelta(usage_dict_pref.FindDict(kInstanceId.ToString())
                                  ->Find(kUsageTimeDurationKey)),
@@ -2237,16 +2241,15 @@
   window->Init(ui::LAYER_NOT_DRAWN);
 
   // Set the window active state and simulate app usage.
-  static constexpr char kAppId[] = "a";
   const base::UnguessableToken& kInstanceId = base::UnguessableToken::Create();
-  ModifyInstance(kInstanceId, kAppId, window.get(),
+  ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
                  ::apps::InstanceState::kActive);
   static constexpr base::TimeDelta kAppRunningDuration = base::Minutes(5);
   task_environment_.FastForwardBy(kAppRunningDuration);
 
   // Close app window to stop tracking further usage and verify usage info is
   // not persisted in the pref store.
-  ModifyInstance(kInstanceId, kAppId, window.get(),
+  ModifyInstance(kInstanceId, kAndroidAppId, window.get(),
                  ::apps::InstanceState::kDestroyed);
   ASSERT_TRUE(GetPrefService()->GetDict(kAppUsageTime).empty());
 }
@@ -2378,7 +2381,7 @@
 
 // Verify no more input event is recorded when the window is destroyed.
 TEST_P(AppPlatformInputMetricsTest, WindowIsDestroyed) {
-  ModifyInstance(/*app_id=*/"a", window(), kActive);
+  ModifyInstance(kAndroidAppId, window(), kActive);
   CreateInputEvent(InputEventSource::kMouse);
   app_platform_input_metrics()->OnFiveMinutes();
   VerifyNoUkm();
@@ -2386,7 +2389,7 @@
   VerifyUkm("app://com.google.A", AppTypeName::kArc, /*event_count=*/1,
             InputEventSource::kMouse);
 
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kDestroyed);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kDestroyed);
   CreateInputEvent(InputEventSource::kMouse);
   app_platform_input_metrics()->OnTwoHours();
   // Verify no more input event is recorded.
@@ -2395,7 +2398,7 @@
 }
 
 TEST_P(AppPlatformInputMetricsTest, MouseEvent) {
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kActive);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
   CreateInputEvent(InputEventSource::kMouse);
   app_platform_input_metrics()->OnFiveMinutes();
   VerifyNoUkm();
@@ -2415,7 +2418,7 @@
 }
 
 TEST_P(AppPlatformInputMetricsTest, TouchEvents) {
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kActive);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
   CreateInputEvent(InputEventSource::kTouch);
   CreateInputEvent(InputEventSource::kTouch);
   app_platform_input_metrics()->OnFiveMinutes();
@@ -2426,7 +2429,7 @@
 }
 
 TEST_P(AppPlatformInputMetricsTest, KeyEvents) {
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kActive);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
   CreateInputEvent(InputEventSource::kKeyboard);
   app_platform_input_metrics()->OnFiveMinutes();
   VerifyNoUkm();
@@ -2463,7 +2466,7 @@
 }
 
 TEST_P(AppPlatformInputMetricsTest, MultipleEvents) {
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kActive);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
   CreateInputEvent(InputEventSource::kMouse);
   CreateInputEvent(InputEventSource::kMouse);
   CreateInputEvent(InputEventSource::kKeyboard);
@@ -2594,13 +2597,13 @@
 }
 
 TEST_P(AppPlatformInputMetricsTest, InputEventsUkmReportAfterReboot) {
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kActive);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
   CreateInputEvent(InputEventSource::kKeyboard);
   CreateInputEvent(InputEventSource::kStylus);
   CreateInputEvent(InputEventSource::kStylus);
   app_platform_input_metrics()->OnFiveMinutes();
   VerifyNoUkm();
-  ModifyInstance(/*app_id=*/"a", window(), kInactiveInstanceState);
+  ModifyInstance(kAndroidAppId, window(), kInactiveInstanceState);
 
   // Reset PlatformMetricsService to simulate the system reboot, and verify
   // AppKM is restored from the user pref and reported after 5 minutes after
@@ -2608,7 +2611,7 @@
   ResetAppPlatformMetricsService();
   VerifyNoUkm();
 
-  ModifyInstance(/*app_id=*/"a", window(), apps::InstanceState::kActive);
+  ModifyInstance(kAndroidAppId, window(), apps::InstanceState::kActive);
   CreateInputEvent(InputEventSource::kStylus);
 
   app_platform_input_metrics()->OnFiveMinutes();
@@ -2644,7 +2647,7 @@
   entries = test_ukm_recorder()->GetEntriesByName("ChromeOSApp.InputEvent");
   ASSERT_EQ(2U, entries.size());
 
-  ModifyInstance(/*app_id=*/"a", window(), kInactiveInstanceState);
+  ModifyInstance(kAndroidAppId, window(), kInactiveInstanceState);
 
   // Reset PlatformMetricsService to simulate the system reboot, and verify
   // only the new AppKM is reported.
@@ -2813,16 +2816,15 @@
       {syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY});
 
   // Launch a pre-installed app and verify the observer is notified.
-  const std::string& app_id = "a";
-  EXPECT_CALL(observer_, OnAppLaunched(app_id, AppType::kArc,
+  EXPECT_CALL(observer_, OnAppLaunched(kAndroidAppId, AppType::kArc,
                                        apps::LaunchSource::kFromChromeInternal))
       .Times(1);
 
   auto* const proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
   proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
   FakePublisher fake_arc_apps(proxy, AppType::kArc);
-  proxy->Launch(app_id, ui::EF_NONE, apps::LaunchSource::kFromChromeInternal,
-                nullptr);
+  proxy->Launch(kAndroidAppId, ui::EF_NONE,
+                apps::LaunchSource::kFromChromeInternal, nullptr);
   task_environment_.RunUntilIdle();
 }
 
@@ -2832,15 +2834,14 @@
       {syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY});
 
   // Uninstall a pre-installed app and verify the observer is notified.
-  const std::string& app_id = "a";
-  EXPECT_CALL(observer_, OnAppUninstalled(app_id, AppType::kArc,
+  EXPECT_CALL(observer_, OnAppUninstalled(kAndroidAppId, AppType::kArc,
                                           UninstallSource::kAppList))
       .Times(1);
 
   auto* const proxy = AppServiceProxyFactory::GetForProfile(profile());
   proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
   FakePublisher fake_arc_apps(proxy, AppType::kArc);
-  proxy->UninstallSilently(app_id, UninstallSource::kAppList);
+  proxy->UninstallSilently(kAndroidAppId, UninstallSource::kAppList);
   task_environment_.RunUntilIdle();
 }
 
@@ -2855,20 +2856,20 @@
 
   // Set the window active state and verify the observer is notified
   // with the appropriate running time with every notification.
-  const std::string& app_id = "a";
   const base::UnguessableToken& instance_id = base::UnguessableToken::Create();
-  ModifyInstance(instance_id, app_id, window.get(), kActiveInstanceState);
+  ModifyInstance(instance_id, kAndroidAppId, window.get(),
+                 kActiveInstanceState);
 
   // Usage metrics are recorded every 5 minutes and on window inactivation, so
   // we can expect two notifications with relevant usage times (5 minutes + 3
   // minutes) across a 8 minute usage window.
   Sequence s;
-  EXPECT_CALL(observer_,
-              OnAppUsage(app_id, AppType::kArc, instance_id, base::Minutes(5)))
+  EXPECT_CALL(observer_, OnAppUsage(kAndroidAppId, AppType::kArc, instance_id,
+                                    base::Minutes(5)))
       .Times(1)
       .InSequence(s);
-  EXPECT_CALL(observer_,
-              OnAppUsage(app_id, AppType::kArc, instance_id, base::Minutes(3)))
+  EXPECT_CALL(observer_, OnAppUsage(kAndroidAppId, AppType::kArc, instance_id,
+                                    base::Minutes(3)))
       .Times(1)
       .InSequence(s);
 
@@ -2877,7 +2878,8 @@
 
   // Set app inactive. This should also trigger the second notification with
   // usage time delta after the first one.
-  ModifyInstance(instance_id, app_id, window.get(), kInactiveInstanceState);
+  ModifyInstance(instance_id, kAndroidAppId, window.get(),
+                 kInactiveInstanceState);
 }
 
 TEST_P(AppPlatformMetricsObserverTest, ShouldNotNotifyUnregisteredObservers) {
@@ -2887,11 +2889,10 @@
 
   // Uninstall a pre-installed app and verify the unregistered observer
   // is not notified.
-  const std::string& app_id = "a";
-  EXPECT_CALL(observer_, OnAppUninstalled(app_id, AppType::kArc,
+  EXPECT_CALL(observer_, OnAppUninstalled(kAndroidAppId, AppType::kArc,
                                           UninstallSource::kAppList))
       .Times(0);
-  proxy->UninstallSilently(app_id, UninstallSource::kAppList);
+  proxy->UninstallSilently(kAndroidAppId, UninstallSource::kAppList);
   task_environment_.RunUntilIdle();
 }
 
@@ -3069,11 +3070,10 @@
   auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
   apps::AppRegistryCache& cache = proxy->AppRegistryCache();
   proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
-  const std::string app_id = "a";
 
   // Install an ARC app to test.
-  AddApp(cache, app_id, AppType::kArc, "com.google.A", Readiness::kReady,
-         InstallReason::kUser, InstallSource::kPlayStore,
+  AddApp(cache, kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
+         Readiness::kReady, InstallReason::kUser, InstallSource::kPlayStore,
          true /* should_notify_initialized */);
 
   // Simulate registering publishers for the launch interface to record metrics.
@@ -3088,25 +3088,25 @@
   base::RunLoop launch_event_run_loop;
   auto launch_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppLaunchEvent(event, app_id, AppType::kArc,
+        ValidateAppLaunchEvent(event, kAndroidAppId, AppType::kArc,
                                LaunchSource::kFromChromeInternal);
         launch_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       launch_record_callback);
 
-  EXPECT_CALL(fake_arc_apps,
-              Launch(app_id, ui::EF_NONE, LaunchSource::kFromChromeInternal, _))
+  EXPECT_CALL(fake_arc_apps, Launch(kAndroidAppId, ui::EF_NONE,
+                                    LaunchSource::kFromChromeInternal, _))
       .Times(1);
-  proxy->Launch(app_id, ui::EF_NONE, LaunchSource::kFromChromeInternal,
+  proxy->Launch(kAndroidAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
                 nullptr);
-  ModifyInstance(app_id, window.get(), apps::InstanceState::kStarted);
+  ModifyInstance(kAndroidAppId, window.get(), apps::InstanceState::kStarted);
   launch_event_run_loop.Run();
 
   // Mark app as kRunning otherwise active event will not trigger since the app
   // isn't considered to be running yet.
   ModifyInstance(
-      app_id, window.get(),
+      kAndroidAppId, window.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
                                        apps::InstanceState::kRunning));
 
@@ -3114,14 +3114,14 @@
   base::RunLoop active_event_run_loop;
   auto active_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kActive);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kActive);
         active_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       active_record_callback);
 
   ModifyInstance(
-      app_id, window.get(),
+      kAndroidAppId, window.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kActive |
                                        apps::InstanceState::kRunning));
   active_event_run_loop.Run();
@@ -3130,14 +3130,14 @@
   base::RunLoop hidden_event_run_loop;
   auto hidden_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kInactive);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kInactive);
         hidden_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       hidden_record_callback);
 
   ModifyInstance(
-      app_id, window.get(),
+      kAndroidAppId, window.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kHidden |
                                        apps::InstanceState::kRunning));
   hidden_event_run_loop.Run();
@@ -3146,14 +3146,14 @@
   base::RunLoop active_event_run_loop2;
   auto active_record_callback2 = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kActive);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kActive);
         active_event_run_loop2.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       active_record_callback2);
 
   ModifyInstance(
-      app_id, window.get(),
+      kAndroidAppId, window.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kActive |
                                        apps::InstanceState::kRunning));
   active_event_run_loop2.Run();
@@ -3162,13 +3162,13 @@
   base::RunLoop closed_event_run_loop;
   auto closed_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kClosed);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kClosed);
         closed_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       closed_record_callback);
 
-  ModifyInstance(app_id, window.get(), apps::InstanceState::kDestroyed);
+  ModifyInstance(kAndroidAppId, window.get(), apps::InstanceState::kDestroyed);
   closed_event_run_loop.Run();
 }
 
@@ -3178,11 +3178,10 @@
   auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile());
   apps::AppRegistryCache& cache = proxy->AppRegistryCache();
   proxy->SetAppPlatformMetricsServiceForTesting(GetAppPlatformMetricsService());
-  const std::string app_id = "a";
 
   // Install an ARC app to test.
-  AddApp(cache, app_id, AppType::kArc, "com.google.A", Readiness::kReady,
-         InstallReason::kUser, InstallSource::kPlayStore,
+  AddApp(cache, kAndroidAppId, AppType::kArc, kAndroidAppPublisherId,
+         Readiness::kReady, InstallReason::kUser, InstallSource::kPlayStore,
          true /* should_notify_initialized */);
 
   // Simulate registering publishers for the launch interface to record metrics.
@@ -3199,30 +3198,30 @@
   base::RunLoop launch_event_run_loop;
   auto launch_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppLaunchEvent(event, app_id, AppType::kArc,
+        ValidateAppLaunchEvent(event, kAndroidAppId, AppType::kArc,
                                LaunchSource::kFromChromeInternal);
         launch_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       launch_record_callback);
 
-  EXPECT_CALL(fake_arc_apps,
-              Launch(app_id, ui::EF_NONE, LaunchSource::kFromChromeInternal, _))
+  EXPECT_CALL(fake_arc_apps, Launch(kAndroidAppId, ui::EF_NONE,
+                                    LaunchSource::kFromChromeInternal, _))
       .Times(1);
-  proxy->Launch(app_id, ui::EF_NONE, LaunchSource::kFromChromeInternal,
+  proxy->Launch(kAndroidAppId, ui::EF_NONE, LaunchSource::kFromChromeInternal,
                 nullptr);
-  ModifyInstance(app_id, window1.get(), apps::InstanceState::kStarted);
-  ModifyInstance(app_id, window2.get(), apps::InstanceState::kStarted);
+  ModifyInstance(kAndroidAppId, window1.get(), apps::InstanceState::kStarted);
+  ModifyInstance(kAndroidAppId, window2.get(), apps::InstanceState::kStarted);
   launch_event_run_loop.Run();
 
   // Mark app as kRunning otherwise active event will not trigger since the app
   // isn't considered to be running yet.
   ModifyInstance(
-      app_id, window1.get(),
+      kAndroidAppId, window1.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
                                        apps::InstanceState::kRunning));
   ModifyInstance(
-      app_id, window2.get(),
+      kAndroidAppId, window2.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kStarted |
                                        apps::InstanceState::kRunning));
 
@@ -3230,14 +3229,14 @@
   base::RunLoop active_event_run_loop;
   auto active_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kActive);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kActive);
         active_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       active_record_callback);
 
   ModifyInstance(
-      app_id, window1.get(),
+      kAndroidAppId, window1.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kActive |
                                        apps::InstanceState::kRunning));
   active_event_run_loop.Run();
@@ -3250,7 +3249,7 @@
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       active_record_callback2);
   ModifyInstance(
-      app_id, window2.get(),
+      kAndroidAppId, window2.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kActive |
                                        apps::InstanceState::kRunning));
 
@@ -3264,7 +3263,7 @@
       hidden_record_callback);
 
   ModifyInstance(
-      app_id, window1.get(),
+      kAndroidAppId, window1.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kHidden |
                                        apps::InstanceState::kRunning));
 
@@ -3272,14 +3271,14 @@
   base::RunLoop inactive_event_run_loop;
   auto inactive_record_callback = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kInactive);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kInactive);
         inactive_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       inactive_record_callback);
 
   ModifyInstance(
-      app_id, window2.get(),
+      kAndroidAppId, window2.get(),
       static_cast<apps::InstanceState>(apps::InstanceState::kVisible |
                                        apps::InstanceState::kRunning));
   inactive_event_run_loop.Run();
@@ -3291,19 +3290,19 @@
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       closed_record_callback);
-  ModifyInstance(app_id, window1.get(), apps::InstanceState::kDestroyed);
+  ModifyInstance(kAndroidAppId, window1.get(), apps::InstanceState::kDestroyed);
 
   // Validate closed event is recorded when both instances are closed.
   base::RunLoop closed_event_run_loop;
   auto closed_record_callback2 = base::BindLambdaForTesting(
       [&, this](const metrics::structured::Event& event) {
-        ValidateAppStateEvent(event, app_id, AppStateChange::kClosed);
+        ValidateAppStateEvent(event, kAndroidAppId, AppStateChange::kClosed);
         closed_event_run_loop.Quit();
       });
   test_structured_metrics_provider()->SetOnEventsRecordClosure(
       closed_record_callback2);
 
-  ModifyInstance(app_id, window2.get(), apps::InstanceState::kDestroyed);
+  ModifyInstance(kAndroidAppId, window2.get(), apps::InstanceState::kDestroyed);
   closed_event_run_loop.Run();
 }
 
diff --git a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
index fbc28e1..d0fadbe 100644
--- a/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
+++ b/chrome/browser/ash/input_method/native_input_method_engine_observer.cc
@@ -1011,6 +1011,7 @@
     const std::string& engine_id,
     const ui::KeyEvent& event,
     TextInputMethod::KeyEventDoneCallback callback) {
+  bool should_supress_auto_repeat = false;
   if (assistive_suggester_->IsAssistiveFeatureEnabled()) {
     switch (assistive_suggester_->OnKeyEvent(event)) {
       case AssistiveSuggesterKeyResult::kHandled:
@@ -1018,9 +1019,8 @@
             ui::ime::KeyEventHandledState::kHandledByAssistiveSuggester);
         return;
       case AssistiveSuggesterKeyResult::kNotHandledSuppressAutoRepeat:
-        std::move(callback).Run(
-            ui::ime::KeyEventHandledState::kNotHandledSuppressAutoRepeat);
-        return;
+        should_supress_auto_repeat = true;
+        break;
       case AssistiveSuggesterKeyResult::kNotHandled:
         break;
     }
@@ -1068,16 +1068,18 @@
         key_event->key = mojom::DomKey::NewCodepoint(
             Utf16ToCodepoint(character_composer_.composed_character()));
       }
-
       auto process_key_event_callback = base::BindOnce(
           [](TextInputMethod::KeyEventDoneCallback original_callback,
-             mojom::KeyEventResult result) {
+             bool should_supress_auto_repeat, mojom::KeyEventResult result) {
             std::move(original_callback)
                 .Run((result == mojom::KeyEventResult::kConsumedByIme)
                          ? ui::ime::KeyEventHandledState::kHandledByIME
-                         : ui::ime::KeyEventHandledState::kNotHandled);
+                         : (should_supress_auto_repeat
+                                ? ui::ime::KeyEventHandledState::
+                                      kNotHandledSuppressAutoRepeat
+                                : ui::ime::KeyEventHandledState::kNotHandled));
           },
-          std::move(callback));
+          std::move(callback), should_supress_auto_repeat);
 
       input_method_->ProcessKeyEvent(std::move(key_event),
                                      std::move(process_key_event_callback));
diff --git a/chrome/browser/ash/locale_change_guard_unittest.cc b/chrome/browser/ash/locale_change_guard_unittest.cc
index 172bd86..c53a176a1 100644
--- a/chrome/browser/ash/locale_change_guard_unittest.cc
+++ b/chrome/browser/ash/locale_change_guard_unittest.cc
@@ -25,10 +25,13 @@
     "ar",   // Arabic
     "as",   // Assamese
     "ast",  // Asturian
+    "ay",   // Aymara
     "az",   // Azerbaijani
     "be",   // Belarusian
     "bg",   // Bulgarian
     "bh",   // Bihari
+    "bho",  // Bhojpuri
+    "bm",   // Bambara
     "bn",   // Bengali
     "br",   // Breton
     "bs",   // Bosnian
@@ -40,6 +43,8 @@
     "cs",   // Czech
     "cy",   // Welsh
     "da",   // Danish
+    "doi",  // Dogri
+    "dv",   // Dhivehi
     "ee",   // Ewe
     "el",   // Greek
     "eo",   // Esperanto
@@ -68,6 +73,7 @@
     "ia",   // Interlingua
     "id",   // Indonesian
     "ig",   // Igbo
+    "ilo",  // Ilocano
     "is",   // Icelandic
     "ja",   // Japanese
     "jv",   // Javanese
@@ -86,7 +92,9 @@
     "ln",   // Lingala
     "lo",   // Laothian
     "lt",   // Lithuanian
+    "lus",  // Mizo
     "lv",   // Latvian
+    "mai",  // Maithili
     "mg",   // Malagasy
     "mi",   // Maori
     "mk",   // Macedonian
@@ -116,6 +124,7 @@
     "ro",   // Romanian
     "ru",   // Russian
     "rw",   // Kinyarwanda
+    "sa",   // Sanskrit
     "sd",   // Sindhi
     "sh",   // Serbo-Croatian
     "si",   // Sinhalese
@@ -139,6 +148,7 @@
     "tn",   // Tswana
     "to",   // Tonga
     "tr",   // Turkish
+    "ts",   // Tsonga
     "tt",   // Tatar
     "tw",   // Twi
     "ug",   // Uighur
diff --git a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
index 1661486ad..e64ed65d 100644
--- a/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
+++ b/chrome/browser/ash/login/oobe_quick_start/target_device_bootstrap_controller.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_ASH_LOGIN_OOBE_QUICK_START_TARGET_DEVICE_BOOTSTRAP_CONTROLLER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/functional/callback_forward.h"
@@ -63,7 +64,7 @@
     Step step = Step::NONE;
     Payload payload;
     std::string ssid;
-    std::string password;
+    absl::optional<std::string> password;
     std::string fido_email;
   };
 
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
index b500e26..1bb1616 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/PersonalDataManager.java
@@ -1423,7 +1423,7 @@
     /**
      * @return Whether the Autofill feature for payment methods mandatory reauth is enabled.
      */
-    public static boolean isAutofillPaymentMethodsMandatoryReauthEnabled() {
+    public static boolean isPaymentMethodsMandatoryReauthEnabled() {
         return getPrefService().getBoolean(Pref.AUTOFILL_PAYMENT_METHODS_MANDATORY_REAUTH);
     }
 
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index 49d1b24..2c0f2f9 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -20,7 +20,6 @@
 #include "base/functional/callback_helpers.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/one_shot_event.h"
 #include "base/strings/utf_string_conversions.h"
@@ -298,9 +297,6 @@
   // outlives the profile storage.
   profile_storage_->AddObserver(this);
 
-  UMA_HISTOGRAM_BOOLEAN("BackgroundMode.OnStartup.IsBackgroundModePrefEnabled",
-                        IsBackgroundModePrefEnabled());
-
   // Listen for the background mode preference changing.
   if (g_browser_process->local_state()) {  // Skip for unit tests
     pref_registrar_.Init(g_browser_process->local_state());
diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DangerousDownloadDialogBridge.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DangerousDownloadDialogBridge.java
index 2d27dfa..e825806f 100644
--- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DangerousDownloadDialogBridge.java
+++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/DangerousDownloadDialogBridge.java
@@ -9,6 +9,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.browser.download.dialogs.DangerousDownloadDialog;
+import org.chromium.chrome.browser.download.interstitial.NewDownloadTab;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
 
@@ -45,7 +46,7 @@
             long totalBytes, int iconId) {
         Activity activity = windowAndroid.getActivity().get();
         if (activity == null) {
-            onCancel(guid);
+            onCancel(guid, windowAndroid);
             return;
         }
 
@@ -55,7 +56,7 @@
                     if (accepted) {
                         onAccepted(guid);
                     } else {
-                        onCancel(guid);
+                        onCancel(guid, windowAndroid);
                     }
                 });
     }
@@ -69,9 +70,10 @@
         DangerousDownloadDialogBridgeJni.get().accepted(mNativeDangerousDownloadDialogBridge, guid);
     }
 
-    private void onCancel(String guid) {
+    private void onCancel(String guid, WindowAndroid windowAndroid) {
         DangerousDownloadDialogBridgeJni.get().cancelled(
                 mNativeDangerousDownloadDialogBridge, guid);
+        NewDownloadTab.closeExistingNewDownloadTab(windowAndroid);
     }
 
     @NativeMethods
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
index 5204f04..265dfda 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc
@@ -894,7 +894,7 @@
   if (reauth_succeeded && browser_context()) {
     PrefService* prefs =
         Profile::FromBrowserContext(browser_context())->GetPrefs();
-    autofill::prefs::SetAutofillPaymentMethodsMandatoryReauth(
+    autofill::prefs::SetPaymentMethodsMandatoryReauthEnabled(
         prefs, !prefs->GetBoolean(
                    autofill::prefs::kAutofillPaymentMethodsMandatoryReauth));
     base::RecordAction(base::UserMetricsAction(
@@ -922,7 +922,7 @@
   if (!personal_data_manager || !personal_data_manager->IsDataLoaded()) {
     return RespondNow(Error(kErrorDataUnavailable));
   }
-  if (personal_data_manager->IsAutofillPaymentMethodsMandatoryReauthEnabled()) {
+  if (personal_data_manager->IsPaymentMethodsMandatoryReauthEnabled()) {
     // If `device_authenticator` is not available, then don't do anything.
     scoped_refptr<device_reauth::DeviceAuthenticator> device_authenticator =
         client->GetDeviceAuthenticator();
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
index 94ad6c1..58a8269 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc
@@ -229,8 +229,7 @@
           base::MakeRefCounted<device_reauth::MockDeviceAuthenticator>();
   TestChromeAutofillClient* test_client = autofill_client();
 
-  test_personal_data_manager_->SetAutofillPaymentMethodsMandatoryReauthEnabled(
-      true);
+  test_personal_data_manager_->SetPaymentMethodsMandatoryReauthEnabled(true);
   test_client->SetPersonalDataManger(test_personal_data_manager_.get());
   test_client->SetDeviceAuthenticator(mock_device_authenticator);
 
diff --git a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
index 03bf484e..fcb04e7 100644
--- a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
+++ b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.cc
@@ -167,24 +167,29 @@
 ~BrailleDisplayPrivateWriteDotsFunction() {
 }
 
-bool BrailleDisplayPrivateWriteDotsFunction::Prepare() {
+ExtensionFunction::ResponseAction
+BrailleDisplayPrivateWriteDotsFunction::Run() {
   params_ = WriteDots::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params_);
   EXTENSION_FUNCTION_VALIDATE(
       params_->cells.size() >=
       static_cast<size_t>(params_->columns * params_->rows));
-  return true;
+
+  bool rv = content::GetIOThreadTaskRunner({})->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(&BrailleDisplayPrivateWriteDotsFunction::WriteDotsOnIO,
+                     this),
+      base::BindOnce(&BrailleDisplayPrivateWriteDotsFunction::Respond, this,
+                     NoArguments()));
+  DCHECK(rv);
+  return RespondLater();
 }
 
-void BrailleDisplayPrivateWriteDotsFunction::Work() {
+void BrailleDisplayPrivateWriteDotsFunction::WriteDotsOnIO() {
   BrailleController::GetInstance()->WriteDots(params_->cells, params_->columns,
                                               params_->rows);
 }
 
-bool BrailleDisplayPrivateWriteDotsFunction::Respond() {
-  return true;
-}
-
 ExtensionFunction::ResponseAction
 BrailleDisplayPrivateUpdateBluetoothBrailleDisplayAddressFunction::Run() {
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h
index 46fca9d..2776910 100644
--- a/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h
+++ b/chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h
@@ -92,7 +92,7 @@
   void ReplyWithState(base::Value::Dict state);
 };
 
-class BrailleDisplayPrivateWriteDotsFunction : public AsyncApiFunction {
+class BrailleDisplayPrivateWriteDotsFunction : public ExtensionFunction {
   DECLARE_EXTENSION_FUNCTION("brailleDisplayPrivate.writeDots",
                              BRAILLEDISPLAYPRIVATE_WRITEDOTS)
  public:
@@ -100,9 +100,9 @@
 
  protected:
   ~BrailleDisplayPrivateWriteDotsFunction() override;
-  bool Prepare() override;
-  void Work() override;
-  bool Respond() override;
+  ResponseAction Run() override;
+
+  void WriteDotsOnIO();
 
  private:
   absl::optional<braille_display_private::WriteDots::Params> params_;
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
index ae8569c..4959e8e 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
@@ -1067,6 +1067,8 @@
 
   tester.ExpectBucketCount(kIsLargeRegexHistogram, true, kNumLargeRegex);
   tester.ExpectBucketCount(kIsLargeRegexHistogram, false, kNumSmallRegex);
+  tester.ExpectTotalCount(kRegexRuleSizeHistogram,
+                          kNumSmallRegex + kNumLargeRegex);
 
   // TODO(crbug.com/879355): CrxInstaller reloads the extension after moving it,
   // which causes it to lose the install warning. This should be fixed.
diff --git a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
index 481b29a..23e812a 100644
--- a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
+++ b/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc
@@ -45,7 +45,10 @@
     extension_misc::kGuestModeTestExtensionId,
     extension_misc::kSelectToSpeakExtensionId,
     extension_misc::kSwitchAccessExtensionId,
-#endif
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+    extension_misc::kEmbeddedA11yHelperExtensionId,
+    extension_misc::kChromeVoxHelperExtensionId,
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 #if BUILDFLAG(IS_CHROMEOS)
     extension_misc::kContactCenterInsightsExtensionId,
     extension_misc::kDeskApiExtensionId,
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinatorTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinatorTest.java
index dfca2ac..82fcf280 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinatorTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementCoordinatorTest.java
@@ -12,6 +12,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -21,12 +22,15 @@
 import org.chromium.chrome.browser.feed.FeedServiceBridge;
 import org.chromium.chrome.browser.feed.FeedServiceBridgeJni;
 import org.chromium.chrome.browser.feed.StreamKind;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.ui.base.TestActivity;
 
 /**
  * Tests {@link FeedManagementCoordinator}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
+@Features.EnableFeatures(ChromeFeatureList.FEED_FOLLOW_UI_UPDATE)
 public class FeedManagementCoordinatorTest {
     private TestActivity mActivity;
     private FeedManagementCoordinator mFeedManagementCoordinator;
@@ -37,6 +41,9 @@
     public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
             new ActivityScenarioRule<>(TestActivity.class);
 
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
     @Mock
     private FeedServiceBridge.Natives mFeedServiceBridgeJniMock;
 
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediator.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediator.java
index 9abc3c9..769ade5 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediator.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediator.java
@@ -78,8 +78,11 @@
                 R.string.feed_manage_activity_description, this::handleActivityClick);
         mModelList.add(new ModelListAdapter.ListItem(
                 FeedManagementItemProperties.DEFAULT_ITEM_TYPE, activityModel));
-        PropertyModel interestsModel = generateListItem(R.string.feed_manage_interests,
-                R.string.feed_manage_interests_description, this::handleInterestsClick);
+        int descResource = ChromeFeatureList.isEnabled(ChromeFeatureList.FEED_FOLLOW_UI_UPDATE)
+                ? R.string.feed_manage_interests_description_ui_update
+                : R.string.feed_manage_interests_description;
+        PropertyModel interestsModel = generateListItem(
+                R.string.feed_manage_interests, descResource, this::handleInterestsClick);
         mModelList.add(new ModelListAdapter.ListItem(
                 FeedManagementItemProperties.DEFAULT_ITEM_TYPE, interestsModel));
         PropertyModel hiddenModel = generateListItem(R.string.feed_manage_hidden,
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediatorTest.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediatorTest.java
index 70c418d..a294b54 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediatorTest.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/feedmanagement/FeedManagementMediatorTest.java
@@ -15,6 +15,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -29,12 +30,14 @@
 import org.chromium.chrome.browser.feed.StreamKind;
 import org.chromium.chrome.browser.feed.v2.FeedUserActionType;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 
 /**
  * Tests {@link FeedManagementMediator}.
  */
 @RunWith(BaseRobolectricTestRunner.class)
+@Features.EnableFeatures(ChromeFeatureList.FEED_FOLLOW_UI_UPDATE)
 public class FeedManagementMediatorTest {
     private static final @StreamKind int TEST_STREAM_KIND = StreamKind.FOR_YOU;
     private Activity mActivity;
@@ -45,6 +48,9 @@
     @Rule
     public JniMocker mocker = new JniMocker();
 
+    @Rule
+    public TestRule mProcessor = new Features.JUnitProcessor();
+
     @Mock
     private FeedServiceBridge.Natives mFeedServiceBridgeJniMock;
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b57e8ba..40e66d1 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3207,7 +3207,7 @@
   {
     "name": "enable-shortcut-customization",
     "owners": [ "jimmyxgong", "zentaro", "cros-peripherals@google.com"],
-    "expiry_milestone": 120
+    "expiry_milestone": 130
   },
   {
     "name": "enable-shortcut-customization-app",
@@ -4516,7 +4516,7 @@
     "name": "improved-keyboard-shortcuts",
     "owners": [ "zentaro@google.com", "jimmyxgong@google.com",
                 "cros-peripherals@google.com"],
-    "expiry_milestone": 120
+    "expiry_milestone": 130
   },
   {
     "name": "in-product-help-demo-mode-choice",
@@ -5913,7 +5913,7 @@
   {
     "name": "os-feedback-jelly",
     "owners": [
-      "//ash/webui/os_feedback_ui/OWNERS"
+      "ashleydp", "cros-peripherals@google.com"
     ],
     "expiry_milestone": 122
   },
@@ -6199,6 +6199,11 @@
     "expiry_milestone": 120
   },
   {
+    "name": "popover-attribute",
+    "owners": [ "masonf" ],
+    "expiry_milestone": 120
+  },
+  {
     "name": "power-bookmark-backend",
     "owners": [ "wylieb", "skym" ],
     "expiry_milestone": 118
@@ -6879,9 +6884,7 @@
   },
   {
     "name": "shortcut-customization-jelly",
-    "owners": [
-      "//ash/webui/shortcut_customization_ui/OWNERS"
-    ],
+    "owners": [ "jimmyxgong", "zentaro", "cros-peripherals@google.com" ],
     "expiry_milestone": 122
   },
   {
@@ -6899,6 +6902,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "show-force-respect-ui-gains-toggle",
+    "owners": ["eddyhsu"],
+    "expiry_milestone": 130
+  },
+  {
     "name": "show-inactive-tabs-count",
     "owners": [ "lpromero@google.com", "bling-flags@google.com" ],
     "expiry_milestone": 120
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 596bcff..feaee03 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1592,6 +1592,12 @@
     "these events can cause breakage on some sites that are still reliant on "
     "these deprecated features.";
 
+const char kHTMLPopoverAttributeName[] = "Enable the popover API";
+const char kHTMLPopoverAttributeDescription[] =
+    "The popover API is a set of features built into the Web to enable the "
+    "construction of popovers. It is accessed primarily via the `popover` "
+    "attribute.";
+
 const char kFillOnAccountSelectName[] = "Fill passwords on account selection";
 const char kFillOnAccountSelectDescription[] =
     "Filling of passwords when an account is explicitly selected by the user "
@@ -6729,6 +6735,11 @@
 const char kIgnoreUiGainsDescription[] =
     "Ignore UI Gains in system mic gain setting";
 
+const char kShowForceRespectUiGainsToggleName[] =
+    "Enable a setting toggle to force respect UI gains";
+const char kShowForceRespectUiGainsToggleDescription[] =
+    "Enable a setting toggle to force respect UI gains.";
+
 const char kCrosPrivacyHubName[] = "Enable ChromeOS Privacy Hub";
 const char kCrosPrivacyHubDescription[] = "Enables ChromeOS Privacy Hub.";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 82a6ef66..170404d6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -890,6 +890,9 @@
 extern const char kMutationEventsName[];
 extern const char kMutationEventsDescription[];
 
+extern const char kHTMLPopoverAttributeName[];
+extern const char kHTMLPopoverAttributeDescription[];
+
 extern const char kFillOnAccountSelectName[];
 extern const char kFillOnAccountSelectDescription[];
 
@@ -3916,6 +3919,9 @@
 
 extern const char kIgnoreUiGainsName[];
 extern const char kIgnoreUiGainsDescription[];
+
+extern const char kShowForceRespectUiGainsToggleName[];
+extern const char kShowForceRespectUiGainsToggleDescription[];
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if defined(ARCH_CPU_X86_FAMILY) && BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 0e973c6..1e1f7e8 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -128,6 +128,7 @@
     &feed::kCormorant,
     &feed::kFeedBackToTop,
     &feed::kFeedDynamicColors,
+    &feed::kFeedFollowUiUpdate,
     &feed::kFeedHeaderStickToTop,
     &feed::kFeedImageMemoryCacheSizePercentage,
     &feed::kFeedLoadingPlaceholder,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index e856e1e..8e15cf6 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -245,6 +245,7 @@
     public static final String FEATURE_NOTIFICATION_GUIDE = "FeatureNotificationGuide";
     public static final String FEED_BACK_TO_TOP = "FeedBackToTop";
     public static final String FEED_DYNAMIC_COLORS = "FeedDynamicColors";
+    public static final String FEED_FOLLOW_UI_UPDATE = "FeedFollowUiUpdate";
     public static final String FEED_HEADER_STICK_TO_TOP = "FeedHeaderStickToTop";
     public static final String FEED_IMAGE_MEMORY_CACHE_SIZE_PERCENTAGE =
             "FeedImageMemoryCacheSizePercentage";
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 6031260..15d0e2a 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -158,6 +158,7 @@
 #include "chrome/browser/ssl/https_first_mode_settings_tracker.h"
 #include "chrome/browser/ssl/sct_reporting_service_factory.h"
 #include "chrome/browser/ssl/stateful_ssl_host_state_delegate_factory.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_factory.h"
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
 #include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
@@ -994,6 +995,7 @@
 #if BUILDFLAG(ENABLE_SPELLCHECK)
   SpellcheckServiceFactory::GetInstance();
 #endif
+  StorageAccessAPIServiceFactory::GetInstance();
 #if !BUILDFLAG(IS_ANDROID)
   StorageNotificationServiceFactory::GetInstance();
 #endif
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 6b25547..8a68356 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -87,6 +87,10 @@
     ]
   }
 
+  if (is_chromeos_lacros && enable_extensions) {
+    public_deps += [ "accessibility:extension_resources" ]
+  }
+
   if (is_win || is_mac || is_linux || is_chromeos_ash) {
     public_deps += [ "connectors_internals:resources" ]
   }
diff --git a/chrome/browser/resources/accessibility/BUILD.gn b/chrome/browser/resources/accessibility/BUILD.gn
index c4b201a..cae2786 100644
--- a/chrome/browser/resources/accessibility/BUILD.gn
+++ b/chrome/browser/resources/accessibility/BUILD.gn
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 import("//ui/webui/resources/tools/build_webui.gni")
+if (is_chromeos_lacros) {
+  import("//chrome/browser/resources/chromeos/accessibility/manifest.gni")
+}
 
 build_webui("build") {
   grd_prefix = "accessibility"
@@ -17,3 +20,36 @@
   ts_definitions = [ "//tools/typescript/definitions/chrome_send.d.ts" ]
   ts_deps = [ "//ui/webui/resources/js:build_ts" ]
 }
+
+if (is_chromeos_lacros) {
+  group("extension_resources") {
+    deps = [
+      ":chromevox_helper_manifest",
+      ":embedded_a11y_helper_manifest",
+      "chromevox_helper:build",
+      "embedded_a11y_helper:build",
+    ]
+  }
+
+  accessibility_out_dir = "$root_out_dir/resources/accessibility/"
+
+  manifest("embedded_a11y_helper_manifest") {
+    input_file = "embedded_a11y_helper_manifest.json.jinja2"
+    output_file = "$accessibility_out_dir/embedded_a11y_helper_manifest.json"
+
+    # The key is generated from packing a local Chrome extension as a .crx
+    # and uniquely maps to the extension ID.
+    # Providing the key in manifest.json will keep the extension ID stable.
+    key = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDGte0H3Gp0HEQdkKbucEndUNKd3BMTzeeX8BV03Ul39LduOlwerEHAQUs1qFXJaG9m0KZ5+KfPdwr/J8OyPpjV0tl4cc8dS6Rz7OL9gP+kUS5M4Yf7Yw7tv0iWEzC6Ro8An7yjCj/fNNJ64K2RUGpTIvY2BJX1tJU6LrO20k9Ns7KPZbjOu+AXO4XOIrkllNpv7Za8kPCvYAJN0g3RRxE957Mu8QI9m4QN3Utj6zCUeYmqXLO3ddNqdnPqDyVMTMMyqtGZhoMRmp46eKqOuCQ3nTmv5tBlY9dfAndXuzohSt8xTucEC/bZCQdm1zV3KhW9pepFyQ93D6Md6mwC+p8BAgMBAAECggEAKRIKA0Ft6UC8XWX78rVXtvwRBBqsAMBimTdAoA5qtHXqgchjKN3OmzeDnXwE+yNwPw5qEjSwLoO6zKEB5DHm8e7qDuhq9/Gd9G3mPqIudF5pFO/aZL2FJOOP06D0xdN2lBjv6rzgFbi7tjUA+tNlnnhP7jtFaWrhGwzzDRrr1DGuMkpn2VYQzzCNR6aT50IOLTqnTIcVehRG79hiDnriE+FnKnlDRABejVLnp/7hLbgwWVPrzbA7qedmwGSyRzLeSc4+68mP6jiqRI0be30ef0BQMHWmM5fmMog+44v6kbFp8aw1ncqrQ8MV0Dk0ZEFoGi4UiOH3LcaVGdxVb3oYHwKBgQDo+c4HrpuiFDYaJt913oH6+4ZGXoFhzNEn+m00vfZ9yWzCmdg2JNby6N3eSmiDGEXdEqC8FcSe5+6TvoiaB9t/5K58DkIda9hu39CsRFx42cHLzl/fhi9ulqRwCtxm7ORAJXKKml1HU4zsdB7DLGOtpnbJD+vOUxXIrjWa7grPawKBgQDaWTny6zDS1rR8KHX38FYZSl/zv4bIuR3YKNOlkW5Bln1lU1QqLocdcIeMauiu5CtRxaBCLX1IPPYaSgVAOtRS2v4RkA73CyukoSJKMpJJ3iYhM5TRwlWNmYk2/xo5JOKIHrDli74ulR4IYhpcgu211ys5FLScRKqyLpcSuPaCQwKBgDrO69n2kmScU3fQfjHDo+3V1fQnYONuDxHXXf+lP/ZKhQCuUux/6h5Q98hn4e/Njb0bOaWgnQw7oXZ77wFqoFatQ/oeo0PH+E8sggEqFGmRFiE7C+phddGlhwadRztX6tniWgJwqsEZbwY03ZsItnScA3oHYc+oG2SXzQI5ulLXAoGABTeomNNh4UfcHDNjTj9h70zFqi9jaxxxSB8Bn60v80VDTc6F7lQaSmvL8WEEmlk9Z8DPKYWovrcmMk6efYBqXmh5NbY5hVGxA0gOZ7N5p2Y1hwmSfsij/rQygSvrVn45I4OIOpPyQW6yvwSJGLjQMU4o7D5AapOBeKyAU6Qzz70CgYBYqsvHr5H26kREs9a0sIk542chuDHsDxtg99r2YplMl+tNygJ3dDOIDNFJGMj3x8LVo5awXxE4vfaNDHl0NfHZJM68bUB09ytLdBlQXCn6ehz8n7oaZ/jhHX7ZYXp/PX/spSFFIHMTM9IX6rzH6OVURMxCLs5u22Ycy+pd9ToCnw=="
+  }
+
+  manifest("chromevox_helper_manifest") {
+    input_file = "chromevox_helper_manifest.json.jinja2"
+    output_file = "$accessibility_out_dir/chromevox_helper_manifest.json"
+
+    # The key is generated from packing a local Chrome extension as a .crx
+    # and uniquely maps to the extension ID.
+    # Providing the key in manifest.json will keep the extension ID stable.
+    key = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDG3J14i99Qtu/QZ5zsJl4Z7EHaG+ndaVIajg91rxleFu3YpuJqS3u3aOE/FCjIOUp6uqn/e5l+8As3RK6NBNiQzGVXZn/5orW/iaRUk2kflUNDCrBjdgvgE5TGgjtfCuHJITc/L184ss2GKBkWrcUWw0ZR9YSTzZ1AeEfFfLT0e0Ic3u4BnM82gAJKpDV6dmvR9rcWs8yhume6kXB7hLMY9pAtBOKlx8vhHo0QAiDHK/9hxDyNIHcgKsCN4jpUn2odIBCA+dfHPlHshztqAW6FkuC91dUP5PH3QS0Z+ZB8uCm0viEXa/twH3KEE+BSkDtThTNX1lDTAT+rilQlSECpAgMBAAECggEADRma6QZD0Ygfj6nHsVnMsKX655CXrR2DSlsJ4y2D/QoDJJbHywtKbNhkoDhmgFwfzyptSPZ+M9nBz2P/oKwNTSqd0W5f51TD5sh9MhIc5nb5fZjeakrsONhpYFNTcG0h1xNQVaH8AprqxZfuISuU1G1MPFlxYKA2p/jUd4rBy+oWBHZrxaThVUdIFENkkDQQsGaCZlegF+B4ma0Mym1i9tqKrCDJuFiCXl6tBQ8iQSW/z7+ekpanVLtAdeb98W/0DDsHB9VFLgfkEynNh8PGro+TrSjmBZRQIxZu9aTFjL1bMbj2pf3oU0r5RqRRRH9Q1w84I9vHxfXkhoqL9yh5gQKBgQDhs+tnYNQAg2ecz9sYvsOA8GQMutfT8HeFslQDsWhs7GDkMbkW5e+HKbONtZKOTlYd6hUh7R0X7aDvaaJWhTfVyesEp65a/V7t5SrrVGE24po+x+VWDT9vgvw3EcGifEgq5kVxvY5Gco2Iw/tEwyQf29ryE7sawcAVhGcEXIO96QKBgQDhjlOtSS3KpKZRQbbBWc6G0cnDpJmyMCr5B9xEz1Fs7OJuI70uRzxkvni/AIhPYCciiAJ04KnDmBReK56600emGm0FoYIZZ1x8gBi3s7VUiervkQLxCi/hko0X06ro8irUTPywELR92ORRKFHk2PXg8t2rHxR87Yh4Ws+dENT0wQKBgH+1yZ6+SFdeA1PUvTmcP42G+GXHl3ZCG69X/3fze44elpiolf2h784AR6wIsLu7EbltibWfsJWojSbLE+N3D+f1j/kbVGoB8iQURxvPrA5dXD/n5hmx91IGwOX8Mx/YyREVKPg59PZhvTpFArme3aL1SUcu6PEz7pd+SrhKDPV5AoGBAJOXkmGdtP0Y6EvbeD7lCSNnO2Nt3bSfmD8ESbos/tcL2s6/TBdwPbCeglxZeNiXzoPmA9V+/Rcj+I/2cxNFFWk3eYdpu7vryrUdDQ+H5GvBI55HgWlAhTxRrUmeFAQCEsyA5AhBphmDR0Nj4tKgtaPQyAQlfj6RH+/BXIEmdnMBAoGBANlUyyA0mcyX/FgcYB27ul2dpcicrqjIQi3lk4LDzdXJp5Qm/P+PUl7PbLrf1UizIv4q/89uyp3zuX3sFw6ISdYExC5Xh4mC4naSEnysQgA/c/329kXdSjCNVHEKkm3ngoDTh1ZT+kU4x/PAEBIrW5Q1lQguYLCBMdsLivuvqnh8"
+  }
+}
diff --git a/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn b/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn
new file mode 100644
index 0000000..3d142e3
--- /dev/null
+++ b/chrome/browser/resources/accessibility/chromevox_helper/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//chrome/common/features.gni")
+import("//tools/typescript/ts_library.gni")
+
+out_dir = "$root_out_dir/resources/accessibility/chromevox_helper"
+tsc_output_dir = "$target_gen_dir/tsc"
+
+group("build") {
+  deps = [ ":chromevox_helper_copied_files" ]
+}
+
+ts_library("build_ts") {
+  in_files = [ "content.ts" ]
+  out_dir = tsc_output_dir
+  definitions = [
+    "//tools/typescript/definitions/runtime.d.ts",
+    "//tools/typescript/definitions/tabs.d.ts",
+  ]
+}
+
+copy("chromevox_helper_copied_files") {
+  sources = [
+    "$tsc_output_dir/content.js",
+    "../../chromeos/accessibility/chromevox/injected/cvox_gdocs_script.js",
+  ]
+  outputs = [ "$out_dir/{{source_file_part}}" ]
+  deps = [ ":build_ts" ]
+}
diff --git a/chrome/browser/resources/accessibility/chromevox_helper/DIR_METADATA b/chrome/browser/resources/accessibility/chromevox_helper/DIR_METADATA
new file mode 100644
index 0000000..a0b6aa9a
--- /dev/null
+++ b/chrome/browser/resources/accessibility/chromevox_helper/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//ash/accessibility/COMMON_METADATA"
diff --git a/chrome/browser/resources/accessibility/chromevox_helper/content.ts b/chrome/browser/resources/accessibility/chromevox_helper/content.ts
new file mode 100644
index 0000000..284d8ae
--- /dev/null
+++ b/chrome/browser/resources/accessibility/chromevox_helper/content.ts
@@ -0,0 +1,12 @@
+// 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.
+
+// TODO(b:272150598): Investigate sharing this with
+// ../embedded_a11y_helper/content.ts, perhaps by renaming cvox_gdocs_script to
+// gdoc_script at buildtime for chromevox_helper.
+(function() {
+const s = document.createElement('script');
+s.src = chrome.runtime.getURL('chromevox_helper/cvox_gdocs_script.js');
+document.documentElement.appendChild(s);
+})();
diff --git a/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2 b/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2
new file mode 100644
index 0000000..ca9f3ec
--- /dev/null
+++ b/chrome/browser/resources/accessibility/chromevox_helper_manifest.json.jinja2
@@ -0,0 +1,34 @@
+{
+{%if key is defined %}
+  "key": "{{key}}",
+{% endif %}
+  "manifest_version": 3,
+  "name": "ChromeVox Helper",
+  "version": "{{set_version}}",
+  "description": "Provides support for ChromeVox within non-Ash browsers",
+  "permissions": [
+    "scripting"
+  ],
+  "incognito": "split",
+  "content_scripts": [
+    {
+      "matches": [ "https://docs.google.com/document*",
+                   "https://docs.sandbox.google.com/document*" ],
+      "all_frames": true,
+      "js": [
+        "chromevox_helper/content.js"
+      ]
+    }
+  ],
+  "host_permissions": [
+    "https://docs.google.com/document*",
+    "https://docs.sandbox.google.com/document*"
+  ],
+  "web_accessible_resources": [
+    {
+      "resources": ["chromevox_helper/cvox_gdocs_script.js"],
+      "matches": [ "https://docs.google.com/*",
+                   "https://docs.sandbox.google.com/*" ]
+    }
+  ]
+}
diff --git a/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn b/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn
new file mode 100644
index 0000000..c128977
--- /dev/null
+++ b/chrome/browser/resources/accessibility/embedded_a11y_helper/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//chrome/common/features.gni")
+import("//tools/typescript/ts_library.gni")
+
+out_dir = "$root_out_dir/resources/accessibility/embedded_a11y_helper"
+tsc_output_dir = "$target_gen_dir/tsc"
+
+group("build") {
+  deps = [ ":embedded_a11y_helper_copied_files" ]
+}
+
+ts_library("build_ts") {
+  in_files = [ "content.ts" ]
+  out_dir = tsc_output_dir
+  definitions = [
+    "//tools/typescript/definitions/runtime.d.ts",
+    "//tools/typescript/definitions/tabs.d.ts",
+  ]
+}
+
+copy("embedded_a11y_helper_copied_files") {
+  sources = [
+    "$tsc_output_dir/content.js",
+    "../../chromeos/accessibility/common/gdocs_script.js",
+  ]
+  outputs = [ "$out_dir/{{source_file_part}}" ]
+  deps = [ ":build_ts" ]
+}
diff --git a/chrome/browser/resources/accessibility/embedded_a11y_helper/DIR_METADATA b/chrome/browser/resources/accessibility/embedded_a11y_helper/DIR_METADATA
new file mode 100644
index 0000000..a0b6aa9a
--- /dev/null
+++ b/chrome/browser/resources/accessibility/embedded_a11y_helper/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//ash/accessibility/COMMON_METADATA"
diff --git a/chrome/browser/resources/accessibility/embedded_a11y_helper/content.ts b/chrome/browser/resources/accessibility/embedded_a11y_helper/content.ts
new file mode 100644
index 0000000..b41d28f0
--- /dev/null
+++ b/chrome/browser/resources/accessibility/embedded_a11y_helper/content.ts
@@ -0,0 +1,9 @@
+// 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.
+
+(function() {
+const s = document.createElement('script');
+s.src = chrome.runtime.getURL('embedded_a11y_helper/gdocs_script.js');
+document.documentElement.appendChild(s);
+})();
diff --git a/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2 b/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2
new file mode 100644
index 0000000..5106ce0
--- /dev/null
+++ b/chrome/browser/resources/accessibility/embedded_a11y_helper_manifest.json.jinja2
@@ -0,0 +1,35 @@
+{
+{%if key is defined %}
+  "key": "{{key}}",
+{% endif %}
+  "manifest_version": 3,
+  "name": "Accessibility Helper",
+  "version": "{{set_version}}",
+  "description": "Provides support for Ash accessibility features from non-Ash browsers",
+  "permissions": [
+    "scripting"
+  ],
+  "incognito": "split",
+  "content_scripts": [
+    {
+      "matches": [ "https://docs.google.com/document*",
+                   "https://docs.sandbox.google.com/document*" ],
+      "all_frames": true,
+      "js": [
+        "embedded_a11y_helper/content.js"
+      ],
+      "run_at": "document_start"
+    }
+  ],
+  "host_permissions": [
+    "https://docs.google.com/document*",
+    "https://docs.sandbox.google.com/document*"
+  ],
+  "web_accessible_resources": [
+    {
+      "resources": ["embedded_a11y_helper/gdocs_script.js"],
+      "matches": [ "https://docs.google.com/*",
+                   "https://docs.sandbox.google.com/*" ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js b/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js
index 5aee889..3e20baf7 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/quick_start.js
@@ -170,12 +170,12 @@
 
   /**
    * @param {string} ssid
-   * @param {string} password
+   * @param {string?} password
    */
   showConnectedToWifi(ssid, password) {
     this.setUIStep(QuickStartUIState.CONNECTED_TO_WIFI);
     this.ssid_ = ssid;
-    this.password_ = password;
+    this.password_ = password ? password : '';
   }
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/main_page_mixin.ts b/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
index 0ca4898..f87a6af 100644
--- a/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
+++ b/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
@@ -231,6 +231,20 @@
         }
 
         /**
+         * Activate and display the first page (Network page). This page
+         * should be the default visible page when the root page is visited.
+         */
+        private activateFirstPage(): void {
+          // The About page does not have the Network page so we should not
+          // attempt to activate it.
+          // TODO(b/282961146) Investigate removing MainPageMixin from
+          // the About page so this check can be removed.
+          if (isRevampWayfindingEnabled() && this.isMainPageContainer) {
+            this.activatePage(FIRST_PAGE_ROUTE);
+          }
+        }
+
+        /**
          * Detects which state transition is appropriate for the given new/old
          * routes.
          */
@@ -288,13 +302,7 @@
                 return;
 
               case RouteState.ROOT:
-                // Do not activate the Network page if the host element is
-                // the About page since it does not contain that page.
-                // TODO(b/282961146) Investigate removing MainPageMixin from
-                // the About page so this check can be removed.
-                if (isRevampWayfindingEnabled() && this.isMainPageContainer) {
-                  this.activatePage(FIRST_PAGE_ROUTE);
-                }
+                this.activateFirstPage();
                 return;
 
               // Nothing to do here for the DIALOG case.
@@ -318,7 +326,7 @@
               // Happens when clearing search results (Navigating from
               // '/?search=foo' to '/')
               case RouteState.ROOT:
-                // TODO(b/282961146) Activate first top-level page (Network)
+                this.activateFirstPage();
                 return;
 
               // Nothing to do here for the DIALOG case.
diff --git a/chrome/browser/resources/settings/languages_page/languages.ts b/chrome/browser/resources/settings/languages_page/languages.ts
index 0640c14..828fd617 100644
--- a/chrome/browser/resources/settings/languages_page/languages.ts
+++ b/chrome/browser/resources/settings/languages_page/languages.ts
@@ -44,6 +44,7 @@
 // Reverse of the map above. Just the languages code that translate uses but
 // Chrome has a different code for.
 const kTranslateToChromeCode: Map<string, string> = new Map([
+  ['no', 'nb'],
   ['tl', 'fil'],
   ['iw', 'he'],
   ['jw', 'jv'],
diff --git a/chrome/browser/resources/settings/performance_page/performance_page.html b/chrome/browser/resources/settings/performance_page/performance_page.html
index 70e9dd6..6b4a710 100644
--- a/chrome/browser/resources/settings/performance_page/performance_page.html
+++ b/chrome/browser/resources/settings/performance_page/performance_page.html
@@ -39,7 +39,7 @@
             pref="[[prefs.performance_tuning.high_efficiency_mode.state]]"
             exportparts="labelWrapper">
           <settings-dropdown-menu id="discardTimeDropdown"
-              label="$i18n{highEfficiencyModeOnTimerLabel}"
+              label="$i18n{highEfficiencyChooseDiscardTimeAriaLabel}"
               disabled="[[!isHighEfficiencyModeEnabledOnTimer_(
                   prefs.performance_tuning.high_efficiency_mode.state.value)]]"
               pref="{{prefs.performance_tuning.high_efficiency_mode.time_before_discard_in_minutes}}"
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
index 4748af0..0ff11fb 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.html
@@ -257,7 +257,8 @@
               on-trailing-icon-clicked="onShowContextMenuClicked_"
               on-checkbox-change="onRowSelectedChange_"
               on-input-change="onRename_"
-              row-aria-label="[[getBookmarkA11yLabel_(item.url, item.title)]]"
+              row-aria-label="[[getBookmarkA11yLabel_(
+                                item.url, item.title, editing_)]]"
               row-aria-description="[[getBookmarkA11yDescription_(item)]]"
               tabindex$="[[tabIndex]]"
               image-urls="[[getBookmarkImageUrls_(
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
index 9246afb..51469fa 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_list.ts
@@ -545,11 +545,16 @@
   }
 
   private getBookmarkA11yLabel_(url: string, title: string): string {
+    if (this.editing_) {
+      if (url) {
+        return loadTimeData.getStringF('selectBookmarkLabel', title);
+      }
+      return loadTimeData.getStringF('selectFolderLabel', title);
+    }
     if (url) {
       return loadTimeData.getStringF('openBookmarkLabel', title);
-    } else {
-      return loadTimeData.getStringF('openFolderLabel', title);
     }
+    return loadTimeData.getStringF('openFolderLabel', title);
   }
 
   private getBookmarkA11yDescription_(
diff --git a/chrome/browser/storage_access_api/BUILD.gn b/chrome/browser/storage_access_api/BUILD.gn
index d31618f..c06b1bc8 100644
--- a/chrome/browser/storage_access_api/BUILD.gn
+++ b/chrome/browser/storage_access_api/BUILD.gn
@@ -2,8 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("permissions") {
+source_set("storage_access_api") {
   sources = [
+    "storage_access_api_service.h",
+    "storage_access_api_service_factory.cc",
+    "storage_access_api_service_factory.h",
+    "storage_access_api_service_impl.cc",
+    "storage_access_api_service_impl.h",
+    "storage_access_api_tab_helper.cc",
+    "storage_access_api_tab_helper.h",
     "storage_access_grant_permission_context.cc",
     "storage_access_grant_permission_context.h",
   ]
diff --git a/chrome/browser/storage_access_api/storage_access_api_service.h b/chrome/browser/storage_access_api/storage_access_api_service.h
new file mode 100644
index 0000000..1cc888b
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service.h
@@ -0,0 +1,25 @@
+// 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_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_H_
+#define CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_H_
+
+namespace url {
+class Origin;
+}  // namespace url
+
+// An abstract class providing the interface for the Storage Access API service.
+// This class exists so that a different implementation may be used in tests.
+class StorageAccessAPIService {
+ public:
+  // May renew Storage Access API permission grants associated with the given
+  // origins.
+  //
+  // The implementations of this method may apply rate limiting and caching in
+  // order to avoid unnecessary disk writes.
+  virtual void RenewPermissionGrant(const url::Origin& embedded_origin,
+                                    const url::Origin& top_frame_origin) = 0;
+};
+
+#endif  // CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_H_
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_factory.cc b/chrome/browser/storage_access_api/storage_access_api_service_factory.cc
new file mode 100644
index 0000000..2f626cbd
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service_factory.cc
@@ -0,0 +1,42 @@
+// 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/storage_access_api/storage_access_api_service_factory.h"
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "chrome/browser/profiles/profile_selections.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_impl.h"
+
+// static
+StorageAccessAPIServiceImpl*
+StorageAccessAPIServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<StorageAccessAPIServiceImpl*>(
+      StorageAccessAPIServiceFactory::GetInstance()
+          ->GetServiceForBrowserContext(context, /*create=*/true));
+}
+
+// static
+StorageAccessAPIServiceFactory* StorageAccessAPIServiceFactory::GetInstance() {
+  static base::NoDestructor<StorageAccessAPIServiceFactory> instance;
+  return instance.get();
+}
+
+StorageAccessAPIServiceFactory::StorageAccessAPIServiceFactory()
+    : ProfileKeyedServiceFactory(
+          "StorageAccessAPIService",
+          ProfileSelections::Builder()
+              .WithRegular(ProfileSelection::kOwnInstance)
+              .WithGuest(ProfileSelection::kOwnInstance)
+              .WithSystem(ProfileSelection::kNone)
+              .Build()) {}
+
+StorageAccessAPIServiceFactory::~StorageAccessAPIServiceFactory() = default;
+
+std::unique_ptr<KeyedService>
+StorageAccessAPIServiceFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+  return std::make_unique<StorageAccessAPIServiceImpl>(context);
+}
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_factory.h b/chrome/browser/storage_access_api/storage_access_api_service_factory.h
new file mode 100644
index 0000000..b036883
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service_factory.h
@@ -0,0 +1,45 @@
+// 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_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+class StorageAccessAPIServiceImpl;
+
+// Singleton that owns StorageAccessAPIServiceImpl objects and associates them
+// with corresponding Profiles.
+//
+// Listens for each Profile's destruction notification and cleans up the
+// associated StorageAccessAPIServiceImpl.
+class StorageAccessAPIServiceFactory : public ProfileKeyedServiceFactory {
+ public:
+  StorageAccessAPIServiceFactory(const StorageAccessAPIServiceFactory&) =
+      delete;
+  StorageAccessAPIServiceFactory& operator=(
+      const StorageAccessAPIServiceFactory&) = delete;
+
+  static StorageAccessAPIServiceImpl* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static StorageAccessAPIServiceFactory* GetInstance();
+
+ private:
+  friend base::NoDestructor<StorageAccessAPIServiceFactory>;
+
+  StorageAccessAPIServiceFactory();
+  ~StorageAccessAPIServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_FACTORY_H_
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_factory_unittest.cc b/chrome/browser/storage_access_api/storage_access_api_service_factory_unittest.cc
new file mode 100644
index 0000000..8722ffe
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service_factory_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/storage_access_api/storage_access_api_service_factory.h"
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_impl.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class StorageAccessAPIServiceFactoryTest : public testing::Test {
+ public:
+  StorageAccessAPIServiceFactoryTest() = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile("Profile");
+  }
+
+  void TearDown() override { profile_manager_.DeleteAllTestingProfiles(); }
+
+  TestingProfile* profile() { return profile_; }
+
+ private:
+  content::BrowserTaskEnvironment env_;
+  TestingProfileManager profile_manager_ =
+      TestingProfileManager(TestingBrowserProcess::GetGlobal());
+  raw_ptr<TestingProfile> profile_ = nullptr;
+};
+
+TEST_F(StorageAccessAPIServiceFactoryTest, RegularProfile_ServiceCreated) {
+  EXPECT_NE(nullptr,
+            StorageAccessAPIServiceFactory::GetForBrowserContext(profile()));
+}
+
+TEST_F(StorageAccessAPIServiceFactoryTest, OffTheRecordProfile_OwnInstance) {
+  StorageAccessAPIServiceImpl* original_service =
+      StorageAccessAPIServiceFactory::GetForBrowserContext(
+          profile()->GetOriginalProfile());
+
+  ASSERT_NE(nullptr, original_service);
+
+  auto otr_profile_id = Profile::OTRProfileID::CreateUniqueForTesting();
+  auto* otr_service = StorageAccessAPIServiceFactory::GetForBrowserContext(
+      profile()->GetOffTheRecordProfile(otr_profile_id,
+                                        /*create_if_needed=*/true));
+
+  EXPECT_NE(nullptr, otr_service);
+  EXPECT_NE(original_service, otr_service);
+}
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_impl.cc b/chrome/browser/storage_access_api/storage_access_api_service_impl.cc
new file mode 100644
index 0000000..d741ccf6
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service_impl.cc
@@ -0,0 +1,27 @@
+// 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 <utility>
+
+#include "chrome/browser/storage_access_api/storage_access_api_service_impl.h"
+
+StorageAccessAPIServiceImpl::StorageAccessAPIServiceImpl(
+    content::BrowserContext* browser_context) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(browser_context);
+}
+
+StorageAccessAPIServiceImpl::~StorageAccessAPIServiceImpl() = default;
+
+void StorageAccessAPIServiceImpl::RenewPermissionGrant(
+    const url::Origin& embedded_origin,
+    const url::Origin& top_frame_origin) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // TODO(https://crbug.com/1450356): implement grant renewal.
+}
+
+void StorageAccessAPIServiceImpl::Shutdown() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_impl.h b/chrome/browser/storage_access_api/storage_access_api_service_impl.h
new file mode 100644
index 0000000..0f24b45
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service_impl.h
@@ -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.
+
+#ifndef CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_IMPL_H_
+#define CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_IMPL_H_
+
+#include "base/sequence_checker.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace url {
+class Origin;
+}  // namespace url
+
+// A profile keyed service for Storage Access API state.
+//
+// This service always exists for a Profile, regardless of whether the Storage
+// Access API feature is enabled.
+class StorageAccessAPIServiceImpl : public StorageAccessAPIService,
+                                    public KeyedService {
+ public:
+  explicit StorageAccessAPIServiceImpl(content::BrowserContext* context);
+  StorageAccessAPIServiceImpl(const StorageAccessAPIServiceImpl&) = delete;
+  StorageAccessAPIServiceImpl& operator=(const StorageAccessAPIServiceImpl&) =
+      delete;
+  ~StorageAccessAPIServiceImpl() override;
+
+  // StorageAccessAPIService:
+  void RenewPermissionGrant(const url::Origin& embedded_origin,
+                            const url::Origin& top_frame_origin) override;
+
+  // KeyedService:
+  void Shutdown() override;
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+#endif  // CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_SERVICE_IMPL_H_
diff --git a/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc b/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc
new file mode 100644
index 0000000..ad1df6677
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_service_impl_unittest.cc
@@ -0,0 +1,58 @@
+// 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/storage_access_api/storage_access_api_service_impl.h"
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class StorageAccessAPIServiceImplTest : public testing::Test {
+ public:
+  StorageAccessAPIServiceImplTest() = default;
+
+  void SetUp() override {
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(profile_manager_->SetUp());
+    profile_ = profile_manager_->CreateTestingProfile("TestProfile");
+    service_ = StorageAccessAPIServiceFactory::GetForBrowserContext(profile_);
+    ASSERT_NE(service_, nullptr);
+  }
+
+  void TearDown() override {
+    DCHECK(service_);
+    // Even though we reassign this in SetUp, service may be persisted between
+    // tests if the factory has already created a service for the testing
+    // profile being used.
+    profile_manager_->DeleteAllTestingProfiles();
+    profile_manager_.reset();
+  }
+
+  content::BrowserTaskEnvironment& env() { return env_; }
+
+ protected:
+  Profile* profile() { return profile_; }
+  StorageAccessAPIServiceImpl* service() { return service_; }
+
+ private:
+  content::BrowserTaskEnvironment env_;
+  std::unique_ptr<TestingProfileManager> profile_manager_;
+  raw_ptr<Profile> profile_;
+  raw_ptr<StorageAccessAPIServiceImpl> service_;
+};
+
+TEST_F(StorageAccessAPIServiceImplTest, RenewPermissionGrant) {
+  StorageAccessAPIServiceImpl* service =
+      StorageAccessAPIServiceFactory::GetForBrowserContext(profile());
+
+  ASSERT_NE(nullptr, service);
+
+  // TODO(https://crbug.com/1450356): test grant renewal.
+}
diff --git a/chrome/browser/storage_access_api/storage_access_api_tab_helper.cc b/chrome/browser/storage_access_api/storage_access_api_tab_helper.cc
new file mode 100644
index 0000000..afd24785
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_tab_helper.cc
@@ -0,0 +1,40 @@
+// 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/storage_access_api/storage_access_api_tab_helper.h"
+
+#include "chrome/browser/storage_access_api/storage_access_api_service.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_factory.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+StorageAccessAPITabHelper::~StorageAccessAPITabHelper() = default;
+
+void StorageAccessAPITabHelper::FrameReceivedUserActivation(
+    content::RenderFrameHost* rfh) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(service_);
+
+  if (rfh->IsInPrimaryMainFrame()) {
+    // No need to do anything in a main frame.
+    return;
+  }
+
+  service_->RenewPermissionGrant(
+      rfh->GetLastCommittedOrigin(),
+      rfh->GetParentOrOuterDocument()->GetLastCommittedOrigin());
+}
+
+StorageAccessAPITabHelper::StorageAccessAPITabHelper(
+    content::WebContents* web_contents,
+    StorageAccessAPIService* service)
+    : content::WebContentsObserver(web_contents),
+      content::WebContentsUserData<StorageAccessAPITabHelper>(*web_contents),
+      service_(service) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(service_);
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(StorageAccessAPITabHelper);
diff --git a/chrome/browser/storage_access_api/storage_access_api_tab_helper.h b/chrome/browser/storage_access_api/storage_access_api_tab_helper.h
new file mode 100644
index 0000000..e770a28
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_tab_helper.h
@@ -0,0 +1,41 @@
+// 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_STORAGE_ACCESS_API_STORAGE_ACCESS_API_TAB_HELPER_H_
+#define CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_TAB_HELPER_H_
+
+#include "base/sequence_checker.h"
+#include "base/thread_annotations.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+class StorageAccessAPIService;
+
+class StorageAccessAPITabHelper
+    : public content::WebContentsObserver,
+      public content::WebContentsUserData<StorageAccessAPITabHelper> {
+ public:
+  StorageAccessAPITabHelper(const StorageAccessAPITabHelper&) = delete;
+  StorageAccessAPITabHelper& operator=(const StorageAccessAPITabHelper&) =
+      delete;
+  ~StorageAccessAPITabHelper() override;
+
+  // WebContentsObserver:
+  void FrameReceivedUserActivation(content::RenderFrameHost* rfh) override;
+
+ private:
+  StorageAccessAPITabHelper(content::WebContents* web_contents,
+                            StorageAccessAPIService* service);
+  friend class content::WebContentsUserData<StorageAccessAPITabHelper>;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+
+  raw_ptr<StorageAccessAPIService> service_
+      GUARDED_BY_CONTEXT(sequence_checker_);
+
+  SEQUENCE_CHECKER(sequence_checker_);
+};
+
+#endif  // CHROME_BROWSER_STORAGE_ACCESS_API_STORAGE_ACCESS_API_TAB_HELPER_H_
diff --git a/chrome/browser/storage_access_api/storage_access_api_tab_helper_unittest.cc b/chrome/browser/storage_access_api/storage_access_api_tab_helper_unittest.cc
new file mode 100644
index 0000000..cdcea6b
--- /dev/null
+++ b/chrome/browser/storage_access_api/storage_access_api_tab_helper_unittest.cc
@@ -0,0 +1,79 @@
+// 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/storage_access_api/storage_access_api_tab_helper.h"
+
+#include "chrome/browser/storage_access_api/storage_access_api_service.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/test/navigation_simulator.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/origin.h"
+
+class MockStorageAccessAPIService : public StorageAccessAPIService {
+ public:
+  MOCK_METHOD(void,
+              RenewPermissionGrant,
+              (const url::Origin& embedded_origin,
+               const url::Origin& top_frame_origin),
+              (override));
+};
+
+class StorageAccessAPITabHelperTest : public ChromeRenderViewHostTestHarness {
+ public:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    StorageAccessAPITabHelper::CreateForWebContents(web_contents(), &service_);
+  }
+
+  content::RenderFrameHost* SimulateNavigateAndCommit(
+      const GURL& url,
+      content::RenderFrameHost* rfh) {
+    std::unique_ptr<content::NavigationSimulator> simulator =
+        content::NavigationSimulator::CreateRendererInitiated(url, rfh);
+    simulator->Commit();
+    return simulator->GetLastThrottleCheckResult().action() ==
+                   content::NavigationThrottle::PROCEED
+               ? simulator->GetFinalRenderFrameHost()
+               : nullptr;
+  }
+
+  StorageAccessAPITabHelper* tab_helper() {
+    return StorageAccessAPITabHelper::FromWebContents(web_contents());
+  }
+
+  MockStorageAccessAPIService& service() { return service_; }
+
+ private:
+  testing::StrictMock<MockStorageAccessAPIService> service_;
+};
+
+TEST_F(StorageAccessAPITabHelperTest, OnFrameReceivedUserActivation_MainFrame) {
+  // The service should not be invoked.
+  EXPECT_CALL(service(), RenewPermissionGrant(testing::_, testing::_)).Times(0);
+
+  NavigateAndCommit(GURL("https://example.test/"));
+
+  // Main-frame user activations are no-ops.
+  tab_helper()->FrameReceivedUserActivation(main_rfh());
+}
+
+TEST_F(StorageAccessAPITabHelperTest, OnFrameReceivedUserActivation_Subframe) {
+  EXPECT_CALL(service(), RenewPermissionGrant(
+                             url::Origin::Create(GURL("https://bar.test")),
+                             url::Origin::Create(GURL("https://example.test"))))
+      .Times(1);
+
+  NavigateAndCommit(GURL("https://example.test/"));
+
+  content::RenderFrameHost* subframe =
+      content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe");
+
+  subframe = SimulateNavigateAndCommit(GURL("https://bar.test/foo"), subframe);
+  ASSERT_NE(nullptr, subframe);
+
+  // Non-main-frame user activations cause the service to be invoked.
+  tab_helper()->FrameReceivedUserActivation(subframe);
+}
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index dd14fc6..d53c8e7 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -509,7 +509,7 @@
 // does not have the display name for a language unless it's in the
 // Accept-Language list.
 static const char* kServerLanguageList[] = {"ak",    "af", "en-CA", "zh", "yi",
-                                            "fr-FR", "tl", "iw",    "in", "xx"};
+                                            "fr-FR", "tl", "iw",    "hz", "xx"};
 
 // Test the fetching of languages from the translate server
 TEST_F(TranslateManagerRenderViewHostTest, FetchLanguagesFromTranslateServer) {
@@ -543,13 +543,17 @@
   current_supported_languages.clear();
   translate::TranslateDownloadManager::GetSupportedLanguages(
       true /* translate_allowed */, &current_supported_languages);
-  // "xx" can't be displayed in the Translate infobar, so this is eliminated.
-  EXPECT_EQ(server_languages.size() - 1, current_supported_languages.size());
+  // "in" is not in the kAcceptList and "xx" can't be displayed, so both are
+  // removed from the downloaded list.
+  EXPECT_EQ(server_languages.size() - 2, current_supported_languages.size());
   // Not sure we need to guarantee the order of languages, so we find them.
   for (size_t i = 0; i < server_languages.size(); ++i) {
     const std::string& lang = server_languages[i];
-    if (lang == "xx")
+    if (lang == "xx") {
       continue;
+    } else if (lang == "hz") {
+      continue;
+    }
     EXPECT_TRUE(base::Contains(current_supported_languages, lang))
         << "lang=" << lang;
   }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 0eb77add..0dda8dd4d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -532,6 +532,7 @@
     "//chrome/browser/resources/usb_internals:resources",
     "//chrome/browser/safe_browsing",
     "//chrome/browser/share",
+    "//chrome/browser/storage_access_api",
     "//chrome/browser/ui/side_panel:side_panel_enums",
     "//chrome/browser/ui/webui:configs",
     "//chrome/browser/ui/webui/omnibox:mojo_bindings",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 9ee5c30..9b2c6a94 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -3342,6 +3342,9 @@
       <message name="IDS_FEED_MANAGE_INTERESTS_DESCRIPTION" desc="Content description to manage settings from the feed header overflow menu.">
         Manage the topics you're interested in
       </message>
+      <message name="IDS_FEED_MANAGE_INTERESTS_DESCRIPTION_UI_UPDATE" desc="Content description to manage settings from the feed header overflow menu.">
+        Manage the topics and searches you're interested in
+      </message>
       <message name="IDS_FEED_MANAGE_HIDDEN" desc="Menu item to manage hiding and unhiding topics.">
         Hidden
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEED_MANAGE_INTERESTS_DESCRIPTION_UI_UPDATE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEED_MANAGE_INTERESTS_DESCRIPTION_UI_UPDATE.png.sha1
new file mode 100644
index 0000000..a3124a0
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_FEED_MANAGE_INTERESTS_DESCRIPTION_UI_UPDATE.png.sha1
@@ -0,0 +1 @@
+4773d868248ca7c83e1f171b5625b30a0ae3b23c
\ No newline at end of file
diff --git a/chrome/browser/ui/messages/android/BUILD.gn b/chrome/browser/ui/messages/android/BUILD.gn
index 7ac6300..1856e2a0 100644
--- a/chrome/browser/ui/messages/android/BUILD.gn
+++ b/chrome/browser/ui/messages/android/BUILD.gn
@@ -46,6 +46,7 @@
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//ui/accessibility:ax_base_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_utils_java",
   ]
@@ -86,6 +87,7 @@
     "//content/public/test/android:content_java_test_support",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit",
+    "//ui/accessibility:ax_base_java",
     "//ui/android:ui_java_test_support",
   ]
 }
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java
index ea0edcb4..fb1c717 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java
@@ -4,12 +4,7 @@
 
 package org.chromium.chrome.browser.ui.messages.snackbar;
 
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT;
-
 import android.app.Activity;
-import android.os.Build;
 import android.os.Handler;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -25,7 +20,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
-import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
+import org.chromium.ui.accessibility.AccessibilityState;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -306,19 +301,12 @@
         int durationMs = snackbar.getDuration();
         if (durationMs == 0) durationMs = sSnackbarDurationMs;
 
-        if (ChromeAccessibilityUtil.get().isAccessibilityEnabled()) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-                return ChromeAccessibilityUtil.get().getRecommendedTimeoutMillis(
-                        durationMs, FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS | FLAG_CONTENT_TEXT);
-            } else {
-                durationMs *= 2;
-                if (durationMs < sAccessibilitySnackbarDurationMs) {
-                    durationMs = sAccessibilitySnackbarDurationMs;
-                }
-            }
-        }
-
-        return durationMs;
+        // If no a11y service that can perform gestures is enabled, use the set duration. Otherwise
+        // multiply the duration by the recommended multiplier and use that with a minimum of 30s.
+        return !AccessibilityState.isPerformGesturesEnabled()
+                ? durationMs
+                : Math.max(sAccessibilitySnackbarDurationMs,
+                        (int) (AccessibilityState.getRecommendedTimeoutMultiplier() * durationMs));
     }
 
     /**
@@ -343,7 +331,7 @@
      * Clears any overrides set for testing.
      */
     @VisibleForTesting
-    public static void restDurationForTesting() {
+    public static void resetDurationForTesting() {
         sSnackbarDurationMs = DEFAULT_SNACKBAR_DURATION_MS;
         sAccessibilitySnackbarDurationMs = ACCESSIBILITY_MODE_SNACKBAR_DURATION_MS;
     }
diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarTest.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarTest.java
index 3349fc2b..5c31f71 100644
--- a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarTest.java
+++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarTest.java
@@ -7,7 +7,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
-import android.os.Build;
 import android.widget.FrameLayout;
 
 import androidx.test.filters.MediumTest;
@@ -27,12 +26,11 @@
 import org.chromium.base.test.BaseActivityTestRule;
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.MaxAndroidSdkLevel;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController;
 import org.chromium.chrome.browser.ui.messages.test.R;
-import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.accessibility.AccessibilityState;
 import org.chromium.ui.test.util.BlankUiTestActivity;
 
 import java.util.concurrent.TimeUnit;
@@ -86,7 +84,7 @@
     @AfterClass
     public static void teardownSuite() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            SnackbarManager.restDurationForTesting();
+            SnackbarManager.resetDurationForTesting();
             sActivity = null;
             sMainParent = null;
             sAlternateParent = null;
@@ -95,8 +93,12 @@
 
     @Before
     public void setupTest() {
-        PostTask.runOrPostTask(TaskTraits.UI_DEFAULT,
-                () -> { mManager = new SnackbarManager(sActivity, sMainParent, null); });
+        PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> {
+            mManager = new SnackbarManager(sActivity, sMainParent, null);
+            mManager.dismissAllSnackbars();
+            AccessibilityState.setIsPerformGesturesEnabledForTesting(false);
+            AccessibilityState.setRecommendedTimeoutMultiplierForTesting(1.0f);
+        });
     }
 
     @Test
@@ -203,12 +205,10 @@
 
     @Test
     @SmallTest
-    @MaxAndroidSdkLevel(value = Build.VERSION_CODES.P,
-            reason = "AccessibilityManager#getRecommendedTimeoutMillis() is only available in Q+")
-    public void
-    testSnackbarDuration() {
+    public void testSnackbarDuration() {
         final Snackbar snackbar = Snackbar.make(
                 "persistent", mDismissController, Snackbar.TYPE_ACTION, Snackbar.UMA_TEST_SNACKBAR);
+
         PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> {
             snackbar.setDuration(0);
             Assert.assertEquals(
@@ -217,13 +217,31 @@
         });
 
         PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> {
+            snackbar.setDuration(7);
+            Assert.assertEquals("Snackbar should use set duration when no gesture performing a11y "
+                            + "services are running.",
+                    7, mManager.getDuration(snackbar));
+        });
+
+        PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> {
+            AccessibilityState.setIsPerformGesturesEnabledForTesting(true);
             snackbar.setDuration(SnackbarManager.getDefaultA11yDurationForTesting() / 3);
-            ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true);
             Assert.assertEquals(
-                    "Default a11y duration should be used if the duration of snackbar in non-a11y mode is less than half of it",
+                    "Snackbar should use default a11y duration when set duration is less "
+                            + "than default and a gesture performing a11y service is running.",
                     SnackbarManager.getDefaultA11yDurationForTesting(),
                     mManager.getDuration(snackbar));
         });
+
+        PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> {
+            AccessibilityState.setIsPerformGesturesEnabledForTesting(true);
+            AccessibilityState.setRecommendedTimeoutMultiplierForTesting(5.0f);
+            snackbar.setDuration(SnackbarManager.getDefaultA11yDurationForTesting() / 3);
+            Assert.assertTrue("Snackbar should use the recommended duration if it is more than "
+                            + "the default a11y duration.",
+                    SnackbarManager.getDefaultA11yDurationForTesting()
+                            < mManager.getDuration(snackbar));
+        });
     }
 
     @Test
@@ -307,8 +325,8 @@
     public void testSupplier_BeforeShowing() {
         final Snackbar snackbar = Snackbar.make(
                 "stack", mDismissController, Snackbar.TYPE_ACTION, Snackbar.UMA_TEST_SNACKBAR);
-        pollSnackbarCondition(
-                "Snackbar isShowing() and isShowingSupplier().get() values are not both false before showing snackbar.",
+        pollSnackbarCondition("Snackbar isShowing() and isShowingSupplier().get() values are not "
+                        + "both false before showing snackbar.",
                 () -> !mManager.isShowing() && !mManager.isShowingSupplier().get());
     }
 
@@ -318,8 +336,8 @@
         final Snackbar snackbar = Snackbar.make(
                 "stack", mDismissController, Snackbar.TYPE_ACTION, Snackbar.UMA_TEST_SNACKBAR);
         PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> mManager.showSnackbar(snackbar));
-        pollSnackbarCondition(
-                "Snackbar isShowing() and isShowingSupplier().get() values are not both true while snackbar is showing.",
+        pollSnackbarCondition("Snackbar isShowing() and isShowingSupplier().get() values are not "
+                        + "both true while snackbar is showing.",
                 () -> mManager.isShowing() && mManager.isShowingSupplier().get());
     }
 
@@ -331,8 +349,8 @@
         PostTask.runOrPostTask(TaskTraits.UI_DEFAULT, () -> mManager.showSnackbar(snackbar));
         PostTask.runOrPostTask(
                 TaskTraits.UI_DEFAULT, () -> mManager.dismissSnackbars(mDismissController));
-        pollSnackbarCondition(
-                "Snackbar isShowing() and isShowingSupplier().get() values are not both false after dismissing snackbar.",
+        pollSnackbarCondition("Snackbar isShowing() and isShowingSupplier().get() values are not "
+                        + "both false after dismissing snackbar.",
                 () -> !mManager.isShowing() && !mManager.isShowingSupplier().get());
     }
 
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index b688e30..67a44bd 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -78,6 +78,9 @@
 #include "chrome/browser/ssl/connection_help_tab_helper.h"
 #include "chrome/browser/ssl/https_only_mode_tab_helper.h"
 #include "chrome/browser/ssl/security_state_tab_helper.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_factory.h"
+#include "chrome/browser/storage_access_api/storage_access_api_service_impl.h"
+#include "chrome/browser/storage_access_api/storage_access_api_tab_helper.h"
 #include "chrome/browser/subresource_filter/chrome_content_subresource_filter_web_contents_helper_factory.h"
 #include "chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h"
@@ -447,6 +450,9 @@
             profile));
   }
   SoundContentSettingObserver::CreateForWebContents(web_contents);
+  StorageAccessAPITabHelper::CreateForWebContents(
+      web_contents, StorageAccessAPIServiceFactory::GetForBrowserContext(
+                        web_contents->GetBrowserContext()));
   SyncEncryptionKeysTabHelper::CreateForWebContents(web_contents);
   sync_sessions::SyncSessionsRouterTabHelper::CreateForWebContents(
       web_contents,
diff --git a/chrome/browser/ui/test/popup_multiscreen_interactive_uitest.cc b/chrome/browser/ui/test/popup_multiscreen_interactive_uitest.cc
index d2ad107..83186f8 100644
--- a/chrome/browser/ui/test/popup_multiscreen_interactive_uitest.cc
+++ b/chrome/browser/ui/test/popup_multiscreen_interactive_uitest.cc
@@ -179,16 +179,8 @@
   }
 }
 
-#if BUILDFLAG(IS_CHROMEOS) && defined(ADDRESS_SANITIZER) && \
-    defined(LEAK_SANITIZER)
-#define MAYBE_MoveToAnotherScreen DISABLED_MoveToAnotherScreen
-#else
-#define MAYBE_MoveToAnotherScreen MoveToAnotherScreen
-#endif
-
 // Tests opening a popup on the same screen, then moving it to another screen.
-// TODO(crbug.com/1444721): Re-enable this test
-IN_PROC_BROWSER_TEST_P(MAYBE_PopupMultiScreenTest, MAYBE_MoveToAnotherScreen) {
+IN_PROC_BROWSER_TEST_P(MAYBE_PopupMultiScreenTest, MoveToAnotherScreen) {
   content::WebContents* opener_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   // Copy the display vector so references are not invalidated while looping.
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index ae0c4345..20ec3ea 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -1245,10 +1245,7 @@
       AddItem(IDC_SHOW_SEARCH_COMPANION, u"Chrome Search Companion");
     }
 #endif
-
-    // TODO(josephjoopark): Update translate string with StringId when
-    // finalized.
-    AddItem(IDC_TRANSLATE_PAGE, u"Google Translate");
+    AddItemWithStringId(IDC_TRANSLATE_PAGE, IDS_SHOW_TRANSLATE);
 
     sub_menus_.push_back(std::make_unique<FindAndEditSubMenuModel>(this));
     AddSubMenuWithStringId(IDC_FIND_AND_EDIT_MENU, IDS_FIND_AND_EDIT_MENU,
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
index dda694f..9d04ac5 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
@@ -538,14 +538,22 @@
   element_bounds.Inset(
       gfx::Insets::VH(/*vertical=*/-kElementBorderPadding, /*horizontal=*/0));
 
-  // At least one row of the popup should be shown in the bounds of the content
-  // area so that the user notices the presence of the popup.
-  int item_height =
-      body_container_ && body_container_->children().size() > 0
-          ? body_container_->children()[0]->GetPreferredSize().height()
-          : 0;
+  // At least first and last rows of the popup -- a suggestion and, if present,
+  // the footer -- should be shown in the bounds of the content area so that the
+  // user notices the presence of the popup and, in particular, the first
+  // suggestion.
+  int min_height = std::numeric_limits<int>::max();
+  if (body_container_) {
+    const View::Views& children = body_container_->children();
+    if (!children.empty()) {
+      min_height = children.front()->GetPreferredSize().height();
+      if (children.size() > 1) {
+        min_height += children.back()->GetPreferredSize().height();
+      }
+    }
+  }
 
-  if (!CanShowDropdownHere(item_height, max_bounds_for_popup, element_bounds)) {
+  if (!CanShowDropdownHere(min_height, max_bounds_for_popup, element_bounds)) {
     controller_->Hide(PopupHidingReason::kInsufficientSpace);
     return false;
   }
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
index 7dc5202..d3b70d6 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_site_permissions_page_view.cc
@@ -38,6 +38,7 @@
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/layout/flex_layout_view.h"
+#include "ui/views/layout/layout_provider.h"
 #include "ui/views/layout/layout_types.h"
 #include "ui/views/metadata/view_factory_internal.h"
 #include "ui/views/view_class_properties.h"
@@ -142,16 +143,49 @@
                                /*adjust_height_for_width =*/true)
           .WithWeight(1);
 
-  ChromeLayoutProvider* const provider = ChromeLayoutProvider::Get();
-  const int icon_size =
-      provider->GetDistanceMetric(DISTANCE_EXTENSIONS_MENU_BUTTON_ICON_SIZE);
-  const int icon_label_spacing =
-      provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+  views::LayoutProvider* layout_provider = views::LayoutProvider::Get();
+  const gfx::Insets dialog_insets =
+      layout_provider->GetInsetsMetric(views::InsetsMetric::INSETS_DIALOG);
+
+  ChromeLayoutProvider* const chrome_layout_provider =
+      ChromeLayoutProvider::Get();
+  const int icon_size = chrome_layout_provider->GetDistanceMetric(
+      DISTANCE_EXTENSIONS_MENU_BUTTON_ICON_SIZE);
+  const int icon_label_spacing = chrome_layout_provider->GetDistanceMetric(
+      views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+  const int vertical_spacing = chrome_layout_provider->GetDistanceMetric(
+      DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE);
+  // This value must be the same as the `HoverButton` vertical margin.
+  const int hover_button_vertical_spacing =
+      chrome_layout_provider->GetDistanceMetric(
+          DISTANCE_CONTROL_LIST_VERTICAL) /
+      2;
+
+  const auto create_separator_builder =
+      [dialog_insets, vertical_spacing, hover_button_vertical_spacing](
+          bool full_width, bool is_bottom_hover_button = false) {
+        const int horizontal_margin = full_width ? 0 : dialog_insets.left();
+        const int bottom_margin =
+            is_bottom_hover_button
+                ? vertical_spacing - hover_button_vertical_spacing
+                : vertical_spacing;
+
+        return views::Builder<views::Separator>().SetProperty(
+            views::kMarginsKey,
+            gfx::Insets::TLBR(vertical_spacing, horizontal_margin,
+                              bottom_margin, dialog_insets.right()));
+      };
 
   const auto create_radio_button_builder =
       [=](PermissionsManager::UserSiteAccess site_access) {
         return views::Builder<views::BoxLayoutView>()
             .SetOrientation(views::BoxLayout::Orientation::kVertical)
+            // Add dialog horizontal margins, and top margin to separate the
+            // items.
+            .SetProperty(
+                views::kMarginsKey,
+                gfx::Insets::TLBR(vertical_spacing, dialog_insets.left(), 0,
+                                  dialog_insets.right()))
             .AddChildren(
                 views::Builder<views::RadioButton>()
                     .SetText(GetSiteAccessRadioButtonText(site_access))
@@ -173,12 +207,16 @@
   views::Builder<ExtensionsMenuSitePermissionsPageView>(this)
       .SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kVertical))
-      // TODO(crbug.com/1390952): Add margins after adding the menu
-      // items, to make sure all items are aligned.
       .AddChildren(
           // Subheader.
           views::Builder<views::FlexLayoutView>()
               .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
+              // Add top dialog margins, since its the first element, and
+              // horizontal dialog margins. Vertical margins will be handled in
+              // between the contents.
+              .SetInteriorMargin(gfx::Insets::TLBR(dialog_insets.top(),
+                                                   dialog_insets.left(), 0,
+                                                   dialog_insets.right()))
               .SetProperty(views::kFlexBehaviorKey, stretch_specification)
               .AddChildren(
                   // Back button.
@@ -208,13 +246,18 @@
                               &extension_icon_),
                           views::Builder<views::Label>().CopyAddressTo(
                               &extension_name_))),
+          create_separator_builder(/*full_width=*/true,
+                                   /*is_bottom_hover_button=*/false),
           // Content.
           views::Builder<views::BoxLayoutView>()
               .SetOrientation(views::BoxLayout::Orientation::kVertical)
               .AddChildren(
                   // Site access section.
-                  views::Builder<views::Separator>(),
                   views::Builder<views::Label>()
+                      // Add dialog horizontal margins. Vertical margins are
+                      // handled by surrounding views.
+                      .SetProperty(views::kMarginsKey,
+                                   gfx::Insets::VH(0, dialog_insets.left()))
                       .SetText(l10n_util::GetStringUTF16(
                           IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SITE_ACCESS_LABEL))
                       .SetHorizontalAlignment(gfx::ALIGN_LEFT),
@@ -225,9 +268,15 @@
                   create_radio_button_builder(
                       PermissionsManager::UserSiteAccess::kOnAllSites),
                   // Requests in toolbar toggle.
-                  views::Builder<views::Separator>(),
+                  create_separator_builder(/*full_width=*/false),
+                  // TODO(crbug.com/1390952): Format this view. Toggle button
+                  // should be on the right and centered.
                   views::Builder<views::FlexLayoutView>()
                       .SetCrossAxisAlignment(views::LayoutAlignment::kStart)
+                      // Add dialog horizontal margins. Vertical margins are
+                      // handled by separators.
+                      .SetProperty(views::kMarginsKey,
+                                   gfx::Insets::VH(0, dialog_insets.left()))
                       .SetProperty(views::kFlexBehaviorKey,
                                    stretch_specification)
                       .AddChildren(
@@ -241,22 +290,36 @@
                                       OnShowRequestsTogglePressed,
                                   base::Unretained(this)))),
                   // Settings button.
-                  views::Builder<views::Separator>(),
-                  views::Builder<HoverButton>(std::make_unique<HoverButton>(
-                      base::BindRepeating(
-                          [](Browser* browser,
-                             extensions::ExtensionId extension_id) {
-                            chrome::ShowExtensions(browser, extension_id);
-                          },
-                          browser, extension_id_),
-                      /*icon_view=*/nullptr,
-                      l10n_util::GetStringUTF16(
-                          IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SETTINGS_BUTTON),
-                      /*subtitle=*/std::u16string(),
-                      std::make_unique<views::ImageView>(
-                          ui::ImageModel::FromVectorIcon(
-                              vector_icons::kLaunchIcon,
-                              ui::kColorIconSecondary))))))
+                  create_separator_builder(/*full_width=*/false,
+                                           /*is_bottom_hover_button=*/true),
+                  views::Builder<HoverButton>(
+                      std::make_unique<HoverButton>(
+                          base::BindRepeating(
+                              [](Browser* browser,
+                                 extensions::ExtensionId extension_id) {
+                                chrome::ShowExtensions(browser, extension_id);
+                              },
+                              browser, extension_id_),
+                          /*icon_view=*/nullptr,
+                          l10n_util::GetStringUTF16(
+                              IDS_EXTENSIONS_MENU_SITE_PERMISSIONS_PAGE_SETTINGS_BUTTON),
+                          /*subtitle=*/std::u16string(),
+                          std::make_unique<views::ImageView>(
+                              ui::ImageModel::FromVectorIcon(
+                                  vector_icons::kLaunchIcon,
+                                  ui::kColorIconSecondary))))
+                      // Align the hover button text by adding the dialog
+                      // horizontal margins for the horizontal borders.
+                      .SetBorder(views::CreateEmptyBorder(
+                          gfx::Insets::VH(0, dialog_insets.left())))
+                      // Add bottom dialog margins since it's the last
+                      // element.
+                      .SetProperty(
+                          views::kMarginsKey,
+                          gfx::Insets::TLBR(0, 0,
+                                            dialog_insets.bottom() -
+                                                hover_button_vertical_spacing,
+                                            0))))
 
       .BuildChildren();
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
index eb1368e..e7d403a 100644
--- a/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_dialog_view.cc
@@ -62,6 +62,7 @@
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/layout_provider.h"
+#include "ui/views/style/typography.h"
 #include "ui/views/views_features.h"
 
 using media_session::mojom::MediaSessionAction;
@@ -72,9 +73,6 @@
 static constexpr int kImageWidthDip = 20;
 static constexpr int kVerticalMarginDip = 10;
 
-// Delta between the font size of the Live Translate title and subtitle.
-static constexpr int kLiveTranslateSubtitleFontSizeDelta = -2;
-
 std::u16string GetLiveCaptionTitle(PrefService* profile_prefs) {
   if (!base::FeatureList::IsEnabled(media::kLiveCaptionMultiLanguage)) {
     return l10n_util::GetStringUTF16(
@@ -286,9 +284,7 @@
     live_translate_container_->SetPreferredSize(
         gfx::Size(width, live_translate_height));
 
-    const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
-    live_translate_subtitle_->SetFontList(base_font_list.DeriveWithSizeDelta(
-        kLiveTranslateSubtitleFontSizeDelta));
+    live_translate_subtitle_->SetTextStyle(views::style::STYLE_SECONDARY);
 
     live_translate_label_wrapper_->SetPreferredSize(gfx::Size(
         width, live_translate_label_wrapper_->GetPreferredSize().height()));
diff --git a/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.cc
index 1745b86..fae7166 100644
--- a/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_entry.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_registry.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_toolbar_container.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/pref_names.h"
@@ -31,9 +32,11 @@
       // TODO(b/269331995): Localize menu item label.
       name_(l10n_util::GetStringUTF16(IDS_SIDE_PANEL_COMPANION_TITLE)),
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-      icon_(vector_icons::kGoogleGLogoIcon),
+      icon_(vector_icons::kGoogleSuperGIcon),
+      disabled_icon_(vector_icons::kGoogleGLogoMonochromeIcon),
 #else
       icon_(vector_icons::kSearchIcon),
+      disabled_icon_(vector_icons::kSearchIcon),
 #endif
       pref_service_(browser->profile()->GetPrefs()) {
   if (auto* template_url_service =
@@ -113,7 +116,7 @@
     }
   }
   if (selection.active_tab_changed()) {
-    MaybeUpdatePinnedButtonEnabledState();
+    MaybeUpdateCompanionEnabledState();
   }
 }
 
@@ -121,7 +124,7 @@
     content::WebContents* contents,
     int index,
     TabChangeType change_type) {
-  MaybeUpdatePinnedButtonEnabledState();
+  MaybeUpdateCompanionEnabledState();
 }
 
 void SearchCompanionSidePanelCoordinator::
@@ -175,8 +178,14 @@
   }
 }
 
-void SearchCompanionSidePanelCoordinator::
-    MaybeUpdatePinnedButtonEnabledState() {
+void SearchCompanionSidePanelCoordinator::MaybeUpdateCompanionEnabledState() {
+  bool enabled = companion::IsCompanionAvailableForCurrentActiveTab(browser_);
+  MaybeUpdatePinnedButtonEnabledState(enabled);
+  MaybeUpdateComboboxEntryEnabledState(enabled);
+}
+
+void SearchCompanionSidePanelCoordinator::MaybeUpdatePinnedButtonEnabledState(
+    bool enabled) {
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser_);
   if (!browser_view) {
     return;
@@ -184,12 +193,32 @@
   SidePanelToolbarContainer* container =
       browser_view->toolbar()->side_panel_container();
   if (container && container->IsPinned(SidePanelEntry::Id::kSearchCompanion)) {
-    bool enabled = companion::IsCompanionAvailableForCurrentActiveTab(browser_);
-    container->GetPinnedButtonForId(SidePanelEntry::Id::kSearchCompanion)
-        .SetEnabled(enabled);
+    ToolbarButton& button =
+        container->GetPinnedButtonForId(SidePanelEntry::Id::kSearchCompanion);
+    button.SetEnabled(enabled);
+    button.SetVectorIcon(enabled ? icon() : disabled_icon());
   }
 }
 
+void SearchCompanionSidePanelCoordinator::MaybeUpdateComboboxEntryEnabledState(
+    bool enabled) {
+  auto* registry = SidePanelRegistry::Get(
+      browser_->tab_strip_model()->GetActiveWebContents());
+  if (!registry) {
+    return;
+  }
+
+  auto* entry = registry->GetEntryForKey(
+      SidePanelEntry::Key(SidePanelEntry::Id::kSearchCompanion));
+  if (!entry) {
+    return;
+  }
+
+  entry->ResetIcon(ui::ImageModel::FromVectorIcon(
+      (enabled ? icon() : disabled_icon()), ui::kColorIcon,
+      /*icon_size=*/16));
+}
+
 void SearchCompanionSidePanelCoordinator::OnTemplateURLServiceShuttingDown() {
   template_url_service_observation_.Reset();
 }
diff --git a/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.h
index 95cc5d08..39285e5 100644
--- a/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/search_companion/search_companion_side_panel_coordinator.h
@@ -46,6 +46,7 @@
 
   std::u16string name() const { return name_; }
   const gfx::VectorIcon& icon() { return *icon_; }
+  const gfx::VectorIcon& disabled_icon() { return *disabled_icon_; }
 
   // TabStripModelObserver:
   void OnTabStripModelChanged(
@@ -74,9 +75,15 @@
   // Updates CSC availability in side panel.
   void UpdateCompanionAvailabilityInSidePanel();
 
+  // Update companion enabled state based on active tab's url.
+  void MaybeUpdateCompanionEnabledState();
+
   // Update whether the CSC pinned toolbar button is enabled if the button is
-  // pinned based on active tab's url.
-  void MaybeUpdatePinnedButtonEnabledState();
+  // pinned.
+  void MaybeUpdatePinnedButtonEnabledState(bool enabled);
+
+  // Update whether the CSC combobox entry is enabled if the entry exists.
+  void MaybeUpdateComboboxEntryEnabledState(bool enabled);
 
   // Called if there is a change in the state of policy pref.
   void OnPolicyPrefChanged();
@@ -87,6 +94,7 @@
   raw_ptr<Browser> browser_;
   std::u16string name_;
   const raw_ref<const gfx::VectorIcon, ExperimentalAsh> icon_;
+  const raw_ref<const gfx::VectorIcon, ExperimentalAsh> disabled_icon_;
   raw_ptr<PrefService> pref_service_;
   bool dsp_is_google_ = false;
   bool csc_enabled_via_policy_ = false;
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc b/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc
index c990ba78..202d2c7 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service_browsertest.cc
@@ -8,6 +8,7 @@
 #include <sstream>
 #include <vector>
 
+#include "base/containers/fixed_flat_set.h"
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/feature_engagement/tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/user_education/user_education_service.h"
 #include "chrome/browser/ui/user_education/user_education_service_factory.h"
@@ -27,10 +29,13 @@
 #include "components/feature_engagement/test/scoped_iph_feature_list.h"
 #include "components/user_education/common/feature_promo_registry.h"
 #include "components/user_education/common/feature_promo_specification.h"
+#include "components/user_education/common/tutorial_identifier.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/interaction_sequence.h"
 
 namespace {
 
@@ -173,9 +178,9 @@
 }
 
 template <typename T>
-std::string FailuresToString(const T& failures) {
+std::string FailuresToString(const T& failures, const char* type) {
   std::ostringstream oss;
-  oss << "Errors found during IPH configuration validation.";
+  oss << "Errors found during " << type << " configuration validation.";
   for (auto& failure : failures) {
     oss << "\n" << failure;
   }
@@ -371,5 +376,98 @@
     }
   }
 
-  EXPECT_TRUE(failures.empty()) << FailuresToString(failures);
+  EXPECT_TRUE(failures.empty()) << FailuresToString(failures, "IPH");
+}
+
+namespace {
+
+enum class TutorialFailureReason {
+  kNone,
+  kLikelySkippedStep,
+  kWaitForAlwaysVisibleElement,
+};
+
+struct TutorialFailure {
+  user_education::TutorialIdentifier tutorial_id;
+  int step_number = -1;
+  ui::ElementIdentifier identifier;
+  TutorialFailureReason reason = TutorialFailureReason::kNone;
+};
+
+std::ostream& operator<<(std::ostream& os, const TutorialFailure& failure) {
+  os << failure.tutorial_id;
+  switch (failure.reason) {
+    case TutorialFailureReason::kNone:
+      NOTREACHED();
+      break;
+    case TutorialFailureReason::kLikelySkippedStep:
+      os << " shows a bubble anchored to an always-visible UI element "
+         << failure.identifier << " (step " << failure.step_number
+         << ") immediately after another bubble. This is likely to cause the "
+            " previous step to be skipped, as the transition will be "
+            "instantaneous. Please insert a hidden step between these steps "
+            "that detects the action you expect the user to take to advance "
+            "the tutorial (e.g. an activation step for a button press, or an "
+            "event step for the result of some process).";
+      break;
+    case TutorialFailureReason::kWaitForAlwaysVisibleElement:
+      os << " is waiting for element " << failure.identifier
+         << " to become visible in the current context (step "
+         << failure.step_number
+         << "), and is set to only show on state change (i.e. not visible -> "
+            "visible). However, this element is already always visible, so the "
+            "bubble will likely never show. If you did not intend to wait for "
+            "a state transition, make sure `transition_only_on_event` is "
+            "false. If you were waiting for another window to appear, make "
+            "sure that `context_mode` is ContextMode::kAny.";
+      break;
+  }
+  return os;
+}
+
+}  // namespace
+
+IN_PROC_BROWSER_TEST_F(BrowserUserEducationServiceBrowserTest,
+                       TutorialConsistencyCheck) {
+  const auto kAlwaysPresentElementIds =
+      base::MakeFixedFlatSet<ui::ElementIdentifier>(
+          {kAppMenuButtonElementId, kAvatarButtonElementId,
+           kBackButtonElementId, kBrowserViewElementId, kForwardButtonElementId,
+           kNewTabButtonElementId, kOmniboxElementId, kSidePanelButtonElementId,
+           kTabSearchButtonElementId, kTabStripElementId,
+           kTabStripRegionElementId, kTopContainerElementId});
+
+  std::vector<TutorialFailure> failures;
+
+  auto* const service =
+      UserEducationServiceFactory::GetForProfile(browser()->profile());
+  const auto& registry = service->tutorial_registry();
+  for (auto identifier : registry.GetTutorialIdentifiers()) {
+    const auto* const description = registry.GetTutorialDescription(identifier);
+    bool was_show_bubble = false;
+    int step_count = 0;
+    for (const auto& step : description->steps) {
+      ++step_count;
+      const bool is_show_bubble =
+          (step.step_type == ui::InteractionSequence::StepType::kShown &&
+           step.body_text_id);
+      const bool is_always_visible =
+          base::Contains(kAlwaysPresentElementIds, step.element_id);
+      if (is_show_bubble && was_show_bubble && is_always_visible &&
+          !step.transition_only_on_event) {
+        failures.push_back(
+            TutorialFailure{identifier, step_count, step.element_id,
+                            TutorialFailureReason::kLikelySkippedStep});
+      } else if (is_always_visible && step.transition_only_on_event &&
+                 step.context_mode !=
+                     ui::InteractionSequence::ContextMode::kAny) {
+        failures.push_back(TutorialFailure{
+            identifier, step_count, step.element_id,
+            TutorialFailureReason::kWaitForAlwaysVisibleElement});
+      }
+      was_show_bubble = is_show_bubble;
+    }
+  }
+
+  EXPECT_TRUE(failures.empty()) << FailuresToString(failures, "Tutorial");
 }
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
index 4f3ca27..442d93a 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
@@ -97,19 +97,21 @@
 
 PWAConfirmationBubbleView::PWAConfirmationBubbleView(
     views::View* anchor_view,
+    content::WebContents* web_contents,
     PageActionIconView* highlight_icon_button,
     std::unique_ptr<WebAppInstallInfo> web_app_info,
     chrome::AppInstallationAcceptanceCallback callback,
     chrome::PwaInProductHelpState iph_state,
     PrefService* prefs,
     feature_engagement::Tracker* tracker)
-    : LocationBarBubbleDelegateView(anchor_view, nullptr),
+    : LocationBarBubbleDelegateView(anchor_view, web_contents),
       highlight_icon_button_(highlight_icon_button),
       web_app_info_(std::move(web_app_info)),
       callback_(std::move(callback)),
       iph_state_(iph_state),
       prefs_(prefs),
       tracker_(tracker) {
+  SetCloseOnMainFrameOriginNavigation(true);
   DCHECK(web_app_info_);
   DCHECK(prefs_);
 
@@ -320,8 +322,8 @@
   feature_engagement::Tracker* tracker =
       feature_engagement::TrackerFactory::GetForBrowserContext(browser_context);
   g_bubble_ = new PWAConfirmationBubbleView(
-      anchor_view, icon, std::move(web_app_info), std::move(callback),
-      iph_state, prefs, tracker);
+      anchor_view, web_contents, icon, std::move(web_app_info),
+      std::move(callback), iph_state, prefs, tracker);
 
   views::BubbleDialogDelegateView::CreateBubble(g_bubble_)->Show();
   base::RecordAction(base::UserMetricsAction("WebAppInstallShown"));
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h
index f5b9bfb8..98595ed 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h
@@ -10,10 +10,13 @@
 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "components/prefs/pref_service.h"
-#include "content/public/browser/web_contents.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/views/widget/widget.h"
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 namespace views {
 class Checkbox;
 }
@@ -33,6 +36,7 @@
   static PWAConfirmationBubbleView* GetBubble();
 
   PWAConfirmationBubbleView(views::View* anchor_view,
+                            content::WebContents* web_contents,
                             PageActionIconView* highlight_icon_button,
                             std::unique_ptr<WebAppInstallInfo> web_app_info,
                             chrome::AppInstallationAcceptanceCallback callback,
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc
index 915dfdf..1a52b5c5 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/os_integration/os_integration_manager.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
@@ -20,14 +21,24 @@
 #include "chrome/browser/web_applications/web_app_prefs_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/test/browser_test.h"
+#include "ui/views/test/widget_test.h"
+#include "ui/views/widget/any_widget_observer.h"
 
-class PWAConfirmationBubbleViewBrowserTest : public InProcessBrowserTest {
+namespace web_app {
+namespace {
+
+class PWAConfirmationBubbleViewBrowserTest
+    : public WebAppControllerBrowserTest {
  public:
-  PWAConfirmationBubbleViewBrowserTest() {
+  PWAConfirmationBubbleViewBrowserTest()
+      : prevent_close_on_deactivate_(
+            PWAConfirmationBubbleView::SetDontCloseOnDeactivateForTesting()) {
     scoped_feature_list_.InitWithFeatures(
         {feature_engagement::kIPHDesktopPwaInstallFeature}, {});
   }
@@ -38,14 +49,14 @@
     app_info->title = u"Test app 2";
     app_info->start_url = GURL("https://example2.com");
     app_info->manifest_id = GURL("https://example2.com");
-    app_info->user_display_mode = web_app::mojom::UserDisplayMode::kStandalone;
+    app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
     return app_info;
   }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-  web_app::OsIntegrationManager::ScopedSuppressForTesting
-      scoped_os_suppression_;
+
+  base::AutoReset<bool> prevent_close_on_deactivate_;
 };
 
 IN_PROC_BROWSER_TEST_F(PWAConfirmationBubbleViewBrowserTest,
@@ -54,9 +65,8 @@
   app_info->title = u"Test app";
   app_info->start_url = GURL("https://example.com");
   Profile* profile = browser()->profile();
-  web_app::AppId app_id =
-      web_app::test::InstallWebApp(profile, std::move(app_info));
-  Browser* browser = web_app::LaunchWebAppBrowser(profile, app_id);
+  AppId app_id = test::InstallWebApp(profile, std::move(app_info));
+  Browser* browser = ::web_app::LaunchWebAppBrowser(profile, app_id);
 
   app_info = GetAppInfo();
   // Tests that we don't crash when showing the install prompt in a PWA window.
@@ -69,7 +79,7 @@
   app_info = std::make_unique<WebAppInstallInfo>();
   app_info->title = u"Test app 3";
   app_info->start_url = GURL("https://example3.com");
-  app_info->user_display_mode = web_app::mojom::UserDisplayMode::kStandalone;
+  app_info->user_display_mode = mojom::UserDisplayMode::kStandalone;
   chrome::ShowPWAInstallBubble(
       browser->tab_strip_model()->GetActiveWebContents(), std::move(app_info),
       base::DoNothing());
@@ -126,20 +136,15 @@
                                       ->GetActiveWebContents()
                                       ->GetBrowserContext())
           ->GetPrefs();
-  web_app::AppId app_id =
-      web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);
-  EXPECT_EQ(
-      web_app::GetIntWebAppPref(pref_service, app_id, web_app::kIphIgnoreCount)
-          .value(),
-      1);
-  EXPECT_TRUE(web_app::GetTimeWebAppPref(pref_service, app_id,
-                                         web_app::kIphLastIgnoreTime)
-                  .has_value());
+  AppId app_id = GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);
+  EXPECT_EQ(GetIntWebAppPref(pref_service, app_id, kIphIgnoreCount).value(), 1);
+  EXPECT_TRUE(
+      GetTimeWebAppPref(pref_service, app_id, kIphLastIgnoreTime).has_value());
   {
     const auto& dict =
         pref_service->GetDict(prefs::kWebAppsAppAgnosticIphState);
-    EXPECT_EQ(dict.FindInt(web_app::kIphIgnoreCount).value_or(0), 1);
-    EXPECT_TRUE(dict.contains(web_app::kIphLastIgnoreTime));
+    EXPECT_EQ(dict.FindInt(kIphIgnoreCount).value_or(0), 1);
+    EXPECT_TRUE(dict.contains(kIphLastIgnoreTime));
   }
 }
 
@@ -147,20 +152,18 @@
                        AcceptDialogResetIphCounters) {
   auto app_info = GetAppInfo();
   GURL start_url = app_info->start_url;
-  web_app::AppId app_id =
-      web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);
+  AppId app_id = GenerateAppId(/*manifest_id=*/absl::nullopt, start_url);
   PrefService* pref_service =
       Profile::FromBrowserContext(browser()
                                       ->tab_strip_model()
                                       ->GetActiveWebContents()
                                       ->GetBrowserContext())
           ->GetPrefs();
-  web_app::UpdateIntWebAppPref(pref_service, app_id, web_app::kIphIgnoreCount,
-                               1);
+  UpdateIntWebAppPref(pref_service, app_id, kIphIgnoreCount, 1);
   {
     ScopedDictPrefUpdate update(pref_service,
                                 prefs::kWebAppsAppAgnosticIphState);
-    update->Set(web_app::kIphIgnoreCount, 1);
+    update->Set(kIphIgnoreCount, 1);
   }
   base::RunLoop loop;
   // Show the PWA install dialog.
@@ -179,13 +182,39 @@
   bubble_dialog->AcceptDialog();
   loop.Run();
 
-  EXPECT_EQ(
-      web_app::GetIntWebAppPref(pref_service, app_id, web_app::kIphIgnoreCount)
-          .value(),
-      0);
+  EXPECT_EQ(GetIntWebAppPref(pref_service, app_id, kIphIgnoreCount).value(), 0);
   {
     const auto& dict =
         pref_service->GetDict(prefs::kWebAppsAppAgnosticIphState);
-    EXPECT_EQ(dict.FindInt(web_app::kIphIgnoreCount).value_or(0), 0);
+    EXPECT_EQ(dict.FindInt(kIphIgnoreCount).value_or(0), 0);
   }
 }
+
+IN_PROC_BROWSER_TEST_F(PWAConfirmationBubbleViewBrowserTest,
+                       CancelFromNavigation) {
+  absl::optional<bool> dialog_accepted_ = absl::nullopt;
+  chrome::ShowPWAInstallBubble(
+      browser()->tab_strip_model()->GetActiveWebContents(), GetAppInfo(),
+      base::BindLambdaForTesting(
+          [&](bool accepted,
+              std::unique_ptr<WebAppInstallInfo> app_info_callback) {
+            dialog_accepted_ = accepted;
+          }));
+  PWAConfirmationBubbleView* bubble_dialog =
+      PWAConfirmationBubbleView::GetBubble();
+
+  base::HistogramTester histograms;
+  views::test::WidgetDestroyedWaiter destroy_waiter(bubble_dialog->GetWidget());
+  ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
+      browser(), GURL(url::kAboutBlankURL), /*number_of_navigations=*/1);
+
+  destroy_waiter.Wait();
+  ASSERT_TRUE(dialog_accepted_);
+  ASSERT_FALSE(dialog_accepted_.value());
+
+  histograms.ExpectUniqueSample("WebApp.InstallConfirmation.CloseReason",
+                                views::Widget::ClosedReason::kUnspecified, 1);
+}
+
+}  // namespace
+}  // namespace web_app
diff --git a/chrome/browser/ui/webauthn/sheet_models.cc b/chrome/browser/ui/webauthn/sheet_models.cc
index d2b9010..867b29c 100644
--- a/chrome/browser/ui/webauthn/sheet_models.cc
+++ b/chrome/browser/ui/webauthn/sheet_models.cc
@@ -1308,11 +1308,11 @@
     default;
 
 std::u16string AuthenticatorConnectingSheetModel::GetStepTitle() const {
-  return u"Connecting with your device (UNTRANSLATED)";
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE);
 }
 
 std::u16string AuthenticatorConnectingSheetModel::GetStepDescription() const {
-  return u"This will just take a moment (UNTRANSLATED)";
+  return u"";
 }
 
 // AuthenticatorConnectedSheetModel ------------------------------------------
@@ -1331,11 +1331,11 @@
 }
 
 std::u16string AuthenticatorConnectedSheetModel::GetStepTitle() const {
-  return u"Check your device (UNTRANSLATED)";
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLE_ACTIVATE_TITLE_DEVICE);
 }
 
 std::u16string AuthenticatorConnectedSheetModel::GetStepDescription() const {
-  return u"Follow the steps described on your device (UNTRANSLATED)";
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION);
 }
 
 // AuthenticatorCableErrorSheetModel ------------------------------------------
@@ -1359,7 +1359,11 @@
 }
 
 std::u16string AuthenticatorCableErrorSheetModel::GetStepDescription() const {
-  return u"Something went wrong (UNTRANSLATED)";
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION);
+}
+
+std::u16string AuthenticatorCableErrorSheetModel::GetCancelButtonLabel() const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE);
 }
 
 // AuthenticatorCreatePasskeySheetModel
diff --git a/chrome/browser/ui/webauthn/sheet_models.h b/chrome/browser/ui/webauthn/sheet_models.h
index 3cb725d2..75b2fd6 100644
--- a/chrome/browser/ui/webauthn/sheet_models.h
+++ b/chrome/browser/ui/webauthn/sheet_models.h
@@ -580,6 +580,7 @@
   bool IsOtherMechanismButtonVisible() const override;
   std::u16string GetStepTitle() const override;
   std::u16string GetStepDescription() const override;
+  std::u16string GetCancelButtonLabel() const override;
 };
 
 class AuthenticatorCreatePasskeySheetModel
diff --git a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc
index 85f4764..c507f1ff2 100644
--- a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.cc
@@ -49,9 +49,14 @@
   CallExternalAPI("showConnectingToWifi");
 }
 
-void QuickStartScreenHandler::ShowConnectedToWifi(std::string ssid,
-                                                  std::string password) {
-  CallExternalAPI("showConnectedToWifi", ssid, password);
+void QuickStartScreenHandler::ShowConnectedToWifi(
+    std::string ssid,
+    absl::optional<std::string> password) {
+  if (password.has_value()) {
+    CallExternalAPI("showConnectedToWifi", ssid, password.value());
+  } else {
+    CallExternalAPI("showConnectedToWifi", ssid);
+  }
 }
 
 void QuickStartScreenHandler::ShowTransferringGaiaCredentials() {
diff --git a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h
index 60ec139..00be8121 100644
--- a/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/quick_start_screen_handler.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_QUICK_START_SCREEN_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_ASH_LOGIN_QUICK_START_SCREEN_HANDLER_H_
 
+#include <optional>
+
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
 #include "chrome/browser/ash/login/oobe_quick_start/verification_shapes.h"
@@ -29,7 +31,8 @@
   virtual void SetQRCode(base::Value::List blob) = 0;
   virtual void SetDiscoverableName(const std::string& discoverable_name) = 0;
   virtual void ShowConnectingToWifi() = 0;
-  virtual void ShowConnectedToWifi(std::string ssid, std::string password) = 0;
+  virtual void ShowConnectedToWifi(std::string ssid,
+                                   absl::optional<std::string> password) = 0;
   virtual void ShowTransferringGaiaCredentials() = 0;
   virtual void ShowFidoAssertionReceived(std::string email) = 0;
 };
@@ -53,7 +56,8 @@
   void SetQRCode(base::Value::List blob) override;
   void SetDiscoverableName(const std::string& discoverable_name) override;
   void ShowConnectingToWifi() override;
-  void ShowConnectedToWifi(std::string ssid, std::string password) override;
+  void ShowConnectedToWifi(std::string ssid,
+                           absl::optional<std::string> password) override;
   void ShowTransferringGaiaCredentials() override;
   void ShowFidoAssertionReceived(std::string email) override;
 
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index c020dcd..e21ebfd2 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -658,6 +658,8 @@
        IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL},
       {"highEfficiencyModeRadioGroupAriaLabel",
        IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL},
+      {"highEfficiencyChooseDiscardTimeAriaLabel",
+       IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_CHOOSE_DISCARD_TIME_ARIA_LABEL},
       {"batteryPageTitle", IDS_SETTINGS_BATTERY_PAGE_TITLE},
       {"batterySaverModeLabel",
        IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING},
diff --git a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
index d35217d..fd529c0 100644
--- a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
@@ -145,6 +145,8 @@
       {"folderMenuLabel", IDS_FOLDER_OPTIONS_LABEL},
       {"openFolderLabel", IDS_BOOKMARKS_OPEN_FOLDER_LABEL},
       {"openBookmarkLabel", IDS_BOOKMARKS_OPEN_BOOKMARK_LABEL},
+      {"selectFolderLabel", IDS_BOOKMARKS_SELECT_FOLDER_LABEL},
+      {"selectBookmarkLabel", IDS_BOOKMARKS_SELECT_BOOKMARK_LABEL},
       {"a11yDescriptionPriceTracking",
        IDS_BOOKMARK_ACCESSIBLE_DESCRIPTION_PRICE_TRACKING},
       {"a11yDescriptionPriceChange",
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
index 5c1d6c3..7d3ab1c2 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -280,6 +280,37 @@
   ContactNextPhoneByName(name);
 }
 
+void AuthenticatorRequestDialogModel::OnCableEvent(
+    device::cablev2::Event event) {
+  switch (event) {
+    case device::cablev2::Event::kPhoneConnected:
+    case device::cablev2::Event::kBLEAdvertReceived:
+      if (current_step_ != Step::kCableV2Connecting) {
+        SetCurrentStep(Step::kCableV2Connecting);
+        cable_connecting_sheet_timer_.Start(
+            FROM_HERE, base::Milliseconds(1250),
+            base::BindOnce(&AuthenticatorRequestDialogModel::
+                               OnCableConnectingTimerComplete,
+                           weak_factory_.GetWeakPtr()));
+      }
+      break;
+    case device::cablev2::Event::kReady:
+      if (cable_connecting_sheet_timer_.IsRunning()) {
+        cable_connecting_ready_to_advance_ = true;
+      } else {
+        SetCurrentStep(Step::kCableV2Connected);
+      }
+      break;
+  }
+}
+
+void AuthenticatorRequestDialogModel::OnCableConnectingTimerComplete() {
+  if (cable_connecting_ready_to_advance_ &&
+      current_step_ == Step::kCableV2Connecting) {
+    SetCurrentStep(Step::kCableV2Connected);
+  }
+}
+
 void AuthenticatorRequestDialogModel::StartPhonePairing() {
   DCHECK(cable_qr_string_);
   SetCurrentStep(Step::kCableV2QRCode);
@@ -589,6 +620,11 @@
   return false;
 }
 
+bool AuthenticatorRequestDialogModel::OnHybridTransportError() {
+  SetCurrentStep(Step::kCableV2Error);
+  return true;
+}
+
 void AuthenticatorRequestDialogModel::OnBluetoothPoweredStateChanged(
     bool powered) {
   transport_availability_.is_ble_powered = powered;
@@ -896,6 +932,11 @@
   }
 
   current_step_ = step;
+
+  // Reset state related to automatically advancing the state.
+  cable_connecting_sheet_timer_.Stop();
+  cable_connecting_ready_to_advance_ = false;
+
   if (should_dialog_be_closed()) {
     // The dialog will close itself.
     showing_dialog_ = false;
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
index df095ac..30c8fd70 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model.h
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -17,6 +17,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/strings/string_piece.h"
+#include "base/timer/timer.h"
 #include "base/types/strong_alias.h"
 #include "build/build_config.h"
 #include "chrome/browser/webauthn/authenticator_reference.h"
@@ -24,6 +25,7 @@
 #include "chrome/browser/webauthn/observable_authenticator_list.h"
 #include "content/public/browser/authenticator_request_client_delegate.h"
 #include "content/public/browser/global_routing_id.h"
+#include "device/fido/cable/v2_constants.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_request_handler_base.h"
 #include "device/fido/fido_transport_protocol.h"
@@ -329,6 +331,13 @@
   // Called when an attempt to contact a phone failed.
   void OnPhoneContactFailed(const std::string& name);
 
+  // Called when some caBLE event (e.g. receiving a BLE message, connecting to
+  // the tunnel server, etc) happens.
+  void OnCableEvent(device::cablev2::Event event);
+
+  // Called when `cable_connecting_sheet_timer_` completes.
+  void OnCableConnectingTimerComplete();
+
   // StartPhonePairing triggers the display of a QR code for pairing a new
   // phone.
   void StartPhonePairing();
@@ -455,6 +464,10 @@
   // Returns true if the event was handled.
   bool OnWinUserCancelled();
 
+  // To be called when a hybrid connection fails. Returns true if the event
+  // was handled.
+  bool OnHybridTransportError();
+
   // To be called when the Bluetooth adapter powered state changes.
   void OnBluetoothPoweredStateChanged(bool powered);
 
@@ -794,6 +807,21 @@
   // to contact the indicated phone.
   base::RepeatingCallback<void(size_t)> contact_phone_callback_;
 
+  // cable_device_ready_ is true if a CTAP-level request has been sent to a
+  // caBLE device. At this point we assume that any transport errors are
+  // cancellations on the device, not networking errors.
+  bool cable_device_ready_ = false;
+
+  // cable_connecting_sheet_timer_ is started when we start displaying
+  // the "connecting..." sheet for a caBLE connection. To avoid flashing the UI,
+  // the sheet won't be automatically replaced until it completes.
+  base::OneShotTimer cable_connecting_sheet_timer_;
+
+  // cable_connecting_ready_to_advance_ is set to true if we are ready to
+  // advance the "connecting" sheet but are waiting for
+  // `cable_connecting_sheet_timer_` to complete.
+  bool cable_connecting_ready_to_advance_ = false;
+
   absl::optional<std::string> cable_qr_string_;
 
   // For MakeCredential requests, the PublicKeyCredentialUserEntity associated
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
index 13829d5..163b63b 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model_unittest.cc
@@ -172,7 +172,9 @@
  public:
   using Step = AuthenticatorRequestDialogModel::Step;
 
-  AuthenticatorRequestDialogModelTest() = default;
+  AuthenticatorRequestDialogModelTest()
+      : ChromeRenderViewHostTestHarness(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   AuthenticatorRequestDialogModelTest(
       const AuthenticatorRequestDialogModelTest&) = delete;
@@ -1243,3 +1245,54 @@
   }
 }
 #endif
+
+TEST_F(AuthenticatorRequestDialogModelTest, AdvanceThroughCableV2States) {
+  AuthenticatorRequestDialogModel model(/*render_frame_host=*/nullptr);
+  model.set_cable_transport_info(/*extension_is_v2=*/absl::nullopt, {},
+                                 base::DoNothing(), absl::nullopt);
+  TransportAvailabilityInfo transports_info;
+  transports_info.is_ble_powered = true;
+  transports_info.request_type = device::FidoRequestType::kGetAssertion;
+  transports_info.available_transports = {AuthenticatorTransport::kHybrid};
+  model.StartFlow(std::move(transports_info),
+                  /*is_conditional_mediation=*/false);
+
+  model.OnCableEvent(device::cablev2::Event::kPhoneConnected);
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connecting);
+  model.OnCableEvent(device::cablev2::Event::kBLEAdvertReceived);
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connecting);
+  model.OnCableEvent(device::cablev2::Event::kReady);
+  // kCableV2Connecting won't flash by too quickly, so it'll still be showing.
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connecting);
+
+  task_environment()->FastForwardBy(base::Seconds(2));
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connected);
+}
+
+TEST_F(AuthenticatorRequestDialogModelTest,
+       AdvanceThroughCableV2StatesStopTimer) {
+  AuthenticatorRequestDialogModel model(/*render_frame_host=*/nullptr);
+  model.set_cable_transport_info(/*extension_is_v2=*/absl::nullopt, {},
+                                 base::DoNothing(), absl::nullopt);
+  TransportAvailabilityInfo transports_info;
+  transports_info.is_ble_powered = true;
+  transports_info.request_type = device::FidoRequestType::kGetAssertion;
+  transports_info.available_transports = {AuthenticatorTransport::kHybrid};
+  model.StartFlow(std::move(transports_info),
+                  /*is_conditional_mediation=*/false);
+
+  model.OnCableEvent(device::cablev2::Event::kPhoneConnected);
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connecting);
+  model.OnCableEvent(device::cablev2::Event::kBLEAdvertReceived);
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connecting);
+  model.OnCableEvent(device::cablev2::Event::kReady);
+  // kCableV2Connecting won't flash by too quickly, so it'll still be showing.
+  EXPECT_EQ(model.current_step(), Step::kCableV2Connecting);
+
+  // Moving to a different step should stop the timer so that kCableV2Connected
+  // never shows.
+  model.SetCurrentStepForTesting(Step::kCableActivate);
+
+  task_environment()->FastForwardBy(base::Seconds(10));
+  EXPECT_EQ(model.current_step(), Step::kCableActivate);
+}
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index 7f90c21e..bb8a88a 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -435,6 +435,14 @@
     return false;
   }
 
+  // If the UI was already in the state where we asked the user to complete the
+  // transaction on the other device then any errors are immediately resolved.
+  // Very likely the user canceled on the phone and doesn't want to see another
+  // error UI on the desktop.
+  if (cable_device_ready_) {
+    return false;
+  }
+
   switch (reason) {
     case InterestingFailureReason::kTimeout:
       dialog_model_->OnRequestTimeout();
@@ -474,6 +482,8 @@
       break;
     case InterestingFailureReason::kWinUserCancelled:
       return dialog_model_->OnWinUserCancelled();
+    case InterestingFailureReason::kHybridTransportError:
+      return dialog_model_->OnHybridTransportError();
   }
   return true;
 }
@@ -649,6 +659,9 @@
         base::BindRepeating(
             &ChromeAuthenticatorRequestDelegate::OnInvalidatedCablePairing,
             weak_ptr_factory_.GetWeakPtr()));
+    discovery_factory->set_cable_event_callback(
+        base::BindRepeating(&ChromeAuthenticatorRequestDelegate::OnCableEvent,
+                            weak_ptr_factory_.GetWeakPtr()));
     if (SystemNetworkContextManager::GetInstance()) {
       discovery_factory->set_network_context(
           SystemNetworkContextManager::GetInstance()->GetContext());
@@ -940,3 +953,16 @@
   // notification has been sent.
   dialog_model_->OnPhoneContactFailed(phone_names_.at(failed_contact_index));
 }
+
+void ChromeAuthenticatorRequestDelegate::OnCableEvent(
+    device::cablev2::Event event) {
+  if (!base::FeatureList::IsEnabled(device::kWebAuthnNewHybridUI)) {
+    return;
+  }
+
+  if (event == device::cablev2::Event::kReady) {
+    cable_device_ready_ = true;
+  }
+
+  dialog_model_->OnCableEvent(event);
+}
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index bdb974d..1dac6bc 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -235,6 +235,7 @@
   bool ShouldPermitCableExtension(const url::Origin& origin);
 
   void OnInvalidatedCablePairing(size_t failed_contact_index);
+  void OnCableEvent(device::cablev2::Event event);
 
   const content::GlobalRenderFrameHostId render_frame_host_id_;
   const std::unique_ptr<AuthenticatorRequestDialogModel> dialog_model_;
@@ -265,6 +266,11 @@
   // See `SetPassEmptyUsbDeviceManagerForTesting`.
   bool pass_empty_usb_device_manager_ = false;
 
+  // cable_device_ready_ is true if a caBLE handshake has completed. At this
+  // point we assume that any errors were communicated on the caBLE device and
+  // don't show errors on the desktop too.
+  bool cable_device_ready_ = false;
+
  private:
   base::WeakPtrFactory<ChromeAuthenticatorRequestDelegate> weak_ptr_factory_{
       this};
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 61967a2..781f21ec 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1685980729-fa83326bccfabab51437a6dfc854819e645b4aa9.profdata
+chrome-mac-arm-main-1685994862-67c48361c713580063ad5470cc6ba7abe2dc053f.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 531295d..1d570c8 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1685966279-93bf058782db7fe1e90b13bb508c67d315685483.profdata
+chrome-mac-main-1685987829-982140572a658d0d60c9601e7598a78f2edf8125.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 75b1964..a6f9049 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1685977067-6fb06967e4238d5c4b1fa6013f2db02bf96266a4.profdata
+chrome-win32-main-1685987829-ceb73971f32ca82b4bfaf56c9dd5cf1e87d8b714.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 72fddd4..a291854 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1685977067-9b2774c50b0d0ffbb9e2c5ac9f7b9f7448ee9709.profdata
+chrome-win64-main-1685987829-7e6f2228e3c057fa790c5be381eaefe489e0e76c.profdata
diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc
index 189a1070..a14ac1b 100644
--- a/chrome/common/extensions/extension_constants.cc
+++ b/chrome/common/extensions/extension_constants.cc
@@ -64,7 +64,10 @@
     kFirstRunDialogId,
     kEspeakSpeechSynthesisExtensionId,
     kGoogleSpeechSynthesisExtensionId,
-#endif        // BUILDFLAG(IS_CHROMEOS_ASH)
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+    kEmbeddedA11yHelperExtensionId,
+    kChromeVoxHelperExtensionId,
+#endif        // BUILDFLAG(IS_CHROMEOS_LACROS)
     nullptr,  // Null-terminated array.
 };
 
@@ -124,7 +127,17 @@
     "gjjabgpgjpampikjhjpfhneeoapjbjaf";
 const char kGoogleSpeechSynthesisOptionsPath[] = "/options.html";
 const char kHelpAppExtensionId[] = "honijodknafkokifofgiaalefdiedpko";
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+const char kEmbeddedA11yHelperExtensionId[] =
+    "kgonammgkackdilhodbgbmodpepjocdp";
+const char kEmbeddedA11yHelperExtensionPath[] = "accessibility";
+const char kEmbeddedA11yHelperManifestFilename[] =
+    "embedded_a11y_helper_manifest.json";
+const char kChromeVoxHelperExtensionId[] = "mlkejohendkgipaomdopolhpbihbhfnf";
+const char kChromeVoxHelperExtensionPath[] = "accessibility";
+const char kChromeVoxHelperManifestFilename[] =
+    "chromevox_helper_manifest.json";
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
 const char kAppStateNotInstalled[] = "not_installed";
 const char kAppStateInstalled[] = "installed";
diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h
index e9c43b0..61d38ef 100644
--- a/chrome/common/extensions/extension_constants.h
+++ b/chrome/common/extensions/extension_constants.h
@@ -239,7 +239,20 @@
 extern const char kEspeakSpeechSynthesisOptionsPath[];
 // The extension id of official HelpApp extension.
 extern const char kHelpAppExtensionId[];
-#endif
+#elif BUILDFLAG(IS_CHROMEOS_LACROS)
+// The extension id of the Lacros accessibility helper extension.
+extern const char kEmbeddedA11yHelperExtensionId[];
+// The path to the Lacros accessibility helper extension.
+extern const char kEmbeddedA11yHelperExtensionPath[];
+// The name of the manifest file for the Lacros accessibility helper extension.
+extern const char kEmbeddedA11yHelperManifestFilename[];
+// The extension id of the Lacros ChromeVox helper extension.
+extern const char kChromeVoxHelperExtensionId[];
+// The path to the Lacros ChromeVox helper extension.
+extern const char kChromeVoxHelperExtensionPath[];
+// The name of the manifest file for the Lacros ChromeVox helper extension.
+extern const char kChromeVoxHelperManifestFilename[];
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
 // What causes an extension to be installed? Used in histograms, so don't
 // change existing values.
diff --git a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc
index 331b56c..38fdbdc 100644
--- a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc
+++ b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder.cc
@@ -4,6 +4,8 @@
 
 #include "quick_start_decoder.h"
 
+#include <optional>
+
 #include "base/base64.h"
 #include "base/containers/fixed_flat_set.h"
 #include "base/containers/flat_tree.h"
@@ -368,15 +370,6 @@
     return;
   }
 
-  std::string* password =
-      wifi_network_information->FindString(kWifiNetworkPasswordKey);
-  if (!password) {
-    LOG(ERROR) << "Password cannot be found within WifiCredentialsResponse";
-    std::move(callback).Run(
-        nullptr, mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
-    return;
-  }
-
   std::string* security_type_string =
       wifi_network_information->FindString(kWifiNetworkSecurityTypeKey);
   if (!security_type_string) {
@@ -387,10 +380,10 @@
     return;
   }
 
-  absl::optional<mojom::WifiSecurityType> security_type =
+  absl::optional<mojom::WifiSecurityType> maybe_security_type =
       WifiSecurityTypeFromString(*security_type_string);
 
-  if (!security_type.has_value()) {
+  if (!maybe_security_type.has_value()) {
     {
       LOG(ERROR) << "Security type was not a valid value.";
       std::move(callback).Run(
@@ -399,6 +392,33 @@
     }
   }
 
+  mojom::WifiSecurityType security_type = maybe_security_type.value();
+
+  // Password may not be included in payload for passwordless, open networks.
+  absl::optional<std::string> password = absl::nullopt;
+  std::string* password_ptr =
+      wifi_network_information->FindString(kWifiNetworkPasswordKey);
+
+  if (password_ptr && security_type == mojom::WifiSecurityType::kOpen) {
+    LOG(ERROR) << "Password is found but network security type is open.";
+    std::move(callback).Run(
+        nullptr, mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+    return;
+  }
+
+  if (!password_ptr && security_type != mojom::WifiSecurityType::kOpen) {
+    LOG(ERROR) << "Password cannot be found within WifiCredentialsResponse but "
+                  "network is not open. wifi_security_type: "
+               << security_type;
+    std::move(callback).Run(
+        nullptr, mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+    return;
+  }
+
+  if (password_ptr) {
+    password = *password_ptr;
+  }
+
   absl::optional<bool> is_hidden =
       wifi_network_information->FindBool(kWifiNetworkIsHiddenKey);
   if (!is_hidden.has_value()) {
@@ -410,8 +430,8 @@
   }
 
   std::move(callback).Run(
-      mojom::WifiCredentials::New(*ssid, security_type.value(),
-                                  is_hidden.value(), *password),
+      mojom::WifiCredentials::New(*ssid, security_type, is_hidden.value(),
+                                  password),
       absl::nullopt);
 }
 
diff --git a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc
index 3b391b9..5f1fdea 100644
--- a/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc
+++ b/chrome/services/sharing/nearby/quick_start_decoder/quick_start_decoder_unittest.cc
@@ -482,6 +482,167 @@
   EXPECT_EQ(future.Get<1>(), absl::nullopt);
 }
 
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationPassesWhenMissingPasswordAndOpenNetwork) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "Open");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  ASSERT_FALSE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<0>()->password, absl::nullopt);
+}
+
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationFailsWhenPasswordFoundAndOpenNetwork) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkPasswordKey, "password");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "Open");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+}
+
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationFailsWhenMissingPasswordAndNotOpenNetwork_PSK) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "PSK");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+}
+
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationFailsWhenMissingPasswordAndNotOpenNetwork_WEP) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "WEP");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+}
+
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationFailsWhenMissingPasswordAndNotOpenNetwork_EAP) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "EAP");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+}
+
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationFailsWhenMissingPasswordAndNotOpenNetwork_OWE) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "OWE");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+}
+
+TEST_F(QuickStartDecoderTest,
+       ExtractWifiInformationFailsWhenMissingPasswordAndNotOpenNetwork_SAE) {
+  base::Value::Dict wifi_information;
+  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
+  wifi_information.Set(kWifiNetworkSecurityTypeKey, "SAE");
+  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
+
+  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
+  message.GetPayload()->Set(kWifiNetworkInformationKey,
+                            std::move(wifi_information));
+
+  base::test::TestFuture<
+      ::ash::quick_start::mojom::WifiCredentialsPtr,
+      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
+      future;
+
+  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
+
+  EXPECT_TRUE(future.Get<0>().is_null());
+  EXPECT_EQ(future.Get<1>(),
+            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
+}
+
 TEST_F(QuickStartDecoderTest, ExtractWifiInformationFailsIfSSIDLengthIsZero) {
   base::Value::Dict wifi_information;
   wifi_information.Set(kWifiNetworkSsidKey, "");
@@ -527,28 +688,6 @@
             mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
 }
 
-TEST_F(QuickStartDecoderTest, ExtractWifiInformationFailsWhenMissingPassword) {
-  base::Value::Dict wifi_information;
-  wifi_information.Set(kWifiNetworkSsidKey, "ssid");
-  wifi_information.Set(kWifiNetworkSecurityTypeKey, "PSK");
-  wifi_information.Set(kWifiNetworkIsHiddenKey, true);
-
-  QuickStartMessage message(QuickStartMessageType::kQuickStartPayload);
-  message.GetPayload()->Set(kWifiNetworkInformationKey,
-                            std::move(wifi_information));
-
-  base::test::TestFuture<
-      ::ash::quick_start::mojom::WifiCredentialsPtr,
-      absl::optional<::ash::quick_start::mojom::QuickStartDecoderError>>
-      future;
-
-  DoDecodeWifiCredentialsResponse(&message, future.GetCallback());
-
-  EXPECT_TRUE(future.Get<0>().is_null());
-  EXPECT_EQ(future.Get<1>(),
-            mojom::QuickStartDecoderError::kMessageDoesNotMatchSchema);
-}
-
 TEST_F(QuickStartDecoderTest,
        ExtractWifiInformationFailsWhenMissingSecurityType) {
   base::Value::Dict wifi_information;
diff --git a/chrome/services/util_win/OWNERS b/chrome/services/util_win/OWNERS
index cab4a9f..9e9a68c 100644
--- a/chrome/services/util_win/OWNERS
+++ b/chrome/services/util_win/OWNERS
@@ -1,2 +1 @@
 pmonette@chromium.org
-noel@chromium.org
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 0eb4325..9ade4bb 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1438,7 +1438,7 @@
       "//chrome/browser/safe_browsing:verdict_cache_manager_factory",
       "//chrome/browser/segmentation_platform:test_utils",
       "//chrome/browser/sharing/proto",
-      "//chrome/browser/storage_access_api:permissions",
+      "//chrome/browser/storage_access_api",
       "//chrome/browser/ui/color:color_headers",
       "//chrome/browser/ui/side_panel:side_panel_enums",
       "//chrome/browser/ui/tabs:tab_enums",
@@ -5420,7 +5420,7 @@
         # TODO(b/248270560): Restore the --flash once the bug is fixed.
         if (is_chromeos_lacros && chrome_pgo_phase == 1 &&
             (override_board == "jacuzzi" || override_board == "jacuzzi64")) {
-          args = [ "bin/cros_update_wrapper --board=${override_board} ../../tools/perf/run_benchmark" ]
+          args = [ "bin/cros_update_wrapper --board=${override_board} --skip-restart-ui ../../tools/perf/run_benchmark" ]
         } else {
           args = [ "bin/cros_update_wrapper --board=${override_board} --flash ../../tools/perf/run_benchmark" ]
         }
@@ -5966,6 +5966,9 @@
     "../browser/status_icons/status_icon_unittest.cc",
     "../browser/status_icons/status_tray_unittest.cc",
     "../browser/storage/durable_storage_permission_context_unittest.cc",
+    "../browser/storage_access_api/storage_access_api_service_factory_unittest.cc",
+    "../browser/storage_access_api/storage_access_api_service_impl_unittest.cc",
+    "../browser/storage_access_api/storage_access_api_tab_helper_unittest.cc",
     "../browser/storage_access_api/storage_access_grant_permission_context_unittest.cc",
     "../browser/subresource_filter/subresource_filter_history_observer_unittest.cc",
     "../browser/sync/session_sync_service_factory_unittest.cc",
@@ -6219,7 +6222,7 @@
     "//chrome/browser/segmentation_platform:test_utils",
     "//chrome/browser/share",
     "//chrome/browser/sharing/proto",
-    "//chrome/browser/storage_access_api:permissions",
+    "//chrome/browser/storage_access_api",
     "//chrome/browser/sync_file_system/drive_backend:sync_file_system_drive_proto",
     "//chrome/browser/top_level_storage_access_api:permissions",
     "//chrome/browser/ui:test_support",
diff --git a/chrome/test/data/webui/chromeos/print_management/print_management_test.ts b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
index a525cc7..4aee1bd 100644
--- a/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
+++ b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
@@ -1250,4 +1250,14 @@
         getElementTextContent('.message-detail'));
     assertEquals(expectedButtonLabel, getElementTextContent('cr-button'));
   });
+
+  // Verify expected illustration used in empty state UI.
+  test('ensureEmptyStateSvg', async () => {
+    const expectedIcon = 'print-management:empty-state';
+    await initPrinterSetupInfoElement();
+
+    const iconEl =
+        querySelector<IronIconElement>(printerSetupInfoElement!, 'iron-icon');
+    assertEquals(expectedIcon, iconEl?.icon);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/main_page_container_test.js b/chrome/test/data/webui/settings/chromeos/main_page_container_test.js
index 25db0f7..97cb719 100644
--- a/chrome/test/data/webui/settings/chromeos/main_page_container_test.js
+++ b/chrome/test/data/webui/settings/chromeos/main_page_container_test.js
@@ -174,7 +174,6 @@
         loadTimeData.overrideValues({isRevampWayfindingEnabled: true});
         document.body.classList.add('revamp-wayfinding-enabled');
 
-        Router.getInstance().navigateTo(routes.BASIC);
         mainPageContainer = init();
       });
 
@@ -246,6 +245,22 @@
 
             assertOnlyActivePageIsVisible('bluetooth');
           });
+
+          test(
+              'to Root by clearing search should show Network page',
+              async () => {
+                Router.getInstance().navigateTo(
+                    routes.BASIC, new URLSearchParams('search=bluetooth'));
+
+                const navigationCompletePromise =
+                    eventToPromise('show-container', window);
+                Router.getInstance().navigateTo(
+                    routes.BASIC, /*dynamicParameters=*/ undefined,
+                    /*removeSearch=*/ true);
+                await navigationCompletePromise;
+
+                assertOnlyActivePageIsVisible('internet');
+              });
         });
 
         suite('From Page', () => {
diff --git a/chrome/test/enterprise/e2e/.vpython3 b/chrome/test/enterprise/e2e/.vpython3
index 1fb3af9..87a829e 100644
--- a/chrome/test/enterprise/e2e/.vpython3
+++ b/chrome/test/enterprise/e2e/.vpython3
@@ -78,8 +78,8 @@
   version: "version:2.4.7"
 >
 wheel: <
-  name: "infra/python/wheels/requests-py2_py3"
-  version: "version:2.26.0"
+  name: "infra/python/wheels/requests-py3"
+  version: "version:2.31.0"
 >
 wheel: <
   name: "infra/python/wheels/idna-py2_py3"
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler.h b/chromeos/ash/components/audio/audio_devices_pref_handler.h
index fc54afdd..75e7820 100644
--- a/chromeos/ash/components/audio/audio_devices_pref_handler.h
+++ b/chromeos/ash/components/audio/audio_devices_pref_handler.h
@@ -83,6 +83,11 @@
       const std::vector<AudioDevice>& connected_devices,
       size_t keep_devices) = 0;
 
+  // Reads whether input force respect ui gains is on from profile prefs.
+  virtual bool GetForceRespectUiGainsState() = 0;
+  // Sets the input force respect ui gains in profile prefs.
+  virtual void SetForceRespectUiGainsState(bool force_respect_ui_gains) = 0;
+
  protected:
   virtual ~AudioDevicesPrefHandler() = default;
 
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc
index 35a84389..d7e5133 100644
--- a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc
+++ b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.cc
@@ -393,6 +393,16 @@
                            noise_cancellation_state);
 }
 
+bool AudioDevicesPrefHandlerImpl::GetForceRespectUiGainsState() {
+  return local_state_->GetBoolean(prefs::kInputForceRespectUiGainsEnabled);
+}
+
+void AudioDevicesPrefHandlerImpl::SetForceRespectUiGainsState(
+    bool force_respect_ui_gains_state) {
+  local_state_->SetBoolean(prefs::kInputForceRespectUiGainsEnabled,
+                           force_respect_ui_gains_state);
+}
+
 AudioDevicesPrefHandlerImpl::AudioDevicesPrefHandlerImpl(
     PrefService* local_state)
     : local_state_(local_state) {
@@ -546,6 +556,8 @@
   registry->RegisterDictionaryPref(prefs::kAudioOutputDevicesUserPriority);
 
   registry->RegisterDictionaryPref(prefs::kAudioDevicesLastSeen);
+
+  registry->RegisterBooleanPref(prefs::kInputForceRespectUiGainsEnabled, false);
 }
 
 }  // namespace ash
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h
index c485314..39f352c8 100644
--- a/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h
+++ b/chromeos/ash/components/audio/audio_devices_pref_handler_impl.h
@@ -63,6 +63,9 @@
   void AddAudioPrefObserver(AudioPrefObserver* observer) override;
   void RemoveAudioPrefObserver(AudioPrefObserver* observer) override;
 
+  bool GetForceRespectUiGainsState() override;
+  void SetForceRespectUiGainsState(bool force_respect_ui_gains) override;
+
   // Registers volume and mute preferences.
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc b/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc
index 6db3a1e5..c405268 100644
--- a/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc
+++ b/chromeos/ash/components/audio/audio_devices_pref_handler_stub.cc
@@ -151,4 +151,13 @@
   observers_.RemoveObserver(observer);
 }
 
+bool AudioDevicesPrefHandlerStub::GetForceRespectUiGainsState() {
+  return force_respect_ui_gains_;
+}
+
+void AudioDevicesPrefHandlerStub::SetForceRespectUiGainsState(
+    bool force_respect_ui_gains) {
+  force_respect_ui_gains_ = force_respect_ui_gains;
+}
+
 }  // namespace ash
diff --git a/chromeos/ash/components/audio/audio_devices_pref_handler_stub.h b/chromeos/ash/components/audio/audio_devices_pref_handler_stub.h
index a8792a5..1d0e6fac 100644
--- a/chromeos/ash/components/audio/audio_devices_pref_handler_stub.h
+++ b/chromeos/ash/components/audio/audio_devices_pref_handler_stub.h
@@ -62,6 +62,9 @@
 
   void SetAudioOutputAllowedValue(bool is_audio_output_allowed);
 
+  bool GetForceRespectUiGainsState() override;
+  void SetForceRespectUiGainsState(bool force_respect_ui_gains) override;
+
  protected:
   ~AudioDevicesPrefHandlerStub() override;
 
@@ -75,6 +78,7 @@
 
   bool is_audio_output_allowed_ = true;
   bool noise_cancellation_state_ = true;
+  bool force_respect_ui_gains_ = false;
 };
 
 }  // namespace ash
diff --git a/chromeos/ash/components/audio/cras_audio_handler.cc b/chromeos/ash/components/audio/cras_audio_handler.cc
index 888c8655..3795321 100644
--- a/chromeos/ash/components/audio/cras_audio_handler.cc
+++ b/chromeos/ash/components/audio/cras_audio_handler.cc
@@ -108,6 +108,8 @@
 
 void CrasAudioHandler::AudioObserver::OnNoiseCancellationStateChanged() {}
 
+void CrasAudioHandler::AudioObserver::OnForceRespectUiGainsStateChanged() {}
+
 void CrasAudioHandler::AudioObserver::OnHotwordTriggered(
     uint64_t /* tv_sec */,
     uint64_t /* tv_nsec */) {}
@@ -596,6 +598,23 @@
   noise_cancellation_supported_ = supported;
 }
 
+bool CrasAudioHandler::GetForceRespectUiGainsState() const {
+  return audio_pref_handler_->GetForceRespectUiGainsState();
+}
+
+void CrasAudioHandler::RefreshForceRespectUiGainsState() {
+  SetForceRespectUiGainsState(GetForceRespectUiGainsState());
+}
+
+void CrasAudioHandler::SetForceRespectUiGainsState(bool state) {
+  CrasAudioClient::Get()->SetForceRespectUiGains(state);
+  audio_pref_handler_->SetForceRespectUiGainsState(state);
+
+  for (auto& observer : observers_) {
+    observer.OnForceRespectUiGainsStateChanged();
+  }
+}
+
 void CrasAudioHandler::SetKeyboardMicActive(bool active) {
   const AudioDevice* keyboard_mic = GetKeyboardMic();
   if (!keyboard_mic)
diff --git a/chromeos/ash/components/audio/cras_audio_handler.h b/chromeos/ash/components/audio/cras_audio_handler.h
index d46b48a1..88d67c74 100644
--- a/chromeos/ash/components/audio/cras_audio_handler.h
+++ b/chromeos/ash/components/audio/cras_audio_handler.h
@@ -142,6 +142,9 @@
     // Called when noise cancellation state changed.
     virtual void OnNoiseCancellationStateChanged();
 
+    // Called when force respect ui gains state changed.
+    virtual void OnForceRespectUiGainsStateChanged();
+
     // Called when hotword is detected.
     virtual void OnHotwordTriggered(uint64_t tv_sec, uint64_t tv_nsec);
 
@@ -355,6 +358,15 @@
   // Simulate noise cancellation support in a test.
   void SetNoiseCancellationSupportedForTesting(bool supported);
 
+  // Gets the state of input force respect ui gains state.
+  bool GetForceRespectUiGainsState() const;
+
+  // Refreshes the input device force respect ui gains state.
+  void RefreshForceRespectUiGainsState();
+
+  // Makes a DBus call to set the state of input force respect ui gains.
+  void SetForceRespectUiGainsState(bool state);
+
   // Whether there is alternative input/output audio device.
   bool has_alternative_input() const;
   bool has_alternative_output() const;
diff --git a/chromeos/ash/components/audio/cras_audio_handler_unittest.cc b/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
index eeb4b7c..42b0b1e 100644
--- a/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
+++ b/chromeos/ash/components/audio/cras_audio_handler_unittest.cc
@@ -287,6 +287,8 @@
     ++nonchrome_output_stopped_change_count_;
   }
 
+  void OnForceRespectUiGainsStateChanged() override {}
+
  private:
   int active_output_node_changed_count_ = 0;
   int active_input_node_changed_count_ = 0;
diff --git a/chromeos/ash/components/dbus/audio/cras_audio_client.cc b/chromeos/ash/components/dbus/audio/cras_audio_client.cc
index 81dbd98..e39113d9e 100644
--- a/chromeos/ash/components/dbus/audio/cras_audio_client.cc
+++ b/chromeos/ash/components/dbus/audio/cras_audio_client.cc
@@ -595,6 +595,18 @@
     cras_proxy_->WaitForServiceToBeAvailable(std::move(callback));
   }
 
+  void SetForceRespectUiGains(bool force_respect_ui_gains) override {
+    VLOG(1) << "cras_audio_client: Setting force_respect_ui_gains state: "
+            << force_respect_ui_gains;
+    dbus::MethodCall method_call(cras::kCrasControlInterface,
+                                 cras::kSetForceRespectUiGains);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendBool(force_respect_ui_gains);
+    cras_proxy_->CallMethod(&method_call,
+                            dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+                            base::DoNothing());
+  }
+
  private:
   // Called when the cras signal is initially connected.
   void SignalConnected(const std::string& interface_name,
diff --git a/chromeos/ash/components/dbus/audio/cras_audio_client.h b/chromeos/ash/components/dbus/audio/cras_audio_client.h
index 15d01c6..47c5abb 100644
--- a/chromeos/ash/components/dbus/audio/cras_audio_client.h
+++ b/chromeos/ash/components/dbus/audio/cras_audio_client.h
@@ -272,6 +272,9 @@
   virtual void WaitForServiceToBeAvailable(
       chromeos::WaitForServiceToBeAvailableCallback callback) = 0;
 
+  // Sets input force respect ui gains state to |force_repsect_ui_gains| value.
+  virtual void SetForceRespectUiGains(bool force_respect_ui_gains) = 0;
+
  protected:
   friend class CrasAudioClientTest;
 
diff --git a/chromeos/ash/components/dbus/audio/cras_audio_client_unittest.cc b/chromeos/ash/components/dbus/audio/cras_audio_client_unittest.cc
index 05aa9d84..7ec3c43 100644
--- a/chromeos/ash/components/dbus/audio/cras_audio_client_unittest.cc
+++ b/chromeos/ash/components/dbus/audio/cras_audio_client_unittest.cc
@@ -1646,4 +1646,20 @@
   base::RunLoop().RunUntilIdle();
 }
 
+TEST_F(CrasAudioClientTest, SetForceRespectUiGainsEnabled) {
+  const bool kForceRespectUiGainsOn = true;
+  // Create response.
+  std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
+
+  // Set expectations.
+  PrepareForMethodCall(
+      cras::kSetForceRespectUiGains,
+      base::BindRepeating(&ExpectBoolArgument, kForceRespectUiGainsOn),
+      response.get());
+  // Call method.
+  client()->SetForceRespectUiGains(kForceRespectUiGainsOn);
+  // Run the message loop.
+  base::RunLoop().RunUntilIdle();
+}
+
 }  // namespace ash
diff --git a/chromeos/ash/components/dbus/audio/fake_cras_audio_client.cc b/chromeos/ash/components/dbus/audio/fake_cras_audio_client.cc
index b4ade42..2ba0905 100644
--- a/chromeos/ash/components/dbus/audio/fake_cras_audio_client.cc
+++ b/chromeos/ash/components/dbus/audio/fake_cras_audio_client.cc
@@ -453,4 +453,6 @@
   return base::ranges::find(node_list_, node_id, &AudioNode::id);
 }
 
+void FakeCrasAudioClient::SetForceRespectUiGains(bool force_respect_ui_gains) {}
+
 }  // namespace ash
diff --git a/chromeos/ash/components/dbus/audio/fake_cras_audio_client.h b/chromeos/ash/components/dbus/audio/fake_cras_audio_client.h
index c8a9d594..5a2b129 100644
--- a/chromeos/ash/components/dbus/audio/fake_cras_audio_client.h
+++ b/chromeos/ash/components/dbus/audio/fake_cras_audio_client.h
@@ -94,6 +94,7 @@
   void ResendBluetoothBattery() override;
   void WaitForServiceToBeAvailable(
       chromeos::WaitForServiceToBeAvailableCallback callback) override;
+  void SetForceRespectUiGains(bool force_respect_ui_gains) override;
 
   // Sets the number of non chrome audio streams in output mode.
   void SetNumberOfNonChromeOutputStreams(int32_t streams);
diff --git a/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom b/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom
index c989b4a..56fcb57d 100644
--- a/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom
+++ b/chromeos/ash/services/nearby/public/mojom/quick_start_decoder_types.mojom
@@ -44,7 +44,7 @@
   string ssid;
   WifiSecurityType security_type;
   bool is_hidden;
-  string password;
+  string? password;
 };
 
 struct GetAssertionResponse {
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 4a93bd4..731d215 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -1103,8 +1103,7 @@
 
   // Check if we need to authenticate the user before filling the local card
   // or full server card.
-  if (personal_data_manager_
-          ->IsAutofillPaymentMethodsMandatoryReauthEnabled()) {
+  if (personal_data_manager_->IsPaymentMethodsMandatoryReauthEnabled()) {
     // `StartDeviceAuthenticationForFilling()` will asynchronously trigger
     // the re-authentication flow, so we should avoid calling `Reset()`
     // until the re-authentication flow is complete.
@@ -1158,8 +1157,7 @@
           base::UTF8ToUTF16(response_details.expiration_year));
       // Check if we need to authenticate the user before filling the virtual
       // card.
-      if (personal_data_manager_
-              ->IsAutofillPaymentMethodsMandatoryReauthEnabled()) {
+      if (personal_data_manager_->IsPaymentMethodsMandatoryReauthEnabled()) {
         // On some operating systems (for example, macOS and Windows), the
         // device authentication prompt freezes Chrome. Thus we can only trigger
         // the prompt after the progress dialog has been closed, which we can do
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index c81c216..563485ce5 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1928,13 +1928,22 @@
          sync_service_->GetUserSettings()->GetSelectedTypes().Has(data_type);
 }
 
-bool PersonalDataManager::IsAutofillPaymentMethodsMandatoryReauthEnabled() {
-  if (!base::FeatureList::IsEnabled(
-          features::kAutofillEnablePaymentsMandatoryReauth)) {
-    return false;
-  }
+void PersonalDataManager::SetPaymentMethodsMandatoryReauthEnabled(
+    bool enabled) {
+  prefs::SetPaymentMethodsMandatoryReauthEnabled(pref_service_, enabled);
+}
 
-  return prefs::IsAutofillPaymentMethodsMandatoryReauthEnabled(pref_service_);
+bool PersonalDataManager::IsPaymentMethodsMandatoryReauthEnabled() {
+  return prefs::IsPaymentMethodsMandatoryReauthEnabled(pref_service_);
+}
+
+bool PersonalDataManager::ShouldShowPaymentMethodsMandatoryReauthPromo() {
+  return prefs::ShouldShowPaymentMethodsMandatoryReauthPromo(pref_service_);
+}
+
+void PersonalDataManager::
+    IncrementPaymentMethodsMandatoryReauthPromoShownCounter() {
+  prefs::IncrementPaymentMethodsMandatoryReauthPromoShownCounter(pref_service_);
 }
 
 AutofillProfileMigrationStrikeDatabase*
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index dece8d78..b1f21bc6 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -597,8 +597,24 @@
   // Returns true if Sync is enabled for `data_type`.
   bool IsSyncEnabledFor(syncer::UserSelectableType data_type) const;
 
-  // Returns true if payments mandatory re-auth is enabled.
-  virtual bool IsAutofillPaymentMethodsMandatoryReauthEnabled();
+  // The functions below are related to the payments mandatory re-auth feature.
+  // All of this functionality is done through per-profile per-device prefs.
+  // `SetPaymentMethodsMandatoryReauthEnabled()` is used to update the opt-in
+  // status of the feature, and is called when a user successfully completes a
+  // full re-auth opt-in flow (with a successful authentication).
+  // `IsPaymentMethodsMandatoryReauthEnabled()` is checked before triggering the
+  // re-auth feature during a payments autofill flow.
+  // `ShouldShowPaymentMethodsMandatoryReauthPromo()` is used to check whether
+  // we should show the re-auth opt-in promo once a user submits a form, and
+  // there was no interactive authentication for the most recent payments
+  // autofill flow. `IncrementPaymentMethodsMandatoryReauthPromoShownCounter()`
+  // increments the counter that denotes the number of times that the promo has
+  // been shown, and this counter is used very similarly to a strike database
+  // when it comes time to check whether we should show the promo.
+  void SetPaymentMethodsMandatoryReauthEnabled(bool enabled);
+  virtual bool IsPaymentMethodsMandatoryReauthEnabled();
+  bool ShouldShowPaymentMethodsMandatoryReauthPromo();
+  void IncrementPaymentMethodsMandatoryReauthPromoShownCounter();
 
   // Used to automatically import addresses without a prompt. Should only be
   // set to true in tests.
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 363e890..cc93709 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -943,6 +943,84 @@
   ExpectSameElements(profiles, personal_data_->GetProfiles());
 }
 
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
+// Test that setting the `kAutofillEnablePaymentsMandatoryReauth` pref works
+// correctly.
+TEST_F(PersonalDataManagerTest, AutofillPaymentMethodsMandatoryReauthEnabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kAutofillEnablePaymentsMandatoryReauth);
+  EXPECT_FALSE(personal_data_->IsPaymentMethodsMandatoryReauthEnabled());
+
+  personal_data_->SetPaymentMethodsMandatoryReauthEnabled(true);
+
+  EXPECT_TRUE(personal_data_->IsPaymentMethodsMandatoryReauthEnabled());
+
+  personal_data_->SetPaymentMethodsMandatoryReauthEnabled(false);
+
+  EXPECT_FALSE(personal_data_->IsPaymentMethodsMandatoryReauthEnabled());
+}
+
+// Test that setting the `kAutofillEnablePaymentsMandatoryReauth` does not
+// enable the feature when the flag is off.
+TEST_F(PersonalDataManagerTest,
+       AutofillPaymentMethodsMandatoryReauthEnabled_FlagOff) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      features::kAutofillEnablePaymentsMandatoryReauth);
+  EXPECT_FALSE(personal_data_->IsPaymentMethodsMandatoryReauthEnabled());
+
+  personal_data_->SetPaymentMethodsMandatoryReauthEnabled(true);
+
+  EXPECT_FALSE(personal_data_->IsPaymentMethodsMandatoryReauthEnabled());
+}
+
+// Test that
+// `PersonalDataManager::ShouldShowPaymentMethodsMandatoryReauthPromo()`
+// only returns that we should show the promo when we are below the max counter
+// limit for showing the promo.
+TEST_F(
+    PersonalDataManagerTest,
+    ShouldShowPaymentMethodsMandatoryReauthPromo_MaxValueForPromoShownCounterReached) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kAutofillEnablePaymentsMandatoryReauth);
+  for (int i = 0; i < prefs::kMaxValueForMandatoryReauthPromoShownCounter;
+       i++) {
+    EXPECT_TRUE(personal_data_->ShouldShowPaymentMethodsMandatoryReauthPromo());
+    personal_data_->IncrementPaymentMethodsMandatoryReauthPromoShownCounter();
+  }
+
+  EXPECT_FALSE(personal_data_->ShouldShowPaymentMethodsMandatoryReauthPromo());
+}
+
+// Test that
+// `PersonalDataManager::ShouldShowPaymentMethodsMandatoryReauthPromo()`
+// returns that we should not show the promo if the user has already made a
+// decision.
+TEST_F(
+    PersonalDataManagerTest,
+    ShouldShowPaymentMethodsMandatoryReauthPromo_UserHasMadeADecisionAlready) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kAutofillEnablePaymentsMandatoryReauth);
+  personal_data_->SetPaymentMethodsMandatoryReauthEnabled(true);
+
+  EXPECT_FALSE(personal_data_->ShouldShowPaymentMethodsMandatoryReauthPromo());
+}
+
+// Test that
+// `PersonalDataManager::ShouldShowPaymentMethodsMandatoryReauthPromo()`
+// returns that we should not show the promo if the flag is off.
+TEST_F(PersonalDataManagerTest,
+       ShouldShowPaymentMethodsMandatoryReauthPromo_FlagOff) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      features::kAutofillEnablePaymentsMandatoryReauth);
+  EXPECT_FALSE(personal_data_->ShouldShowPaymentMethodsMandatoryReauthPromo());
+}
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
+
 TEST_F(PersonalDataManagerTest, NoIBANsAddedIfDisabled) {
   prefs::SetAutofillIBANEnabled(prefs_.get(), false);
   personal_data_->AddIBAN(autofill::test::GetIBAN());
diff --git a/components/autofill/core/browser/test_personal_data_manager.cc b/components/autofill/core/browser/test_personal_data_manager.cc
index 04e6ce0..10fbc4c 100644
--- a/components/autofill/core/browser/test_personal_data_manager.cc
+++ b/components/autofill/core/browser/test_personal_data_manager.cc
@@ -324,11 +324,11 @@
   return &inmemory_profile_update_strike_database_;
 }
 
-bool TestPersonalDataManager::IsAutofillPaymentMethodsMandatoryReauthEnabled() {
-  if (autofill_payment_methods_mandatory_reauth_enabled_) {
-    return true;
+bool TestPersonalDataManager::IsPaymentMethodsMandatoryReauthEnabled() {
+  if (payment_methods_mandatory_reauth_enabled_.has_value()) {
+    return payment_methods_mandatory_reauth_enabled_.value();
   }
-  return PersonalDataManager::IsAutofillPaymentMethodsMandatoryReauthEnabled();
+  return PersonalDataManager::IsPaymentMethodsMandatoryReauthEnabled();
 }
 
 void TestPersonalDataManager::ClearProfiles() {
diff --git a/components/autofill/core/browser/test_personal_data_manager.h b/components/autofill/core/browser/test_personal_data_manager.h
index b1efcd5..767391ff 100644
--- a/components/autofill/core/browser/test_personal_data_manager.h
+++ b/components/autofill/core/browser/test_personal_data_manager.h
@@ -82,7 +82,7 @@
       const override;
   const AutofillProfileUpdateStrikeDatabase* GetProfileUpdateStrikeDatabase()
       const override;
-  bool IsAutofillPaymentMethodsMandatoryReauthEnabled() override;
+  bool IsPaymentMethodsMandatoryReauthEnabled() override;
 
   // Unique to TestPersonalDataManager:
 
@@ -172,8 +172,8 @@
 
   void ClearCreditCardArtImages() { credit_card_art_images_.clear(); }
 
-  void SetAutofillPaymentMethodsMandatoryReauthEnabled(bool val) {
-    autofill_payment_methods_mandatory_reauth_enabled_ = val;
+  void SetPaymentMethodsMandatoryReauthEnabled(bool val) {
+    payment_methods_mandatory_reauth_enabled_ = val;
   }
 
  private:
@@ -187,11 +187,11 @@
   absl::optional<bool> autofill_credit_card_enabled_;
   absl::optional<bool> autofill_wallet_import_enabled_;
   absl::optional<bool> eligible_for_account_storage_;
+  absl::optional<bool> payment_methods_mandatory_reauth_enabled_;
   bool sync_feature_enabled_ = false;
   AutofillSyncSigninState sync_and_signin_state_ =
       AutofillSyncSigninState::kSignedInAndSyncFeatureEnabled;
   CoreAccountInfo account_info_;
-  bool autofill_payment_methods_mandatory_reauth_enabled_ = false;
 
   TestInMemoryStrikeDatabase inmemory_strike_database_;
   AutofillProfileMigrationStrikeDatabase
diff --git a/components/autofill/core/common/autofill_prefs.cc b/components/autofill/core/common/autofill_prefs.cc
index 2517a02..6ff3533 100644
--- a/components/autofill/core/common/autofill_prefs.cc
+++ b/components/autofill/core/common/autofill_prefs.cc
@@ -5,7 +5,9 @@
 #include "components/autofill/core/common/autofill_prefs.h"
 
 #include "base/base64.h"
+#include "base/feature_list.h"
 #include "build/build_config.h"
+#include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -245,24 +247,32 @@
   prefs->SetBoolean(kAutofillWalletImportEnabled, enabled);
 }
 
-bool IsAutofillPaymentMethodsMandatoryReauthEnabled(const PrefService* prefs) {
+bool IsPaymentMethodsMandatoryReauthEnabled(const PrefService* prefs) {
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillEnablePaymentsMandatoryReauth)) {
+    return false;
+  }
+
   return prefs->GetBoolean(kAutofillPaymentMethodsMandatoryReauth);
 #else
   return false;
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 }
 
-void SetAutofillPaymentMethodsMandatoryReauth(PrefService* prefs,
-                                              bool enabled) {
+void SetPaymentMethodsMandatoryReauthEnabled(PrefService* prefs, bool enabled) {
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
   prefs->SetBoolean(kAutofillPaymentMethodsMandatoryReauth, enabled);
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 }
 
-bool ShouldShowAutofillPaymentMethodsMandatoryReauthPromo(
-    const PrefService* prefs) {
+bool ShouldShowPaymentMethodsMandatoryReauthPromo(const PrefService* prefs) {
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillEnablePaymentsMandatoryReauth)) {
+    return false;
+  }
+
   // If the user has made a decision on this feature previously, then we should
   // not show the opt-in promo.
   if (prefs->GetUserPrefValue(kAutofillPaymentMethodsMandatoryReauth)) {
@@ -279,12 +289,20 @@
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 }
 
-void SetAutofillPaymentMethodsMandatoryReauthPromoShownCounter(
-    PrefService* prefs,
-    int count) {
+void IncrementPaymentMethodsMandatoryReauthPromoShownCounter(
+    PrefService* prefs) {
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
-  prefs->SetInteger(kAutofillPaymentMethodsMandatoryReauthPromoShownCounter,
-                    count);
+  if (prefs->GetInteger(
+          kAutofillPaymentMethodsMandatoryReauthPromoShownCounter) >=
+      kMaxValueForMandatoryReauthPromoShownCounter) {
+    return;
+  }
+
+  prefs->SetInteger(
+      kAutofillPaymentMethodsMandatoryReauthPromoShownCounter,
+      prefs->GetInteger(
+          kAutofillPaymentMethodsMandatoryReauthPromoShownCounter) +
+          1);
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID)
 }
 
diff --git a/components/autofill/core/common/autofill_prefs.h b/components/autofill/core/common/autofill_prefs.h
index 5ebc068..84ffe95f 100644
--- a/components/autofill/core/common/autofill_prefs.h
+++ b/components/autofill/core/common/autofill_prefs.h
@@ -99,16 +99,14 @@
 
 void SetPaymentsIntegrationEnabled(PrefService* prefs, bool enabled);
 
-bool IsAutofillPaymentMethodsMandatoryReauthEnabled(const PrefService* prefs);
+bool IsPaymentMethodsMandatoryReauthEnabled(const PrefService* prefs);
 
-void SetAutofillPaymentMethodsMandatoryReauth(PrefService* prefs, bool enabled);
+void SetPaymentMethodsMandatoryReauthEnabled(PrefService* prefs, bool enabled);
 
-bool ShouldShowAutofillPaymentMethodsMandatoryReauthPromo(
-    const PrefService* prefs);
+bool ShouldShowPaymentMethodsMandatoryReauthPromo(const PrefService* prefs);
 
-void SetAutofillPaymentMethodsMandatoryReauthPromoShownCounter(
-    PrefService* prefs,
-    int count);
+void IncrementPaymentMethodsMandatoryReauthPromoShownCounter(
+    PrefService* prefs);
 
 void SetUserOptedInWalletSyncTransport(PrefService* prefs,
                                        const CoreAccountId& account_id,
diff --git a/components/metrics/structured/structured_metrics_provider.cc b/components/metrics/structured/structured_metrics_provider.cc
index e536100..b8316ce 100644
--- a/components/metrics/structured/structured_metrics_provider.cc
+++ b/components/metrics/structured/structured_metrics_provider.cc
@@ -104,7 +104,6 @@
 
   last_provided_independent_metrics_ = base::Time::Now();
 
-  Recorder::GetInstance()->OnProvideIndependentMetrics(uma_proto);
   recorder().ProvideEventMetrics(*uma_proto);
 
   // Independent events should not be associated with the client_id, so clear
diff --git a/components/metrics/structured/structured_metrics_recorder.cc b/components/metrics/structured/structured_metrics_recorder.cc
index 1cb3fc1..0a63689 100644
--- a/components/metrics/structured/structured_metrics_recorder.cc
+++ b/components/metrics/structured/structured_metrics_recorder.cc
@@ -127,6 +127,9 @@
   LogUploadSizeBytes(structured_data->ByteSizeLong());
   LogExternalMetricsScanInUpload(external_metrics_scans_);
   external_metrics_scans_ = 0;
+
+  // Applies custom metadata providers.
+  Recorder::GetInstance()->OnProvideIndependentMetrics(&uma_proto);
 }
 
 void StructuredMetricsRecorder::OnKeyDataInitialized() {
diff --git a/components/metrics/structured/structured_metrics_recorder.h b/components/metrics/structured/structured_metrics_recorder.h
index e77469e..d2128257 100644
--- a/components/metrics/structured/structured_metrics_recorder.h
+++ b/components/metrics/structured/structured_metrics_recorder.h
@@ -68,6 +68,10 @@
 
   void ProvideUmaEventMetrics(ChromeUserMetricsExtension& uma_proto);
 
+  // Provides event metrics stored in the recorder into |uma_proto|.
+  //
+  // This calls OnIndependentMetrics() to populate |uma_proto| with metadata
+  // fields.
   void ProvideEventMetrics(ChromeUserMetricsExtension& uma_proto);
 
   bool can_provide_metrics() const {
diff --git a/components/metrics/structured/structured_metrics_recorder_unittest.cc b/components/metrics/structured/structured_metrics_recorder_unittest.cc
index 3ac5a86..62757e59 100644
--- a/components/metrics/structured/structured_metrics_recorder_unittest.cc
+++ b/components/metrics/structured/structured_metrics_recorder_unittest.cc
@@ -1080,4 +1080,29 @@
   ASSERT_EQ(data.events(0).project_name_hash(), kProjectTwoHash);
 }
 
+class TestProcessor : public EventsProcessorInterface {
+  bool ShouldProcessOnEventRecord(const Event& event) override { return true; }
+
+  // no-op
+  void OnEventsRecord(Event* event) override {}
+
+  void OnProvideIndependentMetrics(
+      ChromeUserMetricsExtension* uma_proto) override {
+    uma_proto->mutable_structured_data()->set_is_device_enrolled(true);
+  }
+};
+
+TEST_F(StructuredMetricsRecorderTest, AppliesProcessorCorrectly) {
+  Init();
+
+  // Processor that sets |is_device_enrolled| to true.
+  Recorder::GetInstance()->AddEventsProcessor(
+      std::make_unique<TestProcessor>());
+
+  events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record();
+  const auto data = GetEventMetrics();
+
+  EXPECT_TRUE(data.is_device_enrolled());
+}
+
 }  // namespace metrics::structured
diff --git a/components/password_manager/core/browser/old_google_credentials_cleaner_unittest.cc b/components/password_manager/core/browser/old_google_credentials_cleaner_unittest.cc
index 06b7fec3..ec6452e3 100644
--- a/components/password_manager/core/browser/old_google_credentials_cleaner_unittest.cc
+++ b/components/password_manager/core/browser/old_google_credentials_cleaner_unittest.cc
@@ -82,6 +82,7 @@
       CreateForm("https://www.google.com/"),
   };
 
+  MockCredentialsCleanerObserver observer;
   OldGoogleCredentialCleaner cleaner{store(), &prefs()};
   ASSERT_TRUE(cleaner.NeedsCleaning());
 
@@ -90,7 +91,6 @@
     EXPECT_CALL(*store(), RemoveLogin(form));
   }
 
-  MockCredentialsCleanerObserver observer;
   EXPECT_CALL(observer, CleaningCompleted);
   cleaner.StartCleaning(&observer);
 
@@ -108,10 +108,10 @@
                                      0,    0, 0, 1};  // 00:01 Jan 1 2012
   ASSERT_TRUE(base::Time::FromUTCExploded(time, &new_form.date_created));
 
+  MockCredentialsCleanerObserver observer;
   OldGoogleCredentialCleaner cleaner{store(), &prefs()};
   ASSERT_TRUE(cleaner.NeedsCleaning());
 
-  MockCredentialsCleanerObserver observer;
   ExpectPasswords({old_form, new_form, CreateForm("http://test.com/")});
   EXPECT_CALL(*store(), RemoveLogin(old_form));
   EXPECT_CALL(observer, CleaningCompleted);
diff --git a/components/performance_manager/execution_context_priority/boosting_vote_aggregator_unittest.cc b/components/performance_manager/execution_context_priority/boosting_vote_aggregator_unittest.cc
index 8b90a88d..52bbf18 100644
--- a/components/performance_manager/execution_context_priority/boosting_vote_aggregator_unittest.cc
+++ b/components/performance_manager/execution_context_priority/boosting_vote_aggregator_unittest.cc
@@ -4,8 +4,10 @@
 
 #include "components/performance_manager/execution_context_priority/boosting_vote_aggregator.h"
 
+#include "components/performance_manager/public/execution_context/execution_context.h"
 #include "components/performance_manager/test_support/voting.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
 
 namespace performance_manager {
 namespace execution_context_priority {
@@ -14,17 +16,30 @@
 
 using DummyVoteObserver = voting::test::DummyVoteObserver<Vote>;
 
-// Some dummy execution contexts.
-const ExecutionContext* kExecutionContext0 =
-    reinterpret_cast<const ExecutionContext*>(0xF5A33000);
-const ExecutionContext* kExecutionContext1 =
-    reinterpret_cast<const ExecutionContext*>(0xF5A33001);
-const ExecutionContext* kExecutionContext2 =
-    reinterpret_cast<const ExecutionContext*>(0xF5A33002);
-const ExecutionContext* kExecutionContext3 =
-    reinterpret_cast<const ExecutionContext*>(0xF5A33003);
-const ExecutionContext* kExecutionContext4 =
-    reinterpret_cast<const ExecutionContext*>(0xF5A33004);
+class DummyExecutionContext : public ExecutionContext {
+ public:
+  DummyExecutionContext() = default;
+  ~DummyExecutionContext() override = default;
+
+  execution_context::ExecutionContextType GetType() const override {
+    return execution_context::ExecutionContextType();
+  }
+  blink::ExecutionContextToken GetToken() const override {
+    return blink::ExecutionContextToken();
+  }
+  Graph* GetGraph() const override { return nullptr; }
+  const GURL& GetUrl() const override { return url_; }
+  const ProcessNode* GetProcessNode() const override { return nullptr; }
+  const PriorityAndReason& GetPriorityAndReason() const override {
+    return par_;
+  }
+  const FrameNode* GetFrameNode() const override { return nullptr; }
+  const WorkerNode* GetWorkerNode() const override { return nullptr; }
+
+ private:
+  GURL url_;
+  PriorityAndReason par_;
+};
 
 static const char kReasonBoost[] = "boosted!";
 
@@ -85,6 +100,14 @@
     EXPECT_EQ(high_priority, node_data.IsActive(2));
   }
 
+  // Some dummy execution contexts.
+  DummyExecutionContext dummy_contexts[5];
+  raw_ptr<const ExecutionContext> kExecutionContext0 = &dummy_contexts[0];
+  raw_ptr<const ExecutionContext> kExecutionContext1 = &dummy_contexts[1];
+  raw_ptr<const ExecutionContext> kExecutionContext2 = &dummy_contexts[2];
+  raw_ptr<const ExecutionContext> kExecutionContext3 = &dummy_contexts[3];
+  raw_ptr<const ExecutionContext> kExecutionContext4 = &dummy_contexts[4];
+
  private:
   // The id of |aggregator_| as seen by its upstream |observer_|.
   voting::VoterId<Vote> aggregator_voter_id_;
diff --git a/components/safe_browsing/core/browser/db/v4_local_database_manager.cc b/components/safe_browsing/core/browser/db/v4_local_database_manager.cc
index 1f305cae..a41b01c 100644
--- a/components/safe_browsing/core/browser/db/v4_local_database_manager.cc
+++ b/components/safe_browsing/core/browser/db/v4_local_database_manager.cc
@@ -1141,6 +1141,7 @@
   if (results.empty()) {
     RespondToClient(std::move(check));
   } else {
+    check->full_hash_to_store_and_hash_prefixes = results;
     AddPendingCheck(check.get());
     PerformFullHashCheck(std::move(check));
   }
diff --git a/components/segmentation_platform/internal/data_collection/training_data_cache.cc b/components/segmentation_platform/internal/data_collection/training_data_cache.cc
index 3679bef..d313af8 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_cache.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_cache.cc
@@ -20,8 +20,10 @@
                                     const proto::TrainingData& data,
                                     bool save_to_db) {
   if (save_to_db) {
-    segment_info_database_->SaveTrainingData(segment_id, std::move(data),
-                                             base::DoNothing());
+    // TODO (ritikagup@) : Add handling for default models, if required.
+    segment_info_database_->SaveTrainingData(
+        segment_id, proto::ModelSource::SERVER_MODEL_SOURCE, std::move(data),
+        base::DoNothing());
   } else {
     cache[segment_id][TrainingRequestId::FromUnsafeValue(data.request_id())] =
         std::move(data);
diff --git a/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc b/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc
index ffc956a..552a2d1 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_cache_unittest.cc
@@ -92,8 +92,9 @@
   training_data.set_request_id(kRequestId.GetUnsafeValue());
 
   // Store a training data request to the DB.
-  test_segment_info_db_->SaveTrainingData(kSegmentId, training_data,
-                                          base::DoNothing());
+  test_segment_info_db_->SaveTrainingData(
+      kSegmentId, proto::ModelSource::SERVER_MODEL_SOURCE, training_data,
+      base::DoNothing());
 
   // DB will return and delete the corresponding training data.
   VerifyGetInputsAndDelete(kSegmentId, kRequestId, training_data);
diff --git a/components/segmentation_platform/internal/database/segment_info_database.cc b/components/segmentation_platform/internal/database/segment_info_database.cc
index 9ed31a3d..7652a3e 100644
--- a/components/segmentation_platform/internal/database/segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/segment_info_database.cc
@@ -153,6 +153,7 @@
 
 void SegmentInfoDatabase::SaveSegmentResult(
     SegmentId segment_id,
+    ModelSource model_source,
     absl::optional<proto::PredictionResult> result,
     SuccessCallback callback) {
   auto segment_info = cache_->GetSegmentInfo(segment_id);
@@ -180,6 +181,7 @@
 }
 
 void SegmentInfoDatabase::SaveTrainingData(SegmentId segment_id,
+                                           ModelSource model_source,
                                            const proto::TrainingData& data,
                                            SuccessCallback callback) {
   auto segment_info = cache_->GetSegmentInfo(segment_id);
diff --git a/components/segmentation_platform/internal/database/segment_info_database.h b/components/segmentation_platform/internal/database/segment_info_database.h
index 987d7c1..75c8c832 100644
--- a/components/segmentation_platform/internal/database/segment_info_database.h
+++ b/components/segmentation_platform/internal/database/segment_info_database.h
@@ -97,15 +97,18 @@
       SuccessCallback callback);
 
   // Called to write the model execution results for a given segment. It will
-  // first read the currently stored result, and then overwrite it with
-  // |result|. If |result| is null, the existing result will be deleted.
+  // first read the currently stored result for given model source, and then
+  // overwrite it with |result|. If |result| is null, the existing result will
+  // be deleted.
   virtual void SaveSegmentResult(SegmentId segment_id,
+                                 ModelSource model_source,
                                  absl::optional<proto::PredictionResult> result,
                                  SuccessCallback callback);
 
-  // Called to write partial training data for a given segment. New training
-  // data are appended to the existing ones.
+  // Called to write partial training data for a given segment and model source.
+  // New training data are appended to the existing ones.
   virtual void SaveTrainingData(SegmentId segment_id,
+                                ModelSource model_source,
                                 const proto::TrainingData& data,
                                 SuccessCallback callback);
 
diff --git a/components/segmentation_platform/internal/database/segment_info_database_unittest.cc b/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
index b1c6cd4..f62bea5 100644
--- a/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
+++ b/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
@@ -99,7 +99,7 @@
     if (result.has_value())
       prediction_result.add_result(result.value());
 
-    segment_db_->SaveSegmentResult(segment_id,
+    segment_db_->SaveSegmentResult(segment_id, model_source,
                                    result.has_value()
                                        ? absl::make_optional(prediction_result)
                                        : absl::nullopt,
@@ -118,7 +118,8 @@
     training_data.add_inputs(data);
     training_data.set_request_id(request_id);
 
-    segment_db_->SaveTrainingData(segment_id, training_data, base::DoNothing());
+    segment_db_->SaveTrainingData(segment_id, model_source, training_data,
+                                  base::DoNothing());
     if (!segment_info_cache_->GetSegmentInfo(segment_id).has_value()) {
       db_->GetCallback(true);
     }
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.cc b/components/segmentation_platform/internal/database/test_segment_info_database.cc
index 51ecde2..fa8e116 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.cc
@@ -74,6 +74,7 @@
 
 void TestSegmentInfoDatabase::SaveSegmentResult(
     SegmentId segment_id,
+    ModelSource model_source,
     absl::optional<proto::PredictionResult> result,
     SuccessCallback callback) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -86,6 +87,7 @@
 }
 
 void TestSegmentInfoDatabase::SaveTrainingData(SegmentId segment_id,
+                                               ModelSource model_source,
                                                const proto::TrainingData& data,
                                                SuccessCallback callback) {
   proto::SegmentInfo* info = FindOrCreateSegment(segment_id);
@@ -222,6 +224,7 @@
   info->mutable_model_metadata()->set_time_unit(time_unit);
 }
 
+// TODO(ritikagup@) : Add ModelSource as param to this function.
 proto::SegmentInfo* TestSegmentInfoDatabase::FindOrCreateSegment(
     SegmentId segment_id) {
   proto::SegmentInfo* info = nullptr;
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.h b/components/segmentation_platform/internal/database/test_segment_info_database.h
index 1812927f..ffe54247 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.h
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.h
@@ -38,9 +38,11 @@
                      absl::optional<proto::SegmentInfo> segment_info,
                      SuccessCallback callback) override;
   void SaveSegmentResult(SegmentId segment_id,
+                         ModelSource model_source,
                          absl::optional<proto::PredictionResult> result,
                          SuccessCallback callback) override;
   void SaveTrainingData(SegmentId segment_id,
+                        ModelSource model_source,
                         const proto::TrainingData& data,
                         SuccessCallback callback) override;
   void GetTrainingData(SegmentId segment_id,
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
index 63ef24f..0c1263d5 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -71,6 +71,7 @@
   MOCK_METHOD(void,
               SaveSegmentResult,
               (SegmentId segment_id,
+               ModelSource model_source,
                absl::optional<proto::PredictionResult> result,
                SuccessCallback callback),
               (override));
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
index d88705af..2973618 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
@@ -98,7 +98,8 @@
   }
 
   segment_database_->SaveSegmentResult(
-      segment_id, success ? absl::make_optional(segment_result) : absl::nullopt,
+      segment_id, proto::ModelSource::SERVER_MODEL_SOURCE,
+      success ? absl::make_optional(segment_result) : absl::nullopt,
       base::BindOnce(&ModelExecutionSchedulerImpl::OnResultSaved,
                      weak_ptr_factory_.GetWeakPtr(), segment_id));
 }
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider.cc b/components/segmentation_platform/internal/selection/segment_result_provider.cc
index 963b7ed..b9de92a 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider.cc
@@ -380,9 +380,10 @@
   }
 
   if (!is_default_model && request_state->options->save_results_to_db) {
+    // TODO (ritikagup@) : Add handling for default models, if required.
     // Saving results to database.
     segment_database_->SaveSegmentResult(
-        segment_info->segment_id(),
+        segment_info->segment_id(), proto::ModelSource::SERVER_MODEL_SOURCE,
         success ? absl::make_optional(prediction_result) : absl::nullopt,
         base::BindOnce(&SegmentResultProviderImpl::RunCallback,
                        weak_ptr_factory_.GetWeakPtr(),
diff --git a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
index 70901f3..c0baf8d8 100644
--- a/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
+++ b/components/segmentation_platform/internal/selection/segment_result_provider_unittest.cc
@@ -147,7 +147,7 @@
     base::RunLoop wait_for_save;
     segment_database_->SetBucketDuration(segment, 1, proto::TimeUnit::DAY);
     segment_database_->SaveSegmentResult(
-        segment, std::move(result),
+        segment, proto::ModelSource::SERVER_MODEL_SOURCE, std::move(result),
         base::BindOnce(
             [](base::OnceClosure quit, bool success) { std::move(quit).Run(); },
             wait_for_save.QuitClosure()));
diff --git a/components/segmentation_platform/internal/signals/history_service_observer.cc b/components/segmentation_platform/internal/signals/history_service_observer.cc
index df1be8ac..8797496d 100644
--- a/components/segmentation_platform/internal/signals/history_service_observer.cc
+++ b/components/segmentation_platform/internal/signals/history_service_observer.cc
@@ -90,8 +90,10 @@
     return;
   }
   for (const auto segment_id : *history_based_segments_) {
+    // TODO(b/285227062) : Add handling for default models, if required.
     storage_service_->segment_info_database()->SaveSegmentResult(
-        segment_id, absl::nullopt, base::DoNothing());
+        segment_id, proto::ModelSource::SERVER_MODEL_SOURCE, absl::nullopt,
+        base::DoNothing());
   }
 
   // If a model refresh was recently posted, then cancel the task and restart
diff --git a/components/services/app_service/public/cpp/intent_filter.h b/components/services/app_service/public/cpp/intent_filter.h
index 9c83ccf..48724963 100644
--- a/components/services/app_service/public/cpp/intent_filter.h
+++ b/components/services/app_service/public/cpp/intent_filter.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
 #define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INTENT_FILTER_H_
 
+#include <memory>
 #include <set>
 #include <string>
 #include <utility>
diff --git a/components/services/app_service/public/cpp/permission.h b/components/services/app_service/public/cpp/permission.h
index 82d323a..ede8540 100644
--- a/components/services/app_service/public/cpp/permission.h
+++ b/components/services/app_service/public/cpp/permission.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_H_
 #define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_PERMISSION_H_
 
+#include <memory>
 #include <utility>
 #include <vector>
 
diff --git a/components/translate/core/browser/translate_language_list.cc b/components/translate/core/browser/translate_language_list.cc
index 577f2c9..b3b6512 100644
--- a/components/translate/core/browser/translate_language_list.cc
+++ b/components/translate/core/browser/translate_language_list.cc
@@ -17,6 +17,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
+#include "components/language/core/browser/accept_languages_service.h"
 #include "components/language/core/common/locale_util.h"
 #include "components/translate/core/browser/translate_browser_metrics.h"
 #include "components/translate/core/browser/translate_download_manager.h"
@@ -41,9 +42,13 @@
     "ak",     // Twi
     "am",     // Amharic
     "ar",     // Arabic
+    "as",     // Assamese
+    "ay",     // Aymara
     "az",     // Azerbaijani
     "be",     // Belarusian
     "bg",     // Bulgarian
+    "bho",    // Bhojpuri
+    "bm",     // Bambara
     "bn",     // Bengali
     "bs",     // Bosnian
     "ca",     // Catalan
@@ -54,6 +59,8 @@
     "cy",     // Welsh
     "da",     // Danish
     "de",     // German
+    "doi",    // Dogri
+    "dv",     // Dhivehi
     "ee",     // Ewe
     "el",     // Greek
     "en",     // English
@@ -80,6 +87,7 @@
     "hy",     // Armenian
     "id",     // Indonesian
     "ig",     // Igbo
+    "ilo",    // Ilocano
     "is",     // Icelandic
     "it",     // Italian
     "iw",     // Hebrew - Chrome uses "he"
@@ -99,7 +107,9 @@
     "ln",     // Lingala
     "lo",     // Lao
     "lt",     // Lithuanian
+    "lus",    // Mizo
     "lv",     // Latvian
+    "mai",    // Maithili
     "mg",     // Malagasy
     "mi",     // Maori
     "mk",     // Macedonian
@@ -124,6 +134,7 @@
     "ro",     // Romanian
     "ru",     // Russian
     "rw",     // Kinyarwanda
+    "sa",     // Sanskrit
     "sd",     // Sindhi
     "si",     // Sinhala
     "sk",     // Slovak
@@ -145,6 +156,7 @@
     "tk",     // Turkmen
     "tl",     // Tagalog - Chrome uses "fil"
     "tr",     // Turkish
+    "ts",     // Tsonga
     "tt",     // Tatar
     "ug",     // Uyghur
     "uk",     // Ukrainian
@@ -341,16 +353,13 @@
     return false;
   }
 
-  const std::string& locale =
-      TranslateDownloadManager::GetInstance()->application_locale();
-
   // Now we can clear language list.
   supported_languages_.clear();
   // ... and replace it with the values we just fetched from the server.
   for (auto kv_pair : *target_languages) {
     const std::string& lang = kv_pair.first;
-    if (!l10n_util::IsLocaleNameTranslated(lang.c_str(), locale)) {
-      // Don't include languages not displayable in current UI language.
+    if (!language::AcceptLanguagesService::CanBeAcceptLanguage(lang.c_str())) {
+      // Don't include languages that can not be Accept-Languages
       continue;
     }
     supported_languages_.push_back(lang);
diff --git a/components/translate/core/browser/translate_language_list_unittest.cc b/components/translate/core/browser/translate_language_list_unittest.cc
index bb166a2..a0133f3 100644
--- a/components/translate/core/browser/translate_language_list_unittest.cc
+++ b/components/translate/core/browser/translate_language_list_unittest.cc
@@ -35,8 +35,10 @@
 TEST_F(TranslateLanguageListTest, SetSupportedLanguages) {
   const std::string language_list(
       "{"
-      "\"sl\":{\"en\":\"English\",\"ja\":\"Japanese\"},"
-      "\"tl\":{\"en\":\"English\",\"ja\":\"Japanese\"}"
+      "\"sl\":{\"en\":\"English\",\"ja\":\"Japanese\",\"tl\":\"Tagalog\","
+      "\"xx\":\"NotALanguage\"},"
+      "\"tl\":{\"en\":\"English\",\"ja\":\"Japanese\",\"tl\":\"Tagalog\","
+      "\"xx\":\"NotALanguage\"}"
       "}");
 
   base::test::TaskEnvironment task_environment;
@@ -52,9 +54,10 @@
   std::vector<std::string> results;
   manager->language_list()->GetSupportedLanguages(true /* translate_allowed */,
                                                   &results);
-  ASSERT_EQ(2u, results.size());
+  ASSERT_EQ(3u, results.size());
   EXPECT_EQ("en", results[0]);
   EXPECT_EQ("ja", results[1]);
+  EXPECT_EQ("tl", results[2]);
   manager->ResetForTesting();
 }
 
diff --git a/components/update_client/features.cc b/components/update_client/features.cc
index 587f548..e6a1723 100644
--- a/components/update_client/features.cc
+++ b/components/update_client/features.cc
@@ -8,11 +8,5 @@
 #include "build/build_config.h"
 
 namespace update_client::features {
-#if BUILDFLAG(IS_ANDROID)
-BASE_FEATURE(kPuffinPatches,
-             "PuffinPatches",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#else
 BASE_FEATURE(kPuffinPatches, "PuffinPatches", base::FEATURE_ENABLED_BY_DEFAULT);
-#endif
 }
diff --git a/components/update_client/puffin_patcher_unittest.cc b/components/update_client/puffin_patcher_unittest.cc
index 21171cff..1babd02 100644
--- a/components/update_client/puffin_patcher_unittest.cc
+++ b/components/update_client/puffin_patcher_unittest.cc
@@ -39,13 +39,9 @@
 };
 
 TEST_F(PuffinPatcherTest, CheckPuffPatch) {
-// TODO(crbug/1446751): once this bug is confirmed fixed, we can remove this
-// android-specific if.
-#if !BUILDFLAG(IS_ANDROID)
   if (!base::FeatureList::IsEnabled(features::kPuffinPatches)) {
     GTEST_SKIP() << "only works when PuffinPatches are enabled.";
   }
-#endif
   // The operation needs a Patcher to access the PatchService.
   scoped_refptr<Patcher> patcher =
       base::MakeRefCounted<PatchChromiumFactory>(
diff --git a/components/user_education/common/tutorial_registry.cc b/components/user_education/common/tutorial_registry.cc
index 26e4b4f..19a0e1b 100644
--- a/components/user_education/common/tutorial_registry.cc
+++ b/components/user_education/common/tutorial_registry.cc
@@ -24,8 +24,8 @@
   return base::Contains(tutorial_registry_, id);
 }
 
-TutorialDescription* TutorialRegistry::GetTutorialDescription(
-    TutorialIdentifier id) {
+const TutorialDescription* TutorialRegistry::GetTutorialDescription(
+    TutorialIdentifier id) const {
   DCHECK(tutorial_registry_.size() > 0);
   auto pair = tutorial_registry_.find(id);
   if (pair == tutorial_registry_.end())
@@ -33,8 +33,8 @@
   return &pair->second;
 }
 
-const std::vector<TutorialIdentifier>
-TutorialRegistry::GetTutorialIdentifiers() {
+const std::vector<TutorialIdentifier> TutorialRegistry::GetTutorialIdentifiers()
+    const {
   DCHECK(tutorial_registry_.size() > 0);
   std::vector<TutorialIdentifier> id_strings;
   base::ranges::transform(tutorial_registry_, std::back_inserter(id_strings),
diff --git a/components/user_education/common/tutorial_registry.h b/components/user_education/common/tutorial_registry.h
index c2e49001..e2cf499 100644
--- a/components/user_education/common/tutorial_registry.h
+++ b/components/user_education/common/tutorial_registry.h
@@ -28,11 +28,12 @@
 
   // Returns a list of Tutorial Identifiers if the tutorial registry exists.
   // If RegisterTutorials has not been called this returns an empty vector.
-  const std::vector<TutorialIdentifier> GetTutorialIdentifiers();
+  const std::vector<TutorialIdentifier> GetTutorialIdentifiers() const;
 
   // Gets the TutorialDescription from the registry. Returns nullptr if
   // there is no registered tutorial under the given ID.
-  TutorialDescription* GetTutorialDescription(TutorialIdentifier id);
+  const TutorialDescription* GetTutorialDescription(
+      TutorialIdentifier id) const;
 
   // Adds a TutorialID, TutorialDescription pair to the registry. This should
   // be used by the RegisterTutorials method to Add Tutorials.
diff --git a/components/user_education/common/tutorial_service.cc b/components/user_education/common/tutorial_service.cc
index fde1ebc..7a55285f 100644
--- a/components/user_education/common/tutorial_service.cc
+++ b/components/user_education/common/tutorial_service.cc
@@ -32,7 +32,7 @@
 }  // namespace
 
 TutorialService::TutorialCreationParams::TutorialCreationParams(
-    TutorialDescription* description,
+    const TutorialDescription* description,
     ui::ElementContext context)
     : description_(description), context_(context) {}
 
@@ -61,7 +61,7 @@
   is_final_bubble_ = false;
 
   // Get the description from the tutorial registry.
-  TutorialDescription* description =
+  const TutorialDescription* const description =
       tutorial_registry_->GetTutorialDescription(id);
   CHECK(description);
 
@@ -90,7 +90,7 @@
 
 void TutorialService::LogIPHLinkClicked(TutorialIdentifier id,
                                         bool iph_link_was_clicked) {
-  TutorialDescription* description =
+  const TutorialDescription* const description =
       tutorial_registry_->GetTutorialDescription(id);
   CHECK(description);
 
@@ -100,7 +100,7 @@
 
 void TutorialService::LogStartedFromWhatsNewPage(TutorialIdentifier id,
                                                  bool success) {
-  TutorialDescription* description =
+  const TutorialDescription* const description =
       tutorial_registry_->GetTutorialDescription(id);
   CHECK(description);
 
diff --git a/components/user_education/common/tutorial_service.h b/components/user_education/common/tutorial_service.h
index 527c62b..8abe6bf 100644
--- a/components/user_education/common/tutorial_service.h
+++ b/components/user_education/common/tutorial_service.h
@@ -88,10 +88,10 @@
   // Struct used to reconstruct a tutorial from the params initially used to
   // create it.
   struct TutorialCreationParams {
-    TutorialCreationParams(TutorialDescription* description,
+    TutorialCreationParams(const TutorialDescription* description,
                            ui::ElementContext context);
 
-    raw_ptr<TutorialDescription, DanglingUntriaged> description_;
+    raw_ptr<const TutorialDescription, DanglingUntriaged> description_;
     ui::ElementContext context_;
   };
 
diff --git a/components/viz/common/frame_sinks/delay_based_time_source.cc b/components/viz/common/frame_sinks/delay_based_time_source.cc
index 7a41ff2..24293fa 100644
--- a/components/viz/common/frame_sinks/delay_based_time_source.cc
+++ b/components/viz/common/frame_sinks/delay_based_time_source.cc
@@ -150,14 +150,16 @@
 void DelayBasedTimeSource::PostNextTickTask(base::TimeTicks now) {
   if (interval_.is_zero()) {
     next_tick_time_ = now;
+    timer_.Start(FROM_HERE, base::TimeTicks(), tick_closure_,
+                 base::subtle::DelayPolicy::kPrecise);
   } else {
     next_tick_time_ = now.SnappedToNextTick(timebase_, interval_);
     if (next_tick_time_ == now)
       next_tick_time_ += interval_;
     DCHECK_GT(next_tick_time_, now);
+    timer_.Start(FROM_HERE, next_tick_time_, tick_closure_,
+                 base::subtle::DelayPolicy::kPrecise);
   }
-  timer_.Start(FROM_HERE, next_tick_time_, tick_closure_,
-               base::subtle::DelayPolicy::kPrecise);
 }
 
 std::string DelayBasedTimeSource::TypeString() const {
diff --git a/components/viz/common/view_transition_element_resource_id.h b/components/viz/common/view_transition_element_resource_id.h
index 17ae568..44f980f 100644
--- a/components/viz/common/view_transition_element_resource_id.h
+++ b/components/viz/common/view_transition_element_resource_id.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <cstdint>
 #include <string>
 #include <vector>
 
diff --git a/components/viz/host/gpu_client.cc b/components/viz/host/gpu_client.cc
index 896a709c..6fe8f4f 100644
--- a/components/viz/host/gpu_client.cc
+++ b/components/viz/host/gpu_client.cc
@@ -13,20 +13,14 @@
 #include "build/chromeos_buildflags.h"
 #include "components/viz/host/gpu_host_impl.h"
 #include "components/viz/host/host_gpu_memory_buffer_manager.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
 
 namespace viz {
-namespace {
-bool IsSizeValid(const gfx::Size& size) {
-  base::CheckedNumeric<int> bytes = size.width();
-  bytes *= size.height();
-  return bytes.IsValid();
-}
-
-}  // namespace
 
 GpuClient::GpuClient(std::unique_ptr<GpuClientDelegate> delegate,
                      int client_id,
@@ -242,7 +236,7 @@
     return;
   }
 
-  if (!IsSizeValid(size)) {
+  if (!gpu::GpuMemoryBufferSupport::IsSizeValid(size)) {
     gpu_memory_buffer_factory_receivers_.ReportBadMessage("Invalid GMB size");
     return;
   }
@@ -286,4 +280,16 @@
   gpu_memory_buffer_factory_receivers_.Add(this, std::move(receiver));
 }
 
+void GpuClient::CreateClientGpuMemoryBufferFactory(
+    mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> receiver) {
+  CHECK(base::FeatureList::IsEnabled(features::kUseClientGmbInterface));
+  // Send the PendingReceiver to GpuService via IPC.
+  if (auto* gpu_host = delegate_->EnsureGpuHost()) {
+    gpu_host->gpu_service()->BindClientGmbInterface(std::move(receiver),
+                                                    client_id_);
+  } else {
+    receiver.ResetWithReason(0, "Can not bind the ClientGmbInterface.");
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/host/gpu_client.h b/components/viz/host/gpu_client.h
index 1841cf6a..29ec07b 100644
--- a/components/viz/host/gpu_client.h
+++ b/components/viz/host/gpu_client.h
@@ -16,6 +16,7 @@
 #include "components/viz/host/gpu_client_delegate.h"
 #include "components/viz/host/gpu_host_impl.h"
 #include "components/viz/host/viz_host_export.h"
+#include "gpu/ipc/common/client_gmb_interface.mojom.h"
 #include "gpu/ipc/common/gpu_disk_cache_type.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -70,9 +71,16 @@
   void CopyGpuMemoryBuffer(gfx::GpuMemoryBufferHandle buffer_handle,
                            base::UnsafeSharedMemoryRegion shared_memory,
                            CopyGpuMemoryBufferCallback callback) override;
+
   // mojom::Gpu overrides:
   void CreateGpuMemoryBufferFactory(
       mojo::PendingReceiver<mojom::GpuMemoryBufferFactory> receiver) override;
+
+  // mojom::ClientGmbInterface is direct interface between renderer and GPU
+  // process to create GpuMemoryBuffers.
+  void CreateClientGpuMemoryBufferFactory(
+      mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> receiver) override;
+
   void EstablishGpuChannel(EstablishGpuChannelCallback callback) override;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index b14ba6a..cb6ff4f2 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
@@ -184,6 +184,10 @@
     std::move(callback).Run(false);
   }
 
+  void BindClientGmbInterface(
+      mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> receiver,
+      int client_id) override {}
+
   void GetVideoMemoryUsageStats(
       GetVideoMemoryUsageStatsCallback callback) override {}
 
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index 5c45f044..9715d3b 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -589,6 +589,11 @@
              : it->second;
 }
 
+bool DirectRenderer::SupportsBGRA() const {
+  // TODO(penghuang): check supported format correctly.
+  return true;
+}
+
 void DirectRenderer::FlushPolygons(
     base::circular_deque<std::unique_ptr<DrawPolygon>>* poly_list,
     const gfx::Rect& render_pass_scissor,
@@ -1158,9 +1163,8 @@
 
 SharedImageFormat DirectRenderer::GetColorSpaceSharedImageFormat(
     gfx::ColorSpace color_space) const {
-  // TODO(penghuang): check supported format correctly.
   gpu::Capabilities caps;
-  caps.texture_format_bgra8888 = true;
+  caps.texture_format_bgra8888 = SupportsBGRA();
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   // TODO(crbug.com/1317015): add support RGBA_F16 in LaCrOS.
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index 3546717a..009d127 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -308,6 +308,7 @@
       const copy_output::RenderPassGeometry& geometry,
       std::unique_ptr<CopyOutputRequest> request) = 0;
   virtual void GenerateMipmap() = 0;
+  virtual bool SupportsBGRA() const;
 
   gfx::Size surface_size_for_swap_buffers() const {
     return reshape_params_ ? reshape_params_->size : gfx::Size();
diff --git a/components/viz/service/display/overlay_candidate_factory.cc b/components/viz/service/display/overlay_candidate_factory.cc
index bc7e5b4a..d54a998 100644
--- a/components/viz/service/display/overlay_candidate_factory.cc
+++ b/components/viz/service/display/overlay_candidate_factory.cc
@@ -127,7 +127,7 @@
   // We don't support an opacity value different than one for an overlay plane.
   // Render pass quads should have their |sqs| opacity integrated directly into
   // their final output buffers.
-  if (!is_delegated_context_ &&
+  if (!context_.is_delegated_context &&
       !cc::MathUtil::IsWithinEpsilon(sqs->opacity, 1.0f)) {
     return CandidateStatus::kFailOpacity;
   }
@@ -151,18 +151,21 @@
       return FromVideoHoleQuad(VideoHoleDrawQuad::MaterialCast(quad),
                                candidate);
     case DrawQuad::Material::kSolidColor:
-      if (!is_delegated_context_)
+      if (!context_.is_delegated_context) {
         return CandidateStatus::kFailQuadNotSupported;
+      }
       return FromSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad),
                                 candidate);
     case DrawQuad::Material::kAggregatedRenderPass:
-      if (!is_delegated_context_)
+      if (!context_.is_delegated_context) {
         return CandidateStatus::kFailQuadNotSupported;
+      }
       return FromAggregateQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad),
                                candidate);
     case DrawQuad::Material::kTiledContent:
-      if (!is_delegated_context_)
+      if (!context_.is_delegated_context) {
         return CandidateStatus::kFailQuadNotSupported;
+      }
       return FromTileQuad(TileDrawQuad::MaterialCast(quad), candidate);
     default:
       break;
@@ -178,20 +181,14 @@
     const SkM44* output_color_matrix,
     const gfx::RectF primary_rect,
     const OverlayProcessorInterface::FilterOperationsMap* render_pass_filters,
-    bool is_delegated_context,
-    bool supports_clip_rect,
-    bool supports_arbitrary_transform,
-    bool supports_rounded_corner_masks)
+    const OverlayContext& context)
     : render_pass_(render_pass),
       resource_provider_(resource_provider),
       surface_damage_rect_list_(surface_damage_rect_list),
       primary_rect_(primary_rect),
       render_pass_filters_(render_pass_filters),
-      is_delegated_context_(is_delegated_context),
-      supports_clip_rect_(supports_clip_rect),
-      supports_arbitrary_transform_(supports_arbitrary_transform),
-      supports_rounded_display_masks_(supports_rounded_corner_masks) {
-  DCHECK(supports_clip_rect_ || !supports_arbitrary_transform_);
+      context_(context) {
+  DCHECK(context_.supports_clip_rect || !context_.supports_arbitrary_transform);
 
   has_custom_color_matrix_ = *output_color_matrix != SkM44();
 
@@ -326,7 +323,7 @@
   const SharedQuadState* sqs = quad->shared_quad_state;
 
   candidate.display_rect = gfx::RectF(quad->rect);
-  if (supports_arbitrary_transform_) {
+  if (context_.supports_arbitrary_transform) {
     gfx::Transform transform = sqs->quad_to_target_transform;
     if (y_flipped) {
       transform.PreConcat(gfx::OverlayTransformToTransform(
@@ -337,9 +334,10 @@
     gfx::OverlayTransform overlay_transform =
         GetOverlayTransform(sqs->quad_to_target_transform, y_flipped);
     if (overlay_transform == gfx::OVERLAY_TRANSFORM_INVALID) {
-      return is_delegated_context_ ? GetReasonForTransformNotAxisAligned(
-                                         sqs->quad_to_target_transform)
-                                   : CandidateStatus::kFailNotAxisAligned;
+      return context_.is_delegated_context
+                 ? GetReasonForTransformNotAxisAligned(
+                       sqs->quad_to_target_transform)
+                 : CandidateStatus::kFailNotAxisAligned;
     }
     candidate.transform = overlay_transform;
 
@@ -373,7 +371,7 @@
   if (resource_id != kInvalidResourceId) {
     candidate.mailbox = resource_provider_->GetMailbox(resource_id);
 
-    if (!is_delegated_context_) {
+    if (!context_.is_delegated_context) {
       struct TrackingIdData {
         gfx::Rect rect;
         FrameSinkId frame_sink_id;
@@ -386,18 +384,19 @@
     }
   }
 
-  if (is_delegated_context_) {
+  if (context_.is_delegated_context) {
     // Lacros cannot currently delegate clip rects on quads that extend outside
     // the primary rect. This is because there are bugs that cause the Lacros
     // window and drop shadow to move incorrectly in that case.
-    bool quad_within_window = primary_rect_.Contains(candidate.display_rect);
-    bool transform_supports_clipping =
-        supports_arbitrary_transform_ ||
+    const bool quad_within_window =
+        primary_rect_.Contains(candidate.display_rect);
+    const bool transform_supports_clipping =
+        context_.supports_arbitrary_transform ||
         absl::holds_alternative<gfx::OverlayTransform>(candidate.transform);
-    bool has_content_clipping = quad->visible_rect != quad->rect;
-    bool can_delegate_clipping = supports_clip_rect_ && quad_within_window &&
-                                 transform_supports_clipping &&
-                                 !has_content_clipping;
+    const bool has_content_clipping = quad->visible_rect != quad->rect;
+    const bool can_delegate_clipping =
+        context_.supports_clip_rect && quad_within_window &&
+        transform_supports_clipping && !has_content_clipping;
     if (can_delegate_clipping) {
       if (candidate.clip_rect.has_value() && candidate.clip_rect->IsEmpty()) {
         return CandidateStatus::kFailVisible;
@@ -491,7 +490,7 @@
     OverlayCandidate& candidate) const {
   candidate.display_rect = gfx::RectF(quad->rect);
   const SharedQuadState* sqs = quad->shared_quad_state;
-  if (supports_arbitrary_transform_) {
+  if (context_.supports_arbitrary_transform) {
     candidate.transform = sqs->quad_to_target_transform;
   } else {
     gfx::OverlayTransform overlay_transform =
@@ -532,7 +531,7 @@
 OverlayCandidate::CandidateStatus OverlayCandidateFactory::FromTextureQuad(
     const TextureDrawQuad* quad,
     OverlayCandidate& candidate) const {
-  if (!is_delegated_context_ &&
+  if (!context_.is_delegated_context &&
       quad->overlay_priority_hint == OverlayPriority::kLow) {
     // For current implementation low priority means this does not promote to
     // overlay.
@@ -540,15 +539,15 @@
   }
 
   if (!quad->rounded_display_masks_info.IsEmpty() &&
-      !supports_rounded_display_masks_) {
-    DCHECK(!is_delegated_context_);
+      !context_.supports_rounded_display_masks) {
+    DCHECK(!context_.is_delegated_context);
     return CandidateStatus::kFailRoundedDisplayMasksNotSupported;
   }
 
   if (quad->nearest_neighbor)
     return CandidateStatus::kFailNearFilter;
 
-  if (is_delegated_context_) {
+  if (context_.is_delegated_context) {
     // Always convey |background_color| even when transparent. This allows for
     // the wayland server to make blending optimizations even when the quad is
     // considered opaque. Specifically Exo will try to ensure the opaqueness of
@@ -572,8 +571,9 @@
                                   candidate);
   if (rtn == CandidateStatus::kSuccess) {
     // Only handle clip rect for required overlays
-    if (!is_delegated_context_ && candidate.requires_overlay)
+    if (!context_.is_delegated_context && candidate.requires_overlay) {
       HandleClipAndSubsampling(candidate);
+    }
 
     // Texture quads for UI elements like scroll bars have empty
     // |size_in_pixels| as 'set_resource_size_in_pixels' is not called as these
@@ -682,7 +682,7 @@
   // For underlays the function 'EstimateVisibleDamage()' is called to update
   // |damage_area_estimate| to more accurately reflect the actual visible
   // damage.
-  if (!is_delegated_context_) {
+  if (!context_.is_delegated_context) {
     candidate.damage_area_estimate =
         GetDamageEstimate(candidate).size().GetArea();
   }
diff --git a/components/viz/service/display/overlay_candidate_factory.h b/components/viz/service/display/overlay_candidate_factory.h
index 6f6ccd4..01e1086 100644
--- a/components/viz/service/display/overlay_candidate_factory.h
+++ b/components/viz/service/display/overlay_candidate_factory.h
@@ -47,6 +47,13 @@
  public:
   using CandidateStatus = OverlayCandidate::CandidateStatus;
 
+  struct VIZ_SERVICE_EXPORT OverlayContext {
+    bool is_delegated_context = false;
+    bool supports_clip_rect = false;
+    bool supports_arbitrary_transform = false;
+    bool supports_rounded_display_masks = false;
+  };
+
   // The coordinate space of |render_pass| is the target space for candidates
   // produced by this factory.
   OverlayCandidateFactory(
@@ -56,10 +63,7 @@
       const SkM44* output_color_matrix,
       const gfx::RectF primary_rect,
       const OverlayProcessorInterface::FilterOperationsMap* render_pass_filters,
-      bool is_delegated_context = false,
-      bool supports_clip_rect = false,
-      bool supports_arbitrary_transform = false,
-      bool supports_rounded_display_masks = false);
+      const OverlayContext& context);
 
   OverlayCandidateFactory(const OverlayCandidateFactory&) = delete;
   OverlayCandidateFactory& operator=(const OverlayCandidateFactory&) = delete;
@@ -148,10 +152,7 @@
   const gfx::RectF primary_rect_;
   raw_ptr<const OverlayProcessorInterface::FilterOperationsMap>
       render_pass_filters_;
-  const bool is_delegated_context_;
-  const bool supports_clip_rect_;
-  const bool supports_arbitrary_transform_;
-  const bool supports_rounded_display_masks_;
+  const OverlayContext context_;
 
   // The union of all surface damages that are not specifically assigned to a
   // draw quad.
diff --git a/components/viz/service/display/overlay_candidate_factory_unittest.cc b/components/viz/service/display/overlay_candidate_factory_unittest.cc
index 341b15cdb..39e0437 100644
--- a/components/viz/service/display/overlay_candidate_factory_unittest.cc
+++ b/components/viz/service/display/overlay_candidate_factory_unittest.cc
@@ -38,6 +38,12 @@
 
 using RoundedDisplayMasksInfo = TextureDrawQuad::RoundedDisplayMasksInfo;
 
+constexpr OverlayCandidateFactory::OverlayContext kOverlayContextForTesting{
+    .is_delegated_context = true,
+    .supports_clip_rect = true,
+    .supports_arbitrary_transform = true,
+    .supports_rounded_display_masks = false};
+
 // TODO(zoraiznaeem): Move resource creation code into OverlayTestBase class.
 class OverlayCandidateFactoryTestBase : public testing::Test {
  public:
@@ -93,14 +99,10 @@
   OverlayCandidateFactory CreateCandidateFactory(
       const AggregatedRenderPass& render_pass,
       const gfx::RectF& primary_rect,
-      bool has_clip_support = true,
-      bool has_arbitrary_transform_support = true,
-      bool supports_rounded_display_masks = true) {
+      const OverlayCandidateFactory::OverlayContext& context) {
     return OverlayCandidateFactory(
         &render_pass, &resource_provider_, &surface_damage_list_, &identity_,
-        primary_rect, &render_pass_filters_, /*is_delegated_context=*/true,
-        has_clip_support, has_arbitrary_transform_support,
-        supports_rounded_display_masks);
+        primary_rect, &render_pass_filters_, context);
   }
 
   ClientResourceProvider child_resource_provider_;
@@ -179,8 +181,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory =
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect));
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
   gfx::Transform identity;
   identity.MakeIdentity();
 
@@ -214,8 +218,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory =
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect));
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
   gfx::Transform quad_to_target_transform;
   quad_to_target_transform.Scale(1.6, 1.6);
 
@@ -270,8 +276,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory =
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect));
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
 
   gfx::Transform transform;
   transform.Translate(1, 2);
@@ -292,8 +300,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory =
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect));
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
 
   gfx::Transform transform;
   transform.Rotate(1);
@@ -314,8 +324,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory =
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect));
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
 
   gfx::Transform transform;
   auto quad = CreateUnclippedDrawQuad(render_pass, gfx::Rect(1, 1), transform);
@@ -348,10 +360,16 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
-  EXPECT_DEATH(
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
-                             false, true),
-      "supports_clip_rect_ \\|\\| !supports_arbitrary_transform_");
+
+  const OverlayCandidateFactory::OverlayContext context = {
+      .is_delegated_context = true,
+      .supports_clip_rect = false,
+      .supports_arbitrary_transform = true};
+
+  EXPECT_DEATH(CreateCandidateFactory(
+                   render_pass, gfx::RectF(render_pass.output_rect), context),
+               "context_.supports_clip_rect \\|\\| "
+               "!context_.supports_arbitrary_transform");
 }
 
 // Resource-less overlays use the overlay quad in target space for damage
@@ -363,8 +381,10 @@
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 2, 2), gfx::Rect(0, 0, 1, 1),
                      gfx::Transform());
-  OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, true);
+
+  OverlayCandidateFactory factory =
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
 
   SharedQuadState* sqs = render_pass.CreateAndAppendSharedQuadState();
   sqs->quad_to_target_transform.Rotate(1);
@@ -385,8 +405,14 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform());
+
+  const OverlayCandidateFactory::OverlayContext context{
+      .is_delegated_context = true,
+      .supports_clip_rect = true,
+      .supports_arbitrary_transform = false};
+
   OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, false);
+      render_pass, gfx::RectF(render_pass.output_rect), context);
 
   gfx::Transform transform;
   transform.Rotate(1);
@@ -405,8 +431,14 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(render_pass_id, gfx::Rect(0, 0, 2, 2), gfx::Rect(),
                      gfx::Transform());
+
+  const OverlayCandidateFactory::OverlayContext context{
+      .is_delegated_context = true,
+      .supports_clip_rect = true,
+      .supports_arbitrary_transform = false};
+
   OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, false);
+      render_pass, gfx::RectF(render_pass.output_rect), context);
 
   QuadList quad_list;
   AggregatedRenderPassDrawQuad* rpdq =
@@ -460,10 +492,11 @@
                      gfx::Rect(0, 0, 2, 2), gfx::Rect(), gfx::Transform());
 
   // Add damage so that the factory has unassigned surface damage internally.
-  surface_damage_list_.push_back(gfx::Rect(1, 1, 1, 1));
+  surface_damage_list_.emplace_back(1, 1, 1, 1);
 
-  OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, true);
+  OverlayCandidateFactory factory =
+      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect),
+                             kOverlayContextForTesting);
 
   // Make a rotated quad which doesn't intersect with the damage, but the
   // axis-aligned bounding box of its target space rect does. This rect should
@@ -539,10 +572,12 @@
     gfx::Rect bounds(100, 100);
     render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1), bounds,
                        gfx::Rect(), gfx::Transform());
+
     // Create a factory without clip rect or arbitrary transform delegation, so
     // that any clips will be baked into the candidate.
     OverlayCandidateFactory factory = CreateCandidateFactory(
-        render_pass, gfx::RectF(render_pass.output_rect), false, false);
+        render_pass, gfx::RectF(render_pass.output_rect),
+        OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
 
     // |transform| maps the rect (0,0 1x1) to (50,50 100x100).
     gfx::Transform transform =
@@ -606,8 +641,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 100, 100), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), false, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
 
   // Entirely clipped
   gfx::Rect clip_rect(0, 0);
@@ -626,8 +663,10 @@
   AggregatedRenderPass render_pass;
   render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1),
                      gfx::Rect(0, 0, 100, 100), gfx::Rect(), gfx::Transform());
+
   OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), false, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
 
   AggregatedRenderPassId rpid(2);
   gfx::Transform transform;
@@ -655,7 +694,8 @@
   render_pass_filters_[rpid] = &filter_ops;
 
   OverlayCandidateFactory factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), false, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
 
   gfx::Transform transform;
   transform.Translate(gfx::Vector2dF(0, 101));
@@ -679,9 +719,12 @@
   auto* quad = AddQuad(rect, identity, &render_pass, clip, rect);
 
   OverlayCandidateFactory noclip_factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), false, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
   OverlayCandidateFactory clip_factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true,
+                                              .supports_clip_rect = true});
 
   OverlayCandidate no_clip_cand;
   OverlayCandidate clip_cand;
@@ -709,9 +752,12 @@
   auto* quad = AddQuad(rect, transform, &render_pass, clip, rect);
 
   OverlayCandidateFactory noclip_factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), false, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
   OverlayCandidateFactory clip_factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true,
+                                              .supports_clip_rect = true});
 
   OverlayCandidate no_clip_cand;
   OverlayCandidate clip_cand;
@@ -740,9 +786,12 @@
   auto* quad = AddQuad(rect, identity, &render_pass, clip, visible_rect);
 
   OverlayCandidateFactory noclip_factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), false, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
   OverlayCandidateFactory clip_factory = CreateCandidateFactory(
-      render_pass, gfx::RectF(render_pass.output_rect), true, false, false);
+      render_pass, gfx::RectF(render_pass.output_rect),
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true,
+                                              .supports_clip_rect = true});
 
   OverlayCandidate no_clip_cand;
   OverlayCandidate clip_cand;
diff --git a/components/viz/service/display/overlay_processor_delegated.cc b/components/viz/service/display/overlay_processor_delegated.cc
index ccef0c3..d1d3c06 100644
--- a/components/viz/service/display/overlay_processor_delegated.cc
+++ b/components/viz/service/display/overlay_processor_delegated.cc
@@ -135,7 +135,6 @@
   DCHECK(candidates->empty());
   auto* render_pass = render_pass_list->back().get();
   QuadList* quad_list = &render_pass->quad_list;
-  constexpr bool is_delegated_context = true;
   delegated_status_ = DelegationStatus::kCompositedOther;
 
   if (!features::IsDelegatedCompositingEnabled()) {
@@ -164,10 +163,13 @@
     return false;
   }
 
+  const OverlayCandidateFactory::OverlayContext context = {
+      .is_delegated_context = true, .supports_clip_rect = supports_clip_rect_};
+
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
       &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
-      &render_pass_filters, is_delegated_context, supports_clip_rect_);
+      &render_pass_filters, context);
 
   unassigned_damage_ = gfx::RectF(candidate_factory.GetUnassignedDamage());
 
diff --git a/components/viz/service/display/overlay_proposed_candidate_unittest.cc b/components/viz/service/display/overlay_proposed_candidate_unittest.cc
index c7a24a73..00049699 100644
--- a/components/viz/service/display/overlay_proposed_candidate_unittest.cc
+++ b/components/viz/service/display/overlay_proposed_candidate_unittest.cc
@@ -154,19 +154,6 @@
     texture_quad->rounded_display_masks_info = rounded_display_masks_info;
   }
 
-  OverlayCandidateFactory CreateCandidateFactory(
-      const AggregatedRenderPass& render_pass,
-      const gfx::RectF& primary_rect,
-      bool has_clip_support = true,
-      bool has_arbitrary_transform_support = false,
-      bool supports_rounded_display_masks = true) {
-    return OverlayCandidateFactory(
-        &render_pass, &resource_provider_, &surface_damage_list_, &identity_,
-        primary_rect, &render_pass_filters_, /*is_delegated_context=*/false,
-        has_clip_support, has_arbitrary_transform_support,
-        supports_rounded_display_masks);
-  }
-
   ClientResourceProvider child_resource_provider_;
   DisplayResourceProviderNull resource_provider_;
   SurfaceDamageRectList surface_damage_list_;
@@ -190,8 +177,11 @@
                                  /*is_overlay_candidate=*/true, identity,
                                  mask_info_, &render_pass);
 
-  OverlayCandidateFactory factory =
-      CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect));
+  OverlayCandidateFactory factory = OverlayCandidateFactory(
+      &render_pass, &resource_provider_, &surface_damage_list_, &identity_,
+      gfx::RectF(render_pass.output_rect), &render_pass_filters_,
+      OverlayCandidateFactory::OverlayContext{.supports_rounded_display_masks =
+                                                  true});
 
   OverlayCandidate candidate;
   OverlayCandidateFactory::CandidateStatus status =
diff --git a/components/viz/service/display/overlay_strategy_fullscreen.cc b/components/viz/service/display/overlay_strategy_fullscreen.cc
index 5e8046d..ee0c3d2 100644
--- a/components/viz/service/display/overlay_strategy_fullscreen.cc
+++ b/components/viz/service/display/overlay_strategy_fullscreen.cc
@@ -52,10 +52,11 @@
     return;
 
   OverlayCandidate candidate;
+
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
       &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
-      &render_pass_filters);
+      &render_pass_filters, /*context=*/{});
   if (candidate_factory.FromDrawQuad(quad, candidate) !=
       OverlayCandidate::CandidateStatus::kSuccess) {
     return;
diff --git a/components/viz/service/display/overlay_strategy_single_on_top.cc b/components/viz/service/display/overlay_strategy_single_on_top.cc
index 4498baa..2906dfee 100644
--- a/components/viz/service/display/overlay_strategy_single_on_top.cc
+++ b/components/viz/service/display/overlay_strategy_single_on_top.cc
@@ -99,14 +99,15 @@
     std::vector<gfx::Rect>* content_bounds) {
   auto* render_pass = render_pass_list->back().get();
   QuadList* quad_list = &render_pass->quad_list;
+
+  const OverlayCandidateFactory::OverlayContext context = {
+      .supports_rounded_display_masks = true};
+
   // Build a list of candidates with the associated quad.
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
       &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
-      &render_pass_filters,
-      /*is_delegated_context=*/false, /*supports_clip_rect=*/false,
-      /*supports_arbitrary_transform=*/false,
-      /*supports_rounded_display_masks=*/true);
+      &render_pass_filters, context);
 
   std::vector<OverlayProposedCandidate> candidates_with_masks;
 
diff --git a/components/viz/service/display/overlay_strategy_underlay.cc b/components/viz/service/display/overlay_strategy_underlay.cc
index 620074b..a96848e 100644
--- a/components/viz/service/display/overlay_strategy_underlay.cc
+++ b/components/viz/service/display/overlay_strategy_underlay.cc
@@ -36,10 +36,11 @@
     std::vector<gfx::Rect>* content_bounds) {
   auto* render_pass = render_pass_list->back().get();
   QuadList& quad_list = render_pass->quad_list;
+
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
       &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
-      &render_pass_filters);
+      &render_pass_filters, /*context=*/{});
 
   for (auto it = quad_list.begin(); it != quad_list.end(); ++it) {
     OverlayCandidate candidate;
diff --git a/components/viz/service/display/overlay_strategy_underlay_cast.cc b/components/viz/service/display/overlay_strategy_underlay_cast.cc
index 4991ba2..2f43093 100644
--- a/components/viz/service/display/overlay_strategy_underlay_cast.cc
+++ b/components/viz/service/display/overlay_strategy_underlay_cast.cc
@@ -56,10 +56,11 @@
   QuadList& quad_list = render_pass->quad_list;
   OverlayCandidate candidate;
   auto overlay_iter = quad_list.end();
+
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
       &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
-      &render_pass_filters);
+      &render_pass_filters, /*context=*/{});
 
   // Original code did reverse iteration.
   // Here we do forward but find the last one. which should be the same thing.
@@ -105,10 +106,11 @@
   QuadList& quad_list = render_pass->quad_list;
   bool found_underlay = false;
   gfx::Rect content_rect;
+
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
       &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
-      &render_pass_filters);
+      &render_pass_filters, /*context=*/{});
 
   for (const auto* quad : base::Reversed(quad_list)) {
     if (OverlayCandidate::IsInvisibleQuad(quad))
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 0775633a..e7e10fc 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -91,6 +91,7 @@
 const gfx::PointF kUVTopLeft(0.1f, 0.2f);
 const gfx::PointF kUVBottomRight(1.0f, 1.0f);
 const gfx::BufferFormat kDefaultBufferFormat = gfx::BufferFormat::RGBA_8888;
+constexpr OverlayCandidateFactory::OverlayContext kTestOverlayContext = {};
 
 class TimeTicksOverride {
  public:
@@ -1676,7 +1677,8 @@
   auto color_mat = GetIdentityColorMatrix();
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters);
+      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
+      kTestOverlayContext);
   auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a);
   OverlayCandidate candidate_b;
   auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b);
@@ -1722,7 +1724,8 @@
   OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters);
+      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
+      kTestOverlayContext);
   auto ret_a = candidate_factory.FromDrawQuad(quad_a, candidate_a);
   OverlayCandidate candidate_b;
   auto ret_b = candidate_factory.FromDrawQuad(quad_b, candidate_b);
@@ -4673,7 +4676,8 @@
   OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters);
+      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
+      kTestOverlayContext);
   candidate_factory.FromDrawQuad(new_quad, candidate);
 
   // Verify that a default candidate is not a required overlay.
@@ -4697,7 +4701,8 @@
   OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters);
+      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
+      kTestOverlayContext);
   candidate_factory.FromDrawQuad(new_quad, candidate);
 
   // Verify that a HW protected video candidate requires overlay.
@@ -4722,7 +4727,8 @@
   OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters);
+      &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
+      kTestOverlayContext);
   candidate_factory.FromDrawQuad(new_quad, candidate);
 
   // Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer
@@ -4759,7 +4765,7 @@
   OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-      &color_mat, primary_rect, &render_pass_filters);
+      &color_mat, primary_rect, &render_pass_filters, kTestOverlayContext);
   candidate_factory.FromDrawQuad(new_quad, candidate);
 
   // Default uv rect is 0.1, 0.2, 1.0, 1.0 which in the 320x240 buffer
@@ -4872,7 +4878,8 @@
     OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
     auto candidate_factory = OverlayCandidateFactory(
         pass.get(), resource_provider_.get(), &surface_damage_rect_list,
-        &color_mat, gfx::RectF(), &render_pass_filters);
+        &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
+        kTestOverlayContext);
     candidate_factory.FromDrawQuad(quad_candidate, candidate);
 
     // Before the 'EstimateOccludedDamage' function is called the damage area
@@ -5470,10 +5477,11 @@
   OverlayCandidate candidate;
   auto color_mat = GetIdentityColorMatrix();
   OverlayProcessorInterface::FilterOperationsMap render_pass_filters;
+
   auto candidate_factory = OverlayCandidateFactory(
       pass.get(), resource_provider_.get(), &surface_damage_rect_list,
       &color_mat, gfx::RectF(pass->output_rect), &render_pass_filters,
-      true /* is_delegated_context */);
+      OverlayCandidateFactory::OverlayContext{.is_delegated_context = true});
 
   pass->shared_quad_state_list.back()->quad_to_target_transform =
       MakePerspectiveTransform();
diff --git a/components/viz/service/display/skia_output_surface.h b/components/viz/service/display/skia_output_surface.h
index f51db8b..bbefa4b 100644
--- a/components/viz/service/display/skia_output_surface.h
+++ b/components/viz/service/display/skia_output_surface.h
@@ -218,6 +218,8 @@
 
   // Enqueue a GPU task to delete the specified shared image.
   virtual void DestroySharedImage(const gpu::Mailbox& mailbox) = 0;
+
+  virtual bool SupportsBGRA() const = 0;
 };
 
 }  // namespace viz
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 18d81548..1b1e08f8 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -3784,6 +3784,10 @@
   return delegated_ink_handler_->GetInkRenderer();
 }
 
+bool SkiaRenderer::SupportsBGRA() const {
+  return skia_output_surface_->SupportsBGRA();
+}
+
 void SkiaRenderer::SetDelegatedInkMetadata(
     std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {
   if (!delegated_ink_handler_) {
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index 7f1f0154..cebd142a 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -109,6 +109,7 @@
   void GenerateMipmap() override;
   void SetDelegatedInkPointRendererSkiaForTest(
       std::unique_ptr<DelegatedInkPointRendererSkia> renderer) override;
+  bool SupportsBGRA() const override;
 
   std::unique_ptr<DelegatedInkHandler> delegated_ink_handler_;
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 2c920ad..61875b13 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -1627,4 +1627,11 @@
                  /*need_framebuffer=*/false);
 }
 
+bool SkiaOutputSurfaceImpl::SupportsBGRA() const {
+  return gr_context_thread_safe_
+      ->defaultBackendFormat(SkColorType::kBGRA_8888_SkColorType,
+                             GrRenderable::kYes)
+      .isValid();
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 2766f8a..0dbb754 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -167,6 +167,7 @@
       const SkColor4f& color,
       const gfx::ColorSpace& color_space) override;
   void DestroySharedImage(const gpu::Mailbox& mailbox) override;
+  bool SupportsBGRA() const override;
 
   // ExternalUseClient implementation:
   gpu::SyncToken ReleaseImageContexts(
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 90310f0e..60bbea53b 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -40,6 +40,7 @@
 #include "gpu/config/gpu_switches.h"
 #include "gpu/config/gpu_util.h"
 #include "gpu/ipc/common/gpu_client_ids.h"
+#include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "gpu/ipc/common/gpu_peak_memory.h"
 #include "gpu/ipc/common/memory_stats.h"
@@ -409,6 +410,16 @@
   gpu_memory_buffer_factory_ = gpu::GpuMemoryBufferFactory::CreateNativeType(
       vulkan_context_provider(), io_runner_);
 
+  if (base::FeatureList::IsEnabled(features::kUseClientGmbInterface)) {
+#if defined(USE_OZONE_PLATFORM_X11)
+    for (const auto& config : gpu_extra_info_.gpu_memory_buffer_support_x11) {
+      supported_gmb_configurations_.emplace(config);
+    }
+#else
+    supported_gmb_configurations_ =
+        gpu::GpuMemoryBufferSupport::GetNativeGpuMemoryBufferConfigurations();
+#endif
+  }
   weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
 }
 
@@ -432,11 +443,14 @@
     base::WaitableEvent wait;
     auto destroy_receiver_task = base::BindOnce(
         [](mojo::Receiver<mojom::GpuService>* receiver,
+           std::unordered_map<int, std::unique_ptr<ClientGmbInterfaceImpl>>
+               client_gmb_interface_impl,
            base::WaitableEvent* wait) {
           receiver->reset();
+          client_gmb_interface_impl.clear();
           wait->Signal();
         },
-        &receiver_, base::Unretained(&wait));
+        &receiver_, std::move(gmb_clients_), base::Unretained(&wait));
     if (io_runner_->PostTask(FROM_HERE, std::move(destroy_receiver_task)))
       wait.Wait();
   }
@@ -846,6 +860,24 @@
       gpu_info_.active_gpu(), std::move(runner));
 }
 
+void GpuServiceImpl::BindClientGmbInterface(
+    mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> pending_receiver,
+    int client_id) {
+  CHECK(base::FeatureList::IsEnabled(features::kUseClientGmbInterface));
+  // Bind the receiver to the IO tread. All IPC in this interface will be
+  // then received on the IO thread.
+  if (main_runner_->BelongsToCurrentThread()) {
+    bind_task_tracker_.PostTask(
+        io_runner_.get(), FROM_HERE,
+        base::BindOnce(&GpuServiceImpl::BindClientGmbInterface,
+                       base::Unretained(this), std::move(pending_receiver),
+                       client_id));
+    return;
+  }
+  gmb_clients_[client_id] = std::make_unique<ClientGmbInterfaceImpl>(
+      client_id, std::move(pending_receiver), this, io_runner_);
+}
+
 void GpuServiceImpl::CreateGpuMemoryBuffer(
     gfx::GpuMemoryBufferId id,
     const gfx::Size& size,
@@ -1031,6 +1063,7 @@
       // We are already exiting so there is no point in responding. Close the
       // receiver so we can safely drop the callback.
       receiver_.reset();
+      gmb_clients_.clear();
       return;
     }
 
@@ -1390,6 +1423,173 @@
 }
 #endif
 
+bool GpuServiceImpl::IsNativeBufferSupported(gfx::BufferFormat format,
+                                             gfx::BufferUsage usage) {
+  CHECK(base::FeatureList::IsEnabled(features::kUseClientGmbInterface));
+  return supported_gmb_configurations_.find(gfx::BufferUsageAndFormat(
+             usage, format)) != supported_gmb_configurations_.end();
+}
+
+GpuServiceImpl::ClientGmbInterfaceImpl::PendingBufferInfo::PendingBufferInfo() =
+    default;
+GpuServiceImpl::ClientGmbInterfaceImpl::PendingBufferInfo::PendingBufferInfo(
+    PendingBufferInfo&&) = default;
+GpuServiceImpl::ClientGmbInterfaceImpl::PendingBufferInfo::
+    ~PendingBufferInfo() = default;
+
+GpuServiceImpl::ClientGmbInterfaceImpl::ClientGmbInterfaceImpl(
+    int client_id,
+    mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> pending_receiver,
+    raw_ptr<GpuServiceImpl> gpu_service,
+    scoped_refptr<base::SingleThreadTaskRunner> io_runner)
+    : client_id_(client_id), gpu_service_(gpu_service) {
+  receiver_.Bind(std::move(pending_receiver));
+  receiver_.set_disconnect_handler(
+      base::BindOnce(&GpuServiceImpl::ClientGmbInterfaceImpl::OnConnectionError,
+                     base::Unretained(this)));
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "GpuServiceImpl::ClientGmbInterfaceImpl", std::move(io_runner));
+  weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
+}
+
+GpuServiceImpl::ClientGmbInterfaceImpl::~ClientGmbInterfaceImpl() {
+  base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
+      this);
+}
+
+void GpuServiceImpl::ClientGmbInterfaceImpl::CreateGpuMemoryBuffer(
+    gfx::GpuMemoryBufferId id,
+    const gfx::Size& size,
+    gfx::BufferFormat format,
+    gfx::BufferUsage usage,
+    gpu::SurfaceHandle surface_handle,
+    CreateGpuMemoryBufferCallback callback) {
+  if (!gpu::GpuMemoryBufferSupport::IsSizeValid(size)) {
+    receiver_.ReportBadMessage("Invalid GMB size");
+    std::move(callback).Run(gfx::GpuMemoryBufferHandle());
+    return;
+  }
+
+  // Ensure that the buffer corresponding to this id is not already pending or
+  // allocated.
+  if ((pending_buffers_.find(id) != pending_buffers_.end()) ||
+      (allocated_buffers_.find(id) != allocated_buffers_.end())) {
+    DLOG(ERROR) << "Allocation request for this buffer is either pending or "
+                   "have already completed. Hence not allocating a new buffer.";
+    std::move(callback).Run(gfx::GpuMemoryBufferHandle());
+    return;
+  }
+
+  // Create a native buffer handle if supported.
+  if (gpu_service_->IsNativeBufferSupported(format, usage)) {
+    PendingBufferInfo pending_buffer_info;
+    pending_buffer_info.size = size;
+    pending_buffer_info.format = format;
+    pending_buffer_info.callback = std::move(callback);
+    pending_buffers_.emplace(id, std::move(pending_buffer_info));
+    gpu_service_->CreateGpuMemoryBuffer(
+        id, size, format, usage, client_id_, surface_handle,
+        base::BindOnce(
+            &GpuServiceImpl::ClientGmbInterfaceImpl::OnGpuMemoryBufferAllocated,
+            weak_ptr_, id));
+    return;
+  }
+
+  // Create shared memory handle since native buffers are not supported.
+  if (gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage) &&
+      gpu::GpuMemoryBufferImplSharedMemory::IsSizeValidForFormat(size,
+                                                                 format)) {
+    gfx::GpuMemoryBufferHandle shm_handle;
+    shm_handle = gpu::GpuMemoryBufferImplSharedMemory::CreateGpuMemoryBuffer(
+        id, size, format, usage);
+    DCHECK_EQ(gfx::SHARED_MEMORY_BUFFER, shm_handle.type);
+    gpu::AllocatedBufferInfo buffer_info(shm_handle, size, format);
+    allocated_buffers_.emplace(id, buffer_info);
+    std::move(callback).Run(std::move(shm_handle));
+    return;
+  }
+  // return null handle.
+  std::move(callback).Run(gfx::GpuMemoryBufferHandle());
+}
+
+void GpuServiceImpl::ClientGmbInterfaceImpl::DestroyGpuMemoryBuffer(
+    gfx::GpuMemoryBufferId id) {
+  // Check if the id is present in the allocated_buffers.
+  auto allocated_buffer = allocated_buffers_.find(id);
+  if (allocated_buffer == allocated_buffers_.end()) {
+    DLOG(ERROR) << "Can not find GpuMemoryBuffer to destroy";
+    return;
+  }
+  DCHECK_NE(gfx::EMPTY_BUFFER, allocated_buffer->second.type());
+  if (allocated_buffer->second.type() != gfx::SHARED_MEMORY_BUFFER) {
+    gpu_service_->DestroyGpuMemoryBuffer(id, client_id_);
+  }
+  allocated_buffers_.erase(allocated_buffer);
+}
+
+void GpuServiceImpl::ClientGmbInterfaceImpl::CopyGpuMemoryBuffer(
+    gfx::GpuMemoryBufferHandle buffer_handle,
+    base::UnsafeSharedMemoryRegion shared_memory,
+    CopyGpuMemoryBufferCallback callback) {
+  gpu_service_->CopyGpuMemoryBuffer(
+      std::move(buffer_handle), std::move(shared_memory), std::move(callback));
+}
+
+bool GpuServiceImpl::ClientGmbInterfaceImpl::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  uint64_t client_tracing_process_id =
+      base::trace_event::MemoryDumpManager::GetInstance()
+          ->GetTracingProcessId();
+  for (const auto& allocated_buffer : allocated_buffers_) {
+    auto& buffer_info = allocated_buffer.second;
+    if (!buffer_info.OnMemoryDump(pmd, client_id_, client_tracing_process_id)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void GpuServiceImpl::ClientGmbInterfaceImpl::OnConnectionError() {
+  // Destroy all the GMBs corresponding to this client.
+  DestroyAllGpuMemoryBuffers();
+
+  // Note that this method destroys the current ClientGmbInterfaceImpl object.
+  // So it is not safe to use this pointer after below line.
+  gpu_service_->RemoveGmbClient(client_id_);
+}
+
+void GpuServiceImpl::ClientGmbInterfaceImpl::OnGpuMemoryBufferAllocated(
+    gfx::GpuMemoryBufferId id,
+    gfx::GpuMemoryBufferHandle handle) {
+  auto pending_buffer = pending_buffers_.find(id);
+  auto pending_buffer_info = std::move(pending_buffer->second);
+  pending_buffers_.erase(pending_buffer);
+  if (!handle.is_null()) {
+    CHECK(handle.id == id);
+    gpu::AllocatedBufferInfo buffer_info(handle, pending_buffer_info.size,
+                                         pending_buffer_info.format);
+    allocated_buffers_.emplace(id, buffer_info);
+  }
+  std::move(pending_buffer_info.callback).Run(std::move(handle));
+}
+
+void GpuServiceImpl::ClientGmbInterfaceImpl::DestroyAllGpuMemoryBuffers() {
+  for (const auto& allocated_buffer : allocated_buffers_) {
+    DCHECK_NE(gfx::EMPTY_BUFFER, allocated_buffer.second.type());
+    if (allocated_buffer.second.type() != gfx::SHARED_MEMORY_BUFFER) {
+      gpu_service_->DestroyGpuMemoryBuffer(allocated_buffer.first, client_id_);
+    }
+  }
+  allocated_buffers_.clear();
+
+  // Run all pending_buffers callback with null handle.
+  for (auto& pending_buffer : pending_buffers_) {
+    std::move(pending_buffer.second.callback).Run(gfx::GpuMemoryBufferHandle());
+  }
+  pending_buffers_.clear();
+}
+
 void GpuServiceImpl::GetDawnInfo(GetDawnInfoCallback callback) {
   DCHECK(io_runner_->BelongsToCurrentThread());
 
@@ -1408,6 +1608,14 @@
                        base::BindOnce(std::move(callback), dawn_info_list));
 }
 
+void GpuServiceImpl::RemoveGmbClient(int client_id) {
+  CHECK(io_runner_->BelongsToCurrentThread());
+  auto it = gmb_clients_.find(client_id);
+  if (it != gmb_clients_.end()) {
+    gmb_clients_.erase(it);
+  }
+}
+
 #if BUILDFLAG(IS_ANDROID)
 void GpuServiceImpl::SetHostProcessId(base::ProcessId pid) {
   host_process_id_ = pid;
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index c48c8b6c..daabf69 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 
 #include "base/clang_profiling_buildflags.h"
 #include "base/compiler_specific.h"
@@ -21,6 +22,7 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
+#include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "components/viz/service/display_embedder/compositor_gpu_thread.h"
@@ -30,7 +32,9 @@
 #include "gpu/command_buffer/service/sequence_id.h"
 #include "gpu/config/gpu_info.h"
 #include "gpu/config/gpu_preferences.h"
+#include "gpu/ipc/common/client_gmb_interface.mojom.h"
 #include "gpu/ipc/common/gpu_disk_cache_type.h"
+#include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
@@ -40,6 +44,7 @@
 #include "gpu/vulkan/buildflags.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/shared_remote.h"
 #include "services/viz/privileged/mojom/gl/gpu_host.mojom.h"
@@ -187,6 +192,11 @@
   void CreateVideoEncodeAcceleratorProvider(
       mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
           vea_provider_receiver) override;
+
+  void BindClientGmbInterface(
+      mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> pending_receiver,
+      int client_id) override;
+
   void CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
                              const gfx::Size& size,
                              gfx::BufferFormat format,
@@ -382,6 +392,68 @@
   void SetVisibilityChangedCallback(VisibilityChangedCallback);
 
  private:
+  // This class is used to receive direct IPCs for GMB from renderers without
+  // needing to go/route via the browser process.
+  class ClientGmbInterfaceImpl : public gpu::mojom::ClientGmbInterface,
+                                 public base::trace_event::MemoryDumpProvider {
+   public:
+    ClientGmbInterfaceImpl(
+        int client_id,
+        mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> pending_receiver,
+        raw_ptr<GpuServiceImpl> gpu_service,
+        scoped_refptr<base::SingleThreadTaskRunner> io_runner);
+    ~ClientGmbInterfaceImpl() override;
+
+    // mojom::ClientGmbInterface override
+    void CreateGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                               const gfx::Size& size,
+                               gfx::BufferFormat format,
+                               gfx::BufferUsage usage,
+                               gpu::SurfaceHandle surface_handle,
+                               CreateGpuMemoryBufferCallback callback) override;
+    void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id) override;
+    void CopyGpuMemoryBuffer(gfx::GpuMemoryBufferHandle buffer_handle,
+                             base::UnsafeSharedMemoryRegion shared_memory,
+                             CopyGpuMemoryBufferCallback callback) override;
+
+    // Overridden from base::trace_event::MemoryDumpProvider:
+    bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                      base::trace_event::ProcessMemoryDump* pmd) override;
+
+    void OnConnectionError();
+    void OnGpuMemoryBufferAllocated(gfx::GpuMemoryBufferId id,
+                                    gfx::GpuMemoryBufferHandle handle);
+    void DestroyAllGpuMemoryBuffers();
+
+   private:
+    struct PendingBufferInfo {
+      PendingBufferInfo();
+      PendingBufferInfo(PendingBufferInfo&&);
+      ~PendingBufferInfo();
+
+      gfx::Size size;
+      gfx::BufferFormat format;
+      base::OnceCallback<void(gfx::GpuMemoryBufferHandle)> callback;
+    };
+
+    const int client_id_;
+    raw_ptr<GpuServiceImpl> gpu_service_;
+    mojo::Receiver<gpu::mojom::ClientGmbInterface> receiver_{this};
+    std::unordered_map<gfx::GpuMemoryBufferId,
+                       PendingBufferInfo,
+                       std::hash<gfx::GpuMemoryBufferId>>
+        pending_buffers_;
+    std::unordered_map<gfx::GpuMemoryBufferId,
+                       gpu::AllocatedBufferInfo,
+                       std::hash<gfx::GpuMemoryBufferId>>
+        allocated_buffers_;
+
+    base::WeakPtr<ClientGmbInterfaceImpl> weak_ptr_;
+    base::WeakPtrFactory<ClientGmbInterfaceImpl> weak_ptr_factory_{this};
+  };
+
+  bool IsNativeBufferSupported(gfx::BufferFormat format,
+                               gfx::BufferUsage usage);
   void RecordLogMessage(int severity,
                         const std::string& header,
                         const std::string& message);
@@ -428,6 +500,8 @@
 
   void GetDawnInfoOnMain(GetDawnInfoCallback callback);
 
+  void RemoveGmbClient(int client_id);
+
   scoped_refptr<base::SingleThreadTaskRunner> main_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
 
@@ -513,6 +587,11 @@
   // Should only be accessed on the IO thread after creation.
   mojo::Receiver<mojom::GpuService> receiver_{this};
 
+  gpu::GpuMemoryBufferConfigurationSet supported_gmb_configurations_;
+
+  // Map of client_id to ClientGmbInterfaceImpl object.
+  std::unordered_map<int, std::unique_ptr<ClientGmbInterfaceImpl>> gmb_clients_;
+
 #if BUILDFLAG(IS_CHROMEOS_ASH) && BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
   scoped_refptr<arc::ProtectedBufferManager> protected_buffer_manager_;
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) &&
diff --git a/components/viz/test/fake_skia_output_surface.cc b/components/viz/test/fake_skia_output_surface.cc
index 481fd98..f5f875e 100644
--- a/components/viz/test/fake_skia_output_surface.cc
+++ b/components/viz/test/fake_skia_output_surface.cc
@@ -416,4 +416,8 @@
   return gpu::Mailbox::GenerateForSharedImage();
 }
 
+bool FakeSkiaOutputSurface::SupportsBGRA() const {
+  return true;
+}
+
 }  // namespace viz
diff --git a/components/viz/test/fake_skia_output_surface.h b/components/viz/test/fake_skia_output_surface.h
index d31f8f8..3e6c4b1 100644
--- a/components/viz/test/fake_skia_output_surface.h
+++ b/components/viz/test/fake_skia_output_surface.h
@@ -115,6 +115,7 @@
       const SkColor4f& color,
       const gfx::ColorSpace& color_space) override;
   void DestroySharedImage(const gpu::Mailbox& mailbox) override {}
+  bool SupportsBGRA() const override;
 
   // ExternalUseClient implementation:
   gpu::SyncToken ReleaseImageContexts(
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 1d58461..eb6f14f 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -864,6 +864,8 @@
     "devtools/shared_worker_devtools_agent_host.h",
     "devtools/shared_worker_devtools_manager.cc",
     "devtools/shared_worker_devtools_manager.h",
+    "devtools/tracing_process_set_monitor.cc",
+    "devtools/tracing_process_set_monitor.h",
     "devtools/web_contents_devtools_agent_host.cc",
     "devtools/web_contents_devtools_agent_host.h",
     "devtools/worker_devtools_agent_host.cc",
diff --git a/content/browser/devtools/auction_worklet_devtools_agent_host.cc b/content/browser/devtools/auction_worklet_devtools_agent_host.cc
index 03d7827..0c21998 100644
--- a/content/browser/devtools/auction_worklet_devtools_agent_host.cc
+++ b/content/browser/devtools/auction_worklet_devtools_agent_host.cc
@@ -78,6 +78,20 @@
   return true;
 }
 
+// static
+scoped_refptr<AuctionWorkletDevToolsAgentHost>
+AuctionWorkletDevToolsAgentHost::Create(DebuggableAuctionWorklet* worklet) {
+  auto self =
+      base::WrapRefCounted(new AuctionWorkletDevToolsAgentHost(worklet));
+  // This needs to be outside of constructor due to bind until we make the base
+  // class a `StartRefCountFromOne` (i.e. REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE).
+  if (auto pid_opt = worklet->GetPid(base::BindOnce(
+          &AuctionWorkletDevToolsAgentHost::SetProcessId, self))) {
+    self->SetProcessId(*pid_opt);
+  }
+  return self;
+}
+
 AuctionWorkletDevToolsAgentHost::AuctionWorkletDevToolsAgentHost(
     DebuggableAuctionWorklet* worklet)
     : DevToolsAgentHostImpl(worklet->UniqueId()), worklet_(worklet) {
@@ -143,8 +157,7 @@
   if (it != hosts_.end())
     return it->second;
 
-  auto host =
-      base::WrapRefCounted(new AuctionWorkletDevToolsAgentHost(worklet));
+  auto host = AuctionWorkletDevToolsAgentHost::Create(worklet);
   hosts_.insert(std::make_pair(worklet, host));
   return host;
 }
diff --git a/content/browser/devtools/auction_worklet_devtools_agent_host.h b/content/browser/devtools/auction_worklet_devtools_agent_host.h
index 0398171..f182a00 100644
--- a/content/browser/devtools/auction_worklet_devtools_agent_host.h
+++ b/content/browser/devtools/auction_worklet_devtools_agent_host.h
@@ -28,6 +28,9 @@
  private:
   friend class AuctionWorkletDevToolsAgentHostManager;
 
+  static scoped_refptr<AuctionWorkletDevToolsAgentHost> Create(
+      DebuggableAuctionWorklet* worklet);
+
   explicit AuctionWorkletDevToolsAgentHost(DebuggableAuctionWorklet* worklet);
   ~AuctionWorkletDevToolsAgentHost() override;
 
diff --git a/content/browser/devtools/browser_devtools_agent_host.cc b/content/browser/devtools/browser_devtools_agent_host.cc
index 2c181c3..e4bea03 100644
--- a/content/browser/devtools/browser_devtools_agent_host.cc
+++ b/content/browser/devtools/browser_devtools_agent_host.cc
@@ -209,7 +209,7 @@
         socket_callback_, tethering_task_runner_);
   }
   session->CreateAndAddHandler<protocol::TracingHandler>(
-      protocol::TracingHandler::kBrowser, GetIOContext());
+      this, GetIOContext(), /* root_session */ nullptr);
 
 #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX) && BUILDFLAG(CLANG_PGO)
   session->CreateAndAddHandler<protocol::NativeProfilingHandler>();
diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc
index 22acf94..a369a3d 100644
--- a/content/browser/devtools/devtools_agent_host_impl.cc
+++ b/content/browser/devtools/devtools_agent_host_impl.cc
@@ -19,8 +19,6 @@
 #include "content/browser/devtools/devtools_pipe_handler.h"
 #include "content/browser/devtools/devtools_stream_file.h"
 #include "content/browser/devtools/forwarding_agent_host.h"
-#include "content/browser/devtools/protocol/page.h"
-#include "content/browser/devtools/protocol/security_handler.h"
 #include "content/browser/devtools/render_frame_devtools_agent_host.h"
 #include "content/browser/devtools/service_worker_devtools_agent_host.h"
 #include "content/browser/devtools/service_worker_devtools_manager.h"
@@ -476,6 +474,30 @@
   GetDevtoolsInstances().erase(id_);
 }
 
+void DevToolsAgentHostImpl::ProcessHostChanged() {
+  RenderProcessHost* host = GetProcessHost();
+  if (!host) {
+    return;
+  }
+  if (host->IsReady()) {
+    SetProcessId(host->GetProcess().Pid());
+  } else {
+    host->PostTaskWhenProcessIsReady(base::BindOnce(
+        &RenderFrameDevToolsAgentHost::ProcessHostChanged, this));
+  }
+}
+
+void DevToolsAgentHostImpl::SetProcessId(base::ProcessId process_id) {
+  CHECK_NE(process_id, base::kNullProcessId);
+  if (process_id_ == process_id) {
+    return;
+  }
+  process_id_ = process_id;
+  for (auto& observer : GetDevtoolsObservers()) {
+    observer.DevToolsAgentHostProcessChanged(this);
+  }
+}
+
 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
     NetworkLoaderFactoryParamsAndInfo() = default;
 DevToolsAgentHostImpl::NetworkLoaderFactoryParamsAndInfo::
diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h
index d1b91fa..a7af3a44 100644
--- a/content/browser/devtools/devtools_agent_host_impl.h
+++ b/content/browser/devtools/devtools_agent_host_impl.h
@@ -11,6 +11,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/process/kill.h"
+#include "base/process/process_handle.h"
 #include "content/browser/devtools/devtools_io_context.h"
 #include "content/browser/devtools/devtools_renderer_channel.h"
 #include "content/browser/devtools/devtools_session.h"
@@ -111,6 +112,8 @@
   virtual protocol::TargetAutoAttacher* auto_attacher();
   virtual std::string GetSubtype();
 
+  base::ProcessId GetProcessId() const { return process_id_; }
+
  protected:
   explicit DevToolsAgentHostImpl(const std::string& id);
   ~DevToolsAgentHostImpl() override;
@@ -125,6 +128,10 @@
   void NotifyCreated();
   void NotifyNavigated();
   void NotifyCrashed(base::TerminationStatus status);
+
+  void SetProcessId(base::ProcessId process_id);
+  void ProcessHostChanged();
+
   void ForceDetachRestrictedSessions(
       const std::vector<DevToolsSession*>& restricted_sessions);
   DevToolsIOContext* GetIOContext() { return &io_context_; }
@@ -162,6 +169,8 @@
       session_by_client_;
   DevToolsIOContext io_context_;
   DevToolsRendererChannel renderer_channel_;
+  base::ProcessId process_id_ = base::kNullProcessId;
+
   static int s_force_creation_count_;
 };
 
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index e36408e..78ece6c5 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -1284,18 +1284,6 @@
   agent_host->DidCreateFencedFrame(fenced_frame);
 }
 
-void DidCreateProcessForAuctionWorklet(RenderFrameHostImpl* owner,
-                                       base::ProcessId pid) {
-  // TracingHandler lives on the very root, not local root.
-  // TODO(morlovich): This may not be right for fenced frames, though
-  // that should not currently matter.
-  WebContents* web_contents = WebContents::FromRenderFrameHost(owner);
-  if (!web_contents) {
-    return;
-  }
-  DispatchToAgents(web_contents, &protocol::TracingHandler::AddProcess, pid);
-}
-
 void WillStartDragging(FrameTreeNode* main_frame_tree_node,
                        const blink::mojom::DragDataPtr drag_data,
                        blink::DragOperationsMask drag_operations_mask,
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index 6379e33..c05a8d3 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -306,11 +306,6 @@
     base::SafeRef<RenderFrameHostImpl> owner_render_frame_host,
     FencedFrame* fenced_frame);
 
-// Tells tracing that process `pid` is being used for an auction worklet
-// associated to `owner`.
-void DidCreateProcessForAuctionWorklet(RenderFrameHostImpl* owner,
-                                       base::ProcessId pid);
-
 void ReportCookieIssue(
     RenderFrameHostImpl* render_frame_host_impl,
     const network::mojom::CookieOrLineWithAccessResultPtr& excluded_cookie,
diff --git a/content/browser/devtools/devtools_session.cc b/content/browser/devtools/devtools_session.cc
index 33123b0..2729165 100644
--- a/content/browser/devtools/devtools_session.cc
+++ b/content/browser/devtools/devtools_session.cc
@@ -615,6 +615,9 @@
   if (!agent_host->AttachInternal(std::move(session)))
     return nullptr;
   child_sessions_[session_id] = session_ptr;
+  for (auto& observer : child_observers_) {
+    observer.SessionAttached(*session_ptr);
+  }
   return session_ptr;
 }
 
@@ -626,4 +629,15 @@
   return child_sessions_.find(session_id) != child_sessions_.end();
 }
 
+void DevToolsSession::AddObserver(ChildObserver* obs) {
+  child_observers_.AddObserver(obs);
+  for (auto& entry : child_sessions_) {
+    obs->SessionAttached(*entry.second);
+  }
+}
+
+void DevToolsSession::RemoveObserver(ChildObserver* obs) {
+  child_observers_.RemoveObserver(obs);
+}
+
 }  // namespace content
diff --git a/content/browser/devtools/devtools_session.h b/content/browser/devtools/devtools_session.h
index ad9c3e5c..b8bb0a5 100644
--- a/content/browser/devtools/devtools_session.h
+++ b/content/browser/devtools/devtools_session.h
@@ -14,6 +14,7 @@
 #include "base/containers/flat_map.h"
 #include "base/containers/span.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "content/browser/devtools/protocol/protocol.h"
 #include "content/public/browser/devtools_agent_host_client_channel.h"
 #include "content/public/browser/devtools_external_agent_proxy.h"
@@ -64,6 +65,12 @@
     kSupportsTabTarget,
     kDoesNotSupportTabTarget,
   };
+
+  class ChildObserver : public base::CheckedObserver {
+   public:
+    virtual void SessionAttached(DevToolsSession& session) = 0;
+  };
+
   // For root sessions (see also private constructor for children).
   DevToolsSession(DevToolsAgentHostClient* client, Mode mode);
   ~DevToolsSession() override;
@@ -117,6 +124,9 @@
   bool HasChildSession(const std::string& session_id);
   Mode session_mode() const { return mode_; }
 
+  void AddObserver(ChildObserver* obs);
+  void RemoveObserver(ChildObserver* obs);
+
  private:
   struct PendingMessage {
     int call_id;
@@ -229,6 +239,7 @@
   base::flat_map<std::string, DevToolsSession*> child_sessions_;
   base::OnceClosure runtime_resume_;
   DevToolsExternalAgentProxyDelegate* proxy_delegate_ = nullptr;
+  base::ObserverList<ChildObserver, true, false> child_observers_;
 
   base::WeakPtrFactory<DevToolsSession> weak_factory_{this};
 };
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index dce4d80..bf3cb1d 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -34,6 +34,7 @@
 #include "content/browser/devtools/devtools_stream_file.h"
 #include "content/browser/devtools/devtools_traceable_screenshot.h"
 #include "content/browser/devtools/devtools_video_consumer.h"
+#include "content/browser/devtools/tracing_process_set_monitor.h"
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/renderer_host/frame_tree.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
@@ -189,30 +190,30 @@
 }
 
 void FillFrameData(base::trace_event::TracedValue* data,
-                   FrameTreeNode* node,
-                   RenderFrameHostImpl* frame_host,
-                   const GURL& url) {
+                   RenderFrameHostImpl* frame_host) {
+  CHECK(frame_host);
   GURL::Replacements strip_fragment;
   strip_fragment.ClearRef();
+  std::string url = frame_host->GetLastCommittedURL()
+                        .ReplaceComponents(strip_fragment)
+                        .spec();
   data->SetString("frame", frame_host->devtools_frame_token().ToString());
-  data->SetString("url", url.ReplaceComponents(strip_fragment).spec());
-  data->SetString("name", node->frame_name());
-  if (node->parent()) {
-    data->SetString("parent",
-                    node->parent()->GetDevToolsFrameToken().ToString());
+  data->SetString("url", url);
+  data->SetString("name", frame_host->GetFrameName());
+  if (frame_host->GetParent()) {
+    data->SetString(
+        "parent", frame_host->GetParent()->GetDevToolsFrameToken().ToString());
   }
-  if (frame_host) {
-    RenderProcessHost* process_host = frame_host->GetProcess();
-    const base::Process& process_handle = process_host->GetProcess();
-    if (!process_handle.IsValid()) {
-      data->SetString("processPseudoId", GetProcessHostHex(process_host));
-      frame_host->GetProcess()->PostTaskWhenProcessIsReady(
-          base::BindOnce(&SendProcessReadyInBrowserEvent,
-                         frame_host->devtools_frame_token(), process_host));
-    } else {
-      // Cast process id to int to be compatible with tracing.
-      data->SetInteger("processId", static_cast<int>(process_handle.Pid()));
-    }
+  RenderProcessHost* process_host = frame_host->GetProcess();
+  const base::Process& process_handle = process_host->GetProcess();
+  if (!process_handle.IsValid()) {
+    data->SetString("processPseudoId", GetProcessHostHex(process_host));
+    frame_host->GetProcess()->PostTaskWhenProcessIsReady(
+        base::BindOnce(&SendProcessReadyInBrowserEvent,
+                       frame_host->devtools_frame_token(), process_host));
+  } else {
+    // Cast process id to int to be compatible with tracing.
+    data->SetInteger("processId", static_cast<int>(process_handle.Pid()));
   }
 }
 
@@ -554,12 +555,13 @@
   base::WeakPtrFactory<PerfettoTracingSession> weak_factory_{this};
 };
 
-TracingHandler::TracingHandler(TargetType target_type,
-                               DevToolsIOContext* io_context)
+TracingHandler::TracingHandler(DevToolsAgentHostImpl* host,
+                               DevToolsIOContext* io_context,
+                               DevToolsSession* root_session)
     : DevToolsDomainHandler(Tracing::Metainfo::domainName),
-      target_type_(target_type),
-      web_contents_(nullptr),
       io_context_(io_context),
+      host_(host),
+      session_for_process_filter_(root_session),
       did_initiate_recording_(false),
       return_as_stream_(false),
       gzip_compression_(false),
@@ -585,14 +587,6 @@
       frame_host->GetRenderWidgetHost()->GetFrameSinkId());
 }
 
-void TracingHandler::ConnectWebContents(WebContents* web_contents) {
-  web_contents_ = web_contents;
-}
-
-void TracingHandler::DisconnectWebContents() {
-  web_contents_ = nullptr;
-}
-
 void TracingHandler::Wire(UberDispatcher* dispatcher) {
   frontend_ = std::make_unique<Tracing::Frontend>(dispatcher->channel());
   Tracing::Dispatcher::wire(dispatcher, this);
@@ -653,6 +647,7 @@
   DCHECK(!trace_data_buffer_state_.slashed);
 
   bool data_loss = session_->HasDataLossOccurred();
+  process_set_monitor_.reset();
   session_.reset();
   frontend_->TracingComplete(data_loss);
 }
@@ -717,6 +712,7 @@
 
 void TracingHandler::OnTraceToStreamComplete(const std::string& stream_handle) {
   bool data_loss = session_->HasDataLossOccurred();
+  process_set_monitor_.reset();
   session_.reset();
   std::string stream_format = (proto_format_ ? Tracing::StreamFormatEnum::Proto
                                              : Tracing::StreamFormatEnum::Json);
@@ -836,14 +832,26 @@
       buffer_usage_reporting_interval.fromMaybe(0);
   did_initiate_recording_ = true;
   trace_config_ = std::move(trace_config);
-  pids_being_traced_.clear();
 
-  GpuProcessHost* gpu_process_host =
-      GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED,
-                          /* force_create */ false);
-  base::ProcessId gpu_pid =
-      gpu_process_host ? gpu_process_host->process_id() : base::kNullProcessId;
-  SetupProcessFilter(gpu_pid, nullptr);
+  if (session_for_process_filter_) {
+    process_set_monitor_ = TracingProcessSetMonitor::Start(
+        *session_for_process_filter_,
+        base::BindRepeating(&TracingHandler::AddProcessToFilter,
+                            base::Unretained(this)));
+    std::unordered_set<base::ProcessId> pids = process_set_monitor_->GetPids();
+
+    base::ProcessId browser_pid = base::Process::Current().Pid();
+    pids.insert(browser_pid);
+    if (auto* gpu_process_host =
+            GpuProcessHost::Get(GPU_PROCESS_KIND_SANDBOXED,
+                                /* force_create */ false)) {
+      base::ProcessId gpu_pid = gpu_process_host->process_id();
+      if (gpu_pid != base::kNullProcessId) {
+        pids.insert(gpu_pid);
+      }
+    }
+    AddPidsToProcessFilter(pids, trace_config_);
+  }
 
   session_ = std::make_unique<PerfettoTracingSession>(proto_format_, *backend);
   session_->EnableTracing(
@@ -867,69 +875,11 @@
           : tracing::mojom::kChromeTraceEventLabel);
 }
 
-void TracingHandler::SetupProcessFilter(
-    base::ProcessId gpu_pid,
-    RenderFrameHost* new_render_frame_host) {
-  if (!web_contents_) {
-    return;
-  }
-
-  base::ProcessId browser_pid = base::Process::Current().Pid();
-  pids_being_traced_.insert(browser_pid);
-
-  if (gpu_pid != base::kNullProcessId)
-    pids_being_traced_.insert(gpu_pid);
-
-  if (new_render_frame_host) {
-    AppendProcessId(new_render_frame_host, &pids_being_traced_);
-  }
-  auto setup_filter_for_frame = [this](RenderFrameHost* rfh) {
-    if (rfh->GetParent()) {
-      return;
-    }
-    RenderFrameHostImpl* rfhi = static_cast<RenderFrameHostImpl*>(rfh);
-    for (FrameTreeNode* node : rfhi->frame_tree()->Nodes()) {
-      if (RenderFrameHost* frame_host = node->current_frame_host()) {
-        AppendProcessId(frame_host, &pids_being_traced_);
-      }
-    }
-  };
-  if (target_type_ == kFrame) {
-    setup_filter_for_frame(web_contents_->GetPrimaryMainFrame());
-  } else if (target_type_ == kTab) {
-    web_contents_->ForEachRenderFrameHost(setup_filter_for_frame);
-  } else {
-    NOTREACHED();
-  }
-
-  AddPidsToProcessFilter(pids_being_traced_, trace_config_);
-}
-
-void TracingHandler::AppendProcessId(
-    RenderFrameHost* render_frame_host,
-    std::unordered_set<base::ProcessId>* process_set) {
-  RenderProcessHost* process_host = render_frame_host->GetProcess();
-  if (process_host->GetProcess().IsValid()) {
-    process_set->insert(process_host->GetProcess().Pid());
-  } else {
-    process_host->PostTaskWhenProcessIsReady(
-        base::BindOnce(&TracingHandler::OnProcessReady,
-                       weak_factory_.GetWeakPtr(), process_host));
-  }
-}
-
-void TracingHandler::OnProcessReady(RenderProcessHost* process_host) {
-  AddProcess(process_host->GetProcess().Pid());
-}
-
-void TracingHandler::AddProcess(base::ProcessId pid) {
-  if (!did_initiate_recording_)
-    return;
-  if (!pids_being_traced_.insert(pid).second)
-    return;
+void TracingHandler::AddProcessToFilter(base::ProcessId pid) {
+  CHECK(did_initiate_recording_);
+  CHECK(session_);
   AddPidsToProcessFilter({pid}, trace_config_);
-  if (session_)
-    session_->ChangeTraceConfig(trace_config_);
+  session_->ChangeTraceConfig(trace_config_);
 }
 
 void TracingHandler::AttemptAdoptStartupSession(
@@ -937,7 +887,8 @@
     bool gzip_compression,
     bool proto_format,
     perfetto::BackendType tracing_backend) {
-  if (target_type_ != kBrowser) {
+  // Only adopt startup session for browser-level sessions.
+  if (session_for_process_filter_) {
     return;
   }
   auto* startup_config = tracing::TraceStartupConfig::GetInstance();
@@ -1139,6 +1090,7 @@
     const scoped_refptr<TracingController::TraceDataEndpoint>& endpoint) {
   DCHECK(session_);
   buffer_usage_poll_timer_.reset();
+  process_set_monitor_.reset();
   if (endpoint) {
     // Will delete |session_|.
     session_->DisableTracing(std::move(endpoint));
@@ -1155,29 +1107,19 @@
 
 void TracingHandler::EmitFrameTree() {
   auto data = std::make_unique<base::trace_event::TracedValue>();
-  if (target_type_ != kBrowser && web_contents_) {
-    RenderFrameHostImpl* primary_frame_host =
-        static_cast<RenderFrameHostImpl*>(web_contents_->GetPrimaryMainFrame());
-    DCHECK(!primary_frame_host->GetParent());
-    data->SetInteger(
-        "frameTreeNodeId",
-        primary_frame_host->frame_tree_node()->frame_tree_node_id());
+  if (WebContents* wc = host_ ? host_->GetWebContents() : nullptr) {
+    auto* frame_host =
+        static_cast<RenderFrameHostImpl*>(wc->GetPrimaryMainFrame());
+    CHECK(frame_host);
+    data->SetInteger("frameTreeNodeId",
+                     frame_host->frame_tree_node()->frame_tree_node_id());
     data->SetBoolean("persistentIds", true);
     data->BeginArray("frames");
-    auto emit_frames_for_host = [&data](RenderFrameHost* rfh) {
-      RenderFrameHostImpl* rfhi = static_cast<RenderFrameHostImpl*>(rfh);
-      for (FrameTreeNode* node : rfhi->frame_tree()->Nodes()) {
-        data->BeginDictionary();
-        FillFrameData(data.get(), node, node->current_frame_host(),
-                      node->current_url());
-        data->EndDictionary();
-      }
-    };
-    if (target_type_ == kTab) {
-      web_contents_->ForEachRenderFrameHost(emit_frames_for_host);
-    } else {
-      emit_frames_for_host(primary_frame_host);
-    }
+    wc->ForEachRenderFrameHost([&data](RenderFrameHost* rfh) {
+      data->BeginDictionary();
+      FillFrameData(data.get(), static_cast<RenderFrameHostImpl*>(rfh));
+      data->EndDictionary();
+    });
     data->EndArray();
   }
   TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
@@ -1190,16 +1132,10 @@
     return;
   }
   auto data = std::make_unique<base::trace_event::TracedValue>();
-  FillFrameData(data.get(), frame_tree_node,
-                frame_tree_node->current_frame_host(),
-                frame_tree_node->current_url());
+  FillFrameData(data.get(), frame_tree_node->current_frame_host());
   TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                        "FrameCommittedInBrowser", TRACE_EVENT_SCOPE_THREAD,
                        "data", std::move(data));
-
-  SetupProcessFilter(base::kNullProcessId,
-                     frame_tree_node->current_frame_host());
-  session_->ChangeTraceConfig(trace_config_);
 }
 
 void TracingHandler::ReadyToCommitNavigation(
@@ -1207,16 +1143,10 @@
   if (!did_initiate_recording_)
     return;
   auto data = std::make_unique<base::trace_event::TracedValue>();
-  FillFrameData(data.get(), navigation_request->frame_tree_node(),
-                navigation_request->GetRenderFrameHost(),
-                navigation_request->GetURL());
+  FillFrameData(data.get(), navigation_request->GetRenderFrameHost());
   TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                        "FrameCommittedInBrowser", TRACE_EVENT_SCOPE_THREAD,
                        "data", std::move(data));
-
-  SetupProcessFilter(base::kNullProcessId,
-                     navigation_request->GetRenderFrameHost());
-  session_->ChangeTraceConfig(trace_config_);
 }
 
 void TracingHandler::FrameDeleted(int frame_tree_node_id) {
diff --git a/content/browser/devtools/protocol/tracing_handler.h b/content/browser/devtools/protocol/tracing_handler.h
index 708864d..2f02889 100644
--- a/content/browser/devtools/protocol/tracing_handler.h
+++ b/content/browser/devtools/protocol/tracing_handler.h
@@ -11,7 +11,6 @@
 #include <memory>
 #include <set>
 #include <string>
-#include <unordered_set>
 #include <vector>
 
 #include "base/gtest_prod_util.h"
@@ -41,17 +40,17 @@
 class DevToolsAgentHostImpl;
 class DevToolsVideoConsumer;
 class DevToolsIOContext;
+class DevToolsSession;
 class NavigationRequest;
-class RenderFrameHost;
-class RenderProcessHost;
+class TracingProcessSetMonitor;
 
 namespace protocol {
 
 class TracingHandler : public DevToolsDomainHandler, public Tracing::Backend {
  public:
-  enum TargetType { kBrowser, kTab, kFrame };
-  CONTENT_EXPORT TracingHandler(TargetType target_type,
-                                DevToolsIOContext* io_context);
+  CONTENT_EXPORT TracingHandler(DevToolsAgentHostImpl* host,
+                                DevToolsIOContext* io_context,
+                                DevToolsSession* root_session);
 
   TracingHandler(const TracingHandler&) = delete;
   TracingHandler& operator=(const TracingHandler&) = delete;
@@ -60,14 +59,9 @@
 
   static std::vector<TracingHandler*> ForAgentHost(DevToolsAgentHostImpl* host);
 
-  // Adds an additional process to tracing configuration, if tracing is active.
-  void AddProcess(base::ProcessId pid);
-
   // DevToolsDomainHandler implementation.
   void SetRenderer(int process_host_id,
                    RenderFrameHostImpl* frame_host) override;
-  void ConnectWebContents(WebContents* web_contents);
-  void DisconnectWebContents();
   void WillInitiatePrerender(FrameTreeNode* ftn);
 
   void Wire(UberDispatcher* dispatcher) override;
@@ -145,24 +139,22 @@
       const base::trace_event::TraceConfig& browser_config,
       bool return_as_stream,
       bool proto_format);
-  void SetupProcessFilter(base::ProcessId gpu_pid, RenderFrameHost*);
-  void AppendProcessId(RenderFrameHost*,
-                       std::unordered_set<base::ProcessId>* process_set);
-  void OnProcessReady(RenderProcessHost*);
   void AttemptAdoptStartupSession(bool return_as_stream,
                                   bool gzip_compression,
                                   bool proto_format,
                                   perfetto::BackendType tracing_backend);
 
-  TargetType target_type_;
-  WebContents* web_contents_;
+  // Adds an additional process to tracing configuration, if tracing is active.
+  void AddProcessToFilter(base::ProcessId pid);
+
   std::unique_ptr<base::RepeatingTimer> buffer_usage_poll_timer_;
 
   std::unique_ptr<Tracing::Frontend> frontend_;
-  DevToolsIOContext* io_context_;
-  // This will be null in agents not attached to a frame host,
-  // or while WebContents is detached.
-  RenderFrameHostImpl* frame_host_ = nullptr;
+  const base::raw_ptr<DevToolsIOContext> io_context_;
+  const base::raw_ptr<DevToolsAgentHostImpl> host_;  // Only null in unit tests.
+
+  // Session is for use in process filter and is null in browser.
+  const base::raw_ptr<DevToolsSession> session_for_process_filter_;
   bool did_initiate_recording_;
   bool return_as_stream_;
   bool gzip_compression_;
@@ -172,8 +164,8 @@
   std::unique_ptr<DevToolsVideoConsumer> video_consumer_;
   int number_of_screenshots_from_video_consumer_ = 0;
   perfetto::TraceConfig trace_config_;
-  std::unordered_set<base::ProcessId> pids_being_traced_;
   std::unique_ptr<PerfettoTracingSession> session_;
+  std::unique_ptr<TracingProcessSetMonitor> process_set_monitor_;
   base::WeakPtrFactory<TracingHandler> weak_factory_{this};
 
   FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest,
diff --git a/content/browser/devtools/protocol/tracing_handler_unittest.cc b/content/browser/devtools/protocol/tracing_handler_unittest.cc
index 5fcd342..25ff10f0 100644
--- a/content/browser/devtools/protocol/tracing_handler_unittest.cc
+++ b/content/browser/devtools/protocol/tracing_handler_unittest.cc
@@ -78,7 +78,7 @@
  public:
   void SetUp() override {
     tracing_handler_ =
-        std::make_unique<TracingHandler>(TracingHandler::kBrowser, nullptr);
+        std::make_unique<TracingHandler>(nullptr, nullptr, nullptr);
   }
 
   void TearDown() override { tracing_handler_.reset(); }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 7604e65..1bef7c7 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -254,6 +254,7 @@
     : DevToolsAgentHostImpl(frame_host->devtools_frame_token().ToString()),
       auto_attacher_(std::make_unique<FrameAutoAttacher>(GetRendererChannel())),
       frame_tree_node_(nullptr) {
+  AddRef();  // Balanced in DestroyOnRenderFrameGone.
   auto* wc = WebContentsImpl::FromRenderFrameHostImpl(frame_host);
   WebContentsObserver::Observe(wc);
   SetFrameTreeNode(frame_tree_node);
@@ -266,7 +267,6 @@
     WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host);
     render_frame_crashed_ = web_contents && web_contents->IsCrashed();
   }
-  AddRef();  // Balanced in DestroyOnRenderFrameGone.
   NotifyCreated();
 }
 
@@ -373,10 +373,10 @@
       session->GetClient()->MayReadLocalFiles());
   session->CreateAndAddHandler<protocol::SecurityHandler>();
   if (!frame_tree_node_ || !frame_tree_node_->parent()) {
-    auto* tracing_handler =
-        session->CreateAndAddHandler<protocol::TracingHandler>(
-            protocol::TracingHandler::kFrame, GetIOContext());
-    tracing_handler->ConnectWebContents(web_contents());
+    DevToolsSession* root_session = session->GetRootSession();
+    CHECK(root_session);
+    session->CreateAndAddHandler<protocol::TracingHandler>(this, GetIOContext(),
+                                                           root_session);
   }
   session->CreateAndAddHandler<protocol::LogHandler>();
   session->CreateAndAddHandler<protocol::FedCmHandler>();
@@ -611,6 +611,7 @@
     DCHECK(WebContentsImpl::FromRenderFrameHostImpl(frame_host_) ==
            web_contents());
     frame_host_->GetProcess()->AddObserver(this);
+    ProcessHostChanged();
   }
 }
 
@@ -693,9 +694,6 @@
 void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
   WebContentsObserver::Observe(nullptr);
   navigation_requests_.clear();
-  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) {
-    tracing->DisconnectWebContents();
-  }
   SetFrameTreeNode(nullptr);
   // UpdateFrameHost may destruct |this|.
   scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
@@ -709,9 +707,6 @@
       static_cast<RenderFrameHostImpl*>(wc->GetPrimaryMainFrame());
   DCHECK(host);
   WebContentsObserver::Observe(wc);
-  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) {
-    tracing->ConnectWebContents(wc);
-  }
   SetFrameTreeNode(host->frame_tree_node());
   UpdateFrameHost(host);
   // UpdateFrameHost may destruct |this|.
@@ -891,6 +886,7 @@
 constexpr char kSubtypeFenced[] = "fenced";
 
 }  // namespace
+
 std::string RenderFrameDevToolsAgentHost::GetSubtype() {
   if (!frame_tree_node_)
     return kSubtypeDisconnected;
@@ -912,6 +908,10 @@
   }
 }
 
+RenderProcessHost* RenderFrameDevToolsAgentHost::GetProcessHost() {
+  return frame_host_ ? frame_host_->GetProcess() : nullptr;
+}
+
 bool RenderFrameDevToolsAgentHost::IsChildFrame() {
   return frame_tree_node_ && frame_tree_node_->parent();
 }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 57386a1..b1cfbffe 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -136,6 +136,7 @@
   void UpdateRendererChannel(bool force) override;
   protocol::TargetAutoAttacher* auto_attacher() override;
   std::string GetSubtype() override;
+  RenderProcessHost* GetProcessHost() override;
 
   // WebContentsObserver overrides.
   void DidStartNavigation(NavigationHandle* navigation_handle) override;
diff --git a/content/browser/devtools/tracing_process_set_monitor.cc b/content/browser/devtools/tracing_process_set_monitor.cc
new file mode 100644
index 0000000..76753d4
--- /dev/null
+++ b/content/browser/devtools/tracing_process_set_monitor.cc
@@ -0,0 +1,78 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/devtools/tracing_process_set_monitor.h"
+
+#include "content/public/browser/render_process_host.h"
+
+namespace content {
+
+// static
+std::unique_ptr<TracingProcessSetMonitor> TracingProcessSetMonitor::Start(
+    DevToolsSession& root_session,
+    ProcessAddedCallback callback) {
+  return base::WrapUnique(
+      new TracingProcessSetMonitor(root_session, std::move(callback)));
+}
+
+void TracingProcessSetMonitor::SessionAttached(DevToolsSession& session) {
+  CHECK(session.GetRootSession() == &*root_session_);
+  auto* const host =
+      static_cast<DevToolsAgentHostImpl*>(session.GetAgentHost());
+  CHECK(host);
+  if (bool inserted = hosts_.insert(host).second) {
+    MaybeAddProcess(host);
+  }
+}
+
+void TracingProcessSetMonitor::DevToolsAgentHostDetached(
+    DevToolsAgentHost* host) {
+  hosts_.erase(host);
+}
+
+void TracingProcessSetMonitor::DevToolsAgentHostDestroyed(
+    DevToolsAgentHost* host) {
+  hosts_.erase(host);
+}
+
+void TracingProcessSetMonitor::DevToolsAgentHostProcessChanged(
+    DevToolsAgentHost* host) {
+  if (!hosts_.contains(host)) {
+    return;
+  }
+  MaybeAddProcess(host);
+}
+
+void TracingProcessSetMonitor::MaybeAddProcess(DevToolsAgentHost* host) {
+  base::ProcessId pid =
+      static_cast<DevToolsAgentHostImpl*>(host)->GetProcessId();
+  if (pid == base::kNullProcessId) {
+    return;
+  }
+  AddProcess(pid);
+}
+
+void TracingProcessSetMonitor::AddProcess(base::ProcessId pid) {
+  const bool inserted = known_pids_.insert(pid).second;
+  if (inserted && !in_init_) {
+    process_added_callback_.Run(pid);
+  }
+}
+
+TracingProcessSetMonitor::TracingProcessSetMonitor(
+    DevToolsSession& root_session,
+    ProcessAddedCallback callback)
+    : root_session_(root_session),
+      process_added_callback_(std::move(callback)) {
+  base::AutoReset<bool> auto_reset(&in_init_, true);
+  session_observation_.Observe(&root_session);
+  DevToolsAgentHost::AddObserver(this);
+  SessionAttached(root_session);
+}
+
+TracingProcessSetMonitor::~TracingProcessSetMonitor() {
+  DevToolsAgentHost::RemoveObserver(this);
+}
+
+}  // namespace content
diff --git a/content/browser/devtools/tracing_process_set_monitor.h b/content/browser/devtools/tracing_process_set_monitor.h
new file mode 100644
index 0000000..741e2af
--- /dev/null
+++ b/content/browser/devtools/tracing_process_set_monitor.h
@@ -0,0 +1,69 @@
+// 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 CONTENT_BROWSER_DEVTOOLS_TRACING_PROCESS_SET_MONITOR_H_
+#define CONTENT_BROWSER_DEVTOOLS_TRACING_PROCESS_SET_MONITOR_H_
+
+#include <memory>
+#include <unordered_set>
+
+#include "base/functional/callback.h"
+#include "base/memory/raw_ref.h"
+#include "base/process/process_handle.h"
+#include "base/scoped_observation.h"
+#include "content/browser/devtools/devtools_agent_host_impl.h"
+#include "content/browser/devtools/devtools_session.h"
+#include "content/public/browser/render_process_host_observer.h"
+
+namespace content {
+
+class TracingProcessSetMonitor : public DevToolsSession::ChildObserver,
+                                 public DevToolsAgentHostObserver {
+ public:
+  using ProcessAddedCallback =
+      base::RepeatingCallback<void(base::ProcessId pid)>;
+
+  // |callback| will be invoked once per each new process added after start.
+  // The process present at start time are not reported via the callback
+  // and are available through |GetPids()| below.
+  static std::unique_ptr<TracingProcessSetMonitor> Start(
+      DevToolsSession& root_session,
+      ProcessAddedCallback callback);
+
+  TracingProcessSetMonitor(const TracingProcessSetMonitor& r) = delete;
+  ~TracingProcessSetMonitor() override;
+
+  const std::unordered_set<base::ProcessId>& GetPids() const {
+    return known_pids_;
+  }
+
+  void AddProcess(base::ProcessId pid);
+
+ private:
+  TracingProcessSetMonitor(DevToolsSession& root_session,
+                           ProcessAddedCallback callback);
+
+  // DevToolsSession::ChildObserver methods.
+  void SessionAttached(DevToolsSession& session) override;
+
+  // DevToolsAgentHostObserver methods.
+  void DevToolsAgentHostDetached(DevToolsAgentHost* host) override;
+  void DevToolsAgentHostDestroyed(DevToolsAgentHost* host) override;
+  void DevToolsAgentHostProcessChanged(DevToolsAgentHost* host) override;
+
+  void MaybeAddProcess(DevToolsAgentHost* host);
+
+  base::raw_ref<DevToolsSession> const root_session_;
+  base::ScopedObservation<DevToolsSession, DevToolsSession::ChildObserver>
+      session_observation_{this};
+  const ProcessAddedCallback process_added_callback_;
+
+  bool in_init_{false};
+  std::unordered_set<const DevToolsAgentHost*> hosts_;
+  std::unordered_set<base::ProcessId> known_pids_;
+};
+
+}  // namespace content
+
+#endif  // #define CONTENT_BROWSER_DEVTOOLS_TRACING_PROCESS_SET_MONITOR_H_
diff --git a/content/browser/devtools/web_contents_devtools_agent_host.cc b/content/browser/devtools/web_contents_devtools_agent_host.cc
index 9e8a13c..55f1a97 100644
--- a/content/browser/devtools/web_contents_devtools_agent_host.cc
+++ b/content/browser/devtools/web_contents_devtools_agent_host.cc
@@ -171,9 +171,6 @@
   const bool inserted =
       g_agent_host_instances.Get().insert(std::make_pair(wc, this)).second;
   CHECK(inserted);
-  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) {
-    tracing->ConnectWebContents(wc);
-  }
   auto_attacher_->SetWebContents(wc);
   Observe(wc);
   // Once created, persist till underlying WC is detached, so that
@@ -183,9 +180,6 @@
 
 void WebContentsDevToolsAgentHost::InnerDetach() {
   DCHECK_EQ(this, FindAgentHost(web_contents()));
-  for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) {
-    tracing->DisconnectWebContents();
-  }
   auto_attacher_->SetWebContents(nullptr);
   g_agent_host_instances.Get().erase(web_contents());
   Observe(nullptr);
@@ -407,8 +401,10 @@
           ? protocol::TargetHandler::AccessMode::kRegular
           : protocol::TargetHandler::AccessMode::kAutoAttachOnly,
       GetId(), auto_attacher_.get(), session);
-  session->CreateAndAddHandler<protocol::TracingHandler>(
-      protocol::TracingHandler::kTab, GetIOContext());
+  DevToolsSession* root_session = session->GetRootSession();
+  CHECK(root_session);
+  session->CreateAndAddHandler<protocol::TracingHandler>(this, GetIOContext(),
+                                                         root_session);
   return true;
 }
 
diff --git a/content/browser/devtools/worker_devtools_agent_host.cc b/content/browser/devtools/worker_devtools_agent_host.cc
index 5bca24a..a3c5593 100644
--- a/content/browser/devtools/worker_devtools_agent_host.cc
+++ b/content/browser/devtools/worker_devtools_agent_host.cc
@@ -73,6 +73,7 @@
   GetRendererChannel()->SetRenderer(std::move(agent_remote),
                                     std::move(host_receiver), process_id,
                                     std::move(connection_error));
+  ProcessHostChanged();
 }
 
 void WorkerDevToolsAgentHost::ChildWorkerCreated(
diff --git a/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc b/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc
index f9485ed..db2f1373 100644
--- a/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc
+++ b/content/browser/gpu/peak_gpu_memory_tracker_impl_browsertest.cc
@@ -99,6 +99,11 @@
   void UnregisterDCOMPSurfaceHandle(
       const base::UnguessableToken& token) override {}
 #endif
+
+  void BindClientGmbInterface(
+      mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> receiver,
+      int client_id) override {}
+
   void CreateVideoEncodeAcceleratorProvider(
       mojo::PendingReceiver<media::mojom::VideoEncodeAcceleratorProvider>
           receiver) override {}
diff --git a/content/browser/interest_group/ad_auction_service_impl.cc b/content/browser/interest_group/ad_auction_service_impl.cc
index 5f52f79..7ae4404 100644
--- a/content/browser/interest_group/ad_auction_service_impl.cc
+++ b/content/browser/interest_group/ad_auction_service_impl.cc
@@ -362,7 +362,7 @@
     const url::Origin& seller,
     GetInterestGroupAdAuctionDataCallback callback) {
   // TODO(behamilton): Implement this functionality.
-  std::move(callback).Run({});
+  std::move(callback).Run({}, "");
 }
 
 void AdAuctionServiceImpl::CreateAdRequest(
diff --git a/content/browser/interest_group/auction_worklet_manager.cc b/content/browser/interest_group/auction_worklet_manager.cc
index 40289c9e..d6b05968 100644
--- a/content/browser/interest_group/auction_worklet_manager.cc
+++ b/content/browser/interest_group/auction_worklet_manager.cc
@@ -344,7 +344,7 @@
       mojo::PendingReceiver<auction_worklet::mojom::BidderWorklet>
           worklet_receiver = bidder_worklet_.BindNewPipeAndPassReceiver();
       worklet_debug_ = base::WrapUnique(new DebuggableAuctionWorklet(
-          delegate->GetFrame(), &process_handle_, worklet_info_.script_url,
+          delegate->GetFrame(), process_handle_, worklet_info_.script_url,
           bidder_worklet_.get()));
       process_handle_.GetService()->LoadBidderWorklet(
           std::move(worklet_receiver),
@@ -368,7 +368,7 @@
       mojo::PendingReceiver<auction_worklet::mojom::SellerWorklet>
           worklet_receiver = seller_worklet_.BindNewPipeAndPassReceiver();
       worklet_debug_ = base::WrapUnique(new DebuggableAuctionWorklet(
-          delegate->GetFrame(), &process_handle_, worklet_info_.script_url,
+          delegate->GetFrame(), process_handle_, worklet_info_.script_url,
           seller_worklet_.get()));
       process_handle_.GetService()->LoadSellerWorklet(
           std::move(worklet_receiver),
diff --git a/content/browser/interest_group/debuggable_auction_worklet.cc b/content/browser/interest_group/debuggable_auction_worklet.cc
index 6293c46..adae110db 100644
--- a/content/browser/interest_group/debuggable_auction_worklet.cc
+++ b/content/browser/interest_group/debuggable_auction_worklet.cc
@@ -5,12 +5,11 @@
 #include "content/browser/interest_group/debuggable_auction_worklet.h"
 
 #include "base/strings/strcat.h"
+#include "base/trace_event/trace_event.h"
 #include "base/uuid.h"
-#include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/interest_group/debuggable_auction_worklet_tracker.h"
 #include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
 #include "content/services/auction_worklet/public/mojom/seller_worklet.mojom.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace content {
 
@@ -43,7 +42,7 @@
 
 DebuggableAuctionWorklet::DebuggableAuctionWorklet(
     RenderFrameHostImpl* owning_frame,
-    AuctionProcessManager::ProcessHandle* process_handle,
+    AuctionProcessManager::ProcessHandle& process_handle,
     const GURL& url,
     auction_worklet::mojom::BidderWorklet* bidder_worklet)
     : owning_frame_(owning_frame),
@@ -58,7 +57,7 @@
 
 DebuggableAuctionWorklet::DebuggableAuctionWorklet(
     RenderFrameHostImpl* owning_frame,
-    AuctionProcessManager::ProcessHandle* process_handle,
+    AuctionProcessManager::ProcessHandle& process_handle,
     const GURL& url,
     auction_worklet::mojom::SellerWorklet* seller_worklet)
     : owning_frame_(owning_frame),
@@ -83,6 +82,11 @@
   }
 }
 
+absl::optional<base::ProcessId> DebuggableAuctionWorklet::GetPid(
+    PidCallback callback) {
+  return process_handle_->GetPid(std::move(callback));
+}
+
 void DebuggableAuctionWorklet::RequestPid() {
   absl::optional<base::ProcessId> maybe_pid = process_handle_->GetPid(
       base::BindOnce(&DebuggableAuctionWorklet::OnHavePid,
@@ -93,9 +97,8 @@
 
 void DebuggableAuctionWorklet::OnHavePid(base::ProcessId process_id) {
   pid_ = process_id;
-  devtools_instrumentation::DidCreateProcessForAuctionWorklet(owning_frame_,
-                                                              process_id);
-
+  // TODO(caseq): move all timeline-specific tracing logic to
+  // devtools side (i.e. AuctionWorkletDevToolsAgentHost).
   TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                        "AuctionWorkletRunningInProcess",
                        TRACE_EVENT_SCOPE_THREAD, "data",
diff --git a/content/browser/interest_group/debuggable_auction_worklet.h b/content/browser/interest_group/debuggable_auction_worklet.h
index 8daca53..b2f1e9f6 100644
--- a/content/browser/interest_group/debuggable_auction_worklet.h
+++ b/content/browser/interest_group/debuggable_auction_worklet.h
@@ -9,6 +9,7 @@
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/process/process_handle.h"
 #include "content/browser/interest_group/auction_process_manager.h"
 #include "content/common/content_export.h"
@@ -61,6 +62,8 @@
   // Returns true if the worklet should start in the paused state.
   bool should_pause_on_start() const { return should_pause_on_start_; }
 
+  absl::optional<base::ProcessId> GetPid(PidCallback callback);
+
  private:
   friend class AuctionRunner;
   friend class AuctionWorkletManager;
@@ -73,12 +76,12 @@
   // `process_handle`.
   DebuggableAuctionWorklet(
       RenderFrameHostImpl* owning_frame,
-      AuctionProcessManager::ProcessHandle* process_handle,
+      AuctionProcessManager::ProcessHandle& process_handle,
       const GURL& url,
       auction_worklet::mojom::BidderWorklet* bidder_worklet);
   DebuggableAuctionWorklet(
       RenderFrameHostImpl* owning_frame,
-      AuctionProcessManager::ProcessHandle* process_handle,
+      AuctionProcessManager::ProcessHandle& process_handle,
       const GURL& url,
       auction_worklet::mojom::SellerWorklet* seller_worklet);
 
@@ -93,7 +96,7 @@
   void TraceProcessData(perfetto::TracedValue trace_context);
 
   const raw_ptr<RenderFrameHostImpl> owning_frame_ = nullptr;
-  const raw_ptr<AuctionProcessManager::ProcessHandle> process_handle_ = nullptr;
+  const raw_ref<AuctionProcessManager::ProcessHandle> process_handle_;
   const GURL url_;
   const std::string unique_id_;
 
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index b7a6e01..c208719 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -14082,7 +14082,8 @@
         let data = await navigator.getInterestGroupAdAuctionData({
           seller: $1
         });
-        return btoa(String.fromCharCode.apply(null, data));
+        return btoa(String.fromCharCode.apply(null, data.request)) + '|' +
+          data.requestId;
       } catch (e) {
         return e.toString();
       }
@@ -14102,7 +14103,7 @@
 
   ASSERT_TRUE(NavigateToURL(shell(), test_url));
 
-  EXPECT_EQ("", GetInterestGroupAdAuctionData(test_origin));
+  EXPECT_EQ("|", GetInterestGroupAdAuctionData(test_origin));
 }
 
 IN_PROC_BROWSER_TEST_F(InterestGroupBiddingAndAuctionServerBrowserTest,
@@ -14161,7 +14162,8 @@
     WebContentsConsoleObserver console_observer(shell()->web_contents());
     console_observer.SetPattern(WarningPermissionsPolicy("*", "*"));
 
-    EXPECT_EQ("", GetInterestGroupAdAuctionData(test_origin, execution_target));
+    EXPECT_EQ("|",
+              GetInterestGroupAdAuctionData(test_origin, execution_target));
 #if BUILDFLAG(IS_ANDROID)
     RenderFrameHost* execution_targets_with_message[] = {cross_origin_iframe};
 #else
@@ -14242,7 +14244,7 @@
           "Feature run-ad-auction is not enabled by Permissions Policy",
           GetInterestGroupAdAuctionData(test_origin, execution_target));
     } else {
-      EXPECT_EQ("",
+      EXPECT_EQ("|",
                 GetInterestGroupAdAuctionData(test_origin, execution_target));
     }
   }
diff --git a/content/browser/renderer_host/render_widget_host_view_ios.h b/content/browser/renderer_host/render_widget_host_view_ios.h
index 4b3c2c1..8db1b23 100644
--- a/content/browser/renderer_host/render_widget_host_view_ios.h
+++ b/content/browser/renderer_host/render_widget_host_view_ios.h
@@ -113,6 +113,11 @@
   void DidNavigate() override;
   bool RequestRepaintForTesting() override;
   void Destroy() override;
+  bool IsSurfaceAvailableForCopy() override;
+  void CopyFromSurface(
+      const gfx::Rect& src_rect,
+      const gfx::Size& dst_size,
+      base::OnceCallback<void(const SkBitmap&)> callback) override;
   ui::Compositor* GetCompositor() override;
 
   BrowserCompositorIOS* BrowserCompositor() const {
diff --git a/content/browser/renderer_host/render_widget_host_view_ios.mm b/content/browser/renderer_host/render_widget_host_view_ios.mm
index 0692919..5f7928b 100644
--- a/content/browser/renderer_host/render_widget_host_view_ios.mm
+++ b/content/browser/renderer_host/render_widget_host_view_ios.mm
@@ -308,6 +308,24 @@
   delete this;
 }
 
+bool RenderWidgetHostViewIOS::IsSurfaceAvailableForCopy() {
+  return browser_compositor_->GetDelegatedFrameHost()
+      ->CanCopyFromCompositingSurface();
+}
+
+void RenderWidgetHostViewIOS::CopyFromSurface(
+    const gfx::Rect& src_rect,
+    const gfx::Size& dst_size,
+    base::OnceCallback<void(const SkBitmap&)> callback) {
+  base::WeakPtr<RenderWidgetHostImpl> popup_host;
+  base::WeakPtr<DelegatedFrameHost> popup_frame_host;
+  RenderWidgetHostViewBase::CopyMainAndPopupFromSurface(
+      host()->GetWeakPtr(),
+      browser_compositor_->GetDelegatedFrameHost()->GetWeakPtr(), popup_host,
+      popup_frame_host, src_rect, dst_size, GetDeviceScaleFactor(),
+      std::move(callback));
+}
+
 void RenderWidgetHostViewIOS::InitAsChild(gfx::NativeView parent_view) {}
 void RenderWidgetHostViewIOS::SetSize(const gfx::Size& size) {}
 void RenderWidgetHostViewIOS::SetBounds(const gfx::Rect& rect) {}
@@ -399,6 +417,18 @@
 
 void RenderWidgetHostViewIOS::ShowWithVisibility(
     PageVisibilityState page_visibility) {
+  if (IsTesting() && !is_visible_) {
+    // There is some circularity in how UpdateScreenInfo works. The base class
+    // sets up some state needed by the browser compositor. The base class also
+    // depends on an update from the browser compositor. In practice this is a
+    // non issue because the function is called many times and values converge,
+    // but this is not necessarily the case in tests. This could be resolved
+    // by rewriting UpdateScreenInfo to interleave the work (see the mac
+    // implementation, eg), but for now we will simply may another call to the
+    // base class.
+    RenderWidgetHostViewBase::UpdateScreenInfo();
+    UpdateScreenInfo();
+  }
   is_visible_ = true;
   browser_compositor_->SetViewVisible(is_visible_);
   OnShowWithPageVisibility(page_visibility);
@@ -491,8 +521,7 @@
 }
 
 void RenderWidgetHostViewIOS::UpdateScreenInfo() {
-  browser_compositor_->UpdateSurfaceFromUIView(
-      gfx::Rect([ui_view_->view_ bounds]).size());
+  browser_compositor_->UpdateSurfaceFromUIView(GetViewBounds().size());
   RenderWidgetHostViewBase::UpdateScreenInfo();
 }
 
diff --git a/content/browser/webauth/authenticator_common_impl.cc b/content/browser/webauth/authenticator_common_impl.cc
index 21015d78..11c2cfba 100644
--- a/content/browser/webauth/authenticator_common_impl.cc
+++ b/content/browser/webauth/authenticator_common_impl.cc
@@ -1261,6 +1261,12 @@
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR, nullptr,
           nullptr, Focus::kDoCheck);
       return;
+    case device::MakeCredentialStatus::kHybridTransportError:
+      SignalFailureToRequestDelegate(
+          AuthenticatorRequestClientDelegate::InterestingFailureReason::
+              kHybridTransportError,
+          blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
+      return;
     case device::MakeCredentialStatus::kUserConsentDenied:
       SignalFailureToRequestDelegate(
           AuthenticatorRequestClientDelegate::InterestingFailureReason::
@@ -1531,6 +1537,12 @@
               kWinUserCancelled,
           blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
       return;
+    case device::GetAssertionStatus::kHybridTransportError:
+      SignalFailureToRequestDelegate(
+          AuthenticatorRequestClientDelegate::InterestingFailureReason::
+              kHybridTransportError,
+          blink::mojom::AuthenticatorStatus::NOT_ALLOWED_ERROR);
+      return;
     case device::GetAssertionStatus::kSuccess:
       break;
   }
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 5f6dadd..6d88e3b 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -166,6 +166,7 @@
 using cbor::Value;
 using device::VirtualCtap2Device;
 using device::VirtualFidoDevice;
+using device::cablev2::Event;
 
 namespace {
 
@@ -9109,7 +9110,7 @@
 
 // AuthenticatorCableV2Test tests features of the caBLEv2 transport and
 // protocol.
-class AuthenticatorCableV2Test : public AuthenticatorImplTest {
+class AuthenticatorCableV2Test : public AuthenticatorImplRequestDelegateTest {
  public:
   void SetUp() override {
     AuthenticatorImplTest::SetUp();
@@ -9156,6 +9157,19 @@
                                base::Unretained(this));
   }
 
+  base::RepeatingCallback<void(Event)> GetEventCallback() {
+    return base::BindRepeating(&AuthenticatorCableV2Test::OnCableEvent,
+                               base::Unretained(this));
+  }
+
+  void EnableConnectionSignalAtTunnelServer() {
+    // Recreate the tunnel server so that it supports the connection signal.
+    network_context_ = device::cablev2::NewMockTunnelServer(
+        base::BindRepeating(&AuthenticatorCableV2Test::OnContact,
+                            base::Unretained(this)),
+        /*supports_connect_signal=*/true);
+  }
+
  protected:
   class DiscoveryFactory : public device::FidoDiscoveryFactory {
    public:
@@ -9250,6 +9264,100 @@
     pairings_.clear();
   }
 
+  void OnCableEvent(Event event) { events_.push_back(event); }
+
+  void DoPairingConnection() {
+    // First do unpaired exchange to get pairing data.
+    auto discovery = std::make_unique<device::cablev2::Discovery>(
+        device::FidoRequestType::kGetAssertion, network_context_.get(),
+        qr_generator_key_, std::move(ble_advert_events_),
+        /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
+        /*contact_device_stream=*/nullptr,
+        /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
+        GetPairingCallback(), GetInvalidatedPairingCallback(),
+        GetEventCallback());
+
+    AuthenticatorEnvironment::GetInstance()
+        ->ReplaceDefaultDiscoveryFactoryForTesting(
+            std::make_unique<DiscoveryFactory>(std::move(discovery)));
+
+    const std::vector<uint8_t> contact_id(/*count=*/200, /*value=*/1);
+    std::unique_ptr<device::cablev2::authenticator::Transaction> transaction =
+        device::cablev2::authenticator::TransactFromQRCode(
+            device::cablev2::authenticator::NewMockPlatform(
+                std::move(ble_advert_callback_), &virtual_device_,
+                /*observer=*/nullptr),
+            network_context_.get(), root_secret_, "Test Authenticator",
+            zero_qr_secret_, peer_identity_x962_, contact_id);
+
+    EXPECT_EQ(AuthenticatorMakeCredential().status,
+              AuthenticatorStatus::SUCCESS);
+    EXPECT_EQ(pairings_.size(), 1u);
+
+    // Now do a pairing-based exchange. Generate a random request type hint to
+    // ensure that all values work.
+    device::FidoRequestType request_type =
+        device::FidoRequestType::kMakeCredential;
+    std::string expected_request_type_string = "mc";
+    if (base::RandDouble() < 0.5) {
+      request_type = device::FidoRequestType::kGetAssertion;
+      expected_request_type_string = "ga";
+    }
+
+    std::tie(ble_advert_callback_, ble_advert_events_) =
+        device::cablev2::Discovery::EventStream<
+            base::span<const uint8_t, device::cablev2::kAdvertSize>>::New();
+    auto callback_and_event_stream =
+        device::cablev2::Discovery::EventStream<size_t>::New();
+    discovery = std::make_unique<device::cablev2::Discovery>(
+        request_type, network_context_.get(), qr_generator_key_,
+        std::move(ble_advert_events_), std::move(pairings_),
+        std::move(callback_and_event_stream.second),
+        /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
+        GetPairingCallback(), GetInvalidatedPairingCallback(),
+        GetEventCallback());
+
+    maybe_contact_phones_callback_ =
+        base::BindLambdaForTesting([&callback_and_event_stream]() {
+          callback_and_event_stream.first.Run(0);
+        });
+
+    const std::array<uint8_t, device::cablev2::kRoutingIdSize> routing_id = {0};
+    bool contact_callback_was_called = false;
+    // When the |cablev2::Discovery| starts it'll make a connection to the
+    // tunnel service with the contact ID from the pairing data. This will be
+    // handled by the |TestNetworkContext| and turned into a call to
+    // |contact_callback_|. This simulates the tunnel server sending a cloud
+    // message to a phone. Given the information from the connection, a
+    // transaction can be created.
+    contact_callback_ = base::BindLambdaForTesting(
+        [this, &transaction, routing_id, contact_id,
+         &contact_callback_was_called, &expected_request_type_string](
+            base::span<const uint8_t, device::cablev2::kTunnelIdSize> tunnel_id,
+            base::span<const uint8_t, device::cablev2::kPairingIDSize>
+                pairing_id,
+            base::span<const uint8_t, device::cablev2::kClientNonceSize>
+                client_nonce,
+            const std::string& request_type_hint) -> void {
+          contact_callback_was_called = true;
+          CHECK_EQ(request_type_hint, expected_request_type_string);
+          transaction = device::cablev2::authenticator::TransactFromFCM(
+              device::cablev2::authenticator::NewMockPlatform(
+                  std::move(ble_advert_callback_), &virtual_device_,
+                  /*observer=*/nullptr),
+              network_context_.get(), root_secret_, routing_id, tunnel_id,
+              pairing_id, client_nonce, contact_id);
+        });
+
+    AuthenticatorEnvironment::GetInstance()
+        ->ReplaceDefaultDiscoveryFactoryForTesting(
+            std::make_unique<DiscoveryFactory>(std::move(discovery)));
+
+    EXPECT_EQ(AuthenticatorMakeCredential().status,
+              AuthenticatorStatus::SUCCESS);
+    EXPECT_TRUE(contact_callback_was_called);
+  }
+
   const std::array<uint8_t, device::cablev2::kRootSecretSize> root_secret_ = {
       0};
   const std::array<uint8_t, device::cablev2::kQRKeySize> qr_generator_key_ = {
@@ -9279,6 +9387,7 @@
                           base::Unretained(this))};
   raw_ptr<ContentBrowserClient> old_client_ = nullptr;
   base::OnceClosure maybe_contact_phones_callback_;
+  std::vector<Event> events_;
 
  private:
   static VirtualCtap2Device::State* DeviceState() {
@@ -9313,7 +9422,8 @@
       /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
       /*contact_device_stream=*/nullptr,
       /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
-      GetPairingCallback(), GetInvalidatedPairingCallback());
+      GetPairingCallback(), GetInvalidatedPairingCallback(),
+      GetEventCallback());
 
   AuthenticatorEnvironment::GetInstance()
       ->ReplaceDefaultDiscoveryFactoryForTesting(
@@ -9332,90 +9442,78 @@
   EXPECT_EQ(pairings_.size(), 0u);
 }
 
-TEST_F(AuthenticatorCableV2Test, PairingBased) {
-  // First do unpaired exchange to get pairing data.
+TEST_F(AuthenticatorCableV2Test, HandshakeError) {
+  base::test::ScopedFeatureList scoped_feature_list{
+      device::kWebAuthnNewHybridUI};
+  // A handshake error should be fatal to the request with
+  // `kHybridTransportError`.
   auto discovery = std::make_unique<device::cablev2::Discovery>(
       device::FidoRequestType::kGetAssertion, network_context_.get(),
       qr_generator_key_, std::move(ble_advert_events_),
       /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
       /*contact_device_stream=*/nullptr,
       /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
-      GetPairingCallback(), GetInvalidatedPairingCallback());
+      GetPairingCallback(), GetInvalidatedPairingCallback(),
+      GetEventCallback());
 
   AuthenticatorEnvironment::GetInstance()
       ->ReplaceDefaultDiscoveryFactoryForTesting(
           std::make_unique<DiscoveryFactory>(std::move(discovery)));
 
-  const std::vector<uint8_t> contact_id(/*count=*/200, /*value=*/1);
   std::unique_ptr<device::cablev2::authenticator::Transaction> transaction =
-      device::cablev2::authenticator::TransactFromQRCode(
+      device::cablev2::authenticator::NewHandshakeErrorDevice(
           device::cablev2::authenticator::NewMockPlatform(
               std::move(ble_advert_callback_), &virtual_device_,
               /*observer=*/nullptr),
-          network_context_.get(), root_secret_, "Test Authenticator",
-          zero_qr_secret_, peer_identity_x962_, contact_id);
+          network_context_.get(), zero_qr_secret_);
 
-  EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
-  EXPECT_EQ(pairings_.size(), 1u);
+  FailureReasonCallbackReceiver failure_reason_receiver;
+  auto mock_delegate = std::make_unique<
+      ::testing::NiceMock<MockAuthenticatorRequestDelegateObserver>>(
+      failure_reason_receiver.callback());
+  auto authenticator = ConnectToFakeAuthenticator(std::move(mock_delegate));
 
-  // Now do a pairing-based exchange. Generate a random request type hint to
-  // ensure that all values work.
-  device::FidoRequestType request_type =
-      device::FidoRequestType::kMakeCredential;
-  std::string expected_request_type_string = "mc";
-  if (base::RandDouble() < 0.5) {
-    request_type = device::FidoRequestType::kGetAssertion;
-    expected_request_type_string = "ga";
-  }
+  TestMakeCredentialCallback callback_receiver;
+  authenticator->MakeCredential(GetTestPublicKeyCredentialCreationOptions(),
+                                callback_receiver.callback());
 
-  std::tie(ble_advert_callback_, ble_advert_events_) =
-      device::cablev2::Discovery::EventStream<
-          base::span<const uint8_t, device::cablev2::kAdvertSize>>::New();
-  auto callback_and_event_stream =
-      device::cablev2::Discovery::EventStream<size_t>::New();
-  discovery = std::make_unique<device::cablev2::Discovery>(
-      request_type, network_context_.get(), qr_generator_key_,
-      std::move(ble_advert_events_), std::move(pairings_),
-      std::move(callback_and_event_stream.second),
-      /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
-      GetPairingCallback(), GetInvalidatedPairingCallback());
+  callback_receiver.WaitForCallback();
+  EXPECT_EQ(AuthenticatorStatus::NOT_ALLOWED_ERROR, callback_receiver.status());
 
-  maybe_contact_phones_callback_ =
-      base::BindLambdaForTesting([&callback_and_event_stream]() {
-        callback_and_event_stream.first.Run(0);
-      });
+  ASSERT_TRUE(failure_reason_receiver.was_called());
+  EXPECT_EQ(AuthenticatorRequestClientDelegate::InterestingFailureReason::
+                kHybridTransportError,
+            std::get<0>(*failure_reason_receiver.result()));
+}
 
-  const std::array<uint8_t, device::cablev2::kRoutingIdSize> routing_id = {0};
-  bool contact_callback_was_called = false;
-  // When the |cablev2::Discovery| starts it'll make a connection to the tunnel
-  // service with the contact ID from the pairing data. This will be handled by
-  // the |TestNetworkContext| and turned into a call to |contact_callback_|.
-  // This simulates the tunnel server sending a cloud message to a phone. Given
-  // the information from the connection, a transaction can be created.
-  contact_callback_ = base::BindLambdaForTesting(
-      [this, &transaction, routing_id, contact_id, &contact_callback_was_called,
-       &expected_request_type_string](
-          base::span<const uint8_t, device::cablev2::kTunnelIdSize> tunnel_id,
-          base::span<const uint8_t, device::cablev2::kPairingIDSize> pairing_id,
-          base::span<const uint8_t, device::cablev2::kClientNonceSize>
-              client_nonce,
-          const std::string& request_type_hint) -> void {
-        contact_callback_was_called = true;
-        CHECK_EQ(request_type_hint, expected_request_type_string);
-        transaction = device::cablev2::authenticator::TransactFromFCM(
-            device::cablev2::authenticator::NewMockPlatform(
-                std::move(ble_advert_callback_), &virtual_device_,
-                /*observer=*/nullptr),
-            network_context_.get(), root_secret_, routing_id, tunnel_id,
-            pairing_id, client_nonce, contact_id);
-      });
+TEST_F(AuthenticatorCableV2Test, PairingBased) {
+  DoPairingConnection();
 
-  AuthenticatorEnvironment::GetInstance()
-      ->ReplaceDefaultDiscoveryFactoryForTesting(
-          std::make_unique<DiscoveryFactory>(std::move(discovery)));
+  const std::vector<Event> kExpectedEvents = {
+      // From the QR connection
+      Event::kBLEAdvertReceived,
+      Event::kReady,
+      // From the paired connection
+      Event::kBLEAdvertReceived,
+      Event::kReady,
+  };
+  EXPECT_EQ(events_, kExpectedEvents);
+}
 
-  EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
-  EXPECT_TRUE(contact_callback_was_called);
+TEST_F(AuthenticatorCableV2Test, PairingBasedWithConnectionSignal) {
+  EnableConnectionSignalAtTunnelServer();
+  DoPairingConnection();
+
+  const std::vector<Event> kExpectedEvents = {
+      // From the QR connection
+      Event::kBLEAdvertReceived,
+      Event::kReady,
+      // From the paired connection
+      Event::kPhoneConnected,
+      Event::kBLEAdvertReceived,
+      Event::kReady,
+  };
+  EXPECT_EQ(events_, kExpectedEvents);
 }
 
 static std::unique_ptr<device::cablev2::Pairing> DummyPairing() {
@@ -9444,7 +9542,8 @@
       qr_generator_key_, std::move(ble_advert_events_), std::move(pairings),
       std::move(callback_and_event_stream.second),
       /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
-      GetPairingCallback(), GetInvalidatedPairingCallback());
+      GetPairingCallback(), GetInvalidatedPairingCallback(),
+      GetEventCallback());
 
   AuthenticatorEnvironment::GetInstance()
       ->ReplaceDefaultDiscoveryFactoryForTesting(
@@ -9518,7 +9617,7 @@
       qr_generator_key_, std::move(ble_advert_events_),
       /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
       /*contact_device_stream=*/nullptr, extension_values, GetPairingCallback(),
-      GetInvalidatedPairingCallback());
+      GetInvalidatedPairingCallback(), GetEventCallback());
 
   AuthenticatorEnvironment::GetInstance()
       ->ReplaceDefaultDiscoveryFactoryForTesting(
@@ -9550,7 +9649,8 @@
       /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
       /*contact_device_stream=*/nullptr,
       /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
-      GetPairingCallback(), GetInvalidatedPairingCallback());
+      GetPairingCallback(), GetInvalidatedPairingCallback(),
+      GetEventCallback());
 
   AuthenticatorEnvironment::GetInstance()
       ->ReplaceDefaultDiscoveryFactoryForTesting(
@@ -9593,7 +9693,8 @@
         /*pairings=*/std::vector<std::unique_ptr<device::cablev2::Pairing>>(),
         /*contact_device_stream=*/nullptr,
         /*extension_contents=*/std::vector<device::CableDiscoveryData>(),
-        GetPairingCallback(), GetInvalidatedPairingCallback());
+        GetPairingCallback(), GetInvalidatedPairingCallback(),
+        GetEventCallback());
 
     AuthenticatorEnvironment::GetInstance()
         ->ReplaceDefaultDiscoveryFactoryForTesting(
diff --git a/content/public/browser/authenticator_request_client_delegate.h b/content/public/browser/authenticator_request_client_delegate.h
index c9e001c..2daf1c9e 100644
--- a/content/public/browser/authenticator_request_client_delegate.h
+++ b/content/public/browser/authenticator_request_client_delegate.h
@@ -202,6 +202,7 @@
     // kWinUserCancelled means that the user clicked "Cancel" in the native
     // Windows UI.
     kWinUserCancelled,
+    kHybridTransportError,
   };
 
   AuthenticatorRequestClientDelegate();
diff --git a/content/public/browser/devtools_agent_host_observer.cc b/content/public/browser/devtools_agent_host_observer.cc
index d763e052..119ae45 100644
--- a/content/public/browser/devtools_agent_host_observer.cc
+++ b/content/public/browser/devtools_agent_host_observer.cc
@@ -20,6 +20,9 @@
 void DevToolsAgentHostObserver::DevToolsAgentHostNavigated(
     DevToolsAgentHost* agent_host) {}
 
+void DevToolsAgentHostObserver::DevToolsAgentHostProcessChanged(
+    DevToolsAgentHost* agent_host) {}
+
 void DevToolsAgentHostObserver::DevToolsAgentHostAttached(
     DevToolsAgentHost* agent_host) {
 }
diff --git a/content/public/browser/devtools_agent_host_observer.h b/content/public/browser/devtools_agent_host_observer.h
index 164139e..489e006e 100644
--- a/content/public/browser/devtools_agent_host_observer.h
+++ b/content/public/browser/devtools_agent_host_observer.h
@@ -28,6 +28,9 @@
   // Called when DevToolsAgentHost was created and is ready to be used.
   virtual void DevToolsAgentHostNavigated(DevToolsAgentHost* agent_host);
 
+  // Called when a process associated with inspected target has changed.
+  virtual void DevToolsAgentHostProcessChanged(DevToolsAgentHost* agent_host);
+
   // Called when client has attached to DevToolsAgentHost.
   virtual void DevToolsAgentHostAttached(DevToolsAgentHost* agent_host);
 
diff --git a/device/fido/cable/fido_tunnel_device.cc b/device/fido/cable/fido_tunnel_device.cc
index c959ad5..ad35e6e 100644
--- a/device/fido/cable/fido_tunnel_device.cc
+++ b/device/fido/cable/fido_tunnel_device.cc
@@ -14,6 +14,7 @@
 #include "crypto/random.h"
 #include "device/fido/cable/cable_discovery_data.h"
 #include "device/fido/cbor_extract.h"
+#include "device/fido/features.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -98,10 +99,13 @@
     network::mojom::NetworkContext* network_context,
     absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         pairing_callback,
+    absl::optional<base::RepeatingCallback<void(Event)>> event_callback,
     base::span<const uint8_t> secret,
     base::span<const uint8_t, kQRSeedSize> local_identity_seed,
     const CableEidArray& decrypted_eid)
-    : info_(absl::in_place_type<QRInfo>), id_(RandomId()) {
+    : info_(absl::in_place_type<QRInfo>),
+      id_(RandomId()),
+      event_callback_(std::move(event_callback)) {
   const eid::Components components = eid::ToComponents(decrypted_eid);
 
   QRInfo& info = absl::get<QRInfo>(info_);
@@ -143,8 +147,11 @@
     FidoRequestType request_type,
     network::mojom::NetworkContext* network_context,
     std::unique_ptr<Pairing> pairing,
-    base::OnceClosure pairing_is_invalid)
-    : info_(absl::in_place_type<PairedInfo>), id_(RandomId()) {
+    base::OnceClosure pairing_is_invalid,
+    absl::optional<base::RepeatingCallback<void(Event)>> event_callback)
+    : info_(absl::in_place_type<PairedInfo>),
+      id_(RandomId()),
+      event_callback_(std::move(event_callback)) {
   uint8_t client_nonce[kClientNonceSize];
   crypto::RandBytes(client_nonce);
 
@@ -176,6 +183,10 @@
   std::vector<network::mojom::HttpHeaderPtr> headers;
   headers.emplace_back(network::mojom::HttpHeader::New(
       kCableClientPayloadHeader, client_payload_hex));
+  if (base::FeatureList::IsEnabled(device::kWebAuthnNewHybridUI)) {
+    headers.emplace_back(
+        network::mojom::HttpHeader::New(kCableSignalConnectionHeader, "true"));
+  }
   network_context->CreateWebSocket(
       url, {kCableWebSocketProtocol}, net::SiteForCookies(),
       net::IsolationInfo(), std::move(headers),
@@ -209,13 +220,15 @@
   info.psk = Derive<EXTENT(*info.psk)>(info.secret, *plaintext,
                                        DerivedValueType::kPSK);
 
-  if (state_ == State::kWaitingForEID) {
+  if (state_ == State::kWaitingForEID ||
+      state_ == State::kWaitingForEIDOrConnectSignal) {
     // We were waiting for this BLE advert in order to start the handshake.
     DCHECK(!handshake_);
     handshake_.emplace(*info.psk, info.peer_identity,
                        /*local_identity=*/absl::nullopt);
     websocket_client_->Write(handshake_->BuildInitialMessage());
-    state_ = State::kHandshakeSent;
+    state_ = state_ == State::kWaitingForEID ? State::kHandshakeSent
+                                             : State::kWaitingForConnectSignal;
   }
 
   return true;
@@ -263,7 +276,8 @@
 
 void FidoTunnelDevice::OnTunnelReady(
     WebSocketAdapter::Result result,
-    absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id) {
+    absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id,
+    WebSocketAdapter::ConnectSignalSupport connect_signal_support) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(State::kConnecting, state_);
 
@@ -288,9 +302,15 @@
 
       if (handshake_) {
         websocket_client_->Write(handshake_->BuildInitialMessage());
-        state_ = State::kHandshakeSent;
+        state_ = connect_signal_support ==
+                         WebSocketAdapter::ConnectSignalSupport::YES
+                     ? State::kWaitingForConnectSignal
+                     : State::kHandshakeSent;
       } else {
-        state_ = State::kWaitingForEID;
+        state_ = connect_signal_support ==
+                         WebSocketAdapter::ConnectSignalSupport::YES
+                     ? State::kWaitingForEIDOrConnectSignal
+                     : State::kWaitingForEID;
       }
       break;
 
@@ -334,6 +354,20 @@
       OnError();
       break;
 
+    case State::kWaitingForConnectSignal:
+    case State::kWaitingForEIDOrConnectSignal:
+      if (!ProcessConnectSignal(*data)) {
+        FIDO_LOG(ERROR) << GetId() << ": bad connection signal";
+        OnError();
+        return;
+      }
+      if (state_ == State::kWaitingForConnectSignal) {
+        state_ = State::kHandshakeSent;
+      } else {
+        state_ = State::kWaitingForEID;
+      }
+      break;
+
     case State::kHandshakeSent: {
       // This is the handshake response.
       HandshakeResult result = handshake_->ProcessResponse(*data);
@@ -433,6 +467,9 @@
       FIDO_LOG(DEBUG) << GetId() << ": established v2." << protocol_revision;
       RecordEvent(CableV2TunnelEvent::kTunnelEstablished);
       state_ = State::kReady;
+      if (event_callback_) {
+        event_callback_->Run(Event::kReady);
+      }
 
       established_connection_ = base::MakeRefCounted<EstablishedConnection>(
           std::move(websocket_client_), GetId(), protocol_revision,
@@ -492,6 +529,17 @@
       FROM_HERE, base::BindOnce(std::move(callback), std::move(reply)));
 }
 
+bool FidoTunnelDevice::ProcessConnectSignal(base::span<const uint8_t> data) {
+  if (data.size() != 1 || data[0] != 0) {
+    return false;
+  }
+  FIDO_LOG(DEBUG) << "caBLE authenticator has connected to the tunnel server.";
+  if (event_callback_) {
+    event_callback_->Run(Event::kPhoneConnected);
+  }
+  return true;
+}
+
 // g_num_established_connection_instances is incremented when an
 // `EstablishedConnection` is created and decremented during its destructor.
 // This is purely for checking that none leak in tests.
diff --git a/device/fido/cable/fido_tunnel_device.h b/device/fido/cable/fido_tunnel_device.h
index d6a98b3..183f4f2 100644
--- a/device/fido/cable/fido_tunnel_device.h
+++ b/device/fido/cable/fido_tunnel_device.h
@@ -39,6 +39,7 @@
       network::mojom::NetworkContext* network_context,
       absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
           pairing_callback,
+      absl::optional<base::RepeatingCallback<void(Event)>> event_callback,
       base::span<const uint8_t> secret,
       base::span<const uint8_t, kQRSeedSize> local_identity_seed,
       const CableEidArray& decrypted_eid);
@@ -47,10 +48,12 @@
   // |Pairing| is reported by the tunnel server to be invalid (which can happen
   // if the user opts to unlink all devices) then |pairing_is_invalid| is
   // run.
-  FidoTunnelDevice(FidoRequestType request_type,
-                   network::mojom::NetworkContext* network_context,
-                   std::unique_ptr<Pairing> pairing,
-                   base::OnceClosure pairing_is_invalid);
+  FidoTunnelDevice(
+      FidoRequestType request_type,
+      network::mojom::NetworkContext* network_context,
+      std::unique_ptr<Pairing> pairing,
+      base::OnceClosure pairing_is_invalid,
+      absl::optional<base::RepeatingCallback<void(Event)>> event_callback);
 
   FidoTunnelDevice(const FidoTunnelDevice&) = delete;
   FidoTunnelDevice& operator=(const FidoTunnelDevice&) = delete;
@@ -105,12 +108,13 @@
     //   (Tunnel server connection completes)           |
     //      |                              (BLE advert is received _then_
     //      V                               tunnel connection completes.)
-    //  kWaitingForEID                                  |
+    //  kWaitingForEID / kWaitingForEIDOrConnectSignal  |
     //      |                                           |
     //   (BLE advert is received and handshake is sent) |
     //      |                                           |
     //      V                                           |
-    //   kHandshakeSent   <------------------------------
+    //   kHandshakeSent / <------------------------------
+    //   kWaitingForConnectSignal (if the tunnel server supports this)
     //      |
     //   (Handshake reply is received)
     //      |
@@ -123,7 +127,9 @@
     //  kReady
     kConnecting,
     kHandshakeSent,
+    kWaitingForConnectSignal,
     kWaitingForEID,
+    kWaitingForEIDOrConnectSignal,
     kWaitingForPostHandshakeMessage,
     kReady,
     kError,
@@ -211,15 +217,18 @@
 
   void OnTunnelReady(
       WebSocketAdapter::Result result,
-      absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id);
+      absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id,
+      WebSocketAdapter::ConnectSignalSupport connect_signal_support);
   void OnTunnelData(absl::optional<base::span<const uint8_t>> data);
   void OnError();
   void DeviceTransactReady(std::vector<uint8_t> command,
                            DeviceCallback callback);
+  bool ProcessConnectSignal(base::span<const uint8_t> data);
 
   State state_ = State::kConnecting;
   absl::variant<QRInfo, PairedInfo> info_;
   const std::array<uint8_t, 8> id_;
+  const absl::optional<base::RepeatingCallback<void(Event)>> event_callback_;
   std::vector<uint8_t> pending_message_;
   DeviceCallback pending_callback_;
   absl::optional<HandshakeInitiator> handshake_;
diff --git a/device/fido/cable/v2_authenticator.cc b/device/fido/cable/v2_authenticator.cc
index 144d6c74..895bb1f 100644
--- a/device/fido/cable/v2_authenticator.cc
+++ b/device/fido/cable/v2_authenticator.cc
@@ -407,7 +407,8 @@
   void OnTunnelReady(
       WebSocketAdapter::Result result,
       absl::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
-          routing_id) {
+          routing_id,
+      WebSocketAdapter::ConnectSignalSupport) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     DCHECK(state_ == State::kConnecting || state_ == State::kConnectingPaired);
     bool ok = (result == WebSocketAdapter::Result::OK);
diff --git a/device/fido/cable/v2_constants.h b/device/fido/cable/v2_constants.h
index 8f4a6f3..f92a03b 100644
--- a/device/fido/cable/v2_constants.h
+++ b/device/fido/cable/v2_constants.h
@@ -88,6 +88,17 @@
   kMaxValue = 2,
 };
 
+enum class Event {
+  // kPhoneConnected means that the phone has connected to the tunnel server
+  // and started BLE advertising.
+  kPhoneConnected,
+  // kBLEAdvertReceived means that a matching BLE advert has been
+  // received and a corresponding "device" has been discovered.
+  kBLEAdvertReceived,
+  // kReady means that the device is ready to receive a CTAP-level message.
+  kReady,
+};
+
 }  // namespace cablev2
 }  // namespace device
 
diff --git a/device/fido/cable/v2_discovery.cc b/device/fido/cable/v2_discovery.cc
index a06191f..48842b6 100644
--- a/device/fido/cable/v2_discovery.cc
+++ b/device/fido/cable/v2_discovery.cc
@@ -5,6 +5,7 @@
 #include "device/fido/cable/v2_discovery.h"
 
 #include "base/containers/contains.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
@@ -13,6 +14,7 @@
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/cable/fido_tunnel_device.h"
 #include "device/fido/cable/v2_handshake.h"
+#include "device/fido/features.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "third_party/boringssl/src/include/openssl/aes.h"
 
@@ -55,7 +57,8 @@
     absl::optional<base::RepeatingCallback<void(std::unique_ptr<Pairing>)>>
         pairing_callback,
     absl::optional<base::RepeatingCallback<void(size_t)>>
-        invalidated_pairing_callback)
+        invalidated_pairing_callback,
+    absl::optional<base::RepeatingCallback<void(Event)>> event_callback)
     : FidoDeviceDiscovery(FidoTransportProtocol::kHybrid),
       request_type_(request_type),
       network_context_(network_context),
@@ -65,7 +68,8 @@
       pairings_(std::move(pairings)),
       contact_device_stream_(std::move(contact_device_stream)),
       pairing_callback_(std::move(pairing_callback)),
-      invalidated_pairing_callback_(std::move(invalidated_pairing_callback)) {
+      invalidated_pairing_callback_(std::move(invalidated_pairing_callback)),
+      event_callback_(std::move(event_callback)) {
   static_assert(EXTENT(*qr_generator_key) == kQRSecretSize + kQRSeedSize, "");
   advert_stream_->Connect(
       base::BindRepeating(&Discovery::OnBLEAdvertSeen, base::Unretained(this)));
@@ -113,6 +117,11 @@
     return;
   }
 
+  if (base::FeatureList::IsEnabled(device::kWebAuthnNewHybridUI) &&
+      device_committed_) {
+    // A device has already been accepted. Ignore other adverts.
+  }
+
   if (base::Contains(observed_adverts_, advert_array)) {
     return;
   }
@@ -131,6 +140,10 @@
                     << " matches pending tunnel)";
     std::unique_ptr<FidoTunnelDevice> device(std::move(*i));
     tunnels_pending_advert_.erase(i);
+    device_committed_ = true;
+    if (event_callback_) {
+      event_callback_->Run(Event::kBLEAdvertReceived);
+    }
     AddDevice(std::move(device));
     return;
   }
@@ -143,9 +156,13 @@
       FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert)
                       << " matches QR code)";
       RecordEvent(CableV2DiscoveryEvent::kQRMatch);
+      device_committed_ = true;
+      if (event_callback_) {
+        event_callback_->Run(Event::kBLEAdvertReceived);
+      }
       AddDevice(std::make_unique<cablev2::FidoTunnelDevice>(
-          network_context_, pairing_callback_, qr_keys_->qr_secret,
-          qr_keys_->local_identity_seed, *plaintext));
+          network_context_, pairing_callback_, event_callback_,
+          qr_keys_->qr_secret, qr_keys_->local_identity_seed, *plaintext));
       return;
     }
   }
@@ -158,9 +175,10 @@
       FIDO_LOG(DEBUG) << "  (" << base::HexEncode(advert)
                       << " matches extension)";
       RecordEvent(CableV2DiscoveryEvent::kExtensionMatch);
+      device_committed_ = true;
       AddDevice(std::make_unique<cablev2::FidoTunnelDevice>(
-          network_context_, base::DoNothing(), extension.qr_secret,
-          extension.local_identity_seed, *plaintext));
+          network_context_, base::DoNothing(), event_callback_,
+          extension.qr_secret, extension.local_identity_seed, *plaintext));
       return;
     }
   }
@@ -178,7 +196,8 @@
   tunnels_pending_advert_.emplace_back(std::make_unique<FidoTunnelDevice>(
       request_type_, network_context_, std::move(pairings_[pairing_index]),
       base::BindOnce(&Discovery::PairingIsInvalid, weak_factory_.GetWeakPtr(),
-                     pairing_index)));
+                     pairing_index),
+      event_callback_));
 }
 
 void Discovery::PairingIsInvalid(size_t pairing_index) {
diff --git a/device/fido/cable/v2_discovery.h b/device/fido/cable/v2_discovery.h
index 7a1aac6..2dd644ec 100644
--- a/device/fido/cable/v2_discovery.h
+++ b/device/fido/cable/v2_discovery.h
@@ -53,7 +53,9 @@
       // invalidated_pairing_callback will be called when a pairing is reported
       // to be invalid by the tunnel server.
       absl::optional<base::RepeatingCallback<void(size_t)>>
-          invalidated_pairing_callback);
+          invalidated_pairing_callback,
+      // event_callback receives updates on cablev2 events.
+      absl::optional<base::RepeatingCallback<void(Event)>> event_callback);
   ~Discovery() override;
   Discovery(const Discovery&) = delete;
   Discovery& operator=(const Discovery&) = delete;
@@ -89,9 +91,11 @@
       pairing_callback_;
   const absl::optional<base::RepeatingCallback<void(size_t)>>
       invalidated_pairing_callback_;
+  const absl::optional<base::RepeatingCallback<void(Event)>> event_callback_;
   std::vector<std::unique_ptr<FidoTunnelDevice>> tunnels_pending_advert_;
   base::flat_set<std::array<uint8_t, kAdvertSize>> observed_adverts_;
   bool started_ = false;
+  bool device_committed_ = false;
   std::vector<std::array<uint8_t, kAdvertSize>> pending_adverts_;
   base::WeakPtrFactory<Discovery> weak_factory_{this};
 };
diff --git a/device/fido/cable/v2_test_util.cc b/device/fido/cable/v2_test_util.cc
index 87809e7f..89f2bec 100644
--- a/device/fido/cable/v2_test_util.cc
+++ b/device/fido/cable/v2_test_util.cc
@@ -43,8 +43,10 @@
 // caBLEv2 tunnel server.
 class TestNetworkContext : public network::TestNetworkContext {
  public:
-  explicit TestNetworkContext(absl::optional<ContactCallback> contact_callback)
-      : contact_callback_(std::move(contact_callback)) {}
+  TestNetworkContext(absl::optional<ContactCallback> contact_callback,
+                     bool supports_connect_signal)
+      : contact_callback_(std::move(contact_callback)),
+        supports_connect_signal_(supports_connect_signal) {}
 
   void CreateWebSocket(
       const GURL& url,
@@ -92,9 +94,14 @@
     } else if (path.find(kContactPrefix) == 0) {
       path.remove_prefix(sizeof(kContactPrefix) - 1);
 
-      CHECK_EQ(additional_headers.size(), 1u);
+      CHECK_GE(additional_headers.size(), 1u);
       CHECK_EQ(additional_headers[0]->name, device::kCableClientPayloadHeader);
 
+      CHECK(additional_headers.size() == 1 ||
+            (additional_headers.size() == 2 &&
+             additional_headers[1]->name ==
+                 device::kCableSignalConnectionHeader));
+
       if (!contact_callback_) {
         // Without a contact callback all attempts are rejected with a 410
         // status to indicate the the contact ID will never work again.
@@ -115,10 +122,13 @@
       uint8_t tunnel_id[kTunnelIdSize];
       crypto::RandBytes(tunnel_id);
 
+      const auto type = supports_connect_signal_
+                            ? Connection::Type::CONTACT_WITH_CONNECTION_SIGNAL
+                            : Connection::Type::CONTACT;
+
       connections_.emplace(
           base::HexEncode(tunnel_id),
-          std::make_unique<Connection>(Connection::Type::CONTACT,
-                                       std::move(handshake_client)));
+          std::make_unique<Connection>(type, std::move(handshake_client)));
 
       const std::vector<uint8_t>& pairing_id_vec =
           map.find(cbor::Value(1))->second.GetBytestring();
@@ -147,6 +157,7 @@
       NEW,
       CONNECT,
       CONTACT,
+      CONTACT_WITH_CONNECTION_SIGNAL,
     };
 
     Connection(Type type,
@@ -203,10 +214,22 @@
 
     void set_peer(std::unique_ptr<Connection> peer) {
       CHECK(!peer_);
+
       peer_ownership_ = std::move(peer);
       peer_ = peer_ownership_.get();
       peer_->set_nonowning_peer(this);
 
+      if (type_ == Type::CONTACT_WITH_CONNECTION_SIGNAL) {
+        CHECK(peer_->buffer_.empty());
+        CHECK(peer_->buffer_i_ == 0);
+        constexpr uint8_t kConnectionSignal[] = {0};
+        peer_->buffer_.push_back(kConnectionSignal[0]);
+        OnOutPipeReady(MOJO_RESULT_OK, mojo::HandleSignalsState());
+        client_receiver_->OnDataFrame(
+            /*fin=*/true, network::mojom::WebSocketMessageType::BINARY,
+            sizeof(kConnectionSignal));
+      }
+
       Flush();
     }
 
@@ -217,6 +240,7 @@
       switch (type_) {
         case Type::NEW:
         case Type::CONTACT:
+        case Type::CONTACT_WITH_CONNECTION_SIGNAL:
           return "A";
         case Type::CONNECT:
           return "B";
@@ -240,6 +264,11 @@
         std::array<uint8_t, kRoutingIdSize> routing_id = {42};
         header->value = base::HexEncode(routing_id);
         response->headers.push_back(std::move(header));
+      } else if (type_ == Type::CONTACT_WITH_CONNECTION_SIGNAL) {
+        auto header = network::mojom::HttpHeader::New();
+        header->name = device::kCableSignalConnectionHeader;
+        header->value = "true";
+        response->headers.push_back(std::move(header));
       }
 
       handshake_client_->OnConnectionEstablished(
@@ -353,6 +382,7 @@
 
   std::map<std::string, std::unique_ptr<Connection>> connections_;
   const absl::optional<ContactCallback> contact_callback_;
+  const bool supports_connect_signal_;
 };
 
 class DummyBLEAdvert
@@ -664,7 +694,8 @@
   void OnTunnelReady(
       WebSocketAdapter::Result result,
       absl::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
-          routing_id) {
+          routing_id,
+      WebSocketAdapter::ConnectSignalSupport connect_signal_support) {
     CHECK_EQ(result, WebSocketAdapter::Result::OK);
     CHECK(routing_id);
 
@@ -840,12 +871,97 @@
   HandshakeHash handshake_hash_;
 };
 
+class HandshakeErrorDevice : public authenticator::Transaction {
+ public:
+  HandshakeErrorDevice(std::unique_ptr<Platform> platform,
+                       network::mojom::NetworkContext* network_context,
+                       base::span<const uint8_t> qr_secret)
+      : platform_(std::move(platform)),
+        network_context_(network_context),
+        tunnel_id_(device::cablev2::Derive<EXTENT(tunnel_id_)>(
+            qr_secret,
+            base::span<uint8_t>(),
+            DerivedValueType::kTunnelID)),
+        eid_key_(device::cablev2::Derive<EXTENT(eid_key_)>(
+            qr_secret,
+            base::span<const uint8_t>(),
+            device::cablev2::DerivedValueType::kEIDKey)),
+        secret_(fido_parsing_utils::Materialize(qr_secret)) {
+    websocket_client_ = std::make_unique<device::cablev2::WebSocketAdapter>(
+        base::BindOnce(&HandshakeErrorDevice::OnTunnelReady,
+                       base::Unretained(this)),
+        base::BindRepeating(&HandshakeErrorDevice::OnTunnelData,
+                            base::Unretained(this)));
+
+    const GURL target = device::cablev2::tunnelserver::GetNewTunnelURL(
+        kTunnelServer, tunnel_id_);
+
+    network_context_->CreateWebSocket(
+        target, {device::kCableWebSocketProtocol}, net::SiteForCookies(),
+        net::IsolationInfo(), /*additional_headers=*/{},
+        network::mojom::kBrowserProcessId, url::Origin::Create(target),
+        network::mojom::kWebSocketOptionBlockAllCookies,
+        net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
+        websocket_client_->BindNewHandshakeClientPipe(),
+        /*url_loader_network_observer=*/mojo::NullRemote(),
+        /*auth_handler=*/mojo::NullRemote(),
+        /*header_client=*/mojo::NullRemote(),
+        /*throttling_profile_id=*/absl::nullopt);
+  }
+
+ private:
+  void OnTunnelReady(
+      WebSocketAdapter::Result result,
+      absl::optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>>
+          routing_id,
+      WebSocketAdapter::ConnectSignalSupport connect_signal_support) {
+    CHECK_EQ(result, WebSocketAdapter::Result::OK);
+    CHECK(routing_id);
+
+    CableEidArray plaintext_eid;
+    device::cablev2::eid::Components components;
+    components.tunnel_server_domain = kTunnelServer;
+    components.routing_id = *routing_id;
+    components.nonce = RandomNonce();
+
+    plaintext_eid = device::cablev2::eid::FromComponents(components);
+
+    ble_advert_ =
+        platform_->SendBLEAdvert(eid::Encrypt(plaintext_eid, eid_key_));
+    psk_ = device::cablev2::Derive<EXTENT(psk_)>(
+        secret_, plaintext_eid, device::cablev2::DerivedValueType::kPSK);
+  }
+
+  std::array<uint8_t, device::cablev2::kNonceSize> RandomNonce() {
+    std::array<uint8_t, device::cablev2::kNonceSize> ret;
+    crypto::RandBytes(ret);
+    return ret;
+  }
+
+  void OnTunnelData(absl::optional<base::span<const uint8_t>> msg) {
+    std::vector<uint8_t> response = {'b', 'o', 'g', 'u', 's'};
+    websocket_client_->Write(response);
+  }
+
+  const std::unique_ptr<Platform> platform_;
+  const raw_ptr<network::mojom::NetworkContext> network_context_;
+  const std::array<uint8_t, kTunnelIdSize> tunnel_id_;
+  const std::array<uint8_t, kEIDKeySize> eid_key_;
+  const std::vector<uint8_t> secret_;
+  std::unique_ptr<WebSocketAdapter> websocket_client_;
+  std::unique_ptr<Platform::BLEAdvert> ble_advert_;
+  std::array<uint8_t, kPSKSize> psk_;
+  GURL target_;
+};
+
 }  // namespace
 }  // namespace authenticator
 
 std::unique_ptr<network::mojom::NetworkContext> NewMockTunnelServer(
-    absl::optional<ContactCallback> contact_callback) {
-  return std::make_unique<TestNetworkContext>(std::move(contact_callback));
+    absl::optional<ContactCallback> contact_callback,
+    bool supports_connect_signal) {
+  return std::make_unique<TestNetworkContext>(std::move(contact_callback),
+                                              supports_connect_signal);
 }
 
 namespace authenticator {
@@ -872,5 +988,13 @@
                                              peer_identity);
 }
 
+std::unique_ptr<Transaction> NewHandshakeErrorDevice(
+    std::unique_ptr<Platform> platform,
+    network::mojom::NetworkContext* network_context,
+    base::span<const uint8_t> qr_secret) {
+  return std::make_unique<HandshakeErrorDevice>(std::move(platform),
+                                                network_context, qr_secret);
+}
+
 }  // namespace authenticator
 }  // namespace device::cablev2
diff --git a/device/fido/cable/v2_test_util.h b/device/fido/cable/v2_test_util.h
index b3c1ea8..ca46a90 100644
--- a/device/fido/cable/v2_test_util.h
+++ b/device/fido/cable/v2_test_util.h
@@ -35,7 +35,8 @@
 // |nullopt| then all contact requests will be rejected with an HTTP 410 status
 // to indicate that the contact ID is disabled.
 std::unique_ptr<network::mojom::NetworkContext> NewMockTunnelServer(
-    absl::optional<ContactCallback> contact_callback);
+    absl::optional<ContactCallback> contact_callback,
+    bool supports_connect_signal = false);
 
 namespace authenticator {
 
@@ -69,6 +70,13 @@
     base::span<const uint8_t> qr_secret,
     base::span<const uint8_t, kP256X962Length> peer_identity);
 
+// NewHandshakeErrorDevice returns a caBLEv2 device that produces an invalid
+// caBLEv2 handshake.
+std::unique_ptr<Transaction> NewHandshakeErrorDevice(
+    std::unique_ptr<Platform> platform,
+    network::mojom::NetworkContext* network_context,
+    base::span<const uint8_t> qr_secret);
+
 }  // namespace authenticator
 
 }  // namespace cablev2
diff --git a/device/fido/cable/websocket_adapter.cc b/device/fido/cable/websocket_adapter.cc
index 15a7728..083674fc 100644
--- a/device/fido/cable/websocket_adapter.cc
+++ b/device/fido/cable/websocket_adapter.cc
@@ -83,7 +83,8 @@
   // This contact ID has been marked as inactive. The pairing information for
   // this device should be dropped.
   if (on_tunnel_ready_) {
-    std::move(on_tunnel_ready_).Run(Result::GONE, absl::nullopt);
+    std::move(on_tunnel_ready_)
+        .Run(Result::GONE, absl::nullopt, ConnectSignalSupport::NO);
     // `this` may be invalid now.
   }
 }
@@ -102,6 +103,7 @@
   }
 
   absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id;
+  ConnectSignalSupport connect_signal_support = ConnectSignalSupport::NO;
   for (const auto& header : response->headers) {
     if (base::EqualsCaseInsensitiveASCII(header->name.c_str(),
                                          kCableRoutingIdHeader)) {
@@ -112,6 +114,10 @@
         return;
       }
     }
+    if (base::EqualsCaseInsensitiveASCII(header->name.c_str(),
+                                         kCableSignalConnectionHeader)) {
+      connect_signal_support = ConnectSignalSupport::YES;
+    }
   }
 
   socket_remote_.Bind(std::move(socket));
@@ -132,7 +138,8 @@
 
   socket_remote_->StartReceiving();
 
-  std::move(on_tunnel_ready_).Run(Result::OK, routing_id);
+  std::move(on_tunnel_ready_)
+      .Run(Result::OK, routing_id, connect_signal_support);
   // `this` may be invalid now.
 }
 
@@ -230,7 +237,8 @@
   // If disconnection happens before |OnConnectionEstablished| then report a
   // failure to establish the tunnel.
   if (on_tunnel_ready_) {
-    std::move(on_tunnel_ready_).Run(Result::FAILED, absl::nullopt);
+    std::move(on_tunnel_ready_)
+        .Run(Result::FAILED, absl::nullopt, ConnectSignalSupport::NO);
     // `this` may be invalid now.
     return;
   }
diff --git a/device/fido/cable/websocket_adapter.h b/device/fido/cable/websocket_adapter.h
index 55b9270f..c9174a4 100644
--- a/device/fido/cable/websocket_adapter.h
+++ b/device/fido/cable/websocket_adapter.h
@@ -38,8 +38,18 @@
     GONE,
   };
 
-  using TunnelReadyCallback = base::OnceCallback<
-      void(Result, absl::optional<std::array<uint8_t, kRoutingIdSize>>)>;
+  // ConnectSignalSupport indicates whether the connection will send a connect
+  // signal. This is a single zero byte, sent by the tunnel server when the peer
+  // connects. This only occurs for "contact" connections.
+  enum class ConnectSignalSupport {
+    NO,
+    YES,
+  };
+
+  using TunnelReadyCallback = base::OnceCallback<void(
+      Result,
+      absl::optional<std::array<uint8_t, kRoutingIdSize>>,
+      ConnectSignalSupport)>;
   using TunnelDataCallback =
       base::RepeatingCallback<void(absl::optional<base::span<const uint8_t>>)>;
   WebSocketAdapter(
diff --git a/device/fido/features.cc b/device/fido/features.cc
index 72ea8b0..3306ae0 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -69,4 +69,9 @@
              "WebAuthenticationICloudKeychain",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Not yet enabled by default.
+BASE_FEATURE(kWebAuthnNewHybridUI,
+             "WebAuthenticationNewHybridUI",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace device
diff --git a/device/fido/features.h b/device/fido/features.h
index 7ddf24c..4497c88 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -66,6 +66,10 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 BASE_DECLARE_FEATURE(kWebAuthnICloudKeychain);
 
+// Enable new hybrid UI
+COMPONENT_EXPORT(DEVICE_FIDO)
+BASE_DECLARE_FEATURE(kWebAuthnNewHybridUI);
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FEATURES_H_
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index 0062967..3a8a7e1 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -401,6 +401,13 @@
 // includes the client's nonce and pairing ID.
 constexpr char kCableClientPayloadHeader[] = "X-caBLE-Client-Payload";
 
+// kCableSignalConnectionHeader is the name of an HTTP header that indicates to
+// the tunnel server that a client supports getting a signal when the
+// authenticator connects to the tunnel server during a state-assisted
+// transaction. The tunnel server echos this header to indicate that the signal
+// will be sent.
+constexpr char kCableSignalConnectionHeader[] = "X-caBLE-Signal-Connection";
+
 // Maximum wait time before client error outs on device.
 COMPONENT_EXPORT(DEVICE_FIDO) extern const base::TimeDelta kDeviceTimeout;
 
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index a44c494..bf25790e 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -1421,6 +1421,13 @@
   }
 }
 
+AuthenticatorType FidoDeviceAuthenticator::GetType() const {
+  if (device_->DeviceTransport() == FidoTransportProtocol::kHybrid) {
+    return AuthenticatorType::kPhone;
+  }
+  return AuthenticatorType::kOther;
+}
+
 std::string FidoDeviceAuthenticator::GetId() const {
   return device_->GetId();
 }
diff --git a/device/fido/fido_device_authenticator.h b/device/fido/fido_device_authenticator.h
index 070ef88..943e4bd 100644
--- a/device/fido/fido_device_authenticator.h
+++ b/device/fido/fido_device_authenticator.h
@@ -136,6 +136,7 @@
 
   void Reset(ResetCallback callback) override;
   void Cancel() override;
+  AuthenticatorType GetType() const override;
   std::string GetId() const override;
   std::string GetDisplayName() const override;
   ProtocolVersion SupportedProtocol() const override;
diff --git a/device/fido/fido_discovery_factory.cc b/device/fido/fido_discovery_factory.cc
index 0ac3665..78b70360 100644
--- a/device/fido/fido_discovery_factory.cc
+++ b/device/fido/fido_discovery_factory.cc
@@ -92,7 +92,8 @@
               std::move(contact_device_stream_),
               cable_data_.value_or(std::vector<CableDiscoveryData>()),
               std::move(cable_pairing_callback_),
-              std::move(cable_invalidated_pairing_callback_)));
+              std::move(cable_invalidated_pairing_callback_),
+              std::move(cable_event_callback_)));
         }
 
         ret.emplace_back(std::move(v1_discovery));
@@ -153,12 +154,17 @@
 
 void FidoDiscoveryFactory::set_cable_pairing_callback(
     base::RepeatingCallback<void(std::unique_ptr<cablev2::Pairing>)> callback) {
-  cable_pairing_callback_.emplace(std::move(callback));
+  cable_pairing_callback_ = std::move(callback);
 }
 
 void FidoDiscoveryFactory::set_cable_invalidated_pairing_callback(
     base::RepeatingCallback<void(size_t)> callback) {
-  cable_invalidated_pairing_callback_.emplace(std::move(callback));
+  cable_invalidated_pairing_callback_ = std::move(callback);
+}
+
+void FidoDiscoveryFactory::set_cable_event_callback(
+    base::RepeatingCallback<void(cablev2::Event)> callback) {
+  cable_event_callback_ = std::move(callback);
 }
 
 base::RepeatingCallback<void(size_t)>
diff --git a/device/fido/fido_discovery_factory.h b/device/fido/fido_discovery_factory.h
index 1be0ebd2..2b769e1 100644
--- a/device/fido/fido_discovery_factory.h
+++ b/device/fido/fido_discovery_factory.h
@@ -84,6 +84,11 @@
   virtual void set_cable_invalidated_pairing_callback(
       base::RepeatingCallback<void(size_t)>);
 
+  // set_cable_event_callback installs a callback which will be called with
+  // when a variety of events occur. See the definition of `cablev2::Event`.
+  virtual void set_cable_event_callback(
+      base::RepeatingCallback<void(cablev2::Event)> callback);
+
   // get_cable_contact_callback returns a callback that can be called with
   // indexes into the vector of pairings passed to |set_cable_data| in order
   // to contact the indexed device. Only a single callback is supported.
@@ -161,6 +166,8 @@
       cable_pairing_callback_;
   absl::optional<base::RepeatingCallback<void(size_t)>>
       cable_invalidated_pairing_callback_;
+  absl::optional<base::RepeatingCallback<void(cablev2::Event)>>
+      cable_event_callback_;
 #if BUILDFLAG(IS_WIN)
   raw_ptr<WinWebAuthnApi> win_webauthn_api_ = nullptr;
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 7d7221e..01e73915 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -744,6 +744,13 @@
       std::move(completion_callback_)
           .Run(GetAssertionStatus::kAuthenticatorResponseInvalid,
                absl::nullopt);
+    } else if (authenticator->GetType() == AuthenticatorType::kPhone &&
+               base::FeatureList::IsEnabled(kWebAuthnNewHybridUI)) {
+      FIDO_LOG(ERROR) << "Status " << static_cast<int>(status) << " from "
+                      << authenticator->GetDisplayName()
+                      << " is fatal to the request";
+      std::move(completion_callback_)
+          .Run(GetAssertionStatus::kHybridTransportError, absl::nullopt);
     } else {
       FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
                       << " from " << authenticator->GetDisplayName();
diff --git a/device/fido/get_assertion_request_handler.h b/device/fido/get_assertion_request_handler.h
index 7d35dae..850346b8 100644
--- a/device/fido/get_assertion_request_handler.h
+++ b/device/fido/get_assertion_request_handler.h
@@ -49,6 +49,7 @@
   // be clearer.
   kAuthenticatorMissingUserVerification,
   kWinNotAllowedError,
+  kHybridTransportError,
 };
 
 class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionRequestHandler
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index 613862c6..111df05 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -10,6 +10,7 @@
 #include "base/barrier_closure.h"
 #include "base/containers/contains.h"
 #include "base/containers/cxx20_erase.h"
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
@@ -19,6 +20,7 @@
 #include "components/cbor/diagnostic_writer.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/device_public_key_extension.h"
+#include "device/fido/features.h"
 #include "device/fido/fido_authenticator.h"
 #include "device/fido/fido_discovery_factory.h"
 #include "device/fido/fido_parsing_utils.h"
@@ -848,6 +850,14 @@
       std::move(completion_callback_)
           .Run(MakeCredentialStatus::kAuthenticatorResponseInvalid,
                absl::nullopt, authenticator);
+    } else if (authenticator->GetType() == AuthenticatorType::kPhone &&
+               base::FeatureList::IsEnabled(kWebAuthnNewHybridUI)) {
+      FIDO_LOG(ERROR) << "Status " << static_cast<int>(status) << " from "
+                      << authenticator->GetDisplayName()
+                      << " is fatal to the request";
+      std::move(completion_callback_)
+          .Run(MakeCredentialStatus::kHybridTransportError, absl::nullopt,
+               authenticator);
     } else {
       FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
                       << " from " << authenticator->GetDisplayName();
diff --git a/device/fido/make_credential_request_handler.h b/device/fido/make_credential_request_handler.h
index 35da15c7..04aaf53 100644
--- a/device/fido/make_credential_request_handler.h
+++ b/device/fido/make_credential_request_handler.h
@@ -59,6 +59,7 @@
   kStorageFull,
   kWinInvalidStateError,
   kWinNotAllowedError,
+  kHybridTransportError,
 };
 
 class COMPONENT_EXPORT(DEVICE_FIDO) MakeCredentialRequestHandler
diff --git a/extensions/browser/api/declarative_net_request/constants.cc b/extensions/browser/api/declarative_net_request/constants.cc
index 1aba661..a817ba57 100644
--- a/extensions/browser/api/declarative_net_request/constants.cc
+++ b/extensions/browser/api/declarative_net_request/constants.cc
@@ -145,6 +145,8 @@
     "Extensions.DeclarativeNetRequest.ReadDynamicRulesJSONStatus";
 const char kIsLargeRegexHistogram[] =
     "Extensions.DeclarativeNetRequest.IsLargeRegexRule";
+const char kRegexRuleSizeHistogram[] =
+    "Extensions.DeclarativeNetRequest.RegexRuleSize";
 const char kLoadRulesetResultHistogram[] =
     "Extensions.DeclarativeNetRequest.LoadRulesetResult";
 
diff --git a/extensions/browser/api/declarative_net_request/constants.h b/extensions/browser/api/declarative_net_request/constants.h
index 85bf1fa..27ca6426 100644
--- a/extensions/browser/api/declarative_net_request/constants.h
+++ b/extensions/browser/api/declarative_net_request/constants.h
@@ -228,6 +228,7 @@
 extern const char kUpdateDynamicRulesStatusHistogram[];
 extern const char kReadDynamicRulesJSONStatusHistogram[];
 extern const char kIsLargeRegexHistogram[];
+extern const char kRegexRuleSizeHistogram[];
 extern const char kLoadRulesetResultHistogram[];
 
 // Placeholder text to use for getBadgeText extension function call, when the
diff --git a/extensions/browser/api/declarative_net_request/indexed_rule.cc b/extensions/browser/api/declarative_net_request/indexed_rule.cc
index 91e0f36..9cb8495 100644
--- a/extensions/browser/api/declarative_net_request/indexed_rule.cc
+++ b/extensions/browser/api/declarative_net_request/indexed_rule.cc
@@ -36,6 +36,7 @@
 constexpr char kAnchorCharacter = '|';
 constexpr char kSeparatorCharacter = '^';
 constexpr char kWildcardCharacter = '*';
+constexpr int kLargeRegexUMALimit = 1024 * 100;
 
 // Returns true if bitmask |sub| is a subset of |super|.
 constexpr bool IsSubset(unsigned sub, unsigned super) {
@@ -416,6 +417,28 @@
   UMA_HISTOGRAM_BOOLEAN(kIsLargeRegexHistogram, is_large_regex);
 }
 
+void RecordRegexRuleSizeUMA(int program_size) {
+  // Max reported size at 100KB.
+  UMA_HISTOGRAM_COUNTS_100000(kRegexRuleSizeHistogram, program_size);
+}
+
+void RecordRuleSizeForLargeRegex(const std::string& regex_string,
+                                 bool is_case_sensitive,
+                                 bool require_capturing) {
+  re2::RE2::Options large_regex_options =
+      CreateRE2Options(is_case_sensitive, require_capturing);
+
+  // Record the size of regex rules that exceed the 2Kb limit, with any rules
+  // exceeding 100Kb recorded as 100Kb. Note that these rules are not enabled.
+  large_regex_options.set_max_mem(kLargeRegexUMALimit);
+  re2::RE2 regex(regex_string, large_regex_options);
+  if (regex.error_code() == re2::RE2::ErrorPatternTooLarge) {
+    RecordRegexRuleSizeUMA(kLargeRegexUMALimit);
+  } else if (regex.ok()) {
+    RecordRegexRuleSizeUMA(regex.ProgramSize());
+  }
+}
+
 ParseResult ValidateHeaders(
     const std::vector<dnr_api::ModifyHeaderInfo>& headers,
     bool are_request_headers) {
@@ -556,6 +579,10 @@
 
     if (regex.error_code() == re2::RE2::ErrorPatternTooLarge) {
       RecordLargeRegexUMA(true);
+      RecordRuleSizeForLargeRegex(*parsed_rule.condition.regex_filter,
+                                  IsCaseSensitive(parsed_rule),
+                                  require_capturing);
+
       return ParseResult::ERROR_REGEX_TOO_LARGE;
     }
 
@@ -568,6 +595,7 @@
       return ParseResult::ERROR_INVALID_REGEX_SUBSTITUTION;
     }
 
+    RecordRegexRuleSizeUMA(regex.ProgramSize());
     RecordLargeRegexUMA(false);
   }
 
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index c528949..13deee8 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -210,6 +210,7 @@
   sources = [ "gpu_channel.mojom" ]
 
   public_deps = [
+    ":surface_handle",
     "//mojo/public/mojom/base",
     "//services/viz/public/mojom:resource_format",
     "//services/viz/public/mojom:shared_image_format",
@@ -291,6 +292,39 @@
   cpp_configs = [ "//gpu:gpu_implementation" ]
 }
 
+mojom("gmb_interface") {
+  generate_java = true
+  visibility = [
+    "//gpu/*",
+    "//services/*",
+  ]
+  sources = [ "client_gmb_interface.mojom" ]
+  deps = [
+    ":surface_handle",
+    "//mojo/public/mojom/base",
+    "//ui/gfx/geometry/mojom",
+    "//ui/gfx/mojom",
+  ]
+}
+
+mojom("surface_handle") {
+  generate_java = true
+  sources = [ "surface_handle.mojom" ]
+  cpp_typemaps = [
+    {
+      types = [
+        {
+          mojom = "gpu.mojom.SurfaceHandle"
+          cpp = "::gpu::SurfaceHandle"
+          copyable_pass_by_value = true
+        },
+      ]
+      traits_headers = [ "surface_handle_mojom_traits.h" ]
+      traits_public_deps = [ "//ui/gfx:native_widget_types" ]
+    },
+  ]
+}
+
 mojom("interfaces") {
   generate_java = true
   sources = [
@@ -305,7 +339,6 @@
     "mailbox.mojom",
     "mailbox_holder.mojom",
     "memory_stats.mojom",
-    "surface_handle.mojom",
     "sync_token.mojom",
     "vulkan_ycbcr_info.mojom",
   ]
@@ -316,6 +349,7 @@
 
   public_deps = [
     ":gpu_preferences_interface",
+    ":surface_handle",
     "//mojo/public/mojom/base",
     "//ui/gfx/geometry/mojom",
     "//ui/gfx/mojom",
@@ -499,17 +533,6 @@
     {
       types = [
         {
-          mojom = "gpu.mojom.SurfaceHandle"
-          cpp = "::gpu::SurfaceHandle"
-          copyable_pass_by_value = true
-        },
-      ]
-      traits_headers = [ "surface_handle_mojom_traits.h" ]
-      traits_public_deps = [ "//ui/gfx:native_widget_types" ]
-    },
-    {
-      types = [
-        {
           mojom = "gpu.mojom.VulkanYCbCrInfo"
           cpp = "::gpu::VulkanYCbCrInfo"
           copyable_pass_by_value = true
diff --git a/gpu/ipc/common/client_gmb_interface.mojom b/gpu/ipc/common/client_gmb_interface.mojom
new file mode 100644
index 0000000..47afb8b6
--- /dev/null
+++ b/gpu/ipc/common/client_gmb_interface.mojom
@@ -0,0 +1,35 @@
+// 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 gpu.mojom;
+
+import "ui/gfx/mojom/buffer_types.mojom";
+import "ui/gfx/geometry/mojom/geometry.mojom";
+import "gpu/ipc/common/surface_handle.mojom";
+import "mojo/public/mojom/base/shared_memory.mojom";
+
+// Interface used by clients in the renderer process to create
+// GpuMemoryBuffers. This interface is only enabled and used when the feature
+// UseClientGmbInterface is enabled.
+interface ClientGmbInterface {
+
+  // Create a new GMB and return the GMB handle.
+  CreateGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id,
+                        gfx.mojom.Size size,
+                        gfx.mojom.BufferFormat format,
+                        gfx.mojom.BufferUsage usage,
+                        gpu.mojom.SurfaceHandle surface_handle)
+      => (gfx.mojom.GpuMemoryBufferHandle buffer_handle);
+
+  // Destroys the GMB.
+  DestroyGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id);
+
+  // Copies GMB pixel data to |shared_memory|.
+  // Returns |true| if the copy has succeeded.
+  CopyGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferHandle buffer_handle,
+                     mojo_base.mojom.UnsafeSharedMemoryRegion shared_memory)
+                     => (bool success);
+
+};
+
diff --git a/gpu/ipc/common/gpu_memory_buffer_support.cc b/gpu/ipc/common/gpu_memory_buffer_support.cc
index 16336eb..cb35f4d6 100644
--- a/gpu/ipc/common/gpu_memory_buffer_support.cc
+++ b/gpu/ipc/common/gpu_memory_buffer_support.cc
@@ -8,6 +8,7 @@
 
 #include "base/check_op.h"
 #include "base/notreached.h"
+#include "base/numerics/checked_math.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "build/build_config.h"
@@ -221,6 +222,13 @@
   return false;
 }
 
+// static
+bool GpuMemoryBufferSupport::IsSizeValid(const gfx::Size& size) {
+  base::CheckedNumeric<int> bytes = size.width();
+  bytes *= size.height();
+  return bytes.IsValid();
+}
+
 std::unique_ptr<GpuMemoryBufferImpl>
 GpuMemoryBufferSupport::CreateGpuMemoryBufferImplFromHandle(
     gfx::GpuMemoryBufferHandle handle,
diff --git a/gpu/ipc/common/gpu_memory_buffer_support.h b/gpu/ipc/common/gpu_memory_buffer_support.h
index 4cfd6f62..da7ee49 100644
--- a/gpu/ipc/common/gpu_memory_buffer_support.h
+++ b/gpu/ipc/common/gpu_memory_buffer_support.h
@@ -69,6 +69,8 @@
   static GpuMemoryBufferConfigurationSet
   GetNativeGpuMemoryBufferConfigurations();
 
+  static bool IsSizeValid(const gfx::Size& size);
+
 #if BUILDFLAG(IS_OZONE)
   gfx::ClientNativePixmapFactory* client_native_pixmap_factory() {
     return client_native_pixmap_factory_.get();
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index c1b21a2..e81e69e 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -58552,7 +58552,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58818,7 +58818,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58900,7 +58900,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58982,7 +58982,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -59072,7 +59072,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -59442,7 +59442,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -59532,7 +59532,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -59712,7 +59712,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -59802,7 +59802,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60252,7 +60252,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60342,7 +60342,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60432,7 +60432,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60522,7 +60522,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60612,7 +60612,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60702,7 +60702,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60882,7 +60882,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -60972,7 +60972,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -61062,7 +61062,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -61605,7 +61605,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -86432,7 +86432,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
diff --git a/infra/config/lib/linux-default.json b/infra/config/lib/linux-default.json
index 99921aa..587e20d3 100644
--- a/infra/config/lib/linux-default.json
+++ b/infra/config/lib/linux-default.json
@@ -119,10 +119,29 @@
     "android-cronet-x86-dbg-oreo-tests": "Ubuntu-22.04",
     "android-cronet-x86-dbg-pie-tests": "Ubuntu-22.04",
     "android-cronet-x86-rel": "Ubuntu-22.04",
+    "android-cronet-x86-rel-kitkat-tests": "Ubuntu-22.04",
+    "android-deterministic-dbg": "Ubuntu-22.04",
+    "android-deterministic-rel": "Ubuntu-22.04",
+    "android-fieldtrial-rel": "Ubuntu-22.04",
+    "android-inverse-fieldtrials-pie-x86-fyi-rel": "Ubuntu-22.04",
     "android-nougat-x86-rel": "Ubuntu-22.04",
+    "android-oreo-arm64-dbg": "Ubuntu-22.04",
+    "android-perfetto-rel": "Ubuntu-22.04",
+    "android-pie-arm64-wpt-rel-non-cq": "Ubuntu-22.04",
+    "android-pie-x86-rel": "Ubuntu-22.04",
+    "android-webview-12-x64-dbg": "Ubuntu-22.04",
+    "android-webview-13-x64-dbg": "Ubuntu-22.04",
+    "android-webview-nougat-arm64-dbg": "Ubuntu-22.04",
+    "android-webview-oreo-arm64-dbg": "Ubuntu-22.04",
+    "android-webview-pie-arm64-dbg": "Ubuntu-22.04",
+    "android-webview-pie-x86-wpt-fyi-rel": "Ubuntu-22.04",
     "android-x64-cast": "Ubuntu-22.04",
+    "android_archive_rel_ng": "Ubuntu-22.04",
+    "android_arm64_dbg_recipe": "Ubuntu-22.04",
+    "android_blink_rel": "Ubuntu-22.04",
     "android_compile_dbg": "Ubuntu-22.04",
     "android_cronet": "Ubuntu-22.04",
+    "android_unswarmed_pixel_aosp": "Ubuntu-22.04",
     "chromeos-amd64-generic-rel": "Ubuntu-22.04",
     "chromeos-amd64-generic-rel-compilator": "Ubuntu-22.04",
     "chromeos-arm-generic-rel": "Ubuntu-22.04",
@@ -198,6 +217,7 @@
     "tricium-metrics-analysis": "Ubuntu-22.04",
     "tricium-oilpan-analysis": "Ubuntu-22.04",
     "tricium-simple": "Ubuntu-22.04",
+    "try-nougat-phone-tester": "Ubuntu-22.04",
     "win-rel": "Ubuntu-22.04"
   }
 }
diff --git a/ios/build/bots/scripts/run.py b/ios/build/bots/scripts/run.py
index f7e3a965..a1c0c5e 100755
--- a/ios/build/bots/scripts/run.py
+++ b/ios/build/bots/scripts/run.py
@@ -21,6 +21,7 @@
 import json
 import logging
 import os
+import shutil
 import subprocess
 import sys
 import traceback
@@ -58,6 +59,9 @@
     self.args = argparse.Namespace()
     self.test_args = []
     self.should_move_xcode_runtime_to_cache = True
+    # Xcode might be corruped, so this the flag to decide
+    # whether we should clear it from cache
+    self.should_delete_xcode_cache = False
 
     if args:
       self.parse_args(args)
@@ -331,6 +335,9 @@
       summary['step_text'] = '%s%s' % (e.__class__.__name__,
                                        ': %s' % e.args[0] if e.args else '')
       return 2
+    except test_runner.MIGServerDiedError as e:
+      self.should_delete_xcode_cache = True
+      return 2
     except test_runner.TestRunnerError as e:
       sys.stderr.write(traceback.format_exc())
       summary['step_text'] = '%s%s' % (e.__class__.__name__,
@@ -388,6 +395,9 @@
               self.args.version)
           iossim_util.delete_simulator_runtime_and_wait(self.args.version)
 
+      if self.should_delete_xcode_cache:
+        shutil.rmtree(self.args.xcode_path)
+
       test_runner.defaults_delete('com.apple.CoreSimulator',
                                   'FramebufferServerRendererPolicy')
 
diff --git a/ios/build/bots/scripts/run_test.py b/ios/build/bots/scripts/run_test.py
index 67e093e..6d1d72c 100755
--- a/ios/build/bots/scripts/run_test.py
+++ b/ios/build/bots/scripts/run_test.py
@@ -10,7 +10,7 @@
 import unittest
 
 import run
-from test_runner import HostIsDownError, SimulatorNotFoundError
+from test_runner import HostIsDownError, MIGServerDiedError, SimulatorNotFoundError
 import test_runner_test
 
 
@@ -686,6 +686,7 @@
     self.assertEqual(1, mock_construct_runtime_cache_folder.call_count)
     self.assertEqual(0, mock_move_runtime.call_count)
     self.assertFalse(self.runner.should_move_xcode_runtime_to_cache)
+    self.assertFalse(self.runner.should_delete_xcode_cache)
     mock_remove_runtimes.assert_called_with('test/xcode/path')
 
   @mock.patch('test_runner.defaults_delete')
@@ -696,6 +697,39 @@
   @mock.patch('xcode_util.construct_runtime_cache_folder', autospec=True)
   @mock.patch('xcode_util.install', autospec=True, return_value=False)
   @mock.patch('xcode_util.move_runtime', autospec=True)
+  @mock.patch('shutil.rmtree', autospec=True)
+  @mock.patch('mac_util.is_macos_13_or_higher', autospec=True)
+  def test_error_mig_server_died(self, mock_macos_13_or_higher,
+                                 mock_shutil_rmtree, mock_move_runtime,
+                                 mock_install,
+                                 mock_construct_runtime_cache_folder, mock_tr,
+                                 _1, _2, _3, _4):
+    mock_macos_13_or_higher.return_value = False
+    mock_construct_runtime_cache_folder.side_effect = lambda a, b: a + b
+    mock_tr.side_effect = MIGServerDiedError
+
+    with mock.patch('run.open', mock.mock_open()):
+      self.runner.run(None)
+
+    mock_install.assert_called_with(
+        'mac_toolchain',
+        'testXcodeVersion',
+        'test/xcode/path',
+        runtime_cache_folder='test/runtime-ios-14.4',
+        ios_version='14.4')
+    self.assertEqual(2, mock_construct_runtime_cache_folder.call_count)
+    self.assertEqual(1, mock_move_runtime.call_count)
+    self.assertTrue(self.runner.should_delete_xcode_cache)
+    mock_shutil_rmtree.assert_called_with('test/xcode/path')
+
+  @mock.patch('test_runner.defaults_delete')
+  @mock.patch('json.dump')
+  @mock.patch('xcode_util.select', autospec=True)
+  @mock.patch('os.path.exists', autospec=True, return_value=True)
+  @mock.patch('xcodebuild_runner.SimulatorParallelTestRunner')
+  @mock.patch('xcode_util.construct_runtime_cache_folder', autospec=True)
+  @mock.patch('xcode_util.install', autospec=True, return_value=False)
+  @mock.patch('xcode_util.move_runtime', autospec=True)
   def test_device_task(self, mock_move_runtime, mock_install,
                        mock_construct_runtime_cache_folder, mock_tr, _1, _2, _3,
                        _4):
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index e660c080..7bbf09d 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -32,6 +32,7 @@
 DERIVED_DATA = os.path.expanduser('~/Library/Developer/Xcode/DerivedData')
 DEFAULT_TEST_REPO = 'https://chromium.googlesource.com/chromium/src'
 HOST_IS_DOWN_ERROR = 'Domain=NSPOSIXErrorDomain Code=64 "Host is down"'
+MIG_SERVER_DIED_ERROR = '(ipc/mig) server died'
 
 
 # TODO(crbug.com/1077277): Move commonly used error classes to
@@ -143,11 +144,18 @@
 
 class HostIsDownError(TestRunnerError):
   """Simulator host is down, usually due to a corrupted runtime."""
-
   def __init__(self):
     super(HostIsDownError, self).__init__('Simulator host is down!')
 
 
+class MIGServerDiedError(TestRunnerError):
+  """(ipc/mig) server died error, causing simulator unable to start"""
+
+  def __init__(self):
+    super(MIGServerDiedError,
+          self).__init__('iOS runtime embedded in Xcode might be corrupted.')
+
+
 def get_device_ios_version(udid):
   """Gets device iOS version.
 
@@ -281,6 +289,11 @@
     if HOST_IS_DOWN_ERROR in line:
       raise HostIsDownError()
 
+    # crbug/1449927: Mitigation to exit earlier to clear Xcode cache
+    # in order to self-recover on the next run.
+    if MIG_SERVER_DIED_ERROR in line:
+      raise MIGServerDiedError()
+
   if parser:
     parser.Finalize()
   LOGGER.debug('Finished print_process_output.')
diff --git a/ios/chrome/test/wpt/tools/.vpython3 b/ios/chrome/test/wpt/tools/.vpython3
index ed9998a..a47181c0 100644
--- a/ios/chrome/test/wpt/tools/.vpython3
+++ b/ios/chrome/test/wpt/tools/.vpython3
@@ -18,8 +18,8 @@
 
 # requests and its transitive dependencies
 wheel: <
-  name: "infra/python/wheels/requests-py2_py3"
-  version: "version:2.26.0"
+  name: "infra/python/wheels/requests-py3"
+  version: "version:2.31.0"
 >
 
 wheel: <
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 5b427f1..0ec4cfb 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -336,12 +336,12 @@
              "PlatformHEVCDecoderSupport",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
 // Enables HEVC hardware accelerated encoding for Windows and Mac.
 BASE_FEATURE(kPlatformHEVCEncoderSupport,
              "PlatformHEVCEncoderSupport",
              base::FEATURE_DISABLED_BY_DEFAULT);
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
 
 // Only decode preload=metadata elements upon visibility.
@@ -530,6 +530,9 @@
 BASE_FEATURE(kIgnoreUiGains,
              "IgnoreUiGains",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kShowForceRespectUiGainsToggle,
+             "ShowForceRespectUiGainsToggle",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
 // Make MSE garbage collection algorithm more aggressive when we are under
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index 4e55bdf..eb2e947 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -197,6 +197,7 @@
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kCrOSDspBasedNsAllowed);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kCrOSDspBasedAgcAllowed);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kIgnoreUiGains);
+MEDIA_EXPORT BASE_DECLARE_FEATURE(kShowForceRespectUiGainsToggle);
 #endif
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kD3D11VideoDecoderUseSharedHandle);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kDedicatedMediaServiceThread);
@@ -269,9 +270,9 @@
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kEnableRtcpReporting);
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC)
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kPlatformHEVCDecoderSupport);
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kPlatformHEVCEncoderSupport);
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC)
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kPlaybackSpeedButton);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kPreloadMediaEngagementData);
diff --git a/media/gpu/v4l2/test/video_decoder.h b/media/gpu/v4l2/test/video_decoder.h
index 8a0e9b61..79b1797 100644
--- a/media/gpu/v4l2/test/video_decoder.h
+++ b/media/gpu/v4l2/test/video_decoder.h
@@ -23,6 +23,10 @@
 uint32_t FileFourccToDriverFourcc(uint32_t header_fourcc);
 
 // VideoDecoder decodes encoded video streams using v4l2 ioctl calls.
+// To implement a decoder, implement the following:
+// 1. A factory function, such as:
+//   std::unique_ptr<VideoDecoder> Create(const base::MemoryMappedFile& stream)
+// 2. DecodeNextFrame
 class VideoDecoder {
  public:
   // Result of decoding the current frame.
@@ -45,6 +49,9 @@
   void CreateOUTPUTQueue(uint32_t compressed_fourcc);
   void CreateCAPTUREQueue(uint32_t num_buffers);
 
+  // Decoders implement this. The function writes the next displayed picture
+  // into the output plane buffers |y_plane|, |u_plane|, and |v_plane|. |size|
+  // is the visible picture size.
   virtual Result DecodeNextFrame(std::vector<uint8_t>& y_plane,
                                  std::vector<uint8_t>& u_plane,
                                  std::vector<uint8_t>& v_plane,
diff --git a/media/media_options.gni b/media/media_options.gni
index 58c3ca1..e45fe3b 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -112,7 +112,7 @@
   # Windows, Mac, and Android.
   enable_hevc_parser_and_hw_decoder =
       proprietary_codecs &&
-      (use_fuzzing_engine || is_win || is_mac || is_android || is_linux)
+      (use_fuzzing_engine || is_win || is_apple || is_android || is_linux)
 
   # Enable inclusion of VVC/H.266 parser/demuxer, and also enable VVC/H.266 decoding
   # with hardware acceleration provided by platform. Disabled by default for all builds.
@@ -141,7 +141,7 @@
 declare_args() {
   platform_has_optional_hevc_support =
       enable_platform_hevc &&
-      (is_win || is_chromeos || is_linux || is_mac || is_android)
+      (is_win || is_chromeos || is_linux || is_apple || is_android)
 }
 
 assert(!enable_platform_ac3_eac3_audio || proprietary_codecs,
diff --git a/net/cert/pki/string_util.h b/net/cert/pki/string_util.h
index b18f716..73404086 100644
--- a/net/cert/pki/string_util.h
+++ b/net/cert/pki/string_util.h
@@ -9,6 +9,7 @@
 
 #include <stdint.h>
 
+#include <cstdint>
 #include <string_view>
 #include <vector>
 
diff --git a/pdf/pdfium/pdfium_document.cc b/pdf/pdfium/pdfium_document.cc
index 5b61e721..83fefbb 100644
--- a/pdf/pdfium/pdfium_document.cc
+++ b/pdf/pdfium/pdfium_document.cc
@@ -12,9 +12,7 @@
 
 namespace chrome_pdf {
 
-namespace {
-
-class FileAvail : public FX_FILEAVAIL {
+class PDFiumDocument::FileAvail : public FX_FILEAVAIL {
  public:
   explicit FileAvail(DocumentLoader* doc_loader) : doc_loader_(doc_loader) {
     DCHECK(doc_loader);
@@ -31,10 +29,10 @@
     return file_avail->doc_loader_->IsDataAvailable(offset, size);
   }
 
-  raw_ptr<DocumentLoader, LeakedDanglingUntriaged> doc_loader_;
+  raw_ptr<DocumentLoader> doc_loader_;
 };
 
-class DownloadHints : public FX_DOWNLOADHINTS {
+class PDFiumDocument::DownloadHints : public FX_DOWNLOADHINTS {
  public:
   explicit DownloadHints(DocumentLoader* doc_loader) : doc_loader_(doc_loader) {
     DCHECK(doc_loader);
@@ -51,10 +49,10 @@
     return download_hints->doc_loader_->RequestData(offset, size);
   }
 
-  raw_ptr<DocumentLoader, LeakedDanglingUntriaged> doc_loader_;
+  raw_ptr<DocumentLoader> doc_loader_;
 };
 
-class FileAccess : public FPDF_FILEACCESS {
+class PDFiumDocument::FileAccess : public FPDF_FILEACCESS {
  public:
   explicit FileAccess(DocumentLoader* doc_loader) : doc_loader_(doc_loader) {
     DCHECK(doc_loader);
@@ -73,11 +71,9 @@
     return file_access->doc_loader_->GetBlock(position, size, buffer);
   }
 
-  raw_ptr<DocumentLoader, LeakedDanglingUntriaged> doc_loader_;
+  raw_ptr<DocumentLoader> doc_loader_;
 };
 
-}  // namespace
-
 PDFiumDocument::PDFiumDocument(DocumentLoader* doc_loader)
     : doc_loader_(doc_loader),
       file_access_(std::make_unique<FileAccess>(doc_loader)),
@@ -86,6 +82,18 @@
 
 PDFiumDocument::~PDFiumDocument() = default;
 
+FPDF_FILEACCESS& PDFiumDocument::file_access() {
+  return *file_access_;
+}
+
+FX_FILEAVAIL& PDFiumDocument::file_availability() {
+  return *file_availability_;
+}
+
+FX_DOWNLOADHINTS& PDFiumDocument::download_hints() {
+  return *download_hints_;
+}
+
 void PDFiumDocument::CreateFPDFAvailability() {
   fpdf_availability_.reset(
       FPDFAvail_Create(file_availability_.get(), file_access_.get()));
diff --git a/pdf/pdfium/pdfium_document.h b/pdf/pdfium/pdfium_document.h
index 65af618..59034b1 100644
--- a/pdf/pdfium/pdfium_document.h
+++ b/pdf/pdfium/pdfium_document.h
@@ -19,14 +19,18 @@
 
 class PDFiumDocument {
  public:
+  class DownloadHints;
+  class FileAccess;
+  class FileAvail;
+
   explicit PDFiumDocument(DocumentLoader* doc_loader);
   PDFiumDocument(const PDFiumDocument&) = delete;
   PDFiumDocument& operator=(const PDFiumDocument&) = delete;
   ~PDFiumDocument();
 
-  FPDF_FILEACCESS& file_access() { return *file_access_; }
-  FX_FILEAVAIL& file_availability() { return *file_availability_; }
-  FX_DOWNLOADHINTS& download_hints() { return *download_hints_; }
+  FPDF_FILEACCESS& file_access();
+  FX_FILEAVAIL& file_availability();
+  FX_DOWNLOADHINTS& download_hints();
 
   FPDF_AVAIL fpdf_availability() const { return fpdf_availability_.get(); }
   FPDF_DOCUMENT doc() const { return doc_handle_.get(); }
@@ -46,13 +50,13 @@
   const raw_ptr<DocumentLoader> doc_loader_;
 
   // Interface structure to provide access to document stream.
-  std::unique_ptr<FPDF_FILEACCESS> file_access_;
+  std::unique_ptr<FileAccess> file_access_;
 
   // Interface structure to check data availability in the document stream.
-  std::unique_ptr<FX_FILEAVAIL> file_availability_;
+  std::unique_ptr<FileAvail> file_availability_;
 
   // Interface structure to request data chunks from the document stream.
-  std::unique_ptr<FX_DOWNLOADHINTS> download_hints_;
+  std::unique_ptr<DownloadHints> download_hints_;
 
   // Pointer to the document availability interface.
   ScopedFPDFAvail fpdf_availability_;
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index d45e62e..0892e875 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -238,6 +238,10 @@
     deps += [ "//ui/linux:linux_ui" ]
   }
 
+  if (is_apple) {
+    configs += [ "//build/config/compiler:enable_arc" ]
+  }
+
   if (is_mac) {
     sources += [
       "print_settings_initializer_mac.cc",
diff --git a/printing/printing_context_mac.h b/printing/printing_context_mac.h
index 59c6989..3f6289a 100644
--- a/printing/printing_context_mac.h
+++ b/printing/printing_context_mac.h
@@ -6,15 +6,19 @@
 #define PRINTING_PRINTING_CONTEXT_MAC_H_
 
 #include <ApplicationServices/ApplicationServices.h>
+
 #include <string>
 
-#include "base/mac/scoped_nsobject.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/strings/string_piece.h"
 #include "printing/mojom/print.mojom.h"
 #include "printing/print_job_constants.h"
 #include "printing/printing_context.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @class NSPrintInfo;
 
 namespace printing {
@@ -107,13 +111,13 @@
   mojom::ResultCode PageDone();
 
   // The native print info object.
-  base::scoped_nsobject<NSPrintInfo> print_info_;
+  NSPrintInfo* __strong print_info_;
 
   // The current page's context; only valid between NewPage and PageDone call
   // pairs.
   // This field is not a raw_ptr<> because it was filtered by the rewriter
   // for: #addr-of
-  RAW_PTR_EXCLUSION CGContext* context_;
+  RAW_PTR_EXCLUSION CGContextRef context_ = nullptr;
 };
 
 }  // namespace printing
diff --git a/printing/printing_context_mac.mm b/printing/printing_context_mac.mm
index 22df5f2..2748468 100644
--- a/printing/printing_context_mac.mm
+++ b/printing/printing_context_mac.mm
@@ -5,13 +5,16 @@
 #include "printing/printing_context_mac.h"
 
 #import <AppKit/AppKit.h>
+#include <CoreFoundation/CoreFoundation.h>
 #import <QuartzCore/QuartzCore.h>
 #include <cups/cups.h>
 
 #import <iomanip>
 #import <numeric>
 
+#include "base/apple/bridging.h"
 #include "base/check.h"
+#include "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/sys_string_conversions.h"
@@ -26,11 +29,15 @@
 #include "printing/printing_features.h"
 #include "printing/units.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace printing {
 
 namespace {
 
-const int kMaxPaperSizeDiffereceInPoints = 2;
+const int kMaxPaperSizeDifferenceInPoints = 2;
 
 // Return true if PPD name of paper is equal.
 bool IsPaperNameEqual(CFStringRef name1, const PMPaper& paper2) {
@@ -46,9 +53,10 @@
                    double height) {
   double best_match = std::numeric_limits<double>::max();
   PMPaper best_matching_paper = nullptr;
-  int num_papers = CFArrayGetCount(paper_list);
-  for (int i = 0; i < num_papers; ++i) {
-    PMPaper paper = (PMPaper)((NSArray*)paper_list)[i];
+
+  CFIndex num_papers = CFArrayGetCount(paper_list);
+  for (CFIndex i = 0; i < num_papers; ++i) {
+    PMPaper paper = (PMPaper)CFArrayGetValueAtIndex(paper_list, i);
     double paper_width = 0.0;
     double paper_height = 0.0;
     PMPaperGetWidth(paper, &paper_width);
@@ -57,8 +65,9 @@
         std::max(fabs(width - paper_width), fabs(height - paper_height));
 
     // Ignore papers with size too different from expected.
-    if (difference > kMaxPaperSizeDiffereceInPoints)
+    if (difference > kMaxPaperSizeDifferenceInPoints) {
       continue;
+    }
 
     if (name && IsPaperNameEqual(name, paper))
       return paper;
@@ -87,8 +96,7 @@
 
 PrintingContextMac::PrintingContextMac(Delegate* delegate)
     : PrintingContext(delegate),
-      print_info_([[NSPrintInfo sharedPrintInfo] copy]),
-      context_(nullptr) {}
+      print_info_([NSPrintInfo.sharedPrintInfo copy]) {}
 
 PrintingContextMac::~PrintingContextMac() {
   ReleaseContext();
@@ -101,7 +109,7 @@
   // Exceptions can also happen when the NSPrintPanel is being
   // deallocated, so it must be autoreleased within this scope.
   @autoreleasepool {
-    DCHECK([NSThread isMainThread]);
+    DCHECK(NSThread.isMainThread);
 
     // We deliberately don't feed max_pages into the dialog, because setting
     // NSPrintLastPage makes the print dialog pre-select the option to only
@@ -111,23 +119,19 @@
     // adding a new custom view to the panel on 10.5; 10.6 has
     // NSPrintPanelShowsPrintSelection).
     NSPrintPanel* panel = [NSPrintPanel printPanel];
-    NSPrintInfo* print_info = print_info_.get();
-
-    NSPrintPanelOptions options = [panel options];
-    options |= NSPrintPanelShowsPaperSize;
-    options |= NSPrintPanelShowsOrientation;
-    options |= NSPrintPanelShowsScaling;
-    [panel setOptions:options];
+    panel.options |= NSPrintPanelShowsPaperSize | NSPrintPanelShowsOrientation |
+                     NSPrintPanelShowsScaling;
 
     // Set the print job title text.
     gfx::NativeView parent_view = delegate_->GetParentView();
     if (parent_view) {
-      NSString* job_title = [[parent_view.GetNativeNSView() window] title];
+      NSString* job_title = parent_view.GetNativeNSView().window.title;
       if (job_title) {
         PMPrintSettings print_settings =
-            (PMPrintSettings)[print_info PMPrintSettings];
-        PMPrintSettingsSetJobName(print_settings, (CFStringRef)job_title);
-        [print_info updateFromPMPrintSettings];
+            static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
+        PMPrintSettingsSetJobName(print_settings,
+                                  base::apple::NSToCFPtrCast(job_title));
+        [print_info_ updateFromPMPrintSettings];
       }
     }
 
@@ -140,9 +144,9 @@
     // after the current transaction. See https://crbug.com/849538.
     __block auto block_callback = std::move(callback);
     [CATransaction setCompletionBlock:^{
-      NSInteger selection = [panel runModalWithPrintInfo:print_info];
+      NSInteger selection = [panel runModalWithPrintInfo:print_info_];
       if (selection == NSModalResponseOK) {
-        print_info_.reset([[panel printInfo] retain]);
+        print_info_ = [panel printInfo];
         settings_->set_ranges(GetPageRangesFromPrintInfo());
         InitPrintSettingsFromPrintInfo();
         std::move(block_callback).Run(mojom::ResultCode::kSuccess);
@@ -156,11 +160,11 @@
 gfx::Size PrintingContextMac::GetPdfPaperSizeDeviceUnits() {
   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
   // with a clean slate.
-  print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
+  print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
   UpdatePageFormatWithPaperInfo();
 
   PMPageFormat page_format =
-      static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+      static_cast<PMPageFormat>([print_info_ PMPageFormat]);
   PMRect paper_rect;
   PMGetAdjustedPaperRect(page_format, &paper_rect);
 
@@ -174,7 +178,7 @@
 mojom::ResultCode PrintingContextMac::UseDefaultSettings() {
   DCHECK(!in_print_job_);
 
-  print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
+  print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
   settings_->set_ranges(GetPageRangesFromPrintInfo());
   InitPrintSettingsFromPrintInfo();
 
@@ -188,7 +192,7 @@
 
   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
   // with a clean slate.
-  print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
+  print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
 
   if (printer_settings.external_preview) {
     if (!SetPrintPreviewJob())
@@ -210,7 +214,7 @@
     return OnError();
   }
 
-  [print_info_.get() updateFromPMPrintSettings];
+  [print_info_ updateFromPMPrintSettings];
 
   InitPrintSettingsFromPrintInfo();
   return mojom::ResultCode::kSuccess;
@@ -218,9 +222,9 @@
 
 bool PrintingContextMac::SetPrintPreviewJob() {
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
   return PMSessionSetDestination(print_session, print_settings,
                                  kPMDestinationPreview, nullptr,
                                  nullptr) == noErr;
@@ -228,9 +232,9 @@
 
 void PrintingContextMac::InitPrintSettingsFromPrintInfo() {
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   PMPageFormat page_format =
-      static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+      static_cast<PMPageFormat>([print_info_ PMPageFormat]);
   PMPrinter printer;
   PMSessionGetCurrentPrinter(print_session, &printer);
   PrintSettingsInitializerMac::InitPrintSettings(printer, page_format,
@@ -238,9 +242,9 @@
 }
 
 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
-  DCHECK(print_info_.get());
+  DCHECK(print_info_);
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
 
   PMPrinter current_printer;
   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
@@ -271,10 +275,10 @@
 
 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
 
   PMPageFormat default_page_format =
-      static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+      static_cast<PMPageFormat>([print_info_ PMPageFormat]);
 
   PMPrinter current_printer = nullptr;
   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
@@ -300,10 +304,10 @@
     PMPaperGetMargins(default_paper, &margins);
     paper_name.reset(tmp_paper_name, base::scoped_policy::RETAIN);
   } else {
-    const double kMutiplier =
+    const double kMultiplier =
         kPointsPerInch / static_cast<float>(kMicronsPerInch);
-    page_width = media.size_microns.width() * kMutiplier;
-    page_height = media.size_microns.height() * kMutiplier;
+    page_width = media.size_microns.width() * kMultiplier;
+    page_height = media.size_microns.height() * kMultiplier;
     paper_name.reset(base::SysUTF8ToCFStringRef(media.vendor_id));
   }
 
@@ -339,7 +343,7 @@
     return false;
   // Copy over the original format with the new page format.
   bool result = (PMCopyPageFormat(new_format, page_format) == noErr);
-  [print_info_.get() updateFromPMPageFormat];
+  [print_info_ updateFromPMPageFormat];
   PMRelease(new_format);
   return result;
 }
@@ -349,19 +353,19 @@
     return false;
 
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
   return PMSetCopies(print_settings, copies, false) == noErr;
 }
 
 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
   return PMSetCollate(print_settings, collate) == noErr;
 }
 
 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
   PMPageFormat page_format =
-      static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+      static_cast<PMPageFormat>([print_info_ PMPageFormat]);
 
   PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
 
@@ -369,11 +373,11 @@
     return false;
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
 
   PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
 
-  [print_info_.get() updateFromPMPageFormat];
+  [print_info_ updateFromPMPageFormat];
   return true;
 }
 
@@ -394,7 +398,7 @@
   }
 
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
   return PMSetDuplex(print_settings, duplexSetting) == noErr;
 }
 
@@ -435,7 +439,7 @@
     return true;
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   PMPrinter current_printer;
   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
     return false;
@@ -445,7 +449,7 @@
   resolution.vRes = dpi_size.height();
 
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
   return PMPrinterSetOutputResolution(current_printer, print_settings,
                                       &resolution) == noErr;
 }
@@ -453,10 +457,10 @@
 bool PrintingContextMac::SetKeyValue(base::StringPiece key,
                                      base::StringPiece value) {
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
-  base::ScopedCFTypeRef<CFStringRef> cf_key(base::SysUTF8ToCFStringRef(key));
-  base::ScopedCFTypeRef<CFStringRef> cf_value(
-      base::SysUTF8ToCFStringRef(value));
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
+  base::ScopedCFTypeRef<CFStringRef> cf_key = base::SysUTF8ToCFStringRef(key);
+  base::ScopedCFTypeRef<CFStringRef> cf_value =
+      base::SysUTF8ToCFStringRef(value);
 
   return PMPrintSettingsSetValue(print_settings, cf_key.get(), cf_value.get(),
                                  /*locked=*/false) == noErr;
@@ -464,7 +468,7 @@
 
 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
   PageRanges page_ranges;
-  NSDictionary* print_info_dict = [print_info_.get() dictionary];
+  NSDictionary* print_info_dict = [print_info_ dictionary];
   if (![print_info_dict[NSPrintAllPages] boolValue]) {
     PageRange range;
     range.from = [print_info_dict[NSPrintFirstPage] intValue] - 1;
@@ -484,14 +488,14 @@
     return mojom::ResultCode::kSuccess;
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   PMPrintSettings print_settings =
-      static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+      static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
   PMPageFormat page_format =
-      static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+      static_cast<PMPageFormat>([print_info_ PMPageFormat]);
 
-  base::ScopedCFTypeRef<CFStringRef> job_title(
-      base::SysUTF16ToCFStringRef(document_name));
+  base::ScopedCFTypeRef<CFStringRef> job_title =
+      base::SysUTF16ToCFStringRef(document_name);
   PMPrintSettingsSetJobName(print_settings, job_title.get());
 
   OSStatus status = PMSessionBeginCGDocumentNoDialog(
@@ -509,9 +513,9 @@
   DCHECK(!context_);
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   PMPageFormat page_format =
-      static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+      static_cast<PMPageFormat>([print_info_ PMPageFormat]);
   OSStatus status;
   status = PMSessionBeginPageNoDialog(print_session, page_format, nullptr);
   if (status != noErr)
@@ -530,7 +534,7 @@
   DCHECK(context_);
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   OSStatus status = PMSessionEndPageNoDialog(print_session);
   if (status != noErr)
     OnError();
@@ -568,7 +572,7 @@
   DCHECK(in_print_job_);
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   OSStatus status = PMSessionEndDocumentNoDialog(print_session);
   if (status != noErr)
     OnError();
@@ -583,12 +587,12 @@
   context_ = nullptr;
 
   PMPrintSession print_session =
-      static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+      static_cast<PMPrintSession>([print_info_ PMPrintSession]);
   PMSessionEndPageNoDialog(print_session);
 }
 
 void PrintingContextMac::ReleaseContext() {
-  print_info_.reset();
+  print_info_ = nil;
   context_ = nullptr;
 }
 
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn
index f374bcc..d906f63b 100644
--- a/remoting/base/BUILD.gn
+++ b/remoting/base/BUILD.gn
@@ -179,6 +179,7 @@
 
   if (is_mac) {
     sources += [ "breakpad_mac.mm" ]
+    configs += [ "//build/config/compiler:enable_arc" ]
   }
 
   if (is_win) {
diff --git a/remoting/base/breakpad_mac.mm b/remoting/base/breakpad_mac.mm
index 096d27d..fa839eb 100644
--- a/remoting/base/breakpad_mac.mm
+++ b/remoting/base/breakpad_mac.mm
@@ -4,6 +4,10 @@
 
 #include "remoting/base/breakpad.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace remoting {
 
 void InitializeCrashReporting() {
diff --git a/remoting/host/input_monitor/BUILD.gn b/remoting/host/input_monitor/BUILD.gn
index f930949..f62046a 100644
--- a/remoting/host/input_monitor/BUILD.gn
+++ b/remoting/host/input_monitor/BUILD.gn
@@ -48,6 +48,7 @@
       "local_keyboard_input_monitor_mac.mm",
       "local_mouse_input_monitor_mac.mm",
     ]
+    configs += [ "//build/config/compiler:enable_arc" ]
   }
 
   if (remoting_use_x11) {
diff --git a/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm b/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm
index 453bd38..bd008b0 100644
--- a/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm
+++ b/remoting/host/input_monitor/local_hotkey_input_monitor_mac.mm
@@ -16,7 +16,6 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
@@ -26,6 +25,10 @@
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace remoting {
 namespace {
 
@@ -69,7 +72,7 @@
 @end
 
 @implementation LocalHotkeyInputMonitorManager {
-  id _eventMonitor;
+  id __strong _eventMonitor;
 
   raw_ptr<remoting::LocalHotkeyInputMonitorMac::EventHandler> _monitor;
 }
@@ -79,13 +82,19 @@
   if ((self = [super init])) {
     _monitor = monitor;
 
+    LocalHotkeyInputMonitorManager* __weak weakSelf = self;
     auto eventHandler = ^NSEvent*(NSEvent* event) {
+      LocalHotkeyInputMonitorManager* strongSelf = weakSelf;
+      if (!strongSelf) {
+        return event;
+      }
+
       const NSEventModifierFlags requiredModifiers =
           NSEventModifierFlagOption | NSEventModifierFlagControl;
       if ((event.keyCode == kVK_Escape) &&
           (event.modifierFlags & requiredModifiers)) {
         // Trigger the callback.
-        _monitor->OnDisconnectShortcut();
+        strongSelf->_monitor->OnDisconnectShortcut();
 
         // Stop the event propagation.
         return nil;
@@ -106,8 +115,6 @@
   if (_eventMonitor) {
     [NSEvent removeMonitor:_eventMonitor];
   }
-
-  [super dealloc];
 }
 
 @end
@@ -145,7 +152,7 @@
   // Task runner on which |window_| is created.
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 
-  base::scoped_nsobject<LocalHotkeyInputMonitorManager> manager_;
+  LocalHotkeyInputMonitorManager* __strong manager_;
 
   // Invoked in the |caller_task_runner_| thread to report session disconnect
   // requests.
@@ -192,19 +199,19 @@
 }
 
 LocalHotkeyInputMonitorMac::Core::~Core() {
-  DCHECK_EQ(manager_.get(), nil);
+  DCHECK_EQ(manager_, nil);
 }
 
 void LocalHotkeyInputMonitorMac::Core::StartOnUiThread() {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
-  manager_.reset([[LocalHotkeyInputMonitorManager alloc] initWithMonitor:this]);
+  manager_ = [[LocalHotkeyInputMonitorManager alloc] initWithMonitor:this];
 }
 
 void LocalHotkeyInputMonitorMac::Core::StopOnUiThread() {
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
-  manager_.reset();
+  manager_ = nil;
 }
 
 void LocalHotkeyInputMonitorMac::Core::OnDisconnectShortcut() {
diff --git a/remoting/host/input_monitor/local_keyboard_input_monitor_mac.mm b/remoting/host/input_monitor/local_keyboard_input_monitor_mac.mm
index be37d37..fd3a202 100644
--- a/remoting/host/input_monitor/local_keyboard_input_monitor_mac.mm
+++ b/remoting/host/input_monitor/local_keyboard_input_monitor_mac.mm
@@ -10,6 +10,10 @@
 #import "base/task/single_thread_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace remoting {
 
 std::unique_ptr<LocalKeyboardInputMonitor> LocalKeyboardInputMonitor::Create(
diff --git a/remoting/host/input_monitor/local_mouse_input_monitor_mac.mm b/remoting/host/input_monitor/local_mouse_input_monitor_mac.mm
index d068ea2..854d2ac 100644
--- a/remoting/host/input_monitor/local_mouse_input_monitor_mac.mm
+++ b/remoting/host/input_monitor/local_mouse_input_monitor_mac.mm
@@ -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/memory/raw_ptr.h"
 #include "remoting/host/input_monitor/local_pointer_input_monitor.h"
 
 #import <AppKit/AppKit.h>
@@ -14,6 +13,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/sequence_checker.h"
 #include "base/synchronization/lock.h"
@@ -21,6 +21,10 @@
 #include "base/task/single_thread_task_runner.h"
 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace remoting {
 namespace {
 
@@ -30,7 +34,7 @@
   // Invoked by LocalInputMonitorManager.
   class EventHandler {
    public:
-    virtual ~EventHandler() {}
+    virtual ~EventHandler() = default;
 
     virtual void OnLocalMouseMoved(const webrtc::DesktopVector& position) = 0;
   };
@@ -84,7 +88,7 @@
   if (pid == 0) {
     CGPoint cgMousePos = CGEventGetLocation(event);
     webrtc::DesktopVector mousePos(cgMousePos.x, cgMousePos.y);
-    [static_cast<LocalInputMonitorManager*>(context) localMouseMoved:mousePos];
+    [(__bridge LocalInputMonitorManager*)context localMouseMoved:mousePos];
   }
   return nullptr;
 }
@@ -98,7 +102,7 @@
 
     _mouseMachPort.reset(CGEventTapCreate(
         kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly,
-        1 << kCGEventMouseMoved, LocalMouseMoved, self));
+        1 << kCGEventMouseMoved, LocalMouseMoved, (__bridge void*)self));
     if (_mouseMachPort) {
       _mouseRunLoopSource.reset(
           CFMachPortCreateRunLoopSource(nullptr, _mouseMachPort, 0));
@@ -106,7 +110,7 @@
                          kCFRunLoopCommonModes);
     } else {
       LOG(ERROR) << "CGEventTapCreate failed.";
-      [self release];
+      self = nil;
       return nil;
     }
   }
@@ -161,7 +165,7 @@
   // Task runner on which |window_| is created.
   scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
 
-  LocalInputMonitorManager* manager_;
+  LocalInputMonitorManager* __strong manager_ = nil;
 
   // Invoked in the |caller_task_runner_| thread to report local mouse events.
   LocalInputMonitor::PointerMoveCallback on_mouse_move_;
@@ -190,7 +194,6 @@
     LocalInputMonitor::PointerMoveCallback on_mouse_move)
     : caller_task_runner_(caller_task_runner),
       ui_task_runner_(ui_task_runner),
-      manager_(nil),
       on_mouse_move_(std::move(on_mouse_move)) {}
 
 void LocalMouseInputMonitorMac::Core::Start() {
@@ -221,7 +224,6 @@
   DCHECK(ui_task_runner_->BelongsToCurrentThread());
 
   [manager_ invalidate];
-  [manager_ release];
   manager_ = nil;
 }
 
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 1f90243..1026207 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -60,6 +60,7 @@
 
   if (is_mac) {
     sources += [ "it2me_confirmation_dialog_mac.mm" ]
+    configs += [ "//build/config/compiler:enable_arc" ]
   }
 
   if (is_win) {
diff --git a/remoting/host/it2me/it2me_confirmation_dialog_mac.mm b/remoting/host/it2me/it2me_confirmation_dialog_mac.mm
index 0e65c92..097cd8d 100644
--- a/remoting/host/it2me/it2me_confirmation_dialog_mac.mm
+++ b/remoting/host/it2me/it2me_confirmation_dialog_mac.mm
@@ -14,7 +14,6 @@
 #include "base/functional/callback.h"
 #include "base/i18n/message_formatter.h"
 #include "base/location.h"
-#include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -23,9 +22,13 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 @interface It2MeConfirmationDialogMacController : NSObject {
  @private
-  base::scoped_nsobject<NSAlert> _confirmation_alert;
+  NSAlert* __strong _confirmation_alert;
   std::u16string _username;
   remoting::It2MeConfirmationDialog::ResultCallback _dialog_action_callback;
 }
@@ -64,14 +67,14 @@
  private:
   void OnDialogAction(Result result);
 
-  base::scoped_nsobject<It2MeConfirmationDialogMacController> controller_;
+  It2MeConfirmationDialogMacController* __strong controller_;
 
   ResultCallback result_callback_;
 
   base::OneShotTimer dialog_timer_;
 };
 
-It2MeConfirmationDialogMac::It2MeConfirmationDialogMac() {}
+It2MeConfirmationDialogMac::It2MeConfirmationDialogMac() = default;
 
 It2MeConfirmationDialogMac::~It2MeConfirmationDialogMac() {
   dialog_timer_.Stop();
@@ -96,9 +99,9 @@
       &It2MeConfirmationDialogMac::OnDialogAction, base::Unretained(this));
 
   @autoreleasepool {
-    controller_.reset([[It2MeConfirmationDialogMacController alloc]
+    controller_ = [[It2MeConfirmationDialogMacController alloc]
         initWithCallback:std::move(dialog_action_callback)
-                username:remote_user_email]);
+                username:remote_user_email];
     [controller_ show];
   }
 }
@@ -109,7 +112,7 @@
   if (controller_) {
     @autoreleasepool {
       [controller_ hide];
-      controller_.reset();
+      controller_ = nil;
     }
   }
 
@@ -138,47 +141,46 @@
 }
 
 - (void)show {
-  _confirmation_alert.reset([[NSAlert alloc] init]);
+  _confirmation_alert = [[NSAlert alloc] init];
 
   std::u16string dialog_text =
       base::i18n::MessageFormatter::FormatWithNumberedArgs(
           l10n_util::GetStringUTF16(
               IDS_SHARE_CONFIRM_DIALOG_MESSAGE_WITH_USERNAME),
           _username);
-  [_confirmation_alert setMessageText:base::SysUTF16ToNSString(dialog_text)];
+  _confirmation_alert.messageText = base::SysUTF16ToNSString(dialog_text);
 
   NSButton* cancel_button = [_confirmation_alert
       addButtonWithTitle:l10n_util::GetNSString(
                              IDS_SHARE_CONFIRM_DIALOG_DECLINE)];
-  [cancel_button setAction:@selector(onCancel:)];
-  [cancel_button setTarget:self];
+  cancel_button.action = @selector(onCancel:);
+  cancel_button.target = self;
 
   NSButton* confirm_button = [_confirmation_alert
       addButtonWithTitle:l10n_util::GetNSString(
                              IDS_SHARE_CONFIRM_DIALOG_CONFIRM)];
-  [confirm_button setAction:@selector(onAccept:)];
-  [confirm_button setTarget:self];
+  confirm_button.action = @selector(onAccept:);
+  confirm_button.target = self;
 
   NSBundle* bundle = [NSBundle bundleForClass:[self class]];
   NSString* imagePath = [bundle pathForResource:@"chromoting128" ofType:@"png"];
-  base::scoped_nsobject<NSImage> image(
-      [[NSImage alloc] initByReferencingFile:imagePath]);
-  [_confirmation_alert setIcon:image];
+  NSImage* image = [[NSImage alloc] initByReferencingFile:imagePath];
+  _confirmation_alert.icon = image;
   [_confirmation_alert layout];
 
   // Force alert to be at the proper level and location.
-  NSWindow* confirmation_window = [_confirmation_alert window];
+  NSWindow* confirmation_window = _confirmation_alert.window;
   [confirmation_window center];
-  [confirmation_window setTitle:l10n_util::GetNSString(IDS_PRODUCT_NAME)];
-  [confirmation_window setLevel:NSNormalWindowLevel];
+  confirmation_window.title = l10n_util::GetNSString(IDS_PRODUCT_NAME);
+  confirmation_window.level = NSNormalWindowLevel;
   [confirmation_window orderFrontRegardless];
   [confirmation_window makeKeyWindow];
 }
 
 - (void)hide {
   if (_confirmation_alert) {
-    [[_confirmation_alert window] close];
-    _confirmation_alert.reset();
+    [_confirmation_alert.window close];
+    _confirmation_alert = nil;
   }
 }
 
diff --git a/remoting/host/setup/BUILD.gn b/remoting/host/setup/BUILD.gn
index d1eca753..e017578c 100644
--- a/remoting/host/setup/BUILD.gn
+++ b/remoting/host/setup/BUILD.gn
@@ -61,6 +61,7 @@
   }
 
   if (is_apple) {
+    configs += [ "//build/config/compiler:enable_arc" ]
     deps += [ "//remoting/host/mac:constants" ]
   }
 
diff --git a/remoting/host/setup/daemon_controller_delegate_mac.mm b/remoting/host/setup/daemon_controller_delegate_mac.mm
index a3b84d9..5141516 100644
--- a/remoting/host/setup/daemon_controller_delegate_mac.mm
+++ b/remoting/host/setup/daemon_controller_delegate_mac.mm
@@ -4,10 +4,15 @@
 
 #include "remoting/host/setup/daemon_controller_delegate_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 #include <launch.h>
 #include <sys/types.h>
 #include <utility>
 
+#include "base/apple/bridging.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
@@ -51,7 +56,7 @@
   // case. Note that -1 is the value returned from
   // base::mac::ExecuteWithPrivilegesAndGetPID() when the child PID could not be
   // determined.
-  ScopedWaitpid(pid_t pid) : pid_(pid) {}
+  explicit ScopedWaitpid(pid_t pid) : pid_(pid) {}
   ~ScopedWaitpid() { MaybeWait(); }
 
   // Executes the waitpid() and resets the scoper. After this, the caller may
@@ -91,7 +96,8 @@
       IDS_HOST_AUTHENTICATION_PROMPT,
       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
   base::mac::ScopedAuthorizationRef authorization =
-      base::mac::AuthorizationCreateToRunAsRoot(base::mac::NSToCFCast(prompt));
+      base::mac::AuthorizationCreateToRunAsRoot(
+          base::apple::NSToCFPtrCast(prompt));
   if (!authorization.get()) {
     LOG(ERROR) << "Failed to obtain authorizationRef";
     return false;
diff --git a/remoting/ios/app/view_utils.mm b/remoting/ios/app/view_utils.mm
index cfc7d3f..26162bb 100644
--- a/remoting/ios/app/view_utils.mm
+++ b/remoting/ios/app/view_utils.mm
@@ -4,6 +4,10 @@
 
 #include "remoting/ios/app/view_utils.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 UIWindow* GetAnyKeyWindow() {
 #if !defined(__IPHONE_13_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
diff --git a/remoting/ios/display/gl_demo_screen.mm b/remoting/ios/display/gl_demo_screen.mm
index 8c71e16..3284dbe 100644
--- a/remoting/ios/display/gl_demo_screen.mm
+++ b/remoting/ios/display/gl_demo_screen.mm
@@ -8,6 +8,10 @@
 #include "remoting/client/display/canvas.h"
 #include "remoting/client/display/gl_math.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace remoting {
 
 namespace {
diff --git a/remoting/ios/mdc/MDCActionImageView.m b/remoting/ios/mdc/MDCActionImageView.m
index 9a62870c..b9c756d 100644
--- a/remoting/ios/mdc/MDCActionImageView.m
+++ b/remoting/ios/mdc/MDCActionImageView.m
@@ -6,6 +6,10 @@
 
 #import <MaterialComponents/MaterialAnimationTiming.h>
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 static const CGFloat kIconRotationRadians = 0.375f * 2 * M_PI;
 static const CGFloat kIconTransitionAnimationDuration = 0.3f;
 static const CGFloat kIconShrinkScale = 0.4f;
diff --git a/services/device/geolocation/wifi_data_provider_common.cc b/services/device/geolocation/wifi_data_provider_common.cc
index f2b5bcc9..209d2dd 100644
--- a/services/device/geolocation/wifi_data_provider_common.cc
+++ b/services/device/geolocation/wifi_data_provider_common.cc
@@ -6,7 +6,6 @@
 
 #include "base/functional/bind.h"
 #include "base/location.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
 #include "components/device_event_log/device_event_log.h"
@@ -68,9 +67,6 @@
   if (!wlan_api_)
     return;
 
-  SCOPED_UMA_HISTOGRAM_TIMER(
-      "Geolocation.WifiDataProviderCommon.WifiScanTaskTime");
-
   bool update_available = false;
   WifiData new_data;
   if (!wlan_api_->GetAccessPointData(&new_data.access_point_data)) {
diff --git a/services/network/attribution/attribution_request_helper.cc b/services/network/attribution/attribution_request_helper.cc
index c8df19a1..2ca82e6 100644
--- a/services/network/attribution/attribution_request_helper.cc
+++ b/services/network/attribution/attribution_request_helper.cc
@@ -113,8 +113,7 @@
   // Returns `kVerificationTokensPerTrigger` messages associated to this
   // verification operation. Each message is represented by concatenating the
   // trigger`s `destination_origin` and a generated aggregatable report ID.
-  std::vector<const std::string> Messages(
-      const url::Origin& destination_origin);
+  std::vector<std::string> Messages(const url::Origin& destination_origin);
 
   // A verification operation requests multiple tokens. Each token is signed
   // over a message that includes a different id as we need one id per report
@@ -126,12 +125,12 @@
   AttributionVerificationMediator mediator;
 };
 
-std::vector<const std::string>
+std::vector<std::string>
 AttributionRequestHelper::VerificationOperation::Messages(
     const url::Origin& destination_origin) {
   std::string destination_site =
       net::SchemefulSite(destination_origin).Serialize();
-  std::vector<const std::string> messages;
+  std::vector<std::string> messages;
   messages.reserve(aggregatable_report_ids.size());
 
   for (const base::Uuid& id : aggregatable_report_ids) {
@@ -298,7 +297,7 @@
 void AttributionRequestHelper::OnDoneProcessingVerificationResponse(
     mojom::URLResponseHead& response,
     base::OnceClosure done,
-    std::vector<const std::string> redemption_tokens) {
+    std::vector<std::string> redemption_tokens) {
   CHECK(verification_operation_);
   std::unique_ptr<VerificationOperation> verification_operation(
       std::move(verification_operation_));
diff --git a/services/network/attribution/attribution_request_helper.h b/services/network/attribution/attribution_request_helper.h
index 623d6a7..abd03af 100644
--- a/services/network/attribution/attribution_request_helper.h
+++ b/services/network/attribution/attribution_request_helper.h
@@ -135,7 +135,7 @@
   void OnDoneProcessingVerificationResponse(
       mojom::URLResponseHead& response,
       base::OnceClosure done,
-      std::vector<const std::string> redemption_tokens);
+      std::vector<std::string> redemption_tokens);
 
   // A mediator can perform a single verification operation. Each redirect does
   // a verification. We use this callback to generate a new mediator instance
diff --git a/services/network/attribution/attribution_verification_mediator.h b/services/network/attribution/attribution_verification_mediator.h
index 4f9c7471..1632bb91 100644
--- a/services/network/attribution/attribution_verification_mediator.h
+++ b/services/network/attribution/attribution_verification_mediator.h
@@ -29,10 +29,10 @@
 //  token which is returned.
 class AttributionVerificationMediator {
  public:
-  using Message = const std::string;
-  using BlindedMessage = const std::string;
-  using BlindedToken = const std::string;
-  using Token = const std::string;
+  using Message = std::string;
+  using BlindedMessage = std::string;
+  using BlindedToken = std::string;
+  using Token = std::string;
 
   // Represents the status/outcome of the execution of
   // `GetHeadersForVerification`. These values are persisted to logs.
diff --git a/services/network/cors/cors_url_loader_shared_dictionary_unittest.cc b/services/network/cors/cors_url_loader_shared_dictionary_unittest.cc
index ba0bffb1..9a3c0cb8 100644
--- a/services/network/cors/cors_url_loader_shared_dictionary_unittest.cc
+++ b/services/network/cors/cors_url_loader_shared_dictionary_unittest.cc
@@ -134,7 +134,7 @@
       std::map<std::string, SharedDictionaryStorageInMemory::DictionaryInfo>>&
   GetInMemoryDictionaryMap(SharedDictionaryStorage* storage) {
     return static_cast<SharedDictionaryStorageInMemory*>(storage)
-        ->GetDictionaryMapForTesting();
+        ->GetDictionaryMap();
   }
 
   net::IsolationInfo isolation_info_;
diff --git a/services/network/shared_dictionary/shared_dictionary_manager.h b/services/network/shared_dictionary/shared_dictionary_manager.h
index 6128443..d7c8605 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager.h
+++ b/services/network/shared_dictionary/shared_dictionary_manager.h
@@ -50,12 +50,6 @@
       scoped_refptr<disk_cache::BackendFileOperationsFactory>
           file_operations_factory);
 
-  // TODO(crbug.com/1413922): Implement a manager which supports persistence
-  // and use if for non-incognito mode. Also, if preventing incognito mode
-  // detection isn't that important, and the maintenance cost of two storagee is
-  // large, consider removing  SharedDictionaryManager and stopping incognito
-  // mode support.
-
   SharedDictionaryManager(const SharedDictionaryManager&) = delete;
   SharedDictionaryManager& operator=(const SharedDictionaryManager&) = delete;
 
@@ -65,10 +59,6 @@
   scoped_refptr<SharedDictionaryStorage> GetStorage(
       const net::SharedDictionaryStorageIsolationKey& isolation_key);
 
-  // TODO(crbug.com/1413922): Add a method to delete dictionaries when the user
-  // clears the browsing data (BrowsingDataRemover::DATA_TYPE_CACHE and
-  // DATA_TYPE_SITE_DATA).
-
   // Called when the SharedDictionaryStorage for the `isolation_key` is
   // deleted.
   void OnStorageDeleted(
diff --git a/services/network/shared_dictionary/shared_dictionary_manager_in_memory.cc b/services/network/shared_dictionary/shared_dictionary_manager_in_memory.cc
index 81d7cfb..bc4583d 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager_in_memory.cc
+++ b/services/network/shared_dictionary/shared_dictionary_manager_in_memory.cc
@@ -4,29 +4,92 @@
 
 #include "services/network/shared_dictionary/shared_dictionary_manager_in_memory.h"
 
+#include <algorithm>
+
 #include "base/functional/callback_helpers.h"
 #include "base/notreached.h"
 #include "services/network/shared_dictionary/shared_dictionary_storage_in_memory.h"
 
 namespace network {
 
+namespace {
+
+class DictionaryReference {
+ public:
+  DictionaryReference(
+      raw_ptr<SharedDictionaryStorageInMemory> storage,
+      raw_ptr<const SharedDictionaryStorageInMemory::DictionaryInfo> dict)
+      : storage_(storage), dict_(dict) {}
+
+  DictionaryReference(const DictionaryReference&) = default;
+  DictionaryReference& operator=(const DictionaryReference&) = default;
+  DictionaryReference(DictionaryReference&& other) = default;
+  DictionaryReference& operator=(DictionaryReference&& other) = default;
+
+  ~DictionaryReference() = default;
+
+  raw_ptr<SharedDictionaryStorageInMemory> storage() const { return storage_; }
+  const raw_ptr<const SharedDictionaryStorageInMemory::DictionaryInfo> dict()
+      const {
+    return dict_;
+  }
+
+ private:
+  raw_ptr<SharedDictionaryStorageInMemory> storage_;
+  raw_ptr<const SharedDictionaryStorageInMemory::DictionaryInfo> dict_;
+};
+
+struct LastUsedTimeLess {
+  bool operator()(const DictionaryReference& a,
+                  const DictionaryReference& b) const noexcept {
+    return a.dict()->last_used_time() < b.dict()->last_used_time();
+  }
+};
+
+class EvictionCandidate {
+ public:
+  EvictionCandidate(raw_ptr<SharedDictionaryStorageInMemory> storage,
+                    const url::SchemeHostPort& host,
+                    const std::string& match)
+      : storage_(storage), host_(host), match_(match) {}
+
+  EvictionCandidate(const EvictionCandidate&) = default;
+  EvictionCandidate& operator=(const EvictionCandidate&) = default;
+  EvictionCandidate(EvictionCandidate&& other) = default;
+  EvictionCandidate& operator=(EvictionCandidate&& other) = default;
+  ~EvictionCandidate() = default;
+
+  const url::SchemeHostPort& host() const { return host_; }
+  raw_ptr<SharedDictionaryStorageInMemory> storage() const { return storage_; }
+  const std::string& match() const { return match_; }
+
+ private:
+  raw_ptr<SharedDictionaryStorageInMemory> storage_;
+  url::SchemeHostPort host_;
+  std::string match_;
+};
+
+}  // namespace
+
 SharedDictionaryManagerInMemory::SharedDictionaryManagerInMemory(
     uint64_t cache_max_size)
     : cache_max_size_(cache_max_size) {}
 
+SharedDictionaryManagerInMemory::~SharedDictionaryManagerInMemory() = default;
+
 scoped_refptr<SharedDictionaryStorage>
 SharedDictionaryManagerInMemory::CreateStorage(
     const net::SharedDictionaryStorageIsolationKey& isolation_key) {
   return base::MakeRefCounted<SharedDictionaryStorageInMemory>(
+      weak_factory_.GetWeakPtr(),
       base::ScopedClosureRunner(
           base::BindOnce(&SharedDictionaryManager::OnStorageDeleted,
                          GetWeakPtr(), isolation_key)));
 }
 
 void SharedDictionaryManagerInMemory::SetCacheMaxSize(uint64_t cache_max_size) {
-  // TODO(crbug.com/1413922): Implement cache eviction logic using
-  // `cache_max_size_`.
   cache_max_size_ = cache_max_size;
+  MaybeRunCacheEviction();
 }
 
 void SharedDictionaryManagerInMemory::ClearData(
@@ -34,9 +97,67 @@
     base::Time end_time,
     base::RepeatingCallback<bool(const GURL&)> url_matcher,
     base::OnceClosure callback) {
-  // TODO(crbug.com/1413922): Implement this.
-  NOTIMPLEMENTED();
+  for (const auto& it : storages()) {
+    SharedDictionaryStorageInMemory* storage =
+        reinterpret_cast<SharedDictionaryStorageInMemory*>(it.second.get());
+    base::RepeatingCallback<bool(const GURL&)> matcher = url_matcher;
+    if (matcher && (matcher.Run(it.first.frame_origin().GetURL()) ||
+                    matcher.Run(it.first.top_frame_site().GetURL()))) {
+      matcher.Reset();
+    }
+    storage->ClearData(start_time, end_time, std::move(matcher));
+  }
   std::move(callback).Run();
 }
 
+void SharedDictionaryManagerInMemory::MaybeRunCacheEviction() {
+  if (cache_max_size_ == 0u) {
+    return;
+  }
+  uint64_t total_size = 0u;
+  size_t dictionary_count = 0u;
+  for (const auto& it1 : storages()) {
+    SharedDictionaryStorageInMemory* storage =
+        reinterpret_cast<SharedDictionaryStorageInMemory*>(it1.second.get());
+    for (const auto& it2 : storage->GetDictionaryMap()) {
+      dictionary_count += it2.second.size();
+      for (const auto& it3 : it2.second) {
+        total_size += it3.second.size();
+      }
+    }
+  }
+  if (total_size <= cache_max_size_) {
+    return;
+  }
+
+  std::vector<DictionaryReference> dictionaries;
+  dictionaries.reserve(dictionary_count);
+  for (auto& it1 : storages()) {
+    SharedDictionaryStorageInMemory* storage =
+        reinterpret_cast<SharedDictionaryStorageInMemory*>(it1.second.get());
+    for (auto& it2 : storage->GetDictionaryMap()) {
+      for (auto& it3 : it2.second) {
+        dictionaries.emplace_back(storage, &it3.second);
+      }
+    }
+  }
+
+  std::sort(dictionaries.begin(), dictionaries.end(), LastUsedTimeLess{});
+
+  uint64_t low_watermark = cache_max_size_ * 0.9;
+  std::vector<EvictionCandidate> eviction_candidates;
+  for (auto& dict_ref : dictionaries) {
+    total_size -= dict_ref.dict()->size();
+    eviction_candidates.emplace_back(
+        dict_ref.storage(), url::SchemeHostPort(dict_ref.dict()->url()),
+        dict_ref.dict()->match());
+    if (low_watermark >= total_size) {
+      break;
+    }
+  }
+  for (auto& candidate : eviction_candidates) {
+    candidate.storage()->DeleteDictionary(candidate.host(), candidate.match());
+  }
+}
+
 }  // namespace network
diff --git a/services/network/shared_dictionary/shared_dictionary_manager_in_memory.h b/services/network/shared_dictionary/shared_dictionary_manager_in_memory.h
index 28dc6e2d..768e3a0 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager_in_memory.h
+++ b/services/network/shared_dictionary/shared_dictionary_manager_in_memory.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_NETWORK_SHARED_DICTIONARY_SHARED_DICTIONARY_MANAGER_IN_MEMORY_H_
 #define SERVICES_NETWORK_SHARED_DICTIONARY_SHARED_DICTIONARY_MANAGER_IN_MEMORY_H_
 
+#include "base/memory/weak_ptr.h"
 #include "services/network/shared_dictionary/shared_dictionary_manager.h"
 
 namespace network {
@@ -15,6 +16,7 @@
 class SharedDictionaryManagerInMemory : public SharedDictionaryManager {
  public:
   explicit SharedDictionaryManagerInMemory(uint64_t cache_max_size);
+  ~SharedDictionaryManagerInMemory() override;
 
   SharedDictionaryManagerInMemory(const SharedDictionaryManagerInMemory&) =
       delete;
@@ -30,8 +32,11 @@
                  base::RepeatingCallback<bool(const GURL&)> url_matcher,
                  base::OnceClosure callback) override;
 
+  void MaybeRunCacheEviction();
+
  private:
   uint64_t cache_max_size_;
+  base::WeakPtrFactory<SharedDictionaryManagerInMemory> weak_factory_{this};
 };
 
 }  // namespace network
diff --git a/services/network/shared_dictionary/shared_dictionary_manager_on_disk.cc b/services/network/shared_dictionary/shared_dictionary_manager_on_disk.cc
index c30dc44..d98ec2f 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager_on_disk.cc
+++ b/services/network/shared_dictionary/shared_dictionary_manager_on_disk.cc
@@ -144,7 +144,8 @@
   void OnDiskCacheIterator(
       std::unique_ptr<disk_cache::Backend::Iterator> disk_cache_iterator) {
     if (!disk_cache_iterator) {
-      manager_->OnFinishSerializedTask();
+      // Disk cache is corrupted. So delete all entry from the metadata.
+      CleanupDatabase();
       return;
     }
     disk_cache_iterator_ = std::move(disk_cache_iterator);
@@ -441,6 +442,11 @@
     const net::SHA256HashValue& hash) {
   if (result != SharedDictionaryWriterOnDisk::Result::kSuccess) {
     CHECK(writing_disk_cache_key_tokens_.erase(disk_cache_key_token) == 1);
+
+    if (result ==
+        SharedDictionaryWriterOnDisk::Result::kErrorCreateEntryFailed) {
+      MaybePostMismatchingEntryDeletionTask();
+    }
     return;
   }
   base::Time last_used_time = base::Time::Now();
@@ -548,7 +554,8 @@
 void SharedDictionaryManagerOnDisk::MaybePostMismatchingEntryDeletionTask() {
   // MismatchingEntryDeletionTask is intended to resolve the mismatch between
   // the disk cache and metadata database. We run MismatchingEntryDeletionTask
-  // when ClearData() is called for the first time.
+  // only once in the lifetime of the manager when ClearData() is called or
+  // disk cache error is detected.
   if (mismatching_entry_deletion_task_posted_) {
     return;
   }
diff --git a/services/network/shared_dictionary/shared_dictionary_manager_on_disk.h b/services/network/shared_dictionary/shared_dictionary_manager_on_disk.h
index 11f91e8..61b1ea55 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager_on_disk.h
+++ b/services/network/shared_dictionary/shared_dictionary_manager_on_disk.h
@@ -78,6 +78,10 @@
 
   void UpdateDictionaryLastUsedTime(net::SharedDictionaryInfo& info);
 
+  // Posts a MismatchingEntryDeletionTask if this method is called for the first
+  // time.
+  void MaybePostMismatchingEntryDeletionTask();
+
  private:
   class SerializedTask {
    public:
@@ -123,7 +127,6 @@
   void OnFinishSerializedTask();
   void MaybeStartSerializedTask();
 
-  void MaybePostMismatchingEntryDeletionTask();
   void MaybePostCacheEvictionTask();
   void MaybePostExpiredDictionaryDeletionTask();
 
diff --git a/services/network/shared_dictionary/shared_dictionary_manager_on_disk_unittest.cc b/services/network/shared_dictionary/shared_dictionary_manager_on_disk_unittest.cc
index 355e2ad1..27e66d7 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager_on_disk_unittest.cc
+++ b/services/network/shared_dictionary/shared_dictionary_manager_on_disk_unittest.cc
@@ -479,7 +479,7 @@
 // Test that corruptted disk cache doesn't cause crash.
 // CorruptDiskCache() doesn't work on Fuchsia. So disabling the following tests
 // on Fuchsia.
-TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCache) {
+TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndWriteData) {
   net::SharedDictionaryStorageIsolationKey isolation_key(
       url::Origin::Create(kUrl), kSite);
 
@@ -510,15 +510,57 @@
     WriteDictionary(storage.get(), GURL("https://origin.test/dict2"),
                     "testfile2*", kTestData2);
     FlushCacheTasks();
-    // Currently, if the disk cache is corrupted, it just prevents adding new
-    // dictionaries.
-    // TODO(crbug.com/1413922): Implement a garbage collection logic to remove
-    // the entry in the database when its disk cache entry is unavailable.
+
+    // Writing dictionary fails, so MismatchingEntryDeletionTask cleans all
+    // dictionary in the metadata store.
+    EXPECT_TRUE(GetOnDiskDictionaryMap(storage.get()).empty());
+  }
+}
+
+TEST_F(SharedDictionaryManagerOnDiskTest, CorruptedDiskCacheAndGetData) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(kUrl), kSite);
+
+  {
+    std::unique_ptr<SharedDictionaryManager> manager =
+        CreateSharedDictionaryManager();
+    scoped_refptr<SharedDictionaryStorage> storage =
+        manager->GetStorage(isolation_key);
+    ASSERT_TRUE(storage);
+    // Write the test data to the dictionary.
+    WriteDictionary(storage.get(), GURL("https://origin.test/dict1"),
+                    "testfile1*", kTestData1);
+    FlushCacheTasks();
+  }
+  CorruptDiskCache();
+  {
+    std::unique_ptr<SharedDictionaryManager> manager =
+        CreateSharedDictionaryManager();
+    scoped_refptr<SharedDictionaryStorage> storage =
+        manager->GetStorage(isolation_key);
+    ASSERT_TRUE(storage);
+    FlushCacheTasks();
     {
       const auto& dictionary_map = GetOnDiskDictionaryMap(storage.get());
       ASSERT_EQ(1u, dictionary_map.size());
       ASSERT_EQ(1u, dictionary_map.begin()->second.size());
     }
+
+    std::unique_ptr<SharedDictionary> dict =
+        storage->GetDictionary(GURL("https://origin.test/testfile1"));
+    ASSERT_TRUE(dict);
+
+    // Reading the dictionary should fail because the disk cache is broken.
+    net::TestCompletionCallback read_callback;
+    EXPECT_EQ(net::ERR_FAILED,
+              read_callback.GetResult(dict->ReadAll(read_callback.callback())));
+
+    FlushCacheTasks();
+
+    // After failing to read the disk cache entry, MismatchingEntryDeletionTask
+    // cleans all dictionary in the metadata store.
+    EXPECT_FALSE(storage->GetDictionary(GURL("https://origin.test/testfile1")));
+    EXPECT_TRUE(GetOnDiskDictionaryMap(storage.get()).empty());
   }
 }
 #endif  // !BUILDFLAG(IS_FUCHSIA)
@@ -527,6 +569,7 @@
   net::SharedDictionaryStorageIsolationKey isolation_key(
       url::Origin::Create(kUrl), kSite);
 
+  base::UnguessableToken token1, token2;
   {
     std::unique_ptr<SharedDictionaryManager> manager =
         CreateSharedDictionaryManager();
@@ -541,6 +584,8 @@
       const auto& dictionary_map = GetOnDiskDictionaryMap(storage.get());
       ASSERT_EQ(1u, dictionary_map.size());
       ASSERT_EQ(1u, dictionary_map.begin()->second.size());
+      token1 =
+          dictionary_map.begin()->second.begin()->second.disk_cache_key_token();
     }
   }
   CorruptDatabase();
@@ -583,10 +628,27 @@
     EXPECT_EQ(kTestData1,
               std::string(reinterpret_cast<const char*>(dict->data()->data()),
                           dict->size()));
-    // Currently the disk cache entries that were added before the database
-    // corruption will not be removed.
-    // TODO(crbug.com/1413922): Implement a garbage collection logic to remove
-    // the entry in the disk cache when its database entry is unavailable.
+
+    {
+      const auto& dictionary_map = GetOnDiskDictionaryMap(storage.get());
+      ASSERT_EQ(1u, dictionary_map.size());
+      ASSERT_EQ(1u, dictionary_map.begin()->second.size());
+      token2 =
+          dictionary_map.begin()->second.begin()->second.disk_cache_key_token();
+    }
+
+    EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token1));
+    EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token2));
+
+    base::RunLoop run_loop;
+    manager->ClearData(
+        base::Time::Now() - base::Days(2), base::Time::Now() - base::Days(1),
+        base::RepeatingCallback<bool(const GURL&)>(), run_loop.QuitClosure());
+    run_loop.Run();
+    FlushCacheTasks();
+
+    EXPECT_FALSE(DiskCacheEntryExists(manager.get(), token1));
+    EXPECT_TRUE(DiskCacheEntryExists(manager.get(), token2));
   }
 }
 
diff --git a/services/network/shared_dictionary/shared_dictionary_manager_unittest.cc b/services/network/shared_dictionary/shared_dictionary_manager_unittest.cc
index d2a625ac..b464aac 100644
--- a/services/network/shared_dictionary/shared_dictionary_manager_unittest.cc
+++ b/services/network/shared_dictionary/shared_dictionary_manager_unittest.cc
@@ -50,6 +50,9 @@
 const net::SchemefulSite kSite1(kUrl1);
 const net::SchemefulSite kSite2(kUrl2);
 
+const std::string kTestData1 = "Hello world";
+const std::string kTestData2 = "Bonjour le monde";
+
 void CheckDiskCacheEntryDataEquals(
     SharedDictionaryDiskCache& disk_cache,
     const base::UnguessableToken& disk_cache_key_token,
@@ -143,7 +146,7 @@
       std::map<std::string, SharedDictionaryStorageInMemory::DictionaryInfo>>&
   GetInMemoryDictionaryMap(SharedDictionaryStorage* storage) {
     return static_cast<SharedDictionaryStorageInMemory*>(storage)
-        ->GetDictionaryMapForTesting();
+        ->GetDictionaryMap();
   }
   const std::map<url::SchemeHostPort,
                  std::map<std::string, net::SharedDictionaryInfo>>&
@@ -156,8 +159,10 @@
     task_environment_.RunUntilIdle();
   }
 
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
  private:
-  base::test::TaskEnvironment task_environment_;
   base::ScopedTempDir tmp_directory_;
   base::FilePath database_path_;
   base::FilePath cache_directory_path_;
@@ -451,4 +456,352 @@
   EXPECT_FALSE(dict);
 }
 
+TEST_P(SharedDictionaryManagerTest, CacheEvictionOnSetCacheMaxSize) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(kUrl1), kSite1);
+
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  ASSERT_TRUE(storage);
+
+  WriteDictionary(storage.get(), GURL("https://origin1.test/d1"), "p1*",
+                  {kTestData1});
+  task_environment_.FastForwardBy(base::Seconds(1));
+  WriteDictionary(storage.get(), GURL("https://origin2.test/d2"), "p2*",
+                  {kTestData1});
+  task_environment_.FastForwardBy(base::Seconds(1));
+  WriteDictionary(storage.get(), GURL("https://origin3.test/d1"), "p3*",
+                  {kTestData1});
+
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+
+  task_environment_.FastForwardBy(base::Seconds(1));
+
+  manager->SetCacheMaxSize(/*cache_max_size=*/kTestData1.size() * 2);
+
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin1.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin2.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin3.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, CacheEvictionOnNewDictionary) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(kUrl1), kSite1);
+
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  manager->SetCacheMaxSize(/*cache_max_size=*/kTestData1.size() * 2);
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  ASSERT_TRUE(storage);
+
+  WriteDictionary(storage.get(), GURL("https://origin1.test/d1"), "p1*",
+                  {kTestData1});
+  WriteDictionary(storage.get(), GURL("https://origin2.test/d2"), "p2*",
+                  {kTestData1});
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin1.test/p1?")));
+  task_environment_.FastForwardBy(base::Seconds(1));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin2.test/p2?")));
+  task_environment_.FastForwardBy(base::Seconds(1));
+  WriteDictionary(storage.get(), GURL("https://origin3.test/d1"), "p3*",
+                  {kTestData1});
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin1.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin2.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin3.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest,
+       CacheEvictionOnNewDictionaryMultiIsolation) {
+  net::SharedDictionaryStorageIsolationKey isolation_key1(
+      url::Origin::Create(kUrl1), kSite1);
+  net::SharedDictionaryStorageIsolationKey isolation_key2(
+      url::Origin::Create(kUrl2), kSite2);
+
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  manager->SetCacheMaxSize(/*cache_max_size=*/kTestData1.size() * 2);
+  scoped_refptr<SharedDictionaryStorage> storage1 =
+      manager->GetStorage(isolation_key1);
+  ASSERT_TRUE(storage1);
+  scoped_refptr<SharedDictionaryStorage> storage2 =
+      manager->GetStorage(isolation_key2);
+  ASSERT_TRUE(storage2);
+
+  WriteDictionary(storage1.get(), GURL("https://origin1.test/d1"), "p1*",
+                  {kTestData1});
+  WriteDictionary(storage2.get(), GURL("https://origin2.test/d2"), "p2*",
+                  {kTestData1});
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+  EXPECT_TRUE(storage1->GetDictionary(GURL("https://origin1.test/p1?")));
+  task_environment_.FastForwardBy(base::Seconds(1));
+  EXPECT_TRUE(storage2->GetDictionary(GURL("https://origin2.test/p2?")));
+  task_environment_.FastForwardBy(base::Seconds(1));
+  WriteDictionary(storage2.get(), GURL("https://origin3.test/d1"), "p3*",
+                  {kTestData1});
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+  EXPECT_FALSE(storage1->GetDictionary(GURL("https://origin1.test/p1?")));
+  EXPECT_FALSE(storage2->GetDictionary(GURL("https://origin2.test/p2?")));
+  EXPECT_TRUE(storage2->GetDictionary(GURL("https://origin3.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, CacheEvictionAfterUpdatingLastUsedTime) {
+  net::SharedDictionaryStorageIsolationKey isolation_key1(
+      url::Origin::Create(kUrl1), kSite1);
+  net::SharedDictionaryStorageIsolationKey isolation_key2(
+      url::Origin::Create(kUrl2), kSite2);
+
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage1 =
+      manager->GetStorage(isolation_key1);
+  ASSERT_TRUE(storage1);
+  scoped_refptr<SharedDictionaryStorage> storage2 =
+      manager->GetStorage(isolation_key2);
+  ASSERT_TRUE(storage2);
+
+  // Dictionary 1-1.
+  WriteDictionary(storage1.get(), GURL("https://origin1.test/d1"), "p1*",
+                  {kTestData1});
+  task_environment_.FastForwardBy(base::Seconds(1));
+  // Dictionary 1-2.
+  WriteDictionary(storage1.get(), GURL("https://origin1.test/d2"), "p2*",
+                  {kTestData1});
+  task_environment_.FastForwardBy(base::Seconds(1));
+  // Dictionary 2-1.
+  WriteDictionary(storage2.get(), GURL("https://origin2.test/d1"), "p1*",
+                  {kTestData1});
+  task_environment_.FastForwardBy(base::Seconds(1));
+  // Dictionary 2-2.
+  WriteDictionary(storage2.get(), GURL("https://origin2.test/d2"), "p2*",
+                  {kTestData1});
+
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+
+  task_environment_.FastForwardBy(base::Seconds(1));
+
+  // Call GetDictionary to update the last used time of the dictionary 1-1.
+  std::unique_ptr<SharedDictionary> dict1 =
+      storage1->GetDictionary(GURL("https://origin1.test/p1?"));
+  ASSERT_TRUE(dict1);
+
+  // Set the max size to kTestData1.size() * 3. The low water mark will be
+  // kTestData1.size() * 2.7 (3 * 0.9).
+  manager->SetCacheMaxSize(/*cache_max_size=*/kTestData1.size() * 3);
+
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+
+  EXPECT_TRUE(storage1->GetDictionary(GURL("https://origin1.test/p1?")));
+  EXPECT_FALSE(storage1->GetDictionary(GURL("https://origin1.test/p2?")));
+  EXPECT_FALSE(storage2->GetDictionary(GURL("https://origin2.test/p1?")));
+  EXPECT_TRUE(storage2->GetDictionary(GURL("https://origin2.test/p2?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, ClearDataMatchFrameOrigin) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(GURL("https://target.test/")),
+      net::SchemefulSite(GURL("https://top-frame.test")));
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  WriteDictionary(storage.get(), GURL("https://origin.test/1"), "p1*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/2"), "p2*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/3"), "p3*",
+                  {kTestData1});
+  // Move the clock forward by 12 hours.
+  task_environment_.FastForwardBy(base::Hours(12));
+
+  base::RunLoop run_loop;
+  manager->ClearData(base::Time::Now() - base::Days(2),
+                     base::Time::Now() - base::Days(1),
+                     base::BindRepeating([](const GURL& url) {
+                       return url == GURL("https://target.test/");
+                     }),
+                     run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, ClearDataMatchTopFrameSite) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(GURL("https://frame.test/")),
+      net::SchemefulSite(GURL("https://target.test")));
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  WriteDictionary(storage.get(), GURL("https://origin.test/1"), "p1*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/2"), "p2*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/3"), "p3*",
+                  {kTestData1});
+  // Move the clock forward by 12 hours.
+  task_environment_.FastForwardBy(base::Hours(12));
+
+  base::RunLoop run_loop;
+  manager->ClearData(base::Time::Now() - base::Days(2),
+                     base::Time::Now() - base::Days(1),
+                     base::BindRepeating([](const GURL& url) {
+                       return url == GURL("https://target.test/");
+                     }),
+                     run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, ClearDataMatchDictionaryUrl) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(GURL("https://frame.test/")),
+      net::SchemefulSite(GURL("https://top-frame.test")));
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  WriteDictionary(storage.get(), GURL("https://target.test/1"), "p1*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://target.test/2"), "p2*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://target.test/3"), "p3*",
+                  {kTestData1});
+  // Move the clock forward by 12 hours.
+  task_environment_.FastForwardBy(base::Hours(12));
+
+  base::RunLoop run_loop;
+  manager->ClearData(base::Time::Now() - base::Days(2),
+                     base::Time::Now() - base::Days(1),
+                     base::BindRepeating([](const GURL& url) {
+                       return url == GURL("https://target.test/");
+                     }),
+                     run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://target.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://target.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://target.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, ClearDataNullUrlMatcher) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(GURL("https://frame.test/")),
+      net::SchemefulSite(GURL("https://top-frame.test")));
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  WriteDictionary(storage.get(), GURL("https://origin.test/1"), "p1*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/2"), "p2*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/3"), "p3*",
+                  {kTestData1});
+  // Move the clock forward by 12 hours.
+  task_environment_.FastForwardBy(base::Hours(12));
+
+  base::RunLoop run_loop;
+  manager->ClearData(
+      base::Time::Now() - base::Days(2), base::Time::Now() - base::Days(1),
+      base::RepeatingCallback<bool(const GURL&)>(), run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p3?")));
+}
+
+TEST_P(SharedDictionaryManagerTest, ClearDataDoNotInvalidateActiveDictionary) {
+  net::SharedDictionaryStorageIsolationKey isolation_key(
+      url::Origin::Create(GURL("https://frame.test/")),
+      net::SchemefulSite(GURL("https://top-frame.test")));
+  std::unique_ptr<SharedDictionaryManager> manager =
+      CreateSharedDictionaryManager();
+  scoped_refptr<SharedDictionaryStorage> storage =
+      manager->GetStorage(isolation_key);
+  WriteDictionary(storage.get(), GURL("https://origin.test/1"), "p1*",
+                  {kTestData1});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/2"), "p2*",
+                  {kTestData2});
+  // Move the clock forward by 1 day.
+  task_environment_.FastForwardBy(base::Days(1));
+  WriteDictionary(storage.get(), GURL("https://origin.test/3"), "p3*",
+                  {kTestData1});
+  // Move the clock forward by 12 hours.
+  task_environment_.FastForwardBy(base::Hours(12));
+
+  if (GetParam() == TestManagerType::kOnDisk) {
+    FlushCacheTasks();
+  }
+
+  // Get a dictionary before calling ClearData().
+  std::unique_ptr<SharedDictionary> dict =
+      storage->GetDictionary(GURL("https://origin.test/p2?"));
+  ASSERT_TRUE(dict);
+
+  base::RunLoop run_loop;
+  manager->ClearData(
+      base::Time::Now() - base::Days(2), base::Time::Now() - base::Days(1),
+      base::RepeatingCallback<bool(const GURL&)>(), run_loop.QuitClosure());
+  run_loop.Run();
+
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p1?")));
+  EXPECT_FALSE(storage->GetDictionary(GURL("https://origin.test/p2?")));
+  EXPECT_TRUE(storage->GetDictionary(GURL("https://origin.test/p3?")));
+
+  // We can still read the deleted dictionary from `dict`.
+  net::TestCompletionCallback read_callback;
+  EXPECT_EQ(net::OK,
+            read_callback.GetResult(dict->ReadAll(read_callback.callback())));
+  EXPECT_EQ(kTestData2,
+            std::string(reinterpret_cast<const char*>(dict->data()->data()),
+                        dict->size()));
+}
+
 }  // namespace network
diff --git a/services/network/shared_dictionary/shared_dictionary_on_disk.cc b/services/network/shared_dictionary/shared_dictionary_on_disk.cc
index dbfd71b..1d713225 100644
--- a/services/network/shared_dictionary/shared_dictionary_on_disk.cc
+++ b/services/network/shared_dictionary/shared_dictionary_on_disk.cc
@@ -14,8 +14,11 @@
     size_t size,
     const net::SHA256HashValue& hash,
     const base::UnguessableToken& disk_cache_key_token,
-    SharedDictionaryDiskCache* disk_cahe)
-    : size_(size), hash_(hash) {
+    SharedDictionaryDiskCache* disk_cahe,
+    base::OnceClosure disk_cache_error_callback)
+    : size_(size),
+      hash_(hash),
+      disk_cache_error_callback_(std::move(disk_cache_error_callback)) {
   auto split_callback = base::SplitOnceCallback(base::BindOnce(
       &SharedDictionaryOnDisk::OnEntry, weak_factory_.GetWeakPtr()));
   disk_cache::EntryResult result = disk_cahe->OpenOrCreateEntry(
@@ -91,6 +94,10 @@
   CHECK_NE(State::kLoading, state);
   CHECK_EQ(State::kLoading, state_);
   state_ = state;
+
+  if (state_ == State::kFailed && disk_cache_error_callback_) {
+    std::move(disk_cache_error_callback_).Run();
+  }
   auto readall_callbacks = std::move(readall_callbacks_);
   for (auto& readall_callback : readall_callbacks) {
     if (state_ == State::kDone) {
diff --git a/services/network/shared_dictionary/shared_dictionary_on_disk.h b/services/network/shared_dictionary/shared_dictionary_on_disk.h
index 555b678..c078c32 100644
--- a/services/network/shared_dictionary/shared_dictionary_on_disk.h
+++ b/services/network/shared_dictionary/shared_dictionary_on_disk.h
@@ -8,6 +8,7 @@
 #include "services/network/shared_dictionary/shared_dictionary.h"
 
 #include "base/component_export.h"
+#include "base/functional/callback.h"
 #include "base/unguessable_token.h"
 #include "net/base/hash_value.h"
 #include "net/disk_cache/disk_cache.h"
@@ -30,7 +31,8 @@
   SharedDictionaryOnDisk(size_t size,
                          const net::SHA256HashValue& hash,
                          const base::UnguessableToken& disk_cache_key_token,
-                         SharedDictionaryDiskCache* disk_cahe);
+                         SharedDictionaryDiskCache* disk_cahe,
+                         base::OnceClosure disk_cache_error_callback);
 
   ~SharedDictionaryOnDisk() override;
 
@@ -52,6 +54,7 @@
 
   const size_t size_;
   const net::SHA256HashValue hash_;
+  base::OnceClosure disk_cache_error_callback_;
 
   std::vector<base::OnceCallback<void(int)>> readall_callbacks_;
   disk_cache::ScopedEntryPtr entry_;
diff --git a/services/network/shared_dictionary/shared_dictionary_on_disk_unittest.cc b/services/network/shared_dictionary/shared_dictionary_on_disk_unittest.cc
index 754814a..3fec30f 100644
--- a/services/network/shared_dictionary/shared_dictionary_on_disk_unittest.cc
+++ b/services/network/shared_dictionary/shared_dictionary_on_disk_unittest.cc
@@ -89,7 +89,8 @@
       });
 
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindOnce([]() { NOTREACHED(); }));
   EXPECT_EQ(expected_size, dictionary->size());
   EXPECT_EQ(hash, dictionary->hash());
 
@@ -152,7 +153,8 @@
       });
 
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindOnce([]() { NOTREACHED(); }));
 
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
@@ -209,7 +211,8 @@
       });
 
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindOnce([]() { NOTREACHED(); }));
 
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
@@ -263,7 +266,8 @@
       });
 
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindOnce([]() { NOTREACHED(); }));
 
   // ReadAll() synchronously returns OK.
   EXPECT_EQ(net::OK, dictionary->ReadAll(base::BindLambdaForTesting(
@@ -289,12 +293,17 @@
         return disk_cache::EntryResult::MakeError(net::ERR_IO_PENDING);
       });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
             dictionary->ReadAll(base::BindLambdaForTesting([&](int rv) {
               EXPECT_EQ(net::ERR_FAILED, rv);
+              EXPECT_TRUE(disk_cache_error_callback_called);
               read_all_finished = true;
             })));
 
@@ -323,11 +332,16 @@
         return disk_cache::EntryResult::MakeError(net::ERR_FAILED);
       });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
 
   EXPECT_EQ(net::ERR_FAILED, dictionary->ReadAll(base::BindLambdaForTesting(
                                  [&](int rv) { ASSERT_TRUE(false); })));
+  EXPECT_TRUE(disk_cache_error_callback_called);
 }
 
 TEST(SharedDictionaryOnDiskTest, AsyncOpenEntryAsyncReadDataFailure) {
@@ -365,13 +379,18 @@
         return net::ERR_IO_PENDING;
       });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
 
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
             dictionary->ReadAll(base::BindLambdaForTesting([&](int rv) {
               EXPECT_EQ(net::ERR_FAILED, rv);
+              EXPECT_TRUE(disk_cache_error_callback_called);
               read_all_finished = true;
             })));
 
@@ -420,13 +439,18 @@
         return net::ERR_FAILED;
       });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
 
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
             dictionary->ReadAll(base::BindLambdaForTesting([&](int rv) {
               EXPECT_EQ(net::ERR_FAILED, rv);
+              EXPECT_TRUE(disk_cache_error_callback_called);
               read_all_finished = true;
             })));
 
@@ -472,13 +496,18 @@
         return disk_cache::EntryResult::MakeOpened(entry.release());
       });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
 
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
             dictionary->ReadAll(base::BindLambdaForTesting([&](int rv) {
               EXPECT_EQ(net::ERR_FAILED, rv);
+              EXPECT_TRUE(disk_cache_error_callback_called);
               read_all_finished = true;
             })));
 
@@ -522,12 +551,17 @@
         return disk_cache::EntryResult::MakeOpened(entry.release());
       });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
 
   // ReadAll() synchronously returns ERR_FAILED.
   EXPECT_EQ(net::ERR_FAILED, dictionary->ReadAll(base::BindLambdaForTesting(
                                  [&](int rv) { ASSERT_TRUE(false); })));
+  EXPECT_TRUE(disk_cache_error_callback_called);
 }
 
 TEST(SharedDictionaryOnDiskTest, UnexpectedDataSize) {
@@ -554,13 +588,18 @@
     return expected_size + 1;  // Returns wrong size.
   });
 
+  bool disk_cache_error_callback_called = false;
   auto dictionary = std::make_unique<SharedDictionaryOnDisk>(
-      expected_size, hash, disk_cache_key_token, disk_cache.get());
+      expected_size, hash, disk_cache_key_token, disk_cache.get(),
+      /*disk_cache_error_callback=*/base::BindLambdaForTesting([&]() {
+        disk_cache_error_callback_called = true;
+      }));
 
   bool read_all_finished = false;
   EXPECT_EQ(net::ERR_IO_PENDING,
             dictionary->ReadAll(base::BindLambdaForTesting([&](int rv) {
               EXPECT_EQ(net::ERR_FAILED, rv);
+              EXPECT_TRUE(disk_cache_error_callback_called);
               read_all_finished = true;
             })));
 
diff --git a/services/network/shared_dictionary/shared_dictionary_storage_in_memory.cc b/services/network/shared_dictionary/shared_dictionary_storage_in_memory.cc
index e77d0691..9168f727 100644
--- a/services/network/shared_dictionary/shared_dictionary_storage_in_memory.cc
+++ b/services/network/shared_dictionary/shared_dictionary_storage_in_memory.cc
@@ -4,34 +4,67 @@
 
 #include "services/network/shared_dictionary/shared_dictionary_storage_in_memory.h"
 
+#include "base/containers/cxx20_erase_map.h"
 #include "base/logging.h"
 #include "base/strings/pattern.h"
 #include "base/strings/string_util.h"
 #include "net/base/io_buffer.h"
 #include "services/network/shared_dictionary/shared_dictionary_in_memory.h"
+#include "services/network/shared_dictionary/shared_dictionary_manager_in_memory.h"
 #include "services/network/shared_dictionary/shared_dictionary_writer_in_memory.h"
 #include "url/scheme_host_port.h"
 
 namespace network {
 
 SharedDictionaryStorageInMemory::SharedDictionaryStorageInMemory(
+    base::WeakPtr<SharedDictionaryManagerInMemory> manager,
     base::ScopedClosureRunner on_deleted_closure_runner)
-    : on_deleted_closure_runner_(std::move(on_deleted_closure_runner)) {}
+    : manager_(manager),
+      on_deleted_closure_runner_(std::move(on_deleted_closure_runner)) {}
 
 SharedDictionaryStorageInMemory::~SharedDictionaryStorageInMemory() = default;
 
 std::unique_ptr<SharedDictionary>
 SharedDictionaryStorageInMemory::GetDictionary(const GURL& url) {
-  const DictionaryInfo* info =
+  DictionaryInfo* info =
       GetMatchingDictionaryFromDictionaryInfoMap(dictionary_info_map_, url);
 
   if (!info) {
     return nullptr;
   }
+  info->set_last_used_time(base::Time::Now());
   return std::make_unique<SharedDictionaryInMemory>(info->data(), info->size(),
                                                     info->hash());
 }
 
+void SharedDictionaryStorageInMemory::DeleteDictionary(
+    const url::SchemeHostPort& host,
+    const std::string& match) {
+  auto it = dictionary_info_map_.find(host);
+  if (it != dictionary_info_map_.end()) {
+    it->second.erase(match);
+    if (it->second.empty()) {
+      dictionary_info_map_.erase(it);
+    }
+  }
+}
+
+void SharedDictionaryStorageInMemory::ClearData(
+    base::Time start_time,
+    base::Time end_time,
+    base::RepeatingCallback<bool(const GURL&)> url_matcher) {
+  for (auto& it : dictionary_info_map_) {
+    base::EraseIf(it.second, [start_time, end_time, url_matcher](auto& it2) {
+      const DictionaryInfo& dict = it2.second;
+      return (dict.response_time() >= start_time) &&
+             (dict.response_time() < end_time) &&
+             (!url_matcher || url_matcher.Run(dict.url().GetWithEmptyPath()));
+    });
+  }
+  base::EraseIf(dictionary_info_map_,
+                [](auto& it) { return it.second.empty(); });
+}
+
 scoped_refptr<SharedDictionaryWriter>
 SharedDictionaryStorageInMemory::CreateWriter(const GURL& url,
                                               base::Time response_time,
@@ -56,7 +89,11 @@
   }
   dictionary_info_map_[url::SchemeHostPort(url)].insert(std::make_pair(
       match,
-      DictionaryInfo(url, response_time, expiration, match, data, size, hash)));
+      DictionaryInfo(url, response_time, expiration, match,
+                     /*last_used_time=*/base::Time::Now(), data, size, hash)));
+  if (manager_) {
+    manager_->MaybeRunCacheEviction();
+  }
 }
 
 SharedDictionaryStorageInMemory::DictionaryInfo::DictionaryInfo(
@@ -64,6 +101,7 @@
     base::Time response_time,
     base::TimeDelta expiration,
     const std::string& match,
+    base::Time last_used_time,
     scoped_refptr<net::IOBuffer> data,
     size_t size,
     const net::SHA256HashValue& hash)
@@ -71,6 +109,7 @@
       response_time_(response_time),
       expiration_(expiration),
       match_(match),
+      last_used_time_(last_used_time),
       data_(std::move(data)),
       size_(size),
       hash_(hash) {}
diff --git a/services/network/shared_dictionary/shared_dictionary_storage_in_memory.h b/services/network/shared_dictionary/shared_dictionary_storage_in_memory.h
index 44f0a2f8..ceb3c90 100644
--- a/services/network/shared_dictionary/shared_dictionary_storage_in_memory.h
+++ b/services/network/shared_dictionary/shared_dictionary_storage_in_memory.h
@@ -9,6 +9,7 @@
 #include <set>
 
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
@@ -27,25 +28,12 @@
 class CorsURLLoaderSharedDictionaryTest;
 }  // namespace cors
 
+class SharedDictionaryManagerInMemory;
+
 // A SharedDictionaryStorage which is managed by
 // SharedDictionaryManagerInMemory.
 class SharedDictionaryStorageInMemory : public SharedDictionaryStorage {
  public:
-  explicit SharedDictionaryStorageInMemory(
-      base::ScopedClosureRunner on_deleted_closure_runner);
-
-  SharedDictionaryStorageInMemory(const SharedDictionaryStorageInMemory&) =
-      delete;
-  SharedDictionaryStorageInMemory& operator=(
-      const SharedDictionaryStorageInMemory&) = delete;
-
-  // SharedDictionaryStorage
-  std::unique_ptr<SharedDictionary> GetDictionary(const GURL& url) override;
-
- private:
-  friend class SharedDictionaryManagerTest;
-  friend class network::cors::CorsURLLoaderSharedDictionaryTest;
-
   // This class is used to keep the dictionary information in memory.
   class DictionaryInfo {
    public:
@@ -53,6 +41,7 @@
                    base::Time response_time,
                    base::TimeDelta expiration,
                    const std::string& match,
+                   base::Time last_used_time,
                    scoped_refptr<net::IOBuffer> data,
                    size_t size,
                    const net::SHA256HashValue& hash);
@@ -69,20 +58,52 @@
     const base::Time& response_time() const { return response_time_; }
     base::TimeDelta expiration() const { return expiration_; }
     const std::string& match() const { return match_; }
+    const base::Time& last_used_time() const { return last_used_time_; }
     const scoped_refptr<net::IOBuffer>& data() const { return data_; }
     size_t size() const { return size_; }
     const net::SHA256HashValue& hash() const { return hash_; }
 
+    void set_last_used_time(base::Time last_used_time) {
+      last_used_time_ = last_used_time;
+    }
+
    private:
     GURL url_;
     base::Time response_time_;
     base::TimeDelta expiration_;
     std::string match_;
+    base::Time last_used_time_;
     scoped_refptr<net::IOBuffer> data_;
     size_t size_;
     net::SHA256HashValue hash_;
   };
 
+  SharedDictionaryStorageInMemory(
+      base::WeakPtr<SharedDictionaryManagerInMemory> manager,
+      base::ScopedClosureRunner on_deleted_closure_runner);
+
+  SharedDictionaryStorageInMemory(const SharedDictionaryStorageInMemory&) =
+      delete;
+  SharedDictionaryStorageInMemory& operator=(
+      const SharedDictionaryStorageInMemory&) = delete;
+
+  // SharedDictionaryStorage
+  std::unique_ptr<SharedDictionary> GetDictionary(const GURL& url) override;
+
+  const std::map<url::SchemeHostPort, std::map<std::string, DictionaryInfo>>&
+  GetDictionaryMap() {
+    return dictionary_info_map_;
+  }
+
+  void DeleteDictionary(const url::SchemeHostPort& host,
+                        const std::string& match);
+  void ClearData(base::Time start_time,
+                 base::Time end_time,
+                 base::RepeatingCallback<bool(const GURL&)> url_matcher);
+
+ private:
+  friend class SharedDictionaryManagerTest;
+  friend class network::cors::CorsURLLoaderSharedDictionaryTest;
   ~SharedDictionaryStorageInMemory() override;
 
   // SharedDictionaryStorage
@@ -102,11 +123,7 @@
                            size_t size,
                            const net::SHA256HashValue& hash);
 
-  const std::map<url::SchemeHostPort, std::map<std::string, DictionaryInfo>>&
-  GetDictionaryMapForTesting() {
-    return dictionary_info_map_;
-  }
-
+  base::WeakPtr<SharedDictionaryManagerInMemory> manager_;
   base::ScopedClosureRunner on_deleted_closure_runner_;
 
   std::map<url::SchemeHostPort, std::map<std::string, DictionaryInfo>>
diff --git a/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc b/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc
index 4b5f491..928896e 100644
--- a/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc
+++ b/services/network/shared_dictionary/shared_dictionary_storage_on_disk.cc
@@ -31,8 +31,13 @@
       const net::SHA256HashValue& hash,
       const base::UnguessableToken& disk_cache_key_token,
       SharedDictionaryDiskCache& disk_cahe,
+      base::OnceClosure disk_cache_error_callback,
       base::ScopedClosureRunner on_deleted_closure_runner)
-      : SharedDictionaryOnDisk(size, hash, disk_cache_key_token, &disk_cahe),
+      : SharedDictionaryOnDisk(size,
+                               hash,
+                               disk_cache_key_token,
+                               &disk_cahe,
+                               std::move(disk_cache_error_callback)),
         on_deleted_closure_runner_(std::move(on_deleted_closure_runner)) {}
 
  private:
@@ -112,6 +117,9 @@
       RefCountedSharedDictionary>(
       info->size(), info->hash(), info->disk_cache_key_token(),
       manager_->disk_cache(),
+      base::BindOnce(
+          &SharedDictionaryManagerOnDisk::MaybePostMismatchingEntryDeletionTask,
+          manager_),
       base::ScopedClosureRunner(base::BindOnce(
           &SharedDictionaryStorageOnDisk::OnRefCountedSharedDictionaryDeleted,
           weak_factory_.GetWeakPtr(), info->disk_cache_key_token())));
diff --git a/services/viz/privileged/mojom/compositing/BUILD.gn b/services/viz/privileged/mojom/compositing/BUILD.gn
index 1da189d5..ee47aae0 100644
--- a/services/viz/privileged/mojom/compositing/BUILD.gn
+++ b/services/viz/privileged/mojom/compositing/BUILD.gn
@@ -21,6 +21,7 @@
 
   public_deps = [
     "//gpu/ipc/common:interfaces",
+    "//gpu/ipc/common:surface_handle",
     "//media/capture/mojom:video_capture",
     "//media/mojo/mojom",
     "//mojo/public/mojom/base",
diff --git a/services/viz/privileged/mojom/gl/BUILD.gn b/services/viz/privileged/mojom/gl/BUILD.gn
index 588f0df..19542f7 100644
--- a/services/viz/privileged/mojom/gl/BUILD.gn
+++ b/services/viz/privileged/mojom/gl/BUILD.gn
@@ -16,8 +16,11 @@
 
   parser_deps = [ "//components/viz/common:buildflags" ]
 
+  deps = [ "//gpu/ipc/common:gmb_interface" ]
+
   public_deps = [
     "//gpu/ipc/common:interfaces",
+    "//gpu/ipc/common:surface_handle",
     "//media/mojo/mojom",
     "//third_party/blink/public/mojom/tokens:tokens",
     "//ui/gfx/geometry/mojom",
diff --git a/services/viz/privileged/mojom/gl/gpu_service.mojom b/services/viz/privileged/mojom/gl/gpu_service.mojom
index 80ab0ff..4cdc627c0 100644
--- a/services/viz/privileged/mojom/gl/gpu_service.mojom
+++ b/services/viz/privileged/mojom/gl/gpu_service.mojom
@@ -25,6 +25,7 @@
 import "gpu/ipc/common/gpu_info.mojom";
 import "gpu/ipc/common/gpu_peak_memory.mojom";
 import "gpu/ipc/common/memory_stats.mojom";
+import "gpu/ipc/common/client_gmb_interface.mojom";
 import "gpu/ipc/common/surface_handle.mojom";
 import "gpu/ipc/common/sync_token.mojom";
 import "media/mojo/mojom/video_encode_accelerator.mojom";
@@ -122,6 +123,10 @@
   [EnableIf=is_win]
   UnregisterDCOMPSurfaceHandle(mojo_base.mojom.UnguessableToken token);
 
+  // Binds the pending receiver.
+  BindClientGmbInterface(
+  pending_receiver<gpu.mojom.ClientGmbInterface> receiver, int32 client_id);
+
   // Creates a VideoEncodeAcceleratorProvider and binds it to |vea_provider|.
   CreateVideoEncodeAcceleratorProvider(
       pending_receiver<media.mojom.VideoEncodeAcceleratorProvider>
diff --git a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index 4280cda..805a606 100644
--- a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -12,7 +12,9 @@
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_restrictions.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "gpu/ipc/common/gpu_memory_buffer_impl.h"
+#include "gpu/ipc/common/gpu_memory_buffer_impl_shared_memory.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
@@ -21,17 +23,21 @@
 namespace viz {
 
 ClientGpuMemoryBufferManager::ClientGpuMemoryBufferManager(
-    mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu)
+    mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu,
+    mojo::PendingRemote<gpu::mojom::ClientGmbInterface> gpu_direct)
     : thread_("GpuMemoryThread"),
       gpu_memory_buffer_support_(
           std::make_unique<gpu::GpuMemoryBufferSupport>()),
-      pool_(base::MakeRefCounted<base::UnsafeSharedMemoryPool>()) {
+      pool_(base::MakeRefCounted<base::UnsafeSharedMemoryPool>()),
+      use_client_gmb_interface_(
+          base::FeatureList::IsEnabled(features::kUseClientGmbInterface)) {
   CHECK(thread_.Start());
   // The thread is owned by this object. Which means the task will not run if
   // the object has been destroyed. So Unretained() is safe.
   thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&ClientGpuMemoryBufferManager::InitThread,
-                                base::Unretained(this), std::move(gpu)));
+                                base::Unretained(this), std::move(gpu),
+                                std::move(gpu_direct)));
 }
 
 ClientGpuMemoryBufferManager::~ClientGpuMemoryBufferManager() {
@@ -42,11 +48,19 @@
 }
 
 void ClientGpuMemoryBufferManager::InitThread(
-    mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu_remote) {
+    mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu_remote,
+    mojo::PendingRemote<gpu::mojom::ClientGmbInterface> gpu_direct_remote) {
   gpu_.Bind(std::move(gpu_remote));
   gpu_.set_disconnect_handler(
       base::BindOnce(&ClientGpuMemoryBufferManager::DisconnectGpuOnThread,
                      base::Unretained(this)));
+
+  if (use_client_gmb_interface_) {
+    gpu_direct_.Bind(std::move(gpu_direct_remote));
+    gpu_direct_.set_disconnect_handler(
+        base::BindOnce(&ClientGpuMemoryBufferManager::DisconnectGpuOnThread,
+                       base::Unretained(this)));
+  }
   weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
 }
 
@@ -56,9 +70,8 @@
 }
 
 void ClientGpuMemoryBufferManager::DisconnectGpuOnThread() {
-  if (!gpu_.is_bound())
-    return;
   gpu_.reset();
+  gpu_direct_.reset();
   for (auto* waiter : pending_allocation_waiters_)
     waiter->Signal();
   pending_allocation_waiters_.clear();
@@ -72,22 +85,28 @@
     base::WaitableEvent* wait) {
   DCHECK(thread_.task_runner()->BelongsToCurrentThread());
 
-  if (!gpu_) {
-    // The Gpu interface may have been disconnected, in which case we can't
-    // fulfill the request.
-    wait->Signal();
-    return;
-  }
-
   // |handle| and |wait| are both on the stack, and will be alive until |wait|
   // is signaled. So it is safe for OnGpuMemoryBufferAllocated() to operate on
   // these.
+  if (gpu_direct_) {
+    gpu_direct_->CreateGpuMemoryBuffer(
+        gfx::GpuMemoryBufferId(++counter_), size, format, usage,
+        gpu::kNullSurfaceHandle,
+        base::BindOnce(
+            &ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread,
+            base::Unretained(this), handle, wait));
+  } else if (gpu_) {
+    gpu_->CreateGpuMemoryBuffer(
+        gfx::GpuMemoryBufferId(++counter_), size, format, usage,
+        base::BindOnce(
+            &ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread,
+            base::Unretained(this), handle, wait));
+  } else {
+    // If the interface are disconnected, we can't fulfill the request.
+    wait->Signal();
+    return;
+  }
   pending_allocation_waiters_.insert(wait);
-  gpu_->CreateGpuMemoryBuffer(
-      gfx::GpuMemoryBufferId(++counter_), size, format, usage,
-      base::BindOnce(
-          &ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread,
-          base::Unretained(this), handle, wait));
 }
 
 void ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread(
@@ -112,7 +131,12 @@
     return;
   }
 
-  if (gpu_) {
+  // Note that when |use_client_gmb_interface_| is enabled, both |gpu_| and
+  // |gpu_direct_| would be connected. |gpu_direct_| should be used to destroy
+  // GMB in that case.
+  if (gpu_direct_) {
+    gpu_direct_->DestroyGpuMemoryBuffer(id);
+  } else if (gpu_) {
     gpu_->DestroyGpuMemoryBuffer(id);
   }
 }
@@ -170,8 +194,14 @@
     return;
   }
 
-  gpu_->CopyGpuMemoryBuffer(std::move(buffer_handle), std::move(memory_region),
-                            std::move(callback));
+  if (gpu_direct_) {
+    gpu_direct_->CopyGpuMemoryBuffer(std::move(buffer_handle),
+                                     std::move(memory_region),
+                                     std::move(callback));
+  } else if (gpu_) {
+    gpu_->CopyGpuMemoryBuffer(std::move(buffer_handle),
+                              std::move(memory_region), std::move(callback));
+  }
 }
 
 bool ClientGpuMemoryBufferManager::CopyGpuMemoryBufferSync(
diff --git a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
index 101027a..23b4d08 100644
--- a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
+++ b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "gpu/ipc/common/client_gmb_interface.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/viz/public/mojom/gpu.mojom.h"
@@ -33,8 +34,9 @@
 // mojom::GpuMemoryBufferFactory
 class ClientGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
  public:
-  explicit ClientGpuMemoryBufferManager(
-      mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu);
+  ClientGpuMemoryBufferManager(
+      mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu,
+      mojo::PendingRemote<gpu::mojom::ClientGmbInterface> gpu_direct);
 
   ClientGpuMemoryBufferManager(const ClientGpuMemoryBufferManager&) = delete;
   ClientGpuMemoryBufferManager& operator=(const ClientGpuMemoryBufferManager&) =
@@ -44,7 +46,8 @@
 
  private:
   void InitThread(
-      mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu_remote);
+      mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu_remote,
+      mojo::PendingRemote<gpu::mojom::ClientGmbInterface> gpu_direct_remote);
   void TearDownThread();
   void DisconnectGpuOnThread();
   void AllocateGpuMemoryBufferOnThread(const gfx::Size& size,
@@ -77,11 +80,13 @@
   // TODO(sad): Explore the option of doing this from an existing thread.
   base::Thread thread_;
   mojo::Remote<mojom::GpuMemoryBufferFactory> gpu_;
+  mojo::Remote<gpu::mojom::ClientGmbInterface> gpu_direct_;
   base::WeakPtr<ClientGpuMemoryBufferManager> weak_ptr_;
   std::set<base::WaitableEvent*> pending_allocation_waiters_;
   std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support_;
 
   scoped_refptr<base::UnsafeSharedMemoryPool> pool_;
+  const bool use_client_gmb_interface_;
 
   base::WeakPtrFactory<ClientGpuMemoryBufferManager> weak_ptr_factory_{this};
 };
diff --git a/services/viz/public/cpp/gpu/gpu.cc b/services/viz/public/cpp/gpu/gpu.cc
index ebbcecd..acd0e10 100644
--- a/services/viz/public/cpp/gpu/gpu.cc
+++ b/services/viz/public/cpp/gpu/gpu.cc
@@ -18,6 +18,8 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "gpu/config/gpu_finch_features.h"
+#include "gpu/ipc/common/client_gmb_interface.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
@@ -40,7 +42,9 @@
 
   void Initialize(mojo::PendingRemote<mojom::Gpu> gpu_remote,
                   mojo::PendingReceiver<mojom::GpuMemoryBufferFactory>
-                      memory_buffer_factory_receiver) {
+                      memory_buffer_factory_receiver,
+                  mojo::PendingReceiver<gpu::mojom::ClientGmbInterface>
+                      client_gmb_interface_receiver) {
     DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
     gpu_remote_.Bind(std::move(gpu_remote));
@@ -48,6 +52,10 @@
         base::BindOnce(&GpuPtrIO::ConnectionError, base::Unretained(this)));
     gpu_remote_->CreateGpuMemoryBufferFactory(
         std::move(memory_buffer_factory_receiver));
+    if (base::FeatureList::IsEnabled(features::kUseClientGmbInterface)) {
+      gpu_remote_->CreateClientGpuMemoryBufferFactory(
+          std::move(client_gmb_interface_receiver));
+    }
   }
 
   void EstablishGpuChannel(scoped_refptr<EstablishRequest> establish_request) {
@@ -253,8 +261,15 @@
   mojo::PendingRemote<mojom::GpuMemoryBufferFactory> gpu_memory_buffer_factory;
   auto gpu_memory_buffer_factory_receiver =
       gpu_memory_buffer_factory.InitWithNewPipeAndPassReceiver();
+
+  mojo::PendingRemote<gpu::mojom::ClientGmbInterface> client_gmb_interface;
+  auto client_gmb_interface_receiver =
+      client_gmb_interface.InitWithNewPipeAndPassReceiver();
+
+  // Note that since |gpu_memory_buffer_manager_| is a owned by this object, it
+  // is safe to provide a |this| pointer to it.
   gpu_memory_buffer_manager_ = std::make_unique<ClientGpuMemoryBufferManager>(
-      std::move(gpu_memory_buffer_factory));
+      std::move(gpu_memory_buffer_factory), std::move(client_gmb_interface));
   // Initialize mojo::Remote<mojom::Gpu> on the IO thread. |gpu_| can only be
   // used on the IO thread after this point. It is safe to use base::Unretained
   // with |gpu_| for IO thread tasks as |gpu_| is destroyed by an IO thread task
@@ -263,7 +278,8 @@
       FROM_HERE,
       base::BindOnce(&GpuPtrIO::Initialize, base::Unretained(gpu_.get()),
                      std::move(gpu_remote),
-                     std::move(gpu_memory_buffer_factory_receiver)));
+                     std::move(gpu_memory_buffer_factory_receiver),
+                     std::move(client_gmb_interface_receiver)));
 }
 
 Gpu::~Gpu() {
diff --git a/services/viz/public/cpp/gpu/gpu_unittest.cc b/services/viz/public/cpp/gpu/gpu_unittest.cc
index a782593..3d1870b 100644
--- a/services/viz/public/cpp/gpu/gpu_unittest.cc
+++ b/services/viz/public/cpp/gpu/gpu_unittest.cc
@@ -50,6 +50,10 @@
   void CreateGpuMemoryBufferFactory(
       mojo::PendingReceiver<mojom::GpuMemoryBufferFactory> receiver) override {}
 
+  void CreateClientGpuMemoryBufferFactory(
+      mojo::PendingReceiver<gpu::mojom::ClientGmbInterface> receiver) override {
+  }
+
   void EstablishGpuChannel(EstablishGpuChannelCallback callback) override {
     if (close_binding_on_request_) {
       // Don't run |callback| and trigger a connection error on the other end.
diff --git a/services/viz/public/mojom/BUILD.gn b/services/viz/public/mojom/BUILD.gn
index 17f0b9ab..1c98e9b 100644
--- a/services/viz/public/mojom/BUILD.gn
+++ b/services/viz/public/mojom/BUILD.gn
@@ -48,6 +48,9 @@
     "hit_test/hit_test_region_list.mojom",
     "hit_test/input_target_client.mojom",
   ]
+
+  deps = [ "//gpu/ipc/common:gmb_interface" ]
+
   public_deps = [
     ":resource_format",
     ":shared_image_format",
diff --git a/services/viz/public/mojom/gpu.mojom b/services/viz/public/mojom/gpu.mojom
index acc98fb4..e9348fc5 100644
--- a/services/viz/public/mojom/gpu.mojom
+++ b/services/viz/public/mojom/gpu.mojom
@@ -6,6 +6,7 @@
 
 import "gpu/ipc/common/gpu_feature_info.mojom";
 import "gpu/ipc/common/gpu_info.mojom";
+import "gpu/ipc/common/client_gmb_interface.mojom";
 import "gpu/ipc/common/sync_token.mojom";
 [EnableIf=is_chromeos_ash]
 import "components/chromeos_camera/common/mjpeg_decode_accelerator.mojom";
@@ -38,6 +39,11 @@
   CreateGpuMemoryBufferFactory(
       pending_receiver<GpuMemoryBufferFactory> receiver);
 
+  // Creates a  direct channel between renderer and gpu process for GMB
+  // creation.
+  CreateClientGpuMemoryBufferFactory(
+      pending_receiver<gpu.mojom.ClientGmbInterface> receiver);
+
   // Tells the GPU service to create a new channel for communication with a
   // client. The GPU service responds with client ID, IPC handle and
   // GPUInfo.
diff --git a/storage/browser/quota/quota_database.cc b/storage/browser/quota/quota_database.cc
index 42059f4..a5c845cd 100644
--- a/storage/browser/quota/quota_database.cc
+++ b/storage/browser/quota/quota_database.cc
@@ -47,8 +47,8 @@
 // Version 2 - 2011-04-25 - http://crrev.com/82847 (unsupported)
 // Version 3 - 2011-07-08 - http://crrev.com/91835 (unsupported)
 // Version 4 - 2011-10-17 - http://crrev.com/105822 (unsupported)
-// Version 5 - 2015-10-19 - https://crrev.com/354932
-// Version 6 - 2021-04-27 - https://crrev.com/c/2757450
+// Version 5 - 2015-10-19 - https://crrev.com/354932 (unsupported)
+// Version 6 - 2021-04-27 - https://crrev.com/c/2757450 (unsupported)
 // Version 7 - 2021-05-20 - https://crrev.com/c/2910136
 // Version 8 - 2021-09-01 - https://crrev.com/c/3119831
 // Version 9 - 2022-05-13 - https://crrev.com/c/3601253
diff --git a/storage/browser/quota/quota_database_migrations.cc b/storage/browser/quota/quota_database_migrations.cc
index 37668a47..1d95a0ddf 100644
--- a/storage/browser/quota/quota_database_migrations.cc
+++ b/storage/browser/quota/quota_database_migrations.cc
@@ -79,22 +79,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
   DCHECK_EQ(0, quota_database.db_->transaction_nesting());
 
-  // Reset tables for versions lower than 5 since they are unsupported.
-  if (quota_database.meta_table_->GetVersionNumber() < 5)
+  // Reset tables for versions lower than 7 since they are unsupported.
+  if (quota_database.meta_table_->GetVersionNumber() < 7) {
     return quota_database.ResetStorage();
-
-  if (quota_database.meta_table_->GetVersionNumber() == 5) {
-    bool success = MigrateFromVersion5ToVersion7(quota_database);
-    RecordMigrationHistogram(/*old_version=*/5, /*new_version=*/7, success);
-    if (!success)
-      return false;
-  }
-
-  if (quota_database.meta_table_->GetVersionNumber() == 6) {
-    bool success = MigrateFromVersion6ToVersion7(quota_database);
-    RecordMigrationHistogram(/*old_version=*/6, /*new_version=*/7, success);
-    if (!success)
-      return false;
   }
 
   if (quota_database.meta_table_->GetVersionNumber() == 7) {
@@ -132,130 +119,6 @@
       success);
 }
 
-bool QuotaDatabaseMigrations::MigrateFromVersion5ToVersion7(
-    QuotaDatabase& quota_database) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
-
-  sql::Database* db = quota_database.db_.get();
-  sql::Transaction transaction(db);
-  if (!transaction.Begin())
-    return false;
-
-  // Create host quota table version 7.
-  // clang-format off
-  static constexpr char kQuotaTableSql[] =
-      "CREATE TABLE IF NOT EXISTS quota("
-         "host TEXT NOT NULL, "
-         "type INTEGER NOT NULL, "
-         "quota INTEGER NOT NULL, "
-         "PRIMARY KEY(host, type)) "
-         "WITHOUT ROWID";
-  // clang-format on
-  if (!db->Execute(kQuotaTableSql))
-    return false;
-
-  // Create buckets table version 7.
-  // clang-format off
-  static constexpr char kBucketsTableSql[] =
-      "CREATE TABLE IF NOT EXISTS buckets("
-        "id INTEGER PRIMARY KEY, "
-        "origin TEXT NOT NULL, "
-        "type INTEGER NOT NULL, "
-        "name TEXT NOT NULL, "
-        "use_count INTEGER NOT NULL, "
-        "last_accessed INTEGER NOT NULL, "
-        "last_modified INTEGER NOT NULL, "
-        "expiration INTEGER NOT NULL, "
-        "quota INTEGER NOT NULL)";
-  // clang-format on
-  if (!db->Execute(kBucketsTableSql))
-    return false;
-
-  // Copy OriginInfoTable data into new buckets table.
-  // clang-format off
-  static constexpr char kImportOriginInfoSql[] =
-      "INSERT INTO buckets("
-          "origin,"
-          "type,"
-          "name,"
-          "use_count,"
-          "last_accessed,"
-          "last_modified,"
-          "expiration,"
-          "quota) "
-        "SELECT "
-          "origin,"
-          "type,"
-          "?,"
-          "used_count,"
-          "last_access_time,"
-          "last_modified_time,"
-          "?,"
-          "0 "
-        "FROM OriginInfoTable";
-  // clang-format on
-  sql::Statement import_origin_info_statement(
-      db->GetCachedStatement(SQL_FROM_HERE, kImportOriginInfoSql));
-  import_origin_info_statement.BindString(0, kDefaultNamePreV10);
-  import_origin_info_statement.BindTime(1, base::Time::Max());
-  if (!import_origin_info_statement.Run())
-    return false;
-
-  // Delete OriginInfoTable.
-  static constexpr char kDeleteOriginInfoTableSql[] =
-      "DROP TABLE OriginInfoTable";
-  if (!db->Execute(kDeleteOriginInfoTableSql))
-    return false;
-
-  // Copy HostQuotaTable data into the new quota table.
-  // clang-format off
-  static constexpr char kImportQuotaSql[] =
-      "INSERT INTO quota(host, type, quota) "
-        "SELECT host, type, quota "
-        "FROM HostQuotaTable";
-  // clang-format on
-  sql::Statement import_quota_statement(
-      db->GetCachedStatement(SQL_FROM_HERE, kImportQuotaSql));
-  if (!import_quota_statement.Run())
-    return false;
-
-  // Delete HostQuotaTable.
-  static constexpr char kDeleteQuotaHostTableSql[] =
-      "DROP TABLE HostQuotaTable";
-  if (!db->Execute(kDeleteQuotaHostTableSql))
-    return false;
-
-  // Delete EvictionInfoTable.
-  static constexpr char kDeleteEvictionInfoTableSql[] =
-      "DROP TABLE EvictionInfoTable";
-  if (!db->Execute(kDeleteEvictionInfoTableSql))
-    return false;
-
-  // Upgrade to version 7 since it already deletes EvictionInfoTable.
-  return quota_database.meta_table_->SetVersionNumber(7) &&
-         quota_database.meta_table_->SetCompatibleVersionNumber(7) &&
-         transaction.Commit();
-}
-
-bool QuotaDatabaseMigrations::MigrateFromVersion6ToVersion7(
-    QuotaDatabase& quota_database) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
-
-  sql::Database* db = quota_database.db_.get();
-  sql::Transaction transaction(db);
-  if (!transaction.Begin())
-    return false;
-
-  static constexpr char kDeleteEvictionInfoTableSql[] =
-      "DROP TABLE eviction_info";
-  if (!db->Execute(kDeleteEvictionInfoTableSql))
-    return false;
-
-  return quota_database.meta_table_->SetVersionNumber(7) &&
-         quota_database.meta_table_->SetCompatibleVersionNumber(7) &&
-         transaction.Commit();
-}
-
 bool QuotaDatabaseMigrations::MigrateFromVersion7ToVersion8(
     QuotaDatabase& quota_database) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(quota_database.sequence_checker_);
diff --git a/storage/browser/quota/quota_database_migrations_unittest.cc b/storage/browser/quota/quota_database_migrations_unittest.cc
index 353de0f..67e6c5d 100644
--- a/storage/browser/quota/quota_database_migrations_unittest.cc
+++ b/storage/browser/quota/quota_database_migrations_unittest.cc
@@ -116,155 +116,6 @@
   EXPECT_EQ(GetCurrentSchema(), GetQuotaDatabaseSchema());
 }
 
-TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV5) {
-  ASSERT_TRUE(LoadDatabase("version_5.sql", DbPath()));
-
-  {
-    sql::Database db;
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), 5);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), 2);
-
-    ASSERT_TRUE(db.DoesTableExist("HostQuotaTable"));
-    ASSERT_TRUE(db.DoesTableExist("EvictionInfoTable"));
-    ASSERT_TRUE(db.DoesTableExist("OriginInfoTable"));
-    ASSERT_FALSE(db.DoesTableExist("buckets"));
-
-    // Check populated data.
-    EXPECT_EQ(
-        "http://a/|0|123|13260644621105493|13242931862595604,"
-        "http://b/|0|111|13250042735631065|13260999511438890,"
-        "http://c/|1|321|13261163582572088|13261079941303629",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM OriginInfoTable ORDER BY origin ASC", "|",
-            ","));
-    EXPECT_EQ("a.com,b.com,c.com",
-              sql::test::ExecuteWithResults(
-                  &db, "SELECT host FROM HostQuotaTable ORDER BY host ASC", "|",
-                  ","));
-  }
-
-  MigrateDatabase();
-
-  // Verify upgraded schema.
-  {
-    sql::Database db;
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), kCurrentSchemaVersion);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), kCurrentSchemaVersion);
-
-    ASSERT_FALSE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-    ASSERT_FALSE(db.DoesTableExist("HostQuotaTable"));
-    ASSERT_FALSE(db.DoesTableExist("EvictionInfoTable"));
-    ASSERT_FALSE(db.DoesTableExist("OriginInfoTable"));
-
-    // Check that OriginInfoTable data is migrated to bucket table.
-    EXPECT_EQ(
-        "1|http://a/|a|0|_default|123|13260644621105493|13242931862595604|"
-        "0|0|0|1,"
-        "2|http://b/|b|0|_default|111|13250042735631065|13260999511438890|"
-        "0|0|0|1",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY storage_key ASC", "|", ","));
-
-    EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema()));
-
-    EXPECT_EQ(GetTotalHistogramCount(), 4u);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV5ToV7",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV7ToV8",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV9ToV10",
-                                   /*sample=*/true, /*expected_count=*/1);
-  }
-}
-
-TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV6) {
-  ASSERT_TRUE(LoadDatabase("version_6.sql", DbPath()));
-
-  {
-    sql::Database db;
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), 6);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), 6);
-
-    ASSERT_TRUE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-    ASSERT_TRUE(db.DoesTableExist("eviction_info"));
-
-    // Check populated data.
-    EXPECT_EQ(
-        "1|http://a/|0|bucket_a|123|13260644621105493|13242931862595604|"
-        "9223372036854775807|0,"
-        "2|http://b/|0|bucket_b|111|13250042735631065|13260999511438890|"
-        "9223372036854775807|1000,"
-        "3|http://c/|2|bucket_c|321|13261163582572088|13261079941303629|"
-        "9223372036854775807|10000",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
-  }
-
-  MigrateDatabase();
-
-  // Verify upgraded schema.
-  {
-    sql::Database db;
-    ASSERT_TRUE(db.Open(DbPath()));
-
-    ASSERT_TRUE(sql::MetaTable::DoesTableExist(&db));
-    sql::MetaTable meta_table;
-    ASSERT_TRUE(
-        meta_table.Init(&db, kCurrentSchemaVersion, kCurrentCompatibleVersion));
-    ASSERT_EQ(meta_table.GetVersionNumber(), kCurrentSchemaVersion);
-    ASSERT_EQ(meta_table.GetCompatibleVersionNumber(), kCurrentSchemaVersion);
-
-    ASSERT_FALSE(db.DoesTableExist("quota"));
-    ASSERT_TRUE(db.DoesTableExist("buckets"));
-    ASSERT_FALSE(db.DoesTableExist("eviction_info"));
-
-    // Check that buckets data is still present.
-    EXPECT_EQ(
-        "1|http://a/|a|0|bucket_a|123|13260644621105493|13242931862595604|"
-        "0|0|0|0,"
-        "2|http://b/|b|0|bucket_b|111|13250042735631065|13260999511438890|"
-        "0|1000|0|0,"
-        "3|http://c/|c|2|bucket_c|321|13261163582572088|13261079941303629|"
-        "0|10000|0|0",
-        sql::test::ExecuteWithResults(
-            &db, "SELECT * FROM buckets ORDER BY id ASC", "|", ","));
-
-    EXPECT_EQ(GetCurrentSchema(), RemoveQuotes(db.GetSchema()));
-
-    EXPECT_EQ(GetTotalHistogramCount(), 4u);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV6ToV7",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV7ToV8",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV8ToV9",
-                                   /*sample=*/true, /*expected_count=*/1);
-    histograms_->ExpectBucketCount("Quota.DatabaseMigrationFromV9ToV10",
-                                   /*sample=*/true, /*expected_count=*/1);
-  }
-}
-
 TEST_F(QuotaDatabaseMigrationsTest, UpgradeSchemaFromV7) {
   ASSERT_TRUE(LoadDatabase("version_7.sql", DbPath()));
 
diff --git a/storage/test/data/quota_database/version_5.sql b/storage/test/data/quota_database/version_5.sql
deleted file mode 100644
index 3ea20b3..0000000
--- a/storage/test/data/quota_database/version_5.sql
+++ /dev/null
@@ -1,36 +0,0 @@
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE HostQuotaTable(host TEXT NOT NULL, type INTEGER NOT NULL, quota INTEGER DEFAULT 0, UNIQUE(host, type));
-
-CREATE TABLE OriginInfoTable(origin TEXT NOT NULL, type INTEGER NOT NULL, used_count INTEGER DEFAULT 0, last_access_time INTEGER DEFAULT 0, last_modified_time INTEGER DEFAULT 0, unique(origin, type));
-
-CREATE TABLE EvictionInfoTable(origin TEXT NOT NULL, type INTEGER NOT NULL, last_eviction_time INTEGER DEFAULT 0, UNIQUE(origin, type));
-
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-
-INSERT INTO meta VALUES('mmap_status', '-1');
-INSERT INTO meta VALUES('last_compatible_version', '2');
-INSERT INTO meta VALUES('version', '5');
-
-INSERT INTO HostQuotaTable(host, type, quota)  VALUES('a.com', 0, 0);
-INSERT INTO HostQuotaTable(host, type, quota)  VALUES('b.com', 0, 1);
-INSERT INTO HostQuotaTable(host, type, quota)  VALUES('c.com', 1, 123);
-
-INSERT INTO OriginInfoTable(origin, type, used_count, last_access_time, last_modified_time)
-  VALUES('http://a/', 0, 123, 13260644621105493, 13242931862595604);
-INSERT INTO OriginInfoTable(origin, type, used_count, last_access_time, last_modified_time)
-  VALUES('http://b/', 0, 111, 13250042735631065, 13260999511438890);
-INSERT INTO OriginInfoTable(origin, type, used_count, last_access_time, last_modified_time)
-  VALUES('http://c/', 1, 321, 13261163582572088, 13261079941303629);
-
-CREATE INDEX HostIndex ON HostQuotaTable(host);
-
-CREATE INDEX OriginInfoIndex ON OriginInfoTable(origin);
-
-CREATE INDEX OriginLastAccessTimeIndex ON OriginInfoTable(last_access_time);
-
-CREATE INDEX OriginLastModifiedTimeIndex ON OriginInfoTable(last_modified_time);
-
-COMMIT;
diff --git a/storage/test/data/quota_database/version_6.sql b/storage/test/data/quota_database/version_6.sql
deleted file mode 100644
index 38d6449..0000000
--- a/storage/test/data/quota_database/version_6.sql
+++ /dev/null
@@ -1,36 +0,0 @@
-PRAGMA foreign_keys=OFF;
-
-BEGIN TRANSACTION;
-
-CREATE TABLE quota(host TEXT NOT NULL, type INTEGER NOT NULL, quota INTEGER NOT NULL, PRIMARY KEY(host, type)) WITHOUT ROWID;
-
-CREATE TABLE buckets(id INTEGER PRIMARY KEY, origin TEXT NOT NULL, type INTEGER NOT NULL, name TEXT NOT NULL, use_count INTEGER NOT NULL, last_accessed INTEGER NOT NULL, last_modified INTEGER NOT NULL, expiration INTEGER NOT NULL, quota INTEGER NOT NULL);
-
-CREATE TABLE eviction_info(origin TEXT NOT NULL, type INTEGER NOT NULL, last_eviction_time INTEGER NOT NULL, PRIMARY KEY(origin, type));
-
-CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
-
-INSERT INTO meta VALUES ('mmap_status', '-1');
-INSERT INTO meta VALUES ('last_compatible_version', '6');
-INSERT INTO meta VALUES ('version', '6');
-
-INSERT INTO quota(host, type, quota)  VALUES('a.com', 0, 0);
-INSERT INTO quota(host, type, quota)  VALUES('b.com', 0, 1);
-INSERT INTO quota(host, type, quota)  VALUES('c.com', 1, 123);
-
-INSERT INTO buckets(origin, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://a/', 0, 'bucket_a', 123, 13260644621105493, 13242931862595604, 9223372036854775807, 0);
-INSERT INTO buckets(origin, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://b/', 0, 'bucket_b', 111, 13250042735631065, 13260999511438890, 9223372036854775807, 1000);
-INSERT INTO buckets(origin, type, name, use_count, last_accessed, last_modified, expiration, quota)
-  VALUES('http://c/', 2, 'bucket_c', 321, 13261163582572088, 13261079941303629, 9223372036854775807, 10000);
-
-CREATE UNIQUE INDEX buckets_by_storage_key ON buckets(origin, type, name);
-
-CREATE INDEX buckets_by_last_accessed ON buckets(type, last_accessed);
-
-CREATE INDEX buckets_by_last_modified ON buckets(type, last_modified);
-
-CREATE INDEX buckets_by_expiration ON buckets(expiration);
-
-COMMIT;
diff --git a/storage/test/test_bundle_data.filelist b/storage/test/test_bundle_data.filelist
index 8bed1c8..e126cdd3 100644
--- a/storage/test/test_bundle_data.filelist
+++ b/storage/test/test_bundle_data.filelist
@@ -4,9 +4,7 @@
 # NOTE: this file is generated by build/ios/update_bundle_filelist.py
 #       If it requires updating, you should get a presubmit error with
 #       instructions on how to regenerate. Otherwise, do not edit.
-data/quota_database/version_5.sql
-data/quota_database/version_6.sql
+data/quota_database/version_10.sql
 data/quota_database/version_7.sql
 data/quota_database/version_8.sql
 data/quota_database/version_9.sql
-data/quota_database/version_10.sql
diff --git a/testing/buildbot/filters/ios.content_browsertests.filter b/testing/buildbot/filters/ios.content_browsertests.filter
index e3f6c57..3337232 100644
--- a/testing/buildbot/filters/ios.content_browsertests.filter
+++ b/testing/buildbot/filters/ios.content_browsertests.filter
@@ -219,9 +219,6 @@
 -SitePerProcessHitTestBrowserTest.ScrollBubblingTargetWithUnrelatedGesture
 -SitePerProcessHitTestBrowserTest.ScrolledNestedPopupMenuTest
 -SitePerProcessHitTestBrowserTest.TouchpadDoubleTapZoomOverOOPIF
--SnapshotBrowserTest.AsyncMultiWindowTest
--SnapshotBrowserTest.SingleWindowTest
--SnapshotBrowserTest.SyncMultiWindowTest
 -SubresourceLoadingTest.URLLoaderFactoryInInitialEmptyDoc_NewPopupToAboutBlank
 -SubresourceLoadingTest.URLLoaderFactoryInInitialEmptyDoc_NewPopupToEmptyUrl
 -TouchInputBrowserTest.MultiPointTouchPress
diff --git a/third_party/android_system_sdk/OWNERS b/third_party/android_system_sdk/OWNERS
index 88308273..d99c934 100644
--- a/third_party/android_system_sdk/OWNERS
+++ b/third_party/android_system_sdk/OWNERS
@@ -1,2 +1,2 @@
+agrieve@chromium.org
 torne@chromium.org
-tobiasjs@chromium.org
diff --git a/third_party/android_toolchain/3pp/3pp.pb b/third_party/android_toolchain/3pp/3pp.pb
index db7cde7..f158813 100644
--- a/third_party/android_toolchain/3pp/3pp.pb
+++ b/third_party/android_toolchain/3pp/3pp.pb
@@ -5,8 +5,8 @@
 create {
   source {
     url {
-      download_url: "https://dl.google.com/android/repository/android-ndk-r25c-linux.zip"
-      version: "r25c"
+      download_url: "https://dl.google.com/android/repository/android-ndk-r23c-linux.zip"
+      version: "r23c"
       extension: ".zip"
     }
     unpack_archive: true
@@ -25,6 +25,6 @@
   # Together with the "3pp"'s parent dirname, this defines the CIPD path to
   # store the generated CIPD package. The value should be:
   #   "chromium/third_party/android_toolchain/<ndk_version>"
-  pkg_prefix: "chromium/third_party/android_toolchain/r25c"
+  pkg_prefix: "chromium/third_party/android_toolchain/r23c"
   universal: true
 }
diff --git a/third_party/android_toolchain/README.chromium b/third_party/android_toolchain/README.chromium
index bec44dd..6adf641a 100644
--- a/third_party/android_toolchain/README.chromium
+++ b/third_party/android_toolchain/README.chromium
@@ -1,6 +1,6 @@
 Name: Android NDK
 URL: http://developer.android.com/ndk/index.html
-Version: r25c
+Version: r23c
 Security Critical: no
 License: Apache Version 2.0
 License File: NOTICE
@@ -10,4 +10,4 @@
 and debugging C/C++ on Android.
 
 Downloaded from: https://developer.android.com/ndk/downloads/index.html
-SHA1 of Linux 64-bit .zip: 53af80a1cce9144025b81c78c8cd556bff42bd0e
\ No newline at end of file
+SHA1 of Linux 64-bit .zip: e5053c126a47e84726d9f7173a04686a71f9a67a
diff --git a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
index c70cea8..8093325 100644
--- a/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
+++ b/third_party/blink/public/mojom/interest_group/ad_auction_service.mojom
@@ -207,6 +207,8 @@
 
   // Gets the ad auction data for running an external auction on a bidding and
   // auction server.
+  // `request_id` is an RFC 4122 UUID encoded using only lowercase hexadecimal
+  // digits, like "f81d4fae-7dec-11d0-a765-00a0c91e6bf6".
   GetInterestGroupAdAuctionData(url.mojom.Origin seller)
-      => (mojo_base.mojom.BigBuffer data);
+      => (mojo_base.mojom.BigBuffer request, string request_id);
 };
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 187e8fe..4d3f44a 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3919,6 +3919,7 @@
   kFedCmLoginHint = 4579,
   kFedCmRpContext = 4580,
   kEventTimingArtificialPointerupOrClick = 4581,
+  kAbortSignalAny = 4582,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index f3e1a06..e3fafde 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -107,6 +107,8 @@
 generated_dictionary_sources_in_modules = [
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_aac_encoder_config.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_aac_encoder_config.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data_config.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data_config.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_ad_properties.cc",
diff --git a/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist b/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist
index bbaeb8ee..4ced7ce 100644
--- a/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist
+++ b/third_party/blink/renderer/controller/blink_unittests_bundle_data.filelist
@@ -153,6 +153,7 @@
 ../../web_tests/images/resources/flip.webp
 ../../web_tests/images/resources/flowchart.jpg
 ../../web_tests/images/resources/full2loop.gif
+../../web_tests/images/resources/gainmap-trattore0.jpg
 ../../web_tests/images/resources/gif-loop-count.gif
 ../../web_tests/images/resources/gif-loop-count.png
 ../../web_tests/images/resources/gracehopper.bmp
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 2c90800..79d8ae12 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -161,7 +161,6 @@
     "//third_party/blink/renderer/core/inspector:generated",
     "//third_party/blink/renderer/core/probe:generated",
     "//third_party/blink/renderer/platform",
-    "//third_party/iccjpeg",
     "//third_party/icu",
     "//third_party/libpng",
     "//third_party/libwebp",
@@ -1269,7 +1268,6 @@
     "//third_party/blink/renderer/bindings:buildflags",
     "//third_party/blink/renderer/platform:make_platform_generated",
     "//third_party/blink/renderer/platform/wtf",
-    "//third_party/iccjpeg",
     "//third_party/libpng",
     "//third_party/libwebp",
     "//third_party/libxml",
diff --git a/third_party/blink/renderer/core/dom/abort_signal.idl b/third_party/blink/renderer/core/dom/abort_signal.idl
index 37d267b6..1ca9a20 100644
--- a/third_party/blink/renderer/core/dom/abort_signal.idl
+++ b/third_party/blink/renderer/core/dom/abort_signal.idl
@@ -16,18 +16,18 @@
     [
         CallWith=ScriptState,
         MeasureAs=AbortSignalTimeout,
-        NewObject,
-        RuntimeEnabled=AbortSignalTimeout
+        NewObject
     ] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
     [
         CallWith=ScriptState,
+        MeasureAs=AbortSignalAny,
         NewObject,
         RuntimeEnabled=AbortSignalAny
     ] static AbortSignal _any(sequence<AbortSignal> signals);
 
     readonly attribute boolean aborted;
     [CallWith=ScriptState] readonly attribute any reason;
-    [CallWith=ScriptState, MeasureAs=AbortSignalThrowIfAborted, RaisesException, RuntimeEnabled=AbortSignalThrowIfAborted] void throwIfAborted();
+    [CallWith=ScriptState, MeasureAs=AbortSignalThrowIfAborted, RaisesException] void throwIfAborted();
 
     attribute EventHandler onabort;
 };
diff --git a/third_party/blink/renderer/core/dom/build.gni b/third_party/blink/renderer/core/dom/build.gni
index abee6a4..417d554 100644
--- a/third_party/blink/renderer/core/dom/build.gni
+++ b/third_party/blink/renderer/core/dom/build.gni
@@ -77,7 +77,6 @@
   "dataset_dom_string_map.h",
   "decoded_data_document_parser.cc",
   "decoded_data_document_parser.h",
-  "document_and_element_event_handlers.h",
   "document_data.h",
   "document_encoding_data.cc",
   "document_encoding_data.h",
diff --git a/third_party/blink/renderer/core/dom/container_node.h b/third_party/blink/renderer/core/dom/container_node.h
index 74003e3..154f79d2 100644
--- a/third_party/blink/renderer/core/dom/container_node.h
+++ b/third_party/blink/renderer/core/dom/container_node.h
@@ -420,6 +420,13 @@
     return EnsureCachedCollection<HTMLCollection>(kPopoverInvokers);
   }
 
+  // DocumentOrElementEventHandlers:
+  // These event listeners are only actually web-exposed on interfaces that
+  // include the DocumentOrElementEventHandlers mixin in their idl.
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(copy, kCopy)
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(cut, kCut)
+  DEFINE_ATTRIBUTE_EVENT_LISTENER(paste, kPaste)
+
   void Trace(Visitor*) const override;
 
  protected:
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 97166d3d..d5be983 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -3942,7 +3942,8 @@
   kNoDialogMultipleConfirmationForNavigation,
   kShowDialog,
   kNoDialogAutoCancelTrue,
-  kMaxValue = kNoDialogAutoCancelTrue,
+  kNotSupportedInDocumentPictureInPicture,
+  kMaxValue = kNotSupportedInDocumentPictureInPicture,
 };
 
 void RecordBeforeUnloadUse(BeforeUnloadUse metric) {
@@ -3963,6 +3964,12 @@
   if (ProcessingBeforeUnload())
     return false;
 
+  if (dom_window_->IsPictureInPictureWindow()) {
+    RecordBeforeUnloadUse(
+        BeforeUnloadUse::kNotSupportedInDocumentPictureInPicture);
+    return true;
+  }
+
   // Since we do not allow registering the beforeunload event handlers in
   // fenced frames, it should not be fired by fencedframes.
   DCHECK(!GetFrame() || !GetFrame()->IsInFencedFrameTree() ||
diff --git a/third_party/blink/renderer/core/dom/document_and_element_event_handlers.h b/third_party/blink/renderer/core/dom/document_and_element_event_handlers.h
deleted file mode 100644
index 570dfbce..0000000
--- a/third_party/blink/renderer/core/dom/document_and_element_event_handlers.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_AND_ELEMENT_EVENT_HANDLERS_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_AND_ELEMENT_EVENT_HANDLERS_H_
-
-#include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class DocumentAndElementEventHandlers {
-  STATIC_ONLY(DocumentAndElementEventHandlers);
-
- public:
-  DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(copy, kCopy)
-  DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(cut, kCut)
-  DEFINE_STATIC_ATTRIBUTE_EVENT_LISTENER(paste, kPaste)
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_DOCUMENT_AND_ELEMENT_EVENT_HANDLERS_H_
diff --git a/third_party/blink/renderer/core/dom/document_and_element_event_handlers.idl b/third_party/blink/renderer/core/dom/document_and_element_event_handlers.idl
index 694f84f..bc3f532 100644
--- a/third_party/blink/renderer/core/dom/document_and_element_event_handlers.idl
+++ b/third_party/blink/renderer/core/dom/document_and_element_event_handlers.idl
@@ -3,10 +3,7 @@
 // found in the LICENSE file.
 
 // https://html.spec.whatwg.org/C/#documentandelementeventhandlers
-
-[
-    LegacyTreatAsPartialInterface
-] interface mixin DocumentAndElementEventHandlers {
+interface mixin DocumentAndElementEventHandlers {
   attribute EventHandler oncopy;
   attribute EventHandler oncut;
   attribute EventHandler onpaste;
diff --git a/third_party/blink/renderer/core/dom/tree_ordered_map.cc b/third_party/blink/renderer/core/dom/tree_ordered_map.cc
index d7f29d4..70a6a1f 100644
--- a/third_party/blink/renderer/core/dom/tree_ordered_map.cc
+++ b/third_party/blink/renderer/core/dom/tree_ordered_map.cc
@@ -62,11 +62,8 @@
 
 inline bool KeyMatchesMapName(const AtomicString& key, const Element& element) {
   auto* html_map_element = DynamicTo<HTMLMapElement>(element);
-  if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled()) {
-    return html_map_element && (html_map_element->GetName() == key ||
-                                html_map_element->GetIdAttribute() == key);
-  }
-  return html_map_element && html_map_element->GetName() == key;
+  return html_map_element && (html_map_element->GetName() == key ||
+                              html_map_element->GetIdAttribute() == key);
 }
 
 inline bool KeyMatchesSlotName(const AtomicString& key,
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc
index f594e22..d9839bd6 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.cc
+++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -180,20 +180,15 @@
 void TreeScope::AddImageMap(HTMLMapElement& image_map) {
   const AtomicString& name = image_map.GetName();
   const AtomicString& id = image_map.GetIdAttribute();
-  if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled()) {
-    if (!name && !id)
-      return;
-  } else {
-    if (!name)
-      return;
+  if (!name && !id) {
+    return;
   }
   if (!image_maps_by_name_)
     image_maps_by_name_ = MakeGarbageCollected<TreeOrderedMap>();
   if (name)
     image_maps_by_name_->Add(name, image_map);
-  if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled()) {
-    if (id)
-      image_maps_by_name_->Add(id, image_map);
+  if (id) {
+    image_maps_by_name_->Add(id, image_map);
   }
 }
 
@@ -202,9 +197,8 @@
     return;
   if (const AtomicString& name = image_map.GetName())
     image_maps_by_name_->Remove(name, image_map);
-  if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled()) {
-    if (const AtomicString& id = image_map.GetIdAttribute())
-      image_maps_by_name_->Remove(id, image_map);
+  if (const AtomicString& id = image_map.GetIdAttribute()) {
+    image_maps_by_name_->Remove(id, image_map);
   }
 }
 
@@ -217,9 +211,9 @@
   if (hash_pos == kNotFound)
     return nullptr;
   String name = url.Substring(hash_pos + 1);
-  if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled() &&
-      name.empty())
+  if (name.empty()) {
     return nullptr;
+  }
   return To<HTMLMapElement>(
       image_maps_by_name_->GetElementByMapName(AtomicString(name), *this));
 }
diff --git a/third_party/blink/renderer/core/html/html_map_element.cc b/third_party/blink/renderer/core/html/html_map_element.cc
index 558b7e3b..53bedc1 100644
--- a/third_party/blink/renderer/core/html/html_map_element.cc
+++ b/third_party/blink/renderer/core/html/html_map_element.cc
@@ -67,14 +67,9 @@
         image_element.FastGetAttribute(html_names::kUsemapAttr)
             .GetString()
             .Substring(1);
-    if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled()) {
-      if (!use_map_name.empty() &&
-          (use_map_name == name_ || use_map_name == GetIdAttribute()))
-        return &image_element;
-    } else {
-      if (use_map_name == name_) {
-        return &image_element;
-      }
+    if (!use_map_name.empty() &&
+        (use_map_name == name_ || use_map_name == GetIdAttribute())) {
+      return &image_element;
     }
   }
 
@@ -90,20 +85,14 @@
     if (params.name == html_names::kIdAttr) {
       // Call base class so that hasID bit gets set.
       HTMLElement::ParseAttribute(params);
-      if (!RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled() &&
-          IsA<HTMLDocument>(GetDocument()))
-        return;
     }
     if (isConnected())
       GetTreeScope().RemoveImageMap(*this);
     String map_name = params.new_value;
     if (map_name[0] == '#')
       map_name = map_name.Substring(1);
-    if (RuntimeEnabledFeatures::HTMLMapToImgMatchingByNameAndIdEnabled()) {
-      // name_ is the parsed name attribute value that is not empty.
-      if (!map_name.empty() && params.name == html_names::kNameAttr)
-        name_ = AtomicString(map_name);
-    } else {
+    // name_ is the parsed name attribute value that is not empty.
+    if (!map_name.empty() && params.name == html_names::kNameAttr) {
       name_ = AtomicString(map_name);
     }
     if (isConnected())
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index f6b47381..5055f53 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -1631,85 +1631,39 @@
   return should_use_touch_event_adjusted_point_;
 }
 
-bool EventHandler::BestClickableNodeForHitTestResult(
+bool EventHandler::BestNodeForHitTestResult(
+    TouchAdjustmentCandidateType candidate_type,
     const HitTestLocation& location,
     const HitTestResult& result,
-    gfx::Point& target_point,
-    Node*& target_node) {
-  // FIXME: Unify this with the other best* functions which are very similar.
-
-  TRACE_EVENT0("input", "EventHandler::bestClickableNodeForHitTestResult");
+    gfx::Point& adjusted_point,
+    Node*& adjusted_node) {
+  TRACE_EVENT0("input", "EventHandler::BestNodeForHitTestResult");
   DCHECK(location.IsRectBasedTest());
 
-  // If the touch is over a scrollbar, don't adjust the touch point since touch
-  // adjustment only takes into account DOM nodes so a touch over a scrollbar
-  // will be adjusted towards nearby nodes. This leads to things like textarea
-  // scrollbars being untouchable.
-  if (result.GetScrollbar() || result.IsOverResizer()) {
-    target_node = nullptr;
+  // If the touch is over a scrollbar or a resizer, we don't adjust the touch
+  // point.  This is because touch adjustment only takes into account DOM nodes
+  // so a touch over a scrollbar or a resizer would be adjusted towards a nearby
+  // DOM node, making the scrollbar/resizer unusable.
+  //
+  // Context-menu hittests are excluded from this consideration because a
+  // right-click/long-press doesn't drag the scrollbar therefore prefers DOM
+  // nodes with relevant contextmenu properties.
+  if (candidate_type != TouchAdjustmentCandidateType::kContextMenu &&
+      (result.GetScrollbar() || result.IsOverResizer())) {
+    adjusted_node = nullptr;
     return false;
   }
 
-  gfx::Point touch_center =
+  gfx::Point touch_hotspot =
       frame_->View()->ConvertToRootFrame(ToRoundedPoint(location.Point()));
   gfx::Rect touch_rect =
       frame_->View()->ConvertToRootFrame(location.ToEnclosingRect());
 
   HeapVector<Member<Node>, 11> nodes(result.ListBasedTestResult());
 
-  // FIXME: the explicit Vector conversion copies into a temporary and is
-  // wasteful.
-  return FindBestClickableCandidate(target_node, target_point, touch_center,
-                                    touch_rect,
-                                    HeapVector<Member<Node>>(nodes));
-}
-
-bool EventHandler::BestContextMenuNodeForHitTestResult(
-    const HitTestLocation& location,
-    const HitTestResult& result,
-    gfx::Point& target_point,
-    Node*& target_node) {
-  DCHECK(location.IsRectBasedTest());
-  gfx::Point touch_center =
-      frame_->View()->ConvertToRootFrame(ToRoundedPoint(location.Point()));
-  gfx::Rect touch_rect =
-      frame_->View()->ConvertToRootFrame(location.ToEnclosingRect());
-  HeapVector<Member<Node>, 11> nodes(result.ListBasedTestResult());
-
-  // FIXME: the explicit Vector conversion copies into a temporary and is
-  // wasteful.
-  return FindBestContextMenuCandidate(target_node, target_point, touch_center,
-                                      touch_rect,
-                                      HeapVector<Member<Node>>(nodes));
-}
-
-bool EventHandler::BestStylusWritableNodeForHitTestResult(
-    const HitTestLocation& location,
-    const HitTestResult& result,
-    gfx::Point& target_point,
-    Node*& target_node) {
-  DCHECK(location.IsRectBasedTest());
-
-  // If the touch is over a scrollbar, don't adjust the touch point since touch
-  // adjustment only takes into account DOM nodes so a touch over a scrollbar
-  // will be adjusted towards nearby nodes. This leads to things like textarea
-  // scrollbars being untouchable.
-  if (result.GetScrollbar() || result.IsOverResizer()) {
-    target_node = nullptr;
-    return false;
-  }
-
-  gfx::Point touch_center =
-      frame_->View()->ConvertToRootFrame(ToRoundedPoint(location.Point()));
-  gfx::Rect touch_rect =
-      frame_->View()->ConvertToRootFrame(location.ToEnclosingRect());
-  HeapVector<Member<Node>, 11> nodes(result.ListBasedTestResult());
-
-  // FIXME: the explicit Vector conversion copies into a temporary and is
-  // wasteful.
-  return FindBestStylusWritableCandidate(target_node, target_point,
-                                         touch_center, touch_rect,
-                                         HeapVector<Member<Node>>(nodes));
+  return FindBestTouchAdjustmentCandidate(candidate_type, adjusted_node,
+                                          adjusted_point, touch_hotspot,
+                                          touch_rect, nodes);
 }
 
 // Update the hover and active state across all frames.  This logic is
@@ -1962,7 +1916,7 @@
   if (location.IsRectBasedTest()) {
     // Adjust the location of the gesture to the most likely nearby node, as
     // appropriate for the type of event.
-    ApplyTouchAdjustment(&adjusted_event, location, &hit_test_result);
+    ApplyTouchAdjustment(&adjusted_event, location, hit_test_result);
     // Do a new hit-test at the (adjusted) gesture coordinates. This is
     // necessary because rect-based hit testing and touch adjustment sometimes
     // return a different node than what a point-based hit test would return for
@@ -1988,39 +1942,40 @@
 
 void EventHandler::ApplyTouchAdjustment(WebGestureEvent* gesture_event,
                                         HitTestLocation& location,
-                                        HitTestResult* hit_test_result) {
-  Node* adjusted_node = nullptr;
-  gfx::Point adjusted_point =
-      gfx::ToFlooredPoint(gesture_event->PositionInRootFrame());
-  bool adjusted = false;
+                                        HitTestResult& hit_test_result) {
+  TouchAdjustmentCandidateType touch_adjustment_candiate_type =
+      TouchAdjustmentCandidateType::kClickable;
   switch (gesture_event->GetType()) {
     case WebInputEvent::Type::kGestureTap:
     case WebInputEvent::Type::kGestureTapUnconfirmed:
     case WebInputEvent::Type::kGestureTapDown:
     case WebInputEvent::Type::kGestureShowPress:
-      adjusted = BestClickableNodeForHitTestResult(
-          location, *hit_test_result, adjusted_point, adjusted_node);
       break;
     case WebInputEvent::Type::kGestureShortPress:
     case WebInputEvent::Type::kGestureLongPress:
     case WebInputEvent::Type::kGestureLongTap:
     case WebInputEvent::Type::kGestureTwoFingerTap:
-      adjusted = BestContextMenuNodeForHitTestResult(
-          location, *hit_test_result, adjusted_point, adjusted_node);
+      touch_adjustment_candiate_type =
+          TouchAdjustmentCandidateType::kContextMenu;
       break;
     default:
       NOTREACHED();
   }
 
+  Node* adjusted_node = nullptr;
+  gfx::Point adjusted_point =
+      gfx::ToFlooredPoint(gesture_event->PositionInRootFrame());
+  bool adjusted =
+      BestNodeForHitTestResult(touch_adjustment_candiate_type, location,
+                               hit_test_result, adjusted_point, adjusted_node);
+
   // Update the hit-test result to be a point-based result instead of a
   // rect-based result.
-  // FIXME: We should do this even when no candidate matches the node filter.
-  // crbug.com/398914
   if (adjusted) {
     PhysicalOffset point(frame_->View()->ConvertFromRootFrame(adjusted_point));
     DCHECK(location.ContainsPoint(gfx::PointF(point)));
     DCHECK(location.IsRectBasedTest());
-    location = hit_test_result->ResolveRectBasedTest(adjusted_node, point);
+    location = hit_test_result.ResolveRectBasedTest(adjusted_node, point);
     gesture_event->ApplyTouchAdjustment(
         gfx::PointF(adjusted_point.x(), adjusted_point.y()));
   }
diff --git a/third_party/blink/renderer/core/input/event_handler.h b/third_party/blink/renderer/core/input/event_handler.h
index f0349c4..e26cacd 100644
--- a/third_party/blink/renderer/core/input/event_handler.h
+++ b/third_party/blink/renderer/core/input/event_handler.h
@@ -186,18 +186,11 @@
   WebInputEventResult HandleGestureScrollEvent(const WebGestureEvent&);
   bool IsScrollbarHandlingGestures() const;
 
-  bool BestClickableNodeForHitTestResult(const HitTestLocation& location,
-                                         const HitTestResult&,
-                                         gfx::Point& target_point,
-                                         Node*& target_node);
-  bool BestContextMenuNodeForHitTestResult(const HitTestLocation& location,
-                                           const HitTestResult&,
-                                           gfx::Point& target_point,
-                                           Node*& target_node);
-  bool BestStylusWritableNodeForHitTestResult(const HitTestLocation& location,
-                                              const HitTestResult&,
-                                              gfx::Point& target_point,
-                                              Node*& target_node);
+  bool BestNodeForHitTestResult(TouchAdjustmentCandidateType candidate_type,
+                                const HitTestLocation& location,
+                                const HitTestResult&,
+                                gfx::Point& adjusted_point,
+                                Node*& adjusted_node);
   void CacheTouchAdjustmentResult(uint32_t, gfx::PointF);
 
   // Dispatch a context menu event. If |override_target_element| is provided,
@@ -294,7 +287,7 @@
       HitTestLocation* hit_test_location = nullptr);
 
   // Updates the event, location and result to the adjusted target.
-  void ApplyTouchAdjustment(WebGestureEvent*, HitTestLocation&, HitTestResult*);
+  void ApplyTouchAdjustment(WebGestureEvent*, HitTestLocation&, HitTestResult&);
 
   void PerformHitTest(const HitTestLocation& location,
                       HitTestResult&,
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index f05d797..b2d6b55 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -25,6 +25,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/pointer_lock_controller.h"
+#include "third_party/blink/renderer/core/page/touch_adjustment.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
 #include "third_party/blink/renderer/core/timing/event_timing.h"
@@ -439,8 +440,9 @@
   gfx::Point adjusted_point;
 
   if (pointer_event.pointer_type == WebPointerProperties::PointerType::kTouch) {
-    bool adjusted = frame_->GetEventHandler().BestClickableNodeForHitTestResult(
-        location, hit_test_result, adjusted_point, adjusted_node);
+    bool adjusted = frame_->GetEventHandler().BestNodeForHitTestResult(
+        TouchAdjustmentCandidateType::kClickable, location, hit_test_result,
+        adjusted_point, adjusted_node);
 
     if (adjusted)
       pointer_event.SetPositionInWidget(adjusted_point.x(), adjusted_point.y());
@@ -453,9 +455,9 @@
                  WebPointerProperties::PointerType::kEraser) {
     // We don't cache the adjusted point for Stylus in EventHandler to avoid
     // taps being adjusted; this is intended only for stylus handwriting.
-    bool adjusted =
-        frame_->GetEventHandler().BestStylusWritableNodeForHitTestResult(
-            location, hit_test_result, adjusted_point, adjusted_node);
+    bool adjusted = frame_->GetEventHandler().BestNodeForHitTestResult(
+        TouchAdjustmentCandidateType::kStylusWritable, location,
+        hit_test_result, adjusted_point, adjusted_node);
 
     if (adjusted)
       pointer_event.SetPositionInWidget(adjusted_point.x(), adjusted_point.y());
diff --git a/third_party/blink/renderer/core/page/touch_adjustment.cc b/third_party/blink/renderer/core/page/touch_adjustment.cc
index 3f3f544..10cc863 100644
--- a/third_party/blink/renderer/core/page/touch_adjustment.cc
+++ b/third_party/blink/renderer/core/page/touch_adjustment.cc
@@ -434,19 +434,19 @@
 bool SnapTo(const SubtargetGeometry& geom,
             const gfx::Point& touch_point,
             const gfx::Rect& touch_area,
-            gfx::Point& adjusted_point) {
+            gfx::Point& snapped_point) {
   LocalFrameView* view = geom.GetNode()->GetDocument().View();
   gfx::QuadF quad = geom.Quad();
 
   if (quad.IsRectilinear()) {
     gfx::Rect bounds = view->ConvertToRootFrame(geom.BoundingBox());
     if (bounds.Contains(touch_point)) {
-      adjusted_point = touch_point;
+      snapped_point = touch_point;
       return true;
     }
     if (bounds.Intersects(touch_area)) {
       bounds.Intersect(touch_area);
-      adjusted_point = bounds.CenterPoint();
+      snapped_point = bounds.CenterPoint();
       return true;
     }
     return false;
@@ -466,7 +466,7 @@
   quad = gfx::QuadF(p1, p2, p3, p4);
 
   if (quad.Contains(gfx::PointF(touch_point))) {
-    adjusted_point = touch_point;
+    snapped_point = touch_point;
     return true;
   }
 
@@ -474,44 +474,44 @@
   gfx::PointF center = quad.CenterPoint();
 
   AdjustPointToRect(center, touch_area);
-  adjusted_point = gfx::ToRoundedPoint(center);
+  snapped_point = gfx::ToRoundedPoint(center);
 
-  return quad.Contains(gfx::PointF(adjusted_point));
+  return quad.Contains(gfx::PointF(snapped_point));
 }
 
 // A generic function for finding the target node with the lowest distance
 // metric. A distance metric here is the result of a distance-like function,
 // that computes how well the touch hits the node.  Distance functions could for
 // instance be distance squared or area of intersection.
-bool FindNodeWithLowestDistanceMetric(Node*& target_node,
-                                      gfx::Point& target_point,
+bool FindNodeWithLowestDistanceMetric(Node*& adjusted_node,
+                                      gfx::Point& adjusted_point,
                                       gfx::Rect& target_area,
                                       const gfx::Point& touch_hotspot,
                                       const gfx::Rect& touch_area,
                                       SubtargetGeometryList& subtargets,
                                       DistanceFunction distance_function) {
-  target_node = nullptr;
+  adjusted_node = nullptr;
   float best_distance_metric = std::numeric_limits<float>::infinity();
   SubtargetGeometryList::const_iterator it = subtargets.begin();
   const SubtargetGeometryList::const_iterator end = subtargets.end();
-  gfx::Point adjusted_point;
+  gfx::Point snapped_point;
 
   for (; it != end; ++it) {
     Node* node = it->GetNode();
     float distance_metric = distance_function(touch_hotspot, touch_area, *it);
     if (distance_metric < best_distance_metric) {
-      if (SnapTo(*it, touch_hotspot, touch_area, adjusted_point)) {
-        target_point = adjusted_point;
+      if (SnapTo(*it, touch_hotspot, touch_area, snapped_point)) {
+        adjusted_point = snapped_point;
+        adjusted_node = node;
         target_area = it->BoundingBox();
-        target_node = node;
         best_distance_metric = distance_metric;
       }
     } else if (distance_metric - best_distance_metric < kZeroTolerance) {
-      if (SnapTo(*it, touch_hotspot, touch_area, adjusted_point)) {
-        if (node->IsDescendantOf(target_node)) {
+      if (SnapTo(*it, touch_hotspot, touch_area, snapped_point)) {
+        if (node->IsDescendantOf(adjusted_node)) {
           // Try to always return the inner-most element.
-          target_point = adjusted_point;
-          target_node = node;
+          adjusted_point = snapped_point;
+          adjusted_node = node;
           target_area = it->BoundingBox();
         }
       }
@@ -519,19 +519,20 @@
   }
 
   // As for HitTestResult.innerNode, we skip over pseudo elements.
-  if (target_node && target_node->IsPseudoElement())
-    target_node = target_node->ParentOrShadowHostNode();
-
-  if (target_node) {
-    target_area =
-        target_node->GetDocument().View()->ConvertToRootFrame(target_area);
+  if (adjusted_node && adjusted_node->IsPseudoElement()) {
+    adjusted_node = adjusted_node->ParentOrShadowHostNode();
   }
 
-  return (target_node);
+  if (adjusted_node) {
+    target_area =
+        adjusted_node->GetDocument().View()->ConvertToRootFrame(target_area);
+  }
+
+  return adjusted_node != nullptr;
 }
 
-bool FindBestCandidate(Node*& target_node,
-                       gfx::Point& target_point,
+bool FindBestCandidate(Node*& adjusted_node,
+                       gfx::Point& adjusted_point,
                        const gfx::Point& touch_hotspot,
                        const gfx::Rect& touch_area,
                        const HeapVector<Member<Node>>& nodes,
@@ -542,40 +543,42 @@
   touch_adjustment::CompileSubtargetList(nodes, subtargets, node_filter,
                                          append_subtargets_for_node);
   return touch_adjustment::FindNodeWithLowestDistanceMetric(
-      target_node, target_point, target_area, touch_hotspot, touch_area,
+      adjusted_node, adjusted_point, target_area, touch_hotspot, touch_area,
       subtargets, touch_adjustment::HybridDistanceFunction);
 }
 
 }  // namespace touch_adjustment
 
-bool FindBestClickableCandidate(Node*& target_node,
-                                gfx::Point& target_point,
-                                const gfx::Point& touch_hotspot,
-                                const gfx::Rect& touch_area,
-                                const HeapVector<Member<Node>>& nodes) {
-  return FindBestCandidate(target_node, target_point, touch_hotspot, touch_area,
-                           nodes, touch_adjustment::NodeRespondsToTapGesture,
-                           touch_adjustment::AppendBasicSubtargetsForNode);
-}
+bool FindBestTouchAdjustmentCandidate(
+    TouchAdjustmentCandidateType candidate_type,
+    Node*& candidate_node,
+    gfx::Point& candidate_point,
+    const gfx::Point& touch_hotspot,
+    const gfx::Rect& touch_area,
+    const HeapVector<Member<Node>>& nodes) {
+  touch_adjustment::NodeFilter node_filter;
+  touch_adjustment::AppendSubtargetsForNode append_subtargets_for_node;
 
-bool FindBestContextMenuCandidate(Node*& target_node,
-                                  gfx::Point& target_point,
-                                  const gfx::Point& touch_hotspot,
-                                  const gfx::Rect& touch_area,
-                                  const HeapVector<Member<Node>>& nodes) {
-  return FindBestCandidate(target_node, target_point, touch_hotspot, touch_area,
-                           nodes, touch_adjustment::ProvidesContextMenuItems,
-                           touch_adjustment::AppendContextSubtargetsForNode);
-}
-
-bool FindBestStylusWritableCandidate(Node*& target_node,
-                                     gfx::Point& target_point,
-                                     const gfx::Point& touch_hotspot,
-                                     const gfx::Rect& touch_area,
-                                     const HeapVector<Member<Node>>& nodes) {
-  return FindBestCandidate(target_node, target_point, touch_hotspot, touch_area,
-                           nodes, touch_adjustment::NodeRespondsToTapOrMove,
-                           touch_adjustment::AppendBasicSubtargetsForNode);
+  switch (candidate_type) {
+    case TouchAdjustmentCandidateType::kClickable:
+      node_filter = touch_adjustment::NodeRespondsToTapGesture;
+      append_subtargets_for_node =
+          touch_adjustment::AppendBasicSubtargetsForNode;
+      break;
+    case TouchAdjustmentCandidateType::kContextMenu:
+      node_filter = touch_adjustment::ProvidesContextMenuItems;
+      append_subtargets_for_node =
+          touch_adjustment::AppendContextSubtargetsForNode;
+      break;
+    case TouchAdjustmentCandidateType::kStylusWritable:
+      node_filter = touch_adjustment::NodeRespondsToTapOrMove;
+      append_subtargets_for_node =
+          touch_adjustment::AppendBasicSubtargetsForNode;
+      break;
+  }
+  return FindBestCandidate(candidate_node, candidate_point, touch_hotspot,
+                           touch_area, nodes, node_filter,
+                           append_subtargets_for_node);
 }
 
 LayoutSize GetHitTestRectForAdjustment(LocalFrame& frame,
diff --git a/third_party/blink/renderer/core/page/touch_adjustment.h b/third_party/blink/renderer/core/page/touch_adjustment.h
index 3efcf7b5..2b660b47 100644
--- a/third_party/blink/renderer/core/page/touch_adjustment.h
+++ b/third_party/blink/renderer/core/page/touch_adjustment.h
@@ -33,21 +33,21 @@
 class Node;
 class LocalFrame;
 
-bool FindBestClickableCandidate(Node*& target_node,
-                                gfx::Point& target_point,
-                                const gfx::Point& touch_hotspot,
-                                const gfx::Rect& touch_area,
-                                const HeapVector<Member<Node>>&);
-bool FindBestContextMenuCandidate(Node*& target_node,
-                                  gfx::Point& target_point,
-                                  const gfx::Point& touch_hotspot,
-                                  const gfx::Rect& touch_area,
-                                  const HeapVector<Member<Node>>&);
-bool FindBestStylusWritableCandidate(Node*& target_node,
-                                     gfx::Point& target_point,
-                                     const gfx::Point& touch_hotspot,
-                                     const gfx::Rect& touch_area,
-                                     const HeapVector<Member<Node>>&);
+enum class TouchAdjustmentCandidateType {
+  kClickable,
+  kContextMenu,
+  kStylusWritable
+};
+
+// Finds the best `candidate_node` and location `candidate_point` for touch
+// adjustment for the given `candidate_type`.
+bool FindBestTouchAdjustmentCandidate(
+    TouchAdjustmentCandidateType candidate_type,
+    Node*& candidate_node,
+    gfx::Point& candidate_point,
+    const gfx::Point& touch_hotspot,
+    const gfx::Rect& touch_area,
+    const HeapVector<Member<Node>>&);
 
 // Applies an upper bound to the touch area as the adjustment rect. The
 // touch_area is in root frame coordinates, which is in physical pixel when
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index b6168f8..220433c5 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -133,6 +133,7 @@
 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/scroll_state.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
+#include "third_party/blink/renderer/core/page/touch_adjustment.h"
 #include "third_party/blink/renderer/core/page/validation_message_client.h"
 #include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
@@ -1919,9 +1920,6 @@
   return range->GetText();
 }
 
-// FIXME: The next four functions are very similar - combine them once
-// bestClickableNode/bestContextMenuNode have been combined..
-
 void Internals::HitTestRect(HitTestLocation& location,
                             HitTestResult& result,
                             int x,
@@ -1940,6 +1938,8 @@
                     HitTestRequest::kListBased);
 }
 
+// TODO(mustaq): The next 5 functions are very similar, can we combine them?
+
 DOMPoint* Internals::touchPositionAdjustedToBestClickableNode(
     int x,
     int y,
@@ -1961,8 +1961,9 @@
   gfx::Point adjusted_point;
 
   EventHandler& event_handler = document->GetFrame()->GetEventHandler();
-  bool found_node = event_handler.BestClickableNodeForHitTestResult(
-      location, result, adjusted_point, target_node);
+  bool found_node = event_handler.BestNodeForHitTestResult(
+      TouchAdjustmentCandidateType::kClickable, location, result,
+      adjusted_point, target_node);
   if (found_node)
     return DOMPoint::Create(adjusted_point.x(), adjusted_point.y());
 
@@ -1988,8 +1989,9 @@
   HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   gfx::Point adjusted_point;
-  document->GetFrame()->GetEventHandler().BestClickableNodeForHitTestResult(
-      location, result, adjusted_point, target_node);
+  document->GetFrame()->GetEventHandler().BestNodeForHitTestResult(
+      TouchAdjustmentCandidateType::kClickable, location, result,
+      adjusted_point, target_node);
   return target_node;
 }
 
@@ -2014,8 +2016,9 @@
   gfx::Point adjusted_point;
 
   EventHandler& event_handler = document->GetFrame()->GetEventHandler();
-  bool found_node = event_handler.BestContextMenuNodeForHitTestResult(
-      location, result, adjusted_point, target_node);
+  bool found_node = event_handler.BestNodeForHitTestResult(
+      TouchAdjustmentCandidateType::kContextMenu, location, result,
+      adjusted_point, target_node);
   if (found_node)
     return DOMPoint::Create(adjusted_point.x(), adjusted_point.y());
 
@@ -2041,8 +2044,9 @@
   HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   gfx::Point adjusted_point;
-  document->GetFrame()->GetEventHandler().BestContextMenuNodeForHitTestResult(
-      location, result, adjusted_point, target_node);
+  document->GetFrame()->GetEventHandler().BestNodeForHitTestResult(
+      TouchAdjustmentCandidateType::kContextMenu, location, result,
+      adjusted_point, target_node);
   return target_node;
 }
 
@@ -2065,10 +2069,9 @@
   HitTestRect(location, result, x, y, width, height, document);
   Node* target_node = nullptr;
   gfx::Point adjusted_point;
-  document->GetFrame()
-      ->GetEventHandler()
-      .BestStylusWritableNodeForHitTestResult(location, result, adjusted_point,
-                                              target_node);
+  document->GetFrame()->GetEventHandler().BestNodeForHitTestResult(
+      TouchAdjustmentCandidateType::kStylusWritable, location, result,
+      adjusted_point, target_node);
   return target_node;
 }
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 3508018..f5a23fa 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -689,11 +689,6 @@
   // determine whether an AXObject can have children.
   children_dirty_ = CanHaveChildren();
 
-  // Ensure that the aria-owns relationship is set before attempting
-  // to update cached attribute values.
-  if (GetNode())
-    AXObjectCache().MaybeNewRelationTarget(*GetNode(), this);
-
   UpdateCachedAttributeValuesIfNeeded(false);
 
   DCHECK(GetDocument()) << "All AXObjects must have a document: "
diff --git a/third_party/blink/renderer/modules/ad_auction/ad_auction_data_config.idl b/third_party/blink/renderer/modules/ad_auction/ad_auction_data_config.idl
index 2256c39..9272dff 100644
--- a/third_party/blink/renderer/modules/ad_auction/ad_auction_data_config.idl
+++ b/third_party/blink/renderer/modules/ad_auction/ad_auction_data_config.idl
@@ -8,3 +8,8 @@
 dictionary AdAuctionDataConfig {
   required USVString seller;
 };
+
+dictionary AdAuctionData {
+  required Uint8Array request;
+  required USVString requestId;
+};
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
index 3645d1cca..aac2958 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_fencedframeconfig_usvstring.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_usvstring_usvstringsequence.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ad_auction_data_config.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ad_properties.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_ad_request_config.h"
@@ -3442,18 +3443,14 @@
 
 void NavigatorAuction::GetInterestGroupAdAuctionDataComplete(
     ScriptPromiseResolver* resolver,
-    mojo_base::BigBuffer data) {
-  ScriptState* script_state = resolver->GetScriptState();
-  v8::Isolate* isolate = script_state->GetIsolate();
-  v8::Local<v8::ArrayBuffer> array_buffer =
-      v8::ArrayBuffer::New(isolate, data.size());
-  if (data.size() > 0) {
-    CHECK(array_buffer->Data());
-    memcpy(array_buffer->Data(), data.data(), data.size());
-  }
-  v8::Local<v8::Uint8Array> uint8_array =
-      v8::Uint8Array::New(array_buffer, 0, data.size());
-  resolver->Resolve(uint8_array);
+    mojo_base::BigBuffer data,
+    const WTF::String& request_id) {
+  AdAuctionData* result = AdAuctionData::Create();
+  auto not_shared =
+      NotShared<DOMUint8Array>(DOMUint8Array::Create(data.data(), data.size()));
+  result->setRequest(std::move(not_shared));
+  result->setRequestId(request_id);
+  resolver->Resolve(result);
 }
 
 /* static */
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
index f6638c2..141f7c6 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.h
@@ -213,7 +213,8 @@
   void ReplaceInURNComplete(ScriptPromiseResolver* resolver);
 
   void GetInterestGroupAdAuctionDataComplete(ScriptPromiseResolver* resolver,
-                                             mojo_base::BigBuffer data);
+                                             mojo_base::BigBuffer request,
+                                             const WTF::String& request_id);
 
   // Manage queues of cross-site join and leave operations that have yet to be
   // sent to the browser process.
diff --git a/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl b/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
index fde8c37..b4994d7 100644
--- a/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
+++ b/third_party/blink/renderer/modules/ad_auction/navigator_auction.idl
@@ -38,7 +38,7 @@
   Promise<void> deprecatedReplaceInURN(UrnOrConfig urn_or_config, record<USVString, USVString> replacements);
 
   [RuntimeEnabled=FledgeBiddingAndAuctionServer, CallWith=ScriptState, Measure, RaisesException]
-  Promise<Uint8Array> getInterestGroupAdAuctionData(AdAuctionDataConfig config);
+  Promise<AdAuctionData> getInterestGroupAdAuctionData(AdAuctionDataConfig config);
 
   [RuntimeEnabled=Parakeet, CallWith=ScriptState, Measure, RaisesException]
   Promise<Ads> createAdRequest(AdRequestConfig config);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 038cc45..a6c6763 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -199,7 +199,6 @@
     "//third_party/blink/renderer/platform/network:make_generated",
     "//third_party/blink/renderer/platform/wtf",
     "//third_party/boringssl",
-    "//third_party/iccjpeg",
     "//third_party/libpng",
     "//third_party/libwebp",
     "//third_party/ots",
diff --git a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
index 17cf633..544a0a97 100644
--- a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
+++ b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
@@ -256,6 +256,7 @@
 ../../web_tests/images/resources/flip.webp
 ../../web_tests/images/resources/flowchart.jpg
 ../../web_tests/images/resources/full2loop.gif
+../../web_tests/images/resources/gainmap-trattore0.jpg
 ../../web_tests/images/resources/gif-loop-count.gif
 ../../web_tests/images/resources/gif-loop-count.png
 ../../web_tests/images/resources/gracehopper.bmp
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
index 6886994..70079a6 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.cc
@@ -767,8 +767,7 @@
   have_parsed_current_data_ = true;
 
   if (!decoder_) {
-    decoder_ = std::unique_ptr<avifDecoder, void (*)(avifDecoder*)>(
-        avifDecoderCreate(), avifDecoderDestroy);
+    decoder_.reset(avifDecoderCreate());
     if (!decoder_)
       return false;
 
@@ -1102,7 +1101,7 @@
                                static_cast<uint32_t>(*to_row - from_row)};
     view.reset(avifImageCreateEmpty());
     const avifResult result = avifImageSetViewRect(view.get(), image, &rect);
-    DCHECK_EQ(result, AVIF_RESULT_OK);
+    CHECK_EQ(result, AVIF_RESULT_OK);
     image = view.get();
   }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h
index ef7ad34..e7fda5d 100644
--- a/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/avif/avif_image_decoder.h
@@ -116,8 +116,8 @@
   avifPixelFormat avif_yuv_format_ = AVIF_PIXEL_FORMAT_NONE;
   wtf_size_t decoded_frame_count_ = 0;
   SkYUVColorSpace yuv_color_space_ = SkYUVColorSpace::kIdentity_SkYUVColorSpace;
-  std::unique_ptr<avifDecoder, void (*)(avifDecoder*)> decoder_{nullptr,
-                                                                nullptr};
+  std::unique_ptr<avifDecoder, decltype(&avifDecoderDestroy)> decoder_{
+      nullptr, avifDecoderDestroy};
   avifIO avif_io_ = {};
   AvifIOData avif_io_data_;
 
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
index 3ba8938..b5caf45 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc
@@ -51,10 +51,9 @@
 #include "third_party/skia/include/private/SkJpegMetadataDecoder.h"
 
 extern "C" {
+#include <setjmp.h>
 #include <stdio.h>  // jpeglib.h needs stdio FILE.
 #include "jpeglib.h"
-#include "iccjpeg.h"
-#include <setjmp.h>
 }
 
 #if defined(ARCH_CPU_BIG_ENDIAN)
@@ -83,9 +82,6 @@
 
 namespace {
 
-constexpr int kExifMarker = JPEG_APP0 + 1;
-constexpr unsigned kExifAPP1SignatureSize = 6;
-
 // JPEG only supports a denominator of 8.
 const unsigned g_scale_denominator = 8;
 
@@ -220,33 +216,6 @@
 void error_exit(j_common_ptr cinfo);
 void emit_message(j_common_ptr cinfo, int msg_level);
 
-static bool IsExifData(jpeg_saved_marker_ptr marker) {
-  // For EXIF data, the APP1 block is followed by 'E', 'x', 'i', 'f', '\0',
-  // then a fill byte, and then a TIFF file that contains the metadata.
-  if (marker->marker != kExifMarker)
-    return false;
-  if (marker->data_length < kExifAPP1SignatureSize)
-    return false;
-  const uint8_t kExifAPP1Signature[5] = {'E', 'x', 'i', 'f', '\0'};
-  if (memcmp(marker->data, kExifAPP1Signature, sizeof(kExifAPP1Signature)) != 0)
-    return false;
-  return true;
-}
-
-static void ReadImageMetaData(jpeg_decompress_struct* info, DecodedImageMetaData& metadata) {
-  // The JPEG decoder looks at EXIF metadata.
-  // FIXME: Possibly implement XMP and IPTC support.
-  for (jpeg_saved_marker_ptr marker = info->marker_list; marker;
-       marker = marker->next) {
-    if (!IsExifData(marker))
-      continue;
-    base::span<const uint8_t> exif_data(
-        marker->data + kExifAPP1SignatureSize,
-        marker->data_length - kExifAPP1SignatureSize);
-    ReadExif(exif_data, metadata);
-  }
-}
-
 static gfx::Size ComputeYUVSize(const jpeg_decompress_struct* info,
                                 int component) {
   return gfx::Size(info->comp_info[component].downsampled_width,
@@ -306,9 +275,6 @@
     info_.progress = &progress_mgr_;
     progress_mgr_.progress_monitor = ProgressMonitor;
 
-    // Retain ICC color profile markers for color management.
-    setup_read_icc_profile(&info_);
-
     // Keep APP1 blocks, for obtaining exif and XMP data.
     jpeg_save_markers(&info_, JPEG_APP0 + 1, 0xFFFF);
 
@@ -319,7 +285,12 @@
   JPEGImageReader(const JPEGImageReader&) = delete;
   JPEGImageReader& operator=(const JPEGImageReader&) = delete;
 
-  ~JPEGImageReader() { jpeg_destroy_decompress(&info_); }
+  ~JPEGImageReader() {
+    // Reset `metadata_decoder_` before `info_` because `metadata_decoder_`
+    // points to memory owned by `info_`.
+    metadata_decoder_ = nullptr;
+    jpeg_destroy_decompress(&info_);
+  }
 
   void SkipBytes(long num_bytes) {
     if (num_bytes <= 0)
@@ -464,6 +435,19 @@
 
         state_ = kJpegStartDecompress;
 
+        // Build the SkJpegMetadataDecoder to extract metadata from the
+        // now-complete header.
+        {
+          std::vector<SkJpegMetadataDecoder::Segment> segments;
+          for (auto* marker = info_.marker_list; marker;
+               marker = marker->next) {
+            segments.emplace_back(
+                marker->marker,
+                SkData::MakeWithoutCopy(marker->data, marker->data_length));
+          }
+          metadata_decoder_ = SkJpegMetadataDecoder::Make(std::move(segments));
+        }
+
         // We can fill in the size now that the header is available.
         if (!decoder_->SetSize(info_.image_width, info_.image_height))
           return false;
@@ -510,17 +494,24 @@
         decoder_->SetDecodedSize(info_.output_width, info_.output_height);
 
         DecodedImageMetaData metadata;
-        ReadImageMetaData(Info(), metadata);
+        if (sk_sp<SkData> exif_data =
+                metadata_decoder_->getExifMetadata(/*copyData=*/false)) {
+          base::span<const uint8_t> exif_span(exif_data->bytes(),
+                                              exif_data->size());
+          ReadExif(exif_span, metadata);
+        }
         decoder_->ApplyMetadata(
             metadata, gfx::Size(info_.output_width, info_.output_height));
 
         // Allow color management of the decoded RGBA pixels if possible.
         if (!decoder_->IgnoresColorSpace()) {
-          JOCTET* profile_buf = nullptr;
-          unsigned profile_length = 0;
-          if (read_icc_profile(Info(), &profile_buf, &profile_length)) {
-            std::unique_ptr<ColorProfile> profile =
-                ColorProfile::Create(profile_buf, profile_length);
+          // Extract the ICC profile data without copying it (the function
+          // ColorProfile::Create will make its own copy).
+          sk_sp<SkData> profile_data =
+              metadata_decoder_->getICCProfileData(/*copyData=*/false);
+          if (profile_data) {
+            std::unique_ptr<ColorProfile> profile = ColorProfile::Create(
+                profile_data->bytes(), profile_data->size());
             if (profile) {
               uint32_t data_color_space =
                   profile->GetProfile()->data_color_space;
@@ -545,7 +536,6 @@
             } else {
               DLOG(ERROR) << "Failed to parse image ICC profile";
             }
-            free(profile_buf);
           }
         }
 
@@ -721,6 +711,9 @@
   JPEGImageDecoder* Decoder() { return decoder_; }
   gfx::Size UvSize() const { return uv_size_; }
   bool HasStartedDecompression() const { return state_ > kJpegStartDecompress; }
+  SkJpegMetadataDecoder* GetMetadataDecoder() {
+    return metadata_decoder_.get();
+  }
 
  private:
 #if defined(USE_SYSTEM_LIBJPEG)
@@ -788,6 +781,10 @@
   jpeg_progress_mgr progress_mgr_;
   jstate state_;
 
+  // The metadata decoder is populated once the full header (all segments up to
+  // the first StartOfScan) has been received.
+  std::unique_ptr<SkJpegMetadataDecoder> metadata_decoder_;
+
   JSAMPARRAY samples_;
   gfx::Size uv_size_;
 };
@@ -974,40 +971,27 @@
 bool JPEGImageDecoder::GetGainmapInfoAndData(
     SkGainmapInfo& out_gainmap_info,
     scoped_refptr<SegmentReader>& out_gainmap_reader) const {
-  if (!reader_) {
+  auto* metadata_decoder = reader_ ? reader_->GetMetadataDecoder() : nullptr;
+  if (!metadata_decoder) {
     return false;
   }
 
-  // Extract the segments (including parameters) and create an
-  // SkJpegMetadataDecoder to use to examine gainmap capabilities.
-  std::vector<SkJpegMetadataDecoder::Segment> segments;
-  if (jpeg_decompress_struct* info = reader_->Info()) {
-    for (auto* marker = info->marker_list; marker; marker = marker->next) {
-      segments.emplace_back(
-          marker->marker,
-          SkData::MakeWithoutCopy(marker->data, marker->data_length));
-    }
-  } else {
+  if (!metadata_decoder->mightHaveGainmapImage()) {
     return false;
   }
-  auto metadata = SkJpegMetadataDecoder::Make(std::move(segments));
-  if (!metadata) {
-    return false;
-  }
-
-  // TODO(https://crbug.com/1404000): Add an early-out that uses only
-  // `segments` to determine if a gainmap is guaranteed to not be present.
-  auto base_image_data = data_->GetAsSkData();
-  DCHECK(base_image_data);
 
   // Extract the SkGainmapInfo and the encoded gainmap image and return them.
+  // TODO(https://crbug.com/1404000): Express `data_` as an SkStream, to avoid
+  // making extra copies.
+  sk_sp<SkData> base_image_data = data_->GetAsSkData();
+  DCHECK(base_image_data);
   // TODO(https://crbug.com/1404000): Rather than extract an SkData, extract
   // the offsets and sizes of the subsets of `base_image_data`, and reference
   // these directly.
   sk_sp<SkData> gainmap_image_data;
   SkGainmapInfo gainmap_info;
-  if (!metadata->findGainmapImage(base_image_data, gainmap_image_data,
-                                  gainmap_info)) {
+  if (!metadata_decoder->findGainmapImage(base_image_data, gainmap_image_data,
+                                          gainmap_info)) {
     return false;
   }
   out_gainmap_info = gainmap_info;
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
index 69c9c34..32d26ad 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -633,4 +633,30 @@
   EXPECT_FALSE(decoder->CanDecodeToYUV());
 }
 
+TEST(JPEGImageDecoderTest, Gainmap) {
+  const char* jpeg_file = "/images/resources/gainmap-trattore0.jpg";
+  scoped_refptr<SharedBuffer> full_data = ReadFile(jpeg_file);
+  ASSERT_TRUE(full_data);
+
+  auto base_decoder = CreateJPEGDecoder();
+  base_decoder->SetData(full_data.get(), true);
+  ASSERT_TRUE(base_decoder->IsSizeAvailable());
+  EXPECT_EQ(gfx::Size(134, 100), base_decoder->DecodedSize());
+
+  SkGainmapInfo gainmap_info;
+  scoped_refptr<SegmentReader> gainmap_data;
+  ASSERT_TRUE(base_decoder->GetGainmapInfoAndData(gainmap_info, gainmap_data));
+
+  // Ensure that the gainmap information was extracted.
+  EXPECT_NEAR(gainmap_info.fDisplayRatioHdr, 2.718f, 1.0e-3f);
+
+  // Ensure that the extracted gainmap image contains an appropriately-sized
+  // image.
+  auto gainmap_decoder = CreateJPEGDecoder();
+  gainmap_decoder->SetData(gainmap_data.get(), true);
+  ASSERT_TRUE(gainmap_decoder->IsSizeAvailable());
+  EXPECT_FALSE(gainmap_decoder->Failed());
+  EXPECT_EQ(gfx::Size(33, 25), gainmap_decoder->DecodedSize());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e5aba46..de23f4da2 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -219,16 +219,6 @@
       status: "stable"
     },
     {
-      name: "AbortSignalThrowIfAborted",
-      status: "stable",
-      base_feature: "none",
-    },
-    {
-      name: "AbortSignalTimeout",
-      status: "stable",
-      base_feature: "none",
-    },
-    {
       name: "Accelerated2dCanvas",
       settable_from_internals: true,
       status: "stable",
@@ -1141,6 +1131,11 @@
       name: "CSSTextWrap",
       status: "stable",
     },
+    // Use the `NGSCoreLineBreaker` for `text-wrap: balance`. crbug.com/1451205
+    {
+      name: "CSSTextWrapBalanceByScore",
+      depends_on: ["CSSTextWrapPretty"],
+    },
     // `text-wrap: pretty`. crbug.com/1432798
     {
       name: "CSSTextWrapPretty",
@@ -1951,11 +1946,6 @@
       base_feature: "none",
     },
     {
-      // A kill-switch for a fix of crbug.com/108489.
-      name: "HTMLMapToImgMatchingByNameAndId",
-      status: "stable",
-    },
-    {
       // A flag, just for local testing to make the
       // HTML parser yield more often and take longer to resume.
       // This helps when debugging flaky tests caused by parser
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 156e3a6..d073a5af 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2881,7 +2881,6 @@
 crbug.com/626703 [ Mac12 ] virtual/threaded-prefer-compositing/external/wpt/scroll-animations/scroll-timelines/effect-updateTiming.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] virtual/threaded/external/wpt/scroll-animations/scroll-timelines/effect-updateTiming.html [ Timeout ]
 crbug.com/626703 [ Mac12 ] virtual/threaded/external/wpt/scroll-animations/view-timelines/view-timeline-range.html [ Timeout ]
-crbug.com/626703 [ Win ] external/wpt/video-rvfc/request-video-frame-callback-webrtc.https.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] external/wpt/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-simulcast.https.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] external/wpt/html/semantics/links/hyperlink-auditing/headers.optional.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] virtual/threaded-prefer-compositing/external/wpt/scroll-animations/scroll-timelines/effect-updateTiming.html [ Timeout ]
@@ -2889,7 +2888,6 @@
 crbug.com/626703 [ Mac13 ] virtual/threaded/external/wpt/scroll-animations/scroll-timelines/effect-updateTiming.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] virtual/threaded/external/wpt/scroll-animations/view-timelines/view-timeline-inset.html [ Pass Timeout ]
 crbug.com/626703 external/wpt/css/css-images/image-set/image-set-negative-resolution-rendering-3.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/video-rvfc/request-video-frame-callback-webrtc.https.html [ Failure Timeout ]
 crbug.com/626703 [ Mac13-arm64 ] external/wpt/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-simulcast.https.html [ Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/threaded-prefer-compositing/external/wpt/scroll-animations/css/view-timeline-range-animation.html [ Timeout ]
 crbug.com/626703 [ Mac12 Release ] virtual/threaded/external/wpt/scroll-animations/css/animation-timeline-view-functional-notation.tentative.html [ Timeout ]
@@ -6631,7 +6629,7 @@
 crbug.com/1447239 virtual/threaded-prefer-compositing/external/wpt/scroll-animations/css/animation-range-visual-test.html [ Failure Pass ]
 crbug.com/1447244 [ Mac ] virtual/threaded/external/wpt/scroll-animations/css/animation-range-visual-test.html [ Failure Pass ]
 
-crbug.com/1377004 [ Mac ] external/wpt/video-rvfc/request-video-frame-callback-webrtc.https.html [ Failure Pass Timeout ]
+crbug.com/1377004 external/wpt/video-rvfc/request-video-frame-callback-webrtc.https.html [ Failure Pass Timeout Crash ]
 
 # Sheriff 2023-05-23
 crbug.com/1376679 [ Mac ] virtual/threaded-prefer-compositing/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/document-picture-in-picture/beforeunload-is-disabled.https.html b/third_party/blink/web_tests/external/wpt/document-picture-in-picture/beforeunload-is-disabled.https.html
new file mode 100644
index 0000000..3dd2b8a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/document-picture-in-picture/beforeunload-is-disabled.https.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<title>Test that onbeforeunload is disabled for document picture in picture</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+  <script>
+    promise_test(async (t) => {
+      await test_driver.bless('request PiP window from top window');
+      const pipWindow = await documentPictureInPicture.requestWindow();
+      var onbeforeunloadDisabled = true;
+      pipWindow.window.onbeforeunload = () => {
+        onbeforeunloadDisabled = false;
+        return "This is a test";
+      }
+      pipWindow.close();
+      assert_true(onbeforeunloadDisabled, 'onbeforeunload should be disabled for document picture in picture');
+    });
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-denormals.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-denormals.https.window-expected.txt
new file mode 100644
index 0000000..fc030d0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-denormals.https.window-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Test denormal behavior in AudioWorkletGlobalScope assert_true: The denormals should be non-zeros in AudioWorkletGlobalScope. expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-denormals.https.window.js b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-denormals.https.window.js
new file mode 100644
index 0000000..39b9be56
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/audioworklet-denormals.https.window.js
@@ -0,0 +1,26 @@
+'use strict';
+
+// Test if the JS code execution in AudioWorkletGlobalScope can handle the
+// denormals properly. For more details, see:
+// https://esdiscuss.org/topic/float-denormal-issue-in-javascript-processor-node-in-web-audio-api
+promise_test(async () => {
+  // In the main thread, the denormals should be non-zeros.
+  assert_not_equals(Number.MIN_VALUE, 0.0,
+                    'The denormals should be non-zeros.');
+
+  const context = new AudioContext();
+  await context.audioWorklet.addModule(
+      './processors/denormal-test-processor.js');
+
+  const denormalTestProcessor = new AudioWorkletNode(context, 'denormal-test');
+
+  return new Promise(resolve => {
+    denormalTestProcessor.port.onmessage = resolve;
+    denormalTestProcessor.connect(context.destination);
+  }).then(event => {
+    // In the AudioWorkletGlobalScope, the denormals should be non-zeros too.
+    assert_true(
+        event.data.result,
+        'The denormals should be non-zeros in AudioWorkletGlobalScope.');
+  });
+}, 'Test denormal behavior in AudioWorkletGlobalScope');
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/denormal-test-processor.js b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/denormal-test-processor.js
new file mode 100644
index 0000000..2b79294
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioworklet-interface/processors/denormal-test-processor.js
@@ -0,0 +1,12 @@
+class DenormalTestProcessor extends AudioWorkletProcessor {
+  process() {
+    // The denormals should be non-zeros. Otherwise, it's a violation of
+    // ECMA specification: https://tc39.es/ecma262/#sec-number.min_value
+    this.port.postMessage({
+      result: Number.MIN_VALUE !== 0.0
+    });
+    return false;
+  }
+}
+
+registerProcessor('denormal-test', DenormalTestProcessor);
diff --git a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-existent-and-non-existent-import.html b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-existent-and-non-existent-import.html
index c40e431..b6f90f8e 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-existent-and-non-existent-import.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-existent-and-non-existent-import.html
@@ -6,8 +6,7 @@
 function passed()
 {
   testPassed("Fired Error event.");
-  shouldComputedColorOfElementBeEqualToRGBString(document.getElementById("test"), "rgb(0, 0, 255)" /* blue */); // i.e. stylesheet.css successfully loaded
-  testFinished();
+  shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished("test", "rgb(0, 0, 255)" /* blue */); // i.e. stylesheet.css successfully loaded
 }
 </script>
 <link rel="stylesheet" href="resources/stylesheet-with-existent-and-non-existent-import.css" onload="testFailedAndNotifyDone('Fired Load event. Should have fired Error event.')" onerror="passed()">
diff --git a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-non-existent-import.html b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-non-existent-import.html
index bff5678..dd01c9d8 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-non-existent-import.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onerror-stylesheet-with-non-existent-import.html
@@ -6,8 +6,7 @@
 function passed()
 {
   testPassed("Fired Error event.");
-  shouldComputedColorOfElementBeEqualToRGBString(document.getElementById("test"), "rgb(255, 255, 0)" /* yellow */);
-  testFinished();
+  shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished("test", "rgb(255, 255, 0)" /* yellow */);
 }
 </script>
 <link rel="stylesheet" href="resources/stylesheet-with-non-existent-import.css" onload="testFailedAndNotifyDone('Fired Load event. Should have fired Error event.')" onerror="passed()">
diff --git a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onload-stylesheet-with-import.html b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onload-stylesheet-with-import.html
index ec4892f6..7bab519d 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onload-stylesheet-with-import.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/link-onload-stylesheet-with-import.html
@@ -6,8 +6,7 @@
 function checkResult()
 {
   testPassed("Fired Load event.");
-  shouldComputedColorOfElementBeEqualToRGBString(document.getElementById("test"), "rgb(0, 0, 255)" /* blue */);
-  testFinished();
+  shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished("test", "rgb(0, 0, 255)" /* blue */);
 }
 </script>
 <link rel="stylesheet" href="resources/stylesheet-with-import.css" onload="checkResult()" onerror="testFailed('Fired Error event. Should have fired Load event.')">
diff --git a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/resources/link-load-utilities.js b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/resources/link-load-utilities.js
index a03d06a4..0db2ddf00 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLLinkElement/resources/link-load-utilities.js
+++ b/third_party/blink/web_tests/fast/dom/HTMLLinkElement/resources/link-load-utilities.js
@@ -21,20 +21,31 @@
     log(line);
   }
   bufferedOutput = [];
-  if (buferredFinished) {
+  if (bufferredFinished) {
     testFinished();
     bufferedFinished = false;
   }
 }
 
-function shouldComputedColorOfElementBeEqualToRGBString(element, expectedColor)
+function shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished(element_id, expectedColor)
 {
-  var elementName = "#" + element.id || element.tagName;
+  let element = document.getElementById(element_id);
+  if (!element) {
+    if (document.readyState == "complete") {
+      log(`FAIL unable to find element with ID "${element_id}".`);
+      testFinished();
+      return;
+    }
+    window.addEventListener("load", () => shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished(element_id, expectedColor));
+    return;
+  }
+  var elementName = "#" + element_id;
   var actualColor = window.getComputedStyle(element, null).color;
   if (actualColor === expectedColor)
     log("PASS " + elementName + " color was " + expectedColor + ".");
   else
     log("FAIL " + elementName + " color should be " + expectedColor + ". Was " + actualColor + ".");
+  testFinished();
 }
 
 function createLinkElementWithStylesheet(stylesheetURL)
diff --git a/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror-with-existent-and-non-existent-import.html b/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror-with-existent-and-non-existent-import.html
index 3f9ea82..28d3972 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror-with-existent-and-non-existent-import.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror-with-existent-and-non-existent-import.html
@@ -6,8 +6,7 @@
 function checkResult()
 {
   testPassed("Error event fired.");
-  shouldComputedColorOfElementBeEqualToRGBString(document.getElementById("test"), "rgb(0, 0, 255)" /* blue */); // i.e. stylesheet.css successfully loaded
-  testFinished();
+  shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished("test", "rgb(0, 0, 255)" /* blue */); // i.e. stylesheet.css successfully loaded
 }
 </script>
 <style onload="testFailedAndNotifyDone('Load event fired. Should have fired Error event.')" onerror="checkResult()">
diff --git a/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror.html b/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror.html
index 4402356..ebf10a1 100644
--- a/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror.html
+++ b/third_party/blink/web_tests/fast/dom/HTMLStyleElement/style-onerror.html
@@ -6,8 +6,7 @@
 function passed()
 {
   testPassed("Error event fired.");
-  shouldComputedColorOfElementBeEqualToRGBString(document.getElementById("test"), "rgb(255, 255, 0)" /* yellow */);
-  testFinished();
+  shouldComputedColorOfElementByIdBeEqualToRGBStringAndTestFinished("test", "rgb(255, 255, 0)" /* yellow */);
 }
 </script>
 <style onload="testFailedAndNotifyDone('Load event fired. Should have fired Error event.')" onerror="passed()">
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-frame.js b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-frame.js
index 82f1e950..f9d96b8 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-frame.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-frame.js
@@ -49,6 +49,10 @@
   await session.evaluateAsync(makeFrameJS);
   const frameTarget = await frameTargetPromise;
   const frameSession = session.createChild(frameTarget.params.sessionId);
+  // Enable auto-attach for the frame, so we attach to auction worklet.
+  frameSession.protocol.Target.setAutoAttach(
+      {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
+
   const winner = await frameSession.evaluateAsync(auctionJs);
   testRunner.log('Auction winner:' + handleUrn(winner));
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js
index b0162008..5acb31a 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet-network.js
@@ -7,6 +7,7 @@
   const testStart = Date.now();
   const testLimit = 5 * 60 * 1000;  // Way longer than the test may take.
 
+  dp.Target.setAutoAttach({autoAttach: true, flatten: true, waitForDebuggerOnStart: false});
   const TracingHelper =
       await testRunner.loadScript('../resources/tracing-test.js');
   const tracingHelper = new TracingHelper(testRunner, session);
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet.js b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet.js
index 1de8236..fe1e368 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/timeline/auction-worklet.js
@@ -4,6 +4,7 @@
   const {page, session, dp} = await testRunner.startBlank(
       'Tracing of FLEDGE worklets.', {url: base + 'fledge_join.html?40'});
 
+  await dp.Target.setAutoAttach({autoAttach: true, flatten: true, waitForDebuggerOnStart: false});
   const TracingHelper =
       await testRunner.loadScript('../resources/tracing-test.js');
   const tracingHelper = new TracingHelper(testRunner, session);
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/prerender.js b/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/prerender.js
index 38e769e0..19f9a46 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/prerender.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/tracing/prerender.js
@@ -26,9 +26,15 @@
 
   page.navigate('../prerender/resources/simple-prerender.html');
   await dp.Preload.oncePrerenderStatusUpdated(e => e.params.status == 'Ready');
-
+  tp.Target.setAutoAttach({autoAttach: true, flatten: true, waitForDebuggerOnStart: false});
+  const prerenderSessionId = (await tp.Target.onceAttachedToTarget(
+      e => e.params.targetInfo.subtype === 'prerender')).params.sessionId;
+  const pp = session.createChild(prerenderSessionId).protocol;
+  await pp.Preload.enable();
   session.evaluate(`document.getElementById('link').click()`);
-  await dp.Preload.oncePrerenderAttemptCompleted();
+
+  await pp.Preload.oncePrerenderAttemptCompleted();
+
   const devtoolsEvents = await tracingHelper.stopTracing();
   const prerenderFrameCommitted =
       tracingHelper
diff --git a/third_party/blink/web_tests/images/resources/gainmap-trattore0.jpg b/third_party/blink/web_tests/images/resources/gainmap-trattore0.jpg
new file mode 100644
index 0000000..ba34dcd
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/gainmap-trattore0.jpg
Binary files differ
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter-expected.txt b/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter-expected.txt
index 76ab560..f3d471d 100644
--- a/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter-expected.txt
@@ -1,6 +1,6 @@
 Tests that tracing does not record unrelated processes.
-Recording started
 Another page
+Recording started
 Tracing complete
 There should be just 3 processes (browser, GPU, and renderer): 3
 
diff --git a/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter.js b/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter.js
index 703c31d..745f61b 100644
--- a/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter.js
+++ b/third_party/blink/web_tests/inspector-protocol/timeline/tracing-process-filter.js
@@ -3,14 +3,20 @@
 // found in the LICENSE file.
 
 (async function(testRunner) {
-  var {session} = await testRunner.startBlank('Tests that tracing does not record unrelated processes.');
+  const {dp, session} = await testRunner.startBlank('Tests that tracing does not record unrelated processes.');
 
-  var TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
-  var tracingHelper = new TracingHelper(testRunner, session);
+  const TracingHelper = await testRunner.loadScript('../resources/tracing-test.js');
+  const tracingHelper = new TracingHelper(testRunner, session);
+
+  // Create another page, then disconnect to make sure it's not the part
+  // of the trace via session tracking.
+  const {session: otherSession} = await testRunner.startURL('data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==', 'Another page');
+  await otherSession.disconnect();
 
   await tracingHelper.startTracing();
-  await session.evaluateAsync(`return Promise.resolve(42)`);
-  await testRunner.startURL('data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==', 'Another page');
+  await session.evaluateAsync(`new Promise(resolve => requestAnimationFrame(resolve))`);
+  // Make sure GPU process pops up in trace as well.
+  await dp.Page.captureScreenshot({format: 'png'});
   const events = await tracingHelper.stopTracing();
 
   const pids = new Set();
diff --git a/third_party/eigen3/README.chromium b/third_party/eigen3/README.chromium
index 8d77468..1f0af6c 100644
--- a/third_party/eigen3/README.chromium
+++ b/third_party/eigen3/README.chromium
@@ -1,8 +1,8 @@
 Name: Eigen
 Short Name: eigen3
 URL: http://eigen.tuxfamily.org/
-Version: c18f94e3b017104284cd541e553472e62e85e526
-Date: 2023/05/23
+Version: 316eab8deb574d150f9cfc7f8b170156dc0cdd9f
+Date: 2023/06/05
 License: MPL 2
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/flatbuffers/BUILD.gn b/third_party/flatbuffers/BUILD.gn
index 2729d08..98f0f8e 100644
--- a/third_party/flatbuffers/BUILD.gn
+++ b/third_party/flatbuffers/BUILD.gn
@@ -6,7 +6,10 @@
 import("//third_party/flatbuffers/flatbuffer.gni")
 
 config("flatbuffers_config") {
-  include_dirs = [ "src/include" ]
+  include_dirs = [
+    "src/include",
+    "src/src",
+  ]
 
   # Required to prevent static initialization of locale
   # in util.cpp
diff --git a/third_party/flatbuffers/README.chromium b/third_party/flatbuffers/README.chromium
index ea8687c1..977a1411 100644
--- a/third_party/flatbuffers/README.chromium
+++ b/third_party/flatbuffers/README.chromium
@@ -1,8 +1,8 @@
 Name: FlatBuffers
 Short Name: flatbuffers
 URL: https://github.com/google/flatbuffers
-Version: a56f9ec50e908362e20254fcef28e62a2f148d91
-Date: 2023-02-16
+Version: 13fc75cb6b7b44793f3f5b4ba025ff403d012c9f
+Date: 2023-06-05
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 30a34f3..baa2e148 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-13-0-144-g80a507a6b
-Revision: 80a507a6b8e3d2906ad2c8ba69329bd2fb2a85ef
+Version: VER-2-13-0-148-g4e1c0e8fb
+Revision: 4e1c0e8fba456b2050c672c9c2c876987f03a3e6
 CPEPrefix: cpe:/a:freetype:freetype:2.12.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/ruy/README.chromium b/third_party/ruy/README.chromium
index 5487d08b1..7f3d039b 100644
--- a/third_party/ruy/README.chromium
+++ b/third_party/ruy/README.chromium
@@ -1,8 +1,8 @@
 Name: The ruy matrix multiplication library
 Short Name: ruy
 URL: https://github.com/google/ruy
-Version: 72d107f88082aeca462ffbe91b3a3b0e02686a73
-Date: 2023/05/23
+Version: caa244343de289f913c505100e6a463d46c174de
+Date: 2023/06/05
 License: Apache 2
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index f86da07..76b31e6 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: be1a33f1566b40bd43ecfc53dbd8c3dfbc7f2aa1
-Date: 2023/05/23
+Version: 5abeda2494b0eb551b5c4689e47008fe374684dc
+Date: 2023/06/05
 License: Apache 2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc b/third_party/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc
index ad1a985..64892bc 100644
--- a/third_party/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc
+++ b/third_party/zlib/contrib/tests/fuzzers/deflate_fuzzer.cc
@@ -38,6 +38,9 @@
   int ret =
       deflateInit2(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy);
   ASSERT(ret == Z_OK);
+
+  size_t deflate_bound = deflateBound(&stream, src.size());
+
   std::vector<uint8_t> compressed(src.size() * 2 + 1000);
   stream.next_out = compressed.data();
   stream.avail_out = compressed.size();
@@ -54,6 +57,9 @@
   compressed.resize(compressed.size() - stream.avail_out);
   deflateEnd(&stream);
 
+  // Check that the bound was correct.
+  ASSERT(compressed.size() <= deflate_bound);
+
   // Verify that the data decompresses correctly.
   ret = inflateInit2(&stream, windowBits);
   ASSERT(ret == Z_OK);
diff --git a/third_party/zlib/zconf.h b/third_party/zlib/zconf.h
index 6d54d00..55fe101 100644
--- a/third_party/zlib/zconf.h
+++ b/third_party/zlib/zconf.h
@@ -539,7 +539,7 @@
 #if !defined(_WIN32) && defined(Z_LARGE64)
 #  define z_off64_t off64_t
 #else
-#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+#  if defined(_WIN32) && !defined(__GNUC__)
 #    define z_off64_t __int64
 #  else
 #    define z_off64_t z_off_t
diff --git a/third_party/zlib/zconf.h.cmakein b/third_party/zlib/zconf.h.cmakein
index 9cc20bf..310c4392 100644
--- a/third_party/zlib/zconf.h.cmakein
+++ b/third_party/zlib/zconf.h.cmakein
@@ -526,7 +526,7 @@
 #if !defined(_WIN32) && defined(Z_LARGE64)
 #  define z_off64_t off64_t
 #else
-#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+#  if defined(_WIN32) && !defined(__GNUC__)
 #    define z_off64_t __int64
 #  else
 #    define z_off64_t z_off_t
diff --git a/tools/android/python_utils/git_metadata_utils.py b/tools/android/python_utils/git_metadata_utils.py
index 02fecf6..cc3643c 100644
--- a/tools/android/python_utils/git_metadata_utils.py
+++ b/tools/android/python_utils/git_metadata_utils.py
@@ -29,9 +29,6 @@
         checkout containing this file.
     """
     _CHROMIUM_SRC_ROOT = pathlib.Path(__file__).resolve(strict=True).parents[3]
-    if _CHROMIUM_SRC_ROOT.name != 'src':
-        raise AssertionError(
-            f'_CHROMIUM_SRC_ROOT "{_CHROMIUM_SRC_ROOT}" should end in "src".')
 
     try:
         _assert_git_repository(_CHROMIUM_SRC_ROOT)
diff --git a/tools/clang/plugins/tests/blocklisted_dirs.txt b/tools/clang/plugins/tests/blocklisted_dirs.txt
index 9bef7f9..44db5fc 100644
--- a/tools/clang/plugins/tests/blocklisted_dirs.txt
+++ b/tools/clang/plugins/tests/blocklisted_dirs.txt
@@ -4,12 +4,12 @@
                      override
 /src/chromium/src/myheader.h:2:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void foo();  // Should warn about missing 'override'.
-  ^~~~~~~~
+  ^~~~~~~
 /src/chrome-breakpad/src/myheader.h:124:21: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void foo();  // Should warn about missing 'override'.
                     ^
                      override
 /src/chrome-breakpad/src/myheader.h:124:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void foo();  // Should warn about missing 'override'.
-  ^~~~~~~~
+  ^~~~~~~
 4 warnings generated.
diff --git a/tools/clang/plugins/tests/overridden_methods.txt b/tools/clang/plugins/tests/overridden_methods.txt
index 5a70c3a..cf3a84b 100644
--- a/tools/clang/plugins/tests/overridden_methods.txt
+++ b/tools/clang/plugins/tests/overridden_methods.txt
@@ -5,84 +5,84 @@
                              override
 ./overridden_methods.h:25:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethod() = 0;
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:38:42: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void WebKitModifiedSomething() {}
                                          ^
                                           override
 ./overridden_methods.h:38:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void WebKitModifiedSomething() {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:46:27: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual ~DerivedClass() {}
                           ^
                            override
 ./overridden_methods.h:46:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual ~DerivedClass() {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:48:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethod();
                            ^
                             override
 ./overridden_methods.h:48:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethod();
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:52:35: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeInlineMethod() {}
                                   ^
                                    override
 ./overridden_methods.h:52:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeInlineMethod() {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:54:41: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void WebKitModifiedSomething();
                                         ^
                                          override
 ./overridden_methods.h:54:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void WebKitModifiedSomething();
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:56:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeConstMethod() const {}
                                        ^
                                         override
 ./overridden_methods.h:56:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeConstMethod() const {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:58:54: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethodWithExceptionSpec() throw() {}
                                                      ^
                                                       override
 ./overridden_methods.h:58:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethodWithExceptionSpec() throw() {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:61:68: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeConstMethodWithExceptionSpec() const throw(int) {}
                                                                    ^
                                                                     override
 ./overridden_methods.h:61:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeConstMethodWithExceptionSpec() const throw(int) {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:63:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeNonPureBaseMethod() {}
                                        ^
                                         override
 ./overridden_methods.h:63:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeNonPureBaseMethod() {}
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:65:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethodWithComment();  // This is a comment.
                                       ^
                                        override
 ./overridden_methods.h:65:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethodWithComment();  // This is a comment.
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:67:47: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
                                               ^
                                                override
 ./overridden_methods.h:67:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
-  ^~~~~~~~
+  ^~~~~~~
 ./overridden_methods.h:78:36: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   ~DerivedClassWithDefaultedDtor() = default;
                                    ^
@@ -93,75 +93,75 @@
                              override
 overridden_methods.cpp:17:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethod() = 0;
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:24:41: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual ~ImplementationDerivedClass() {}
                                         ^
                                          override
 overridden_methods.cpp:24:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual ~ImplementationDerivedClass() {}
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:26:28: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethod();
                            ^
                             override
 overridden_methods.cpp:26:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethod();
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:30:35: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeInlineMethod() {}
                                   ^
                                    override
 overridden_methods.cpp:30:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeInlineMethod() {}
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:32:41: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void WebKitModifiedSomething();
                                         ^
                                          override
 overridden_methods.cpp:32:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void WebKitModifiedSomething();
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:34:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeConstMethod() const {}
                                        ^
                                         override
 overridden_methods.cpp:34:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeConstMethod() const {}
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:36:54: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethodWithExceptionSpec() throw() {}
                                                      ^
                                                       override
 overridden_methods.cpp:36:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethodWithExceptionSpec() throw() {}
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:39:68: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeConstMethodWithExceptionSpec() const throw(int) {}
                                                                    ^
                                                                     override
 overridden_methods.cpp:39:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeConstMethodWithExceptionSpec() const throw(int) {}
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:41:40: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeNonPureBaseMethod() {}
                                        ^
                                         override
 overridden_methods.cpp:41:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeNonPureBaseMethod() {}
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:43:39: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethodWithComment();  // This is a comment.
                                       ^
                                        override
 overridden_methods.cpp:43:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethodWithComment();  // This is a comment.
-  ^~~~~~~~
+  ^~~~~~~
 overridden_methods.cpp:45:47: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
                                               ^
                                                override
 overridden_methods.cpp:45:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void SomeMethodWithCommentAndBody() {}  // This is a comment.
-  ^~~~~~~~
+  ^~~~~~~
 47 warnings generated.
diff --git a/tools/clang/plugins/tests/virtual_base_method_also_final.txt b/tools/clang/plugins/tests/virtual_base_method_also_final.txt
index 2670ad28..ba8b53a 100644
--- a/tools/clang/plugins/tests/virtual_base_method_also_final.txt
+++ b/tools/clang/plugins/tests/virtual_base_method_also_final.txt
@@ -1,7 +1,7 @@
 virtual_base_method_also_final.cpp:10:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
   virtual void F() final {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_base_method_also_final.cpp:13:8: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
   void virtual G() final {}
-       ^~~~~~~~
+       ^~~~~~~
 2 warnings generated.
diff --git a/tools/clang/plugins/tests/virtual_specifiers.txt b/tools/clang/plugins/tests/virtual_specifiers.txt
index 1d92c0c6..ca551c0 100644
--- a/tools/clang/plugins/tests/virtual_specifiers.txt
+++ b/tools/clang/plugins/tests/virtual_specifiers.txt
@@ -8,43 +8,43 @@
             override
 virtual_specifiers.cpp:46:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
   virtual ~VirtualAndOverride() OVERRIDE {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:47:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
   virtual void F() OVERRIDE {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:52:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
   virtual ~VirtualAndFinal() FINAL {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:53:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
   virtual void F() FINAL {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:58:38: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
   virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {}
-                                     ^~~~~~~~~
+                                     ^~~~~~~~
 virtual_specifiers.cpp:13:18: note: expanded from macro 'OVERRIDE'
 #define OVERRIDE override
                  ^
 virtual_specifiers.cpp:58:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
   virtual ~VirtualAndOverrideFinal() OVERRIDE FINAL {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:59:20: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
   virtual void F() OVERRIDE FINAL {}
-                   ^~~~~~~~~
+                   ^~~~~~~~
 virtual_specifiers.cpp:13:18: note: expanded from macro 'OVERRIDE'
 #define OVERRIDE override
                  ^
 virtual_specifiers.cpp:59:3: warning: [chromium-style] 'virtual' is redundant; 'final' implies 'virtual'.
   virtual void F() OVERRIDE FINAL {}
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:64:23: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
   ~OverrideAndFinal() OVERRIDE FINAL {}
-                      ^~~~~~~~~
+                      ^~~~~~~~
 virtual_specifiers.cpp:13:18: note: expanded from macro 'OVERRIDE'
 #define OVERRIDE override
                  ^
 virtual_specifiers.cpp:65:12: warning: [chromium-style] 'override' is redundant; 'final' implies 'override'.
   void F() OVERRIDE FINAL {}
-           ^~~~~~~~~
+           ^~~~~~~~
 virtual_specifiers.cpp:13:18: note: expanded from macro 'OVERRIDE'
 #define OVERRIDE override
                  ^
@@ -54,22 +54,22 @@
                     override
 virtual_specifiers.cpp:70:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual void F() = 0;
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:74:12: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   void F() = 0;
            ^
             override
 virtual_specifiers.cpp:82:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
   virtual void F() override = 0;
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:111:20: warning: [chromium-style] Overriding method must be marked with 'override' or 'final'.
   virtual ~MyTest();
                    ^
                     override
 virtual_specifiers.cpp:111:3: warning: [chromium-style] 'virtual' will be redundant; 'override' implies 'virtual'.
   virtual ~MyTest();
-  ^~~~~~~~
+  ^~~~~~~
 virtual_specifiers.cpp:112:3: warning: [chromium-style] 'virtual' is redundant; 'override' implies 'virtual'.
   virtual void SetUp() override;
-  ^~~~~~~~
+  ^~~~~~~
 19 warnings generated.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 404d343..a580353 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -27256,6 +27256,7 @@
   <int value="2" label="Presentation section selected"/>
   <int value="3" label="Protocol Handlers section selected"/>
   <int value="4" label="Icons section selected"/>
+  <int value="5" label="Window Controls Overlay section selected"/>
 </enum>
 
 <enum name="DevToolsMediaType">
@@ -42687,6 +42688,7 @@
   <int value="4579" label="FedCmLoginHint"/>
   <int value="4580" label="FedCmRpContext"/>
   <int value="4581" label="EventTimingArtificialPointerupOrClick"/>
+  <int value="4582" label="AbortSignalAny"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -59199,6 +59201,7 @@
   <int value="-1958315092" label="EnableGamepadButtonAxisEvents:disabled"/>
   <int value="-1957328398" label="MacSystemShareMenu:disabled"/>
   <int value="-1956747298" label="LayeredAPI:enabled"/>
+  <int value="-1956656203" label="ShowForceRespectUiGainsToggle:disabled"/>
   <int value="-1956349722" label="disable-smooth-scrolling"/>
   <int value="-1956231275" label="ForceSpectreVariant2Mitigation:enabled"/>
   <int value="-1955923385" label="EnableGamepadButtonAxisEvents:enabled"/>
@@ -60471,6 +60474,7 @@
   <int value="-1294050129" label="ContentFullscreen:disabled"/>
   <int value="-1293987566" label="OmniboxZeroSuggestionsOnNTPRealbox:disabled"/>
   <int value="-1292766410" label="IntentChipSkipsPicker:disabled"/>
+  <int value="-1292735488" label="HTMLPopoverAttribute:disabled"/>
   <int value="-1292615467"
       label="OmniboxSuggestionTransparencyOptions:disabled"/>
   <int value="-1291963295" label="ComputePressure:disabled"/>
@@ -62852,6 +62856,7 @@
   <int value="-40935502" label="ContextualSuggestionsSlimPeekUI:enabled"/>
   <int value="-37966026" label="PermissionPredictions:enabled"/>
   <int value="-37556470" label="FeedBottomSyncBanner:enabled"/>
+  <int value="-36550471" label="HTMLPopoverAttribute:enabled"/>
   <int value="-36503306" label="HomepageLocationPolicy:enabled"/>
   <int value="-36234530" label="PluginVmShowMicrophonePermissions:enabled"/>
   <int value="-36077995" label="SplitCacheByNetworkIsolationKey:disabled"/>
@@ -63475,6 +63480,7 @@
   <int value="292731264" label="PasswordChangeInSettings:disabled"/>
   <int value="292842744"
       label="AvoidUnnecessaryNavigationCancellations:disabled"/>
+  <int value="292936174" label="ShowForceRespectUiGainsToggle:enabled"/>
   <int value="293025775" label="UseDnsHttpsSvcb:enabled"/>
   <int value="293134455" label="AutofillSendBillingCustomerNumber:disabled"/>
   <int value="293996306" label="ArrayPrototypeValues:disabled"/>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 809b605..f7e63b4 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -1078,7 +1078,7 @@
 </histogram>
 
 <histogram name="Blink.FedCm.ClosedSheetType.Desktop" enum="FedCmSheetType"
-    expires_after="2023-07-16">
+    expires_after="2024-01-16">
   <owner>npm@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index a5a0345b..2bc577e 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -826,6 +826,17 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.DeclarativeNetRequest.RegexRuleSize" units="bytes"
+    expires_after="2024-06-01">
+  <owner>kelvinjiang@chromium.org</owner>
+  <owner>src/extensions/OWNERS</owner>
+  <summary>
+    Logs the size of a regex rule in bytes, including rules that exceed the
+    per-rule memory limit that aren't indexed. Note that any rules above 100Kb
+    are recorded as 100Kb. Emitted for every regex rule an extension adds.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.DeclarativeNetRequest.RequestHeaderAdded"
     enum="WebRequest.RequestHeader" expires_after="2023-08-08">
   <owner>rdevlin.cronin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/geolocation/histograms.xml b/tools/metrics/histograms/metadata/geolocation/histograms.xml
index fdca122e..a6d01f7e 100644
--- a/tools/metrics/histograms/metadata/geolocation/histograms.xml
+++ b/tools/metrics/histograms/metadata/geolocation/histograms.xml
@@ -132,16 +132,6 @@
   </summary>
 </histogram>
 
-<histogram name="Geolocation.WifiDataProviderCommon.WifiScanTaskTime"
-    units="ms" expires_after="2023-07-15">
-  <owner>reillyg@chromium.org</owner>
-  <owner>deviceapi-team@google.com</owner>
-  <summary>
-    Records the time taken on Windows, macOS and Linux to query the operating
-    system for nearby access points.
-  </summary>
-</histogram>
-
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index b49080e0..e4e25cc 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -4429,7 +4429,7 @@
 </histogram>
 
 <histogram name="Media.Remoting.SessionStartFailedReason"
-    enum="RemotingStartFailReason" expires_after="2023-09-12">
+    enum="RemotingStartFailReason" expires_after="2023-12-01">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Tracks the reason that a session failed to start.</summary>
@@ -6667,7 +6667,7 @@
 </histogram>
 
 <histogram name="MediaRouter.RemotePlayback.SessionLoadTime" units="ms"
-    expires_after="2023-11-19">
+    expires_after="2023-12-01">
   <owner>muyaoxu@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6680,7 +6680,7 @@
 </histogram>
 
 <histogram name="MediaRouter.RemotePlayback.SessionStartsBeforeTimeout"
-    enum="Boolean" expires_after="2023-11-19">
+    enum="Boolean" expires_after="2023-12-01">
   <owner>muyaoxu@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6690,7 +6690,7 @@
 </histogram>
 
 <histogram name="MediaRouter.RemotePlayback.SinkCapability.SupportedAudioCodec"
-    enum="AudioCodec" expires_after="2023-10-22">
+    enum="AudioCodec" expires_after="2023-12-01">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6702,7 +6702,7 @@
 </histogram>
 
 <histogram name="MediaRouter.RemotePlayback.SinkCapability.SupportedVideoCodec"
-    enum="VideoCodec" expires_after="2023-10-22">
+    enum="VideoCodec" expires_after="2023-12-01">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6715,7 +6715,7 @@
 
 <histogram
     name="MediaRouter.RemotePlayback.SinkCapability.UnsupportedAudioCodec"
-    enum="AudioCodec" expires_after="2023-06-15">
+    enum="AudioCodec" expires_after="2023-12-01">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6728,7 +6728,7 @@
 
 <histogram
     name="MediaRouter.RemotePlayback.SinkCapability.UnsupportedVideoCodec"
-    enum="VideoCodec" expires_after="2023-06-15">
+    enum="VideoCodec" expires_after="2023-12-01">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -6740,7 +6740,7 @@
 </histogram>
 
 <histogram name="MediaRouter.RemotePlayback.SinkModelCompatibility"
-    enum="Boolean" expires_after="2023-10-22">
+    enum="Boolean" expires_after="2023-12-01">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 158ebc43..fd71d5a 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -5842,7 +5842,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.IncludedEmail" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5853,7 +5853,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.IncludedFile" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5864,7 +5864,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.IncludedScreenshot" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5875,7 +5875,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.IncludedSystemInfo" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5886,7 +5886,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.IncludedUrl" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5897,7 +5897,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.Openduration" units="ms"
-    expires_after="2023-07-15">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5920,7 +5920,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.TimeOnPage.{FeedbackAppPage}" units="s"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5948,7 +5948,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.ViewedImage" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5970,7 +5970,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.ViewedScreenshot" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
@@ -5981,7 +5981,7 @@
 </histogram>
 
 <histogram name="Feedback.ChromeOSApp.ViewedSystemAndAppInfo" units="boolean"
-    expires_after="2023-07-27">
+    expires_after="2024-06-04">
   <owner>longbowei@google.com</owner>
   <owner>xiangdongkong@google.com</owner>
   <owner>cros-feedback-app@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index bf518a1..d1641fb6 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -71,8 +71,6 @@
     attempting to migrate the database.
   </summary>
   <token key="VersionUpgrades">
-    <variant name="FromV5ToV7"/>
-    <variant name="FromV6ToV7"/>
     <variant name="FromV7ToV8"/>
     <variant name="FromV8ToV9"/>
     <variant name="FromV9ToV10"/>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index aed6625..b36e598 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -361,6 +361,9 @@
 
 <histogram name="Variations.ResourceRequestsAllowed"
     enum="VariationsResourceRequestsAllowedState" expires_after="2023-06-04">
+  <expired_intentionally>
+    Kept as a diagnostic metric.
+  </expired_intentionally>
   <owner>asvitkine@chromium.org</owner>
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
diff --git a/tools/typescript/definitions/runtime.d.ts b/tools/typescript/definitions/runtime.d.ts
index 3d89059..6197387 100644
--- a/tools/typescript/definitions/runtime.d.ts
+++ b/tools/typescript/definitions/runtime.d.ts
@@ -30,6 +30,8 @@
           sender: MessageSender,
           sendResponse: (response?: any) => void) => void> { }
       export const onMessageExternal: ExtensionMessageEvent;
+
+      export function getURL(path: string): string;
     }
   }
 }
diff --git a/ui/accessibility/android/java/src/org/chromium/ui/accessibility/AccessibilityState.java b/ui/accessibility/android/java/src/org/chromium/ui/accessibility/AccessibilityState.java
index 34985581..d92244c 100644
--- a/ui/accessibility/android/java/src/org/chromium/ui/accessibility/AccessibilityState.java
+++ b/ui/accessibility/android/java/src/org/chromium/ui/accessibility/AccessibilityState.java
@@ -7,6 +7,9 @@
 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_SPOKEN;
 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.Activity;
@@ -183,6 +186,11 @@
     @Deprecated
     private static boolean sAccessibilitySpeakPasswordEnabled;
 
+    // The recommended multiplier to apply to a timeout for changes to the UI needed by the user.
+    // Uses all content flags, cached from {AccessibilityManager#getRecommendedTimeoutMillis}.
+    // Requires Android Q, for versions < Q, this will always be equal to 1.0f.
+    private static float sRecommendedTimeoutMultiplier;
+
     // The IDs of all running accessibility services.
     private static String[] sServiceIds;
 
@@ -250,6 +258,11 @@
         return sAccessibilitySpeakPasswordEnabled;
     }
 
+    public static float getRecommendedTimeoutMultiplier() {
+        if (!sInitialized) updateAccessibilityServices();
+        return sRecommendedTimeoutMultiplier;
+    }
+
     static void updateAccessibilityServices() {
         if (!sInitialized) {
             sState = new State(false, false, false, false, false, false, false, false);
@@ -280,6 +293,15 @@
         Context context = ContextUtils.getApplicationContext();
         AccessibilityManager accessibilityManager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+        sRecommendedTimeoutMultiplier = 1.0f;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            sRecommendedTimeoutMultiplier =
+                    accessibilityManager.getRecommendedTimeoutMillis(
+                            100, FLAG_CONTENT_ICONS | FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS)
+                    / 100.0f;
+        }
+
         List<AccessibilityServiceInfo> services =
                 accessibilityManager.getEnabledAccessibilityServiceList(
                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
@@ -778,5 +800,12 @@
         sEventTypeMask = mask;
     }
 
+    @VisibleForTesting
+    public static void setRecommendedTimeoutMultiplierForTesting(float multiplier) {
+        if (!sInitialized) updateAccessibilityServices();
+
+        sRecommendedTimeoutMultiplier = multiplier;
+    }
+
     // clang-format on
 }
diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc
index 59553a2b..d2224af 100644
--- a/ui/base/l10n/l10n_util.cc
+++ b/ui/base/l10n/l10n_util.cc
@@ -67,9 +67,12 @@
 #endif                 // defined(ENABLE_PSEUDOLOCALES)
     "as",              // Assamese
     "ast",             // Asturian
+    "ay",              // Aymara
     "az",              // Azerbaijani
     "be",              // Belarusian
     "bg",              // Bulgarian
+    "bho",             // Bhojpuri
+    "bm",              // Bambara
     "bn",              // Bengali
     "br",              // Breton
     "bs",              // Bosnian
@@ -86,6 +89,8 @@
     "de-CH",           // German (Switzerland)
     "de-DE",           // German (Germany)
     "de-LI",           // German (Liechtenstein)
+    "doi",             // Dogri
+    "dv",              // Dhivehi
     "ee",              // Ewe
     "el",              // Greek
     "en",              // English
@@ -143,6 +148,7 @@
     "ia",      // Interlingua
     "id",      // Indonesian
     "ig",      // Igbo
+    "ilo",     // Ilocano
     "is",      // Icelandic
     "it",      // Italian
     "it-CH",   // Italian (Switzerland)
@@ -164,7 +170,9 @@
     "ln",      // Lingala
     "lo",      // Laothian
     "lt",      // Lithuanian
+    "lus",     // Mizo
     "lv",      // Latvian
+    "mai",     // Maithili
     "mg",      // Malagasy
     "mi",      // Maori
     "mk",      // Macedonian
@@ -196,6 +204,7 @@
     "ro",      // Romanian
     "ru",      // Russian
     "rw",      // Kinyarwanda
+    "sa",      // Sanskrit
     "sd",      // Sindhi
     "sh",      // Serbo-Croatian
     "si",      // Sinhalese
@@ -219,6 +228,7 @@
     "tn",      // Tswana
     "to",      // Tonga
     "tr",      // Turkish
+    "ts",      // Tsonga
     "tt",      // Tatar
     "tw",      // Twi
     "ug",      // Uyghur
@@ -1025,11 +1035,9 @@
 
 bool IsLanguageAccepted(const std::string& display_locale,
                         const std::string& locale) {
-  for (const char* accept_language : kAcceptLanguageList) {
-    if (accept_language == locale &&
-        l10n_util::IsLocaleNameTranslated(locale.c_str(), display_locale)) {
-      return true;
-    }
+  if (std::binary_search(std::begin(kAcceptLanguageList),
+                         std::end(kAcceptLanguageList), locale)) {
+    return l10n_util::IsLocaleNameTranslated(locale.c_str(), display_locale);
   }
   return false;
 }
diff --git a/ui/base/l10n/l10n_util_unittest.cc b/ui/base/l10n/l10n_util_unittest.cc
index d9f0db60..eb64e34 100644
--- a/ui/base/l10n/l10n_util_unittest.cc
+++ b/ui/base/l10n/l10n_util_unittest.cc
@@ -670,6 +670,36 @@
   }
 }
 
+TEST_F(L10nUtilTest, AcceptLocalesIsSorted) {
+  // Accept-Language List should be sorted and have no duplicates.
+  const char* const* locales = l10n_util::GetAcceptLanguageListForTesting();
+  const size_t locales_size = l10n_util::GetAcceptLanguageListSizeForTesting();
+
+  // All 0-length and 1-length lists are sorted.
+  if (locales_size <= 1) {
+    return;
+  }
+
+  const char* last_locale = locales[0];
+  for (size_t i = 1; i < locales_size; i++) {
+    const char* cur_locale = locales[i];
+    EXPECT_LT(strcmp(last_locale, cur_locale), 0)
+        << "Incorrect ordering in kPlatformLocales: " << last_locale
+        << " >= " << cur_locale;
+    last_locale = cur_locale;
+  }
+}
+
+TEST_F(L10nUtilTest, IsLanguageAccepted) {
+  EXPECT_TRUE(l10n_util::IsLanguageAccepted("en", "es-419"));
+  EXPECT_TRUE(l10n_util::IsLanguageAccepted("en", "en-GB"));
+  EXPECT_TRUE(l10n_util::IsLanguageAccepted("es", "fil"));
+  EXPECT_TRUE(l10n_util::IsLanguageAccepted("de", "zu"));
+
+  // The old code for "he" is not supported.
+  EXPECT_FALSE(l10n_util::IsLanguageAccepted("es", "iw"));
+}
+
 TEST_F(L10nUtilTest, FormatStringComputeCorrectOffsetInRTL) {
   base::i18n::SetICUDefaultLocale("ar");
   ASSERT_EQ(true, base::i18n::IsRTL());
diff --git a/ui/compositor/compositor_lock_unittest.cc b/ui/compositor/compositor_lock_unittest.cc
index 93f5335e..ed9f9ca 100644
--- a/ui/compositor/compositor_lock_unittest.cc
+++ b/ui/compositor/compositor_lock_unittest.cc
@@ -56,15 +56,13 @@
 }  // namespace
 
 TEST_F(CompositorLockTest, LocksTimeOut) {
-  std::unique_ptr<CompositorLock> lock;
-
   base::TimeDelta timeout = base::Milliseconds(100);
 
   {
     testing::StrictMock<MockCompositorLockClient> lock_client;
     // This lock has a timeout.
-    lock = lock_manager()->GetCompositorLock(&lock_client, timeout,
-                                             CreateReleaseCallback());
+    std::unique_ptr<CompositorLock> lock = lock_manager()->GetCompositorLock(
+        &lock_client, timeout, CreateReleaseCallback());
     EXPECT_TRUE(lock_manager()->IsLocked());
     EXPECT_CALL(lock_client, CompositorLockTimedOut()).Times(1);
     task_runner()->FastForwardBy(timeout);
@@ -76,8 +74,8 @@
   {
     testing::StrictMock<MockCompositorLockClient> lock_client;
     // This lock has no timeout.
-    lock = lock_manager()->GetCompositorLock(&lock_client, base::TimeDelta(),
-                                             CreateReleaseCallback());
+    std::unique_ptr<CompositorLock> lock = lock_manager()->GetCompositorLock(
+        &lock_client, base::TimeDelta(), CreateReleaseCallback());
     EXPECT_TRUE(lock_manager()->IsLocked());
     EXPECT_CALL(lock_client, CompositorLockTimedOut()).Times(0);
     task_runner()->FastForwardBy(timeout);
diff --git a/ui/compositor_extra/shadow.cc b/ui/compositor_extra/shadow.cc
index f67a231..9c95600 100644
--- a/ui/compositor_extra/shadow.cc
+++ b/ui/compositor_extra/shadow.cc
@@ -127,6 +127,7 @@
   shadow_layer()->SetFillsBoundsOpaquely(false);
   layer()->Add(shadow_layer());
 
+  details_ = nullptr;
   UpdateLayerBounds();
 }
 
diff --git a/ui/compositor_extra/shadow_unittest.cc b/ui/compositor_extra/shadow_unittest.cc
index 7e8c193..2295d93 100644
--- a/ui/compositor_extra/shadow_unittest.cc
+++ b/ui/compositor_extra/shadow_unittest.cc
@@ -18,6 +18,9 @@
 constexpr int kElevationLarge = 24;
 constexpr int kElevationSmall = 6;
 
+// A specific elevation used for testing EvictUniquelyOwnedDetail.
+constexpr int kElevationUnique = 66;
+
 gfx::Insets InsetsForElevation(int elevation) {
   return -gfx::Insets(2 * elevation) +
          gfx::Insets::TLBR(elevation, 0, -elevation, 0);
@@ -32,6 +35,14 @@
   return bounds.size();
 }
 
+// Calculates the minimum shadow content size for given elevation and corner
+// radius.
+gfx::Size MinContentSizeForElevationAndCornerRadius(int elevation,
+                                                    int corner_radius) {
+  const int dimension = 4 * elevation + 2 * corner_radius;
+  return gfx::Size(dimension, dimension);
+}
+
 class ShadowTest : public testing::Test {
  public:
   ShadowTest(const ShadowTest&) = delete;
@@ -128,5 +139,68 @@
             shadow.details_for_testing()->ninebox_image.size());
 }
 
+// Test that the uniquely owned shadow image is evicted from the cache when new
+// shadow details are created.
+TEST_F(ShadowTest, EvictUniquelyOwnedDetail) {
+  // Insert a new shadow with unique details which will evict existing details
+  // from the cache.
+  {
+    Shadow shadow_new;
+    shadow_new.Init(kElevationUnique);
+    shadow_new.SetRoundedCornerRadius(2);
+
+    const gfx::Size min_content_size =
+        MinContentSizeForElevationAndCornerRadius(kElevationUnique, 2);
+    shadow_new.SetContentBounds(gfx::Rect(min_content_size));
+    // The cache size should be 1.
+    EXPECT_EQ(1u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+
+    // Creating a shadow with the same detail won't increase the cache size.
+    Shadow shadow_same;
+    shadow_same.Init(kElevationUnique);
+    shadow_same.SetRoundedCornerRadius(2);
+    shadow_same.SetContentBounds(
+        gfx::Rect(gfx::Point(10, 10), min_content_size + gfx::Size(50, 50)));
+    // The cache size is unchanged.
+    EXPECT_EQ(1u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+
+    // Creating a new uniquely owned detail will increase the cache size.
+    gfx::ShadowDetails::Get(kElevationUnique, 3);
+    EXPECT_EQ(2u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+
+    // Creating a shadow with different details will replace the uniquely owned
+    // detail.
+    Shadow shadow_small;
+    shadow_small.Init(kElevationSmall);
+    shadow_small.SetRoundedCornerRadius(2);
+    shadow_small.SetContentBounds(gfx::Rect(
+        MinContentSizeForElevationAndCornerRadius(kElevationSmall, 2)));
+    EXPECT_EQ(2u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+
+    // Changing the shadow appearance will insert a new detail in the cache and
+    // make the old detail uniquely owned.
+    shadow_small.SetRoundedCornerRadius(3);
+    EXPECT_EQ(3u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+
+    // Changing the shadow with another appearance will replace the uniquely
+    // owned detail.
+    shadow_small.SetRoundedCornerRadius(4);
+    EXPECT_EQ(3u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+  }
+
+  // After destroying the all the shadows, the cache has 3 uniquely owned
+  // details.
+  EXPECT_EQ(3u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+
+  // After inserting a new detail, the uniquely owned details will be evicted.
+  Shadow shadow_large;
+  shadow_large.Init(kElevationLarge);
+  shadow_large.SetRoundedCornerRadius(2);
+  shadow_large.SetContentBounds(
+      gfx::Rect(MinContentSizeForElevationAndCornerRadius(kElevationLarge, 2)));
+  // The cache size is unchanged.
+  EXPECT_EQ(1u, gfx::ShadowDetails::GetDetailsCacheSizeForTest());
+}
+
 }  // namespace
 }  // namespace ui
diff --git a/ui/gfx/image/image_skia.cc b/ui/gfx/image/image_skia.cc
index 7608fc74..e946002 100644
--- a/ui/gfx/image/image_skia.cc
+++ b/ui/gfx/image/image_skia.cc
@@ -483,6 +483,10 @@
   }
 }
 
+bool ImageSkia::IsUniquelyOwned() const {
+  return storage_->HasOneRef();
+}
+
 void ImageSkia::Init(const ImageSkiaRep& image_rep) {
   DCHECK(!image_rep.is_null());
   storage_ = new internal::ImageSkiaStorage(
diff --git a/ui/gfx/image/image_skia.h b/ui/gfx/image/image_skia.h
index 15e93b9..d8dd887 100644
--- a/ui/gfx/image/image_skia.h
+++ b/ui/gfx/image/image_skia.h
@@ -165,6 +165,9 @@
   // based on |scale|.
   void RemoveUnsupportedRepresentationsForScale(float scale);
 
+  // Returns true if the image storage is uniquely owned.
+  bool IsUniquelyOwned() const;
+
  private:
   friend class test::TestOnThread;
   FRIEND_TEST_ALL_PREFIXES(ImageSkiaTest, EmptyOnThreadTest);
diff --git a/ui/gfx/shadow_util.cc b/ui/gfx/shadow_util.cc
index d77e035c..30204e18 100644
--- a/ui/gfx/shadow_util.cc
+++ b/ui/gfx/shadow_util.cc
@@ -7,6 +7,7 @@
 #include <map>
 #include <vector>
 
+#include "base/containers/cxx20_erase.h"
 #include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
@@ -94,6 +95,11 @@
   if (iter != g_shadow_cache.Get().end())
     return iter->second;
 
+  // Evict the details whose ninebox image does not have any shadow owners.
+  base::EraseIf(g_shadow_cache.Get(), [](auto& pair) {
+    return pair.second.ninebox_image.IsUniquelyOwned();
+  });
+
   auto insertion = g_shadow_cache.Get().emplace(
       std::make_pair(elevation, corner_radius), ShadowDetails());
   DCHECK(insertion.second);
@@ -118,4 +124,8 @@
   return *shadow;
 }
 
+size_t ShadowDetails::GetDetailsCacheSizeForTest() {
+  return g_shadow_cache.Get().size();
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/shadow_util.h b/ui/gfx/shadow_util.h
index 89fd2c0..c15991c6 100644
--- a/ui/gfx/shadow_util.h
+++ b/ui/gfx/shadow_util.h
@@ -36,6 +36,8 @@
       int radius,
       ShadowStyle style = ShadowStyle::kMaterialDesign);
 
+  static size_t GetDetailsCacheSizeForTest();
+
   // Description of the shadows.
   gfx::ShadowValues values;
   // Cached ninebox image based on |values|.
diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc
index df3caf8..7639d05 100644
--- a/ui/gfx/switches.cc
+++ b/ui/gfx/switches.cc
@@ -60,7 +60,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
 BASE_FEATURE(kVariableGoogleSansFont,
              "VariableGoogleSansFont",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 GFX_SWITCHES_EXPORT bool UseVariableGoogleSansFont() {
   return base::FeatureList::IsEnabled(kVariableGoogleSansFont);
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
index 6e97b2e2..8dfbbd9 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html
@@ -1,5 +1,6 @@
     <style include="cr-shared-style cr-icons">
       :host {
+        display: block;
         height: 40px;
         transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
             width 150ms cubic-bezier(0.4, 0, 0.2, 1);