diff --git a/DEPS b/DEPS
index 861d7c03..bbda4a8 100644
--- a/DEPS
+++ b/DEPS
@@ -260,19 +260,19 @@
   # 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': 'c35e492a19b047618dcea58043ba915ece6fc99d',
+  'skia_revision': '6721d353a3a750604d15a60f43e539610c93cb4a',
   # 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': '1e36a0692f526f9923d9e57e15e072287fe52950',
+  'v8_revision': 'de8b3c70ab70a521727ea15a23a87a98b53efeac',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '7d31a47fc7c367390bd8fda2ea6ab1e5819b7d6d',
+  'angle_revision': '454efd1d54d5a6870bbeae1e6622ff745468a43f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'f88fc3b53c52b3e49c32e81fd0820edfe507aa20',
+  'swiftshader_revision': 'b92f7acb7e81673db59389cf579f95e353f3114f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -327,7 +327,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '46b602cdaf2c2983e0b5cdbb2eca3c711d1417e8',
+  'catapult_revision': 'ef89d1327c8b5dcb10edd61169d1927dc2222b6e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -371,7 +371,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': 'a9b856e10e7d0662eef7c425f3aefe6fcd0d9991',
+  'dawn_revision': '5441f45b41ead7d74f1e21cef7000134a2f4ad3e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1113,7 +1113,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '41c57603f9f598bf0e3fe50989dde5ba426532e1',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '381db68adc980da8e158f53405d25495c65aa8b2',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1723,7 +1723,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '969db1ed93fc2d956f1fbbfe459e92e0f6fa36df',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '720aa9d5c782ddec09f18d301721eea70d77ad6b',
+    Var('webrtc_git') + '/src.git' + '@' + '76d9c18c3a5c8dcb48cb260dd29731c2ee709bf8',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1793,7 +1793,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cd83df06fc2667cb1b3224af7e5b55e09292fef3',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@81278241bda1b1d0939c3afc76e04ba0ed1e9ac0',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index b98a87b..a5a0b886 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -328,16 +328,25 @@
   if (!autofill_provider && !autocomplete_enabled)
     return;
 
+  autofill::AutofillManager::EnableDownloadManager enable_download_manager(
+      !autofill::AutofillProvider::is_download_manager_disabled_for_testing());
+
   AwAutofillClient::CreateForWebContents(web_contents);
+
+  // WebView browser tests may shall use BrowserAutofillManager if
+  // `!autofill_provider`.
+  ContentAutofillDriverFactory::DriverInitCallback driver_init_hook =
+      autofill_provider
+          ? base::BindRepeating(&autofill::AndroidDriverInitHook,
+                                AwAutofillClient::FromWebContents(web_contents),
+                                enable_download_manager)
+          : base::BindRepeating(&autofill::BrowserDriverInitHook,
+                                AwAutofillClient::FromWebContents(web_contents),
+                                base::android::GetDefaultLocaleString());
+
   ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       web_contents, AwAutofillClient::FromWebContents(web_contents),
-      base::android::GetDefaultLocaleString(),
-      autofill::AutofillProvider::is_download_manager_disabled_for_testing()
-          ? autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER
-          : autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
-      autofill_provider
-          ? base::BindRepeating(&autofill::AndroidAutofillManager::Create)
-          : autofill::AutofillManager::AutofillManagerFactoryCallback());
+      std::move(driver_init_hook));
 }
 
 void AwContents::SetAwAutofillClient(const JavaRef<jobject>& client) {
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
index 3f3841d..396e318 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
@@ -23,7 +23,6 @@
 import org.chromium.base.TraceEvent;
 import org.chromium.base.compat.ApiHelperForM;
 import org.chromium.base.compat.ApiHelperForN;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.components.embedder_support.util.WebResourceResponseInfo;
 import org.chromium.support_lib_boundary.util.Features;
 import org.chromium.support_lib_callback_glue.SupportLibWebViewContentsClientAdapter;
@@ -161,8 +160,6 @@
                 mWebViewClient.onReceivedError(mWebView, new WebResourceRequestAdapter(request),
                         new WebResourceErrorAdapter(error));
             }
-            RecordHistogram.recordSparseHistogram(
-                    "Android.WebView.onReceivedError.ErrorCode", error.errorCode);
         } finally {
             TraceEvent.end("WebViewContentsClientAdapter.onReceivedError");
         }
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 564fa34..0f435cb 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -953,8 +953,6 @@
     "style/default_color_constants.h",
     "style/default_colors.cc",
     "style/default_colors.h",
-    "style/highlight_border.cc",
-    "style/highlight_border.h",
     "style/icon_button.cc",
     "style/icon_button.h",
     "style/pill_button.cc",
@@ -1284,8 +1282,12 @@
     "system/network/fake_network_detailed_network_view.h",
     "system/network/fake_network_detailed_view_delegate.cc",
     "system/network/fake_network_detailed_view_delegate.h",
+    "system/network/fake_network_list_mobile_header_view.cc",
+    "system/network/fake_network_list_mobile_header_view.h",
     "system/network/fake_network_list_network_header_view_delegate.cc",
     "system/network/fake_network_list_network_header_view_delegate.h",
+    "system/network/fake_network_list_wifi_header_view.cc",
+    "system/network/fake_network_list_wifi_header_view.h",
     "system/network/network_detailed_network_view.cc",
     "system/network/network_detailed_network_view.h",
     "system/network/network_detailed_network_view_impl.cc",
@@ -1315,6 +1317,8 @@
     "system/network/network_list_header_view.h",
     "system/network/network_list_mobile_header_view.cc",
     "system/network/network_list_mobile_header_view.h",
+    "system/network/network_list_mobile_header_view_impl.cc",
+    "system/network/network_list_mobile_header_view_impl.h",
     "system/network/network_list_network_header_view.cc",
     "system/network/network_list_network_header_view.h",
     "system/network/network_list_view.cc",
@@ -1325,6 +1329,8 @@
     "system/network/network_list_view_controller_impl.h",
     "system/network/network_list_wifi_header_view.cc",
     "system/network/network_list_wifi_header_view.h",
+    "system/network/network_list_wifi_header_view_impl.cc",
+    "system/network/network_list_wifi_header_view_impl.h",
     "system/network/network_observer.h",
     "system/network/network_row_title_view.cc",
     "system/network/network_row_title_view.h",
@@ -2661,6 +2667,7 @@
     "system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc",
     "system/bluetooth/unified_bluetooth_detailed_view_controller_unittest.cc",
     "system/caps_lock_notification_controller_unittest.cc",
+    "system/dark_mode/dark_mode_feature_pod_controller_unittest.cc",
     "system/diagnostics/async_log_unittest.cc",
     "system/diagnostics/diagnostics_log_controller_unittest.cc",
     "system/diagnostics/log_test_helpers_unittest.cc",
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index 63f10b0..0d390512 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -32,7 +32,6 @@
 #include "ash/search_box/search_box_constants.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "base/bind.h"
 #include "base/check.h"
 #include "base/check_op.h"
@@ -56,6 +55,7 @@
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/focus/focus_manager.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 
@@ -553,10 +553,10 @@
       AshColorProvider::Get()->GetBaseLayerColor(
           AshColorProvider::BaseLayerType::kTransparent80),
       kBubbleCornerRadius));
-  SetBorder(std::make_unique<HighlightBorder>(
-      kBubbleCornerRadius, HighlightBorder::Type::kHighlightBorder1,
+  SetBorder(std::make_unique<views::HighlightBorder>(
+      kBubbleCornerRadius, views::HighlightBorder::Type::kHighlightBorder1,
       /*use_light_colors=*/false,
-      /*insets_type=*/HighlightBorder::InsetsType::kHalfInsets));
+      /*insets_type=*/views::HighlightBorder::InsetsType::kHalfInsets));
 }
 
 void AppListBubbleView::Layout() {
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index 20c6e6d..5028a85 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -35,7 +35,6 @@
 #include "ash/public/cpp/pagination/pagination_model.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/system_shadow.h"
 #include "base/barrier_closure.h"
 #include "base/bind.h"
@@ -60,6 +59,7 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
@@ -1052,10 +1052,10 @@
     return;
   }
 
-  background_view_->SetBorder(std::make_unique<HighlightBorder>(
+  background_view_->SetBorder(std::make_unique<views::HighlightBorder>(
       GetAppListConfig()->folder_background_radius(),
-      HighlightBorder::Type::kHighlightBorder1,
-      /*use_light_colors=*/true));
+      views::HighlightBorder::Type::kHighlightBorder1,
+      /*use_light_colors=*/!features::IsDarkLightModeEnabled()));
 }
 
 void AppListFolderView::UpdatePreferredBounds() {
diff --git a/ash/app_list/views/app_list_toast_view.cc b/ash/app_list/views/app_list_toast_view.cc
index f29357c1..bc42ad1 100644
--- a/ash/app_list/views/app_list_toast_view.cc
+++ b/ash/app_list/views/app_list_toast_view.cc
@@ -11,7 +11,6 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/icon_button.h"
 #include "ash/style/pill_button.h"
 #include "components/vector_icons/vector_icons.h"
@@ -25,6 +24,7 @@
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/view_class_properties.h"
 
@@ -214,8 +214,8 @@
         ColorProvider::Get()->GetBaseLayerColor(
             ColorProvider::BaseLayerType::kTransparent80),
         kCornerRadius));
-    SetBorder(std::make_unique<HighlightBorder>(
-        kCornerRadius, HighlightBorder::Type::kHighlightBorder1,
+    SetBorder(std::make_unique<views::HighlightBorder>(
+        kCornerRadius, views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   } else {
     SetBackground(views::CreateRoundedRectBackground(
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 0cd0583..54c04a8 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1391,12 +1391,15 @@
       view_model_.view_size() + pulsing_blocks_model_.view_size();
   int slot_index = 0;
   for (int i = 0; i < total_views; ++i) {
-    AppListItemView* item_view = view_model_.view_at(i);
-    if (i < view_model_.view_size() && item_view == drag_view_) {
+    // NOTE: Because of pulsing blocks, `i` can count up to a value higher than
+    // the view model size. So verify that `i` is less than the view model size
+    // before fetching at index `i` from the view model.
+    if (i < view_model_.view_size() && view_model_.view_at(i) == drag_view_) {
       continue;
     }
 
-    if (i < view_model_.view_size() && item_view == view_with_locked_position) {
+    if (i < view_model_.view_size() &&
+        view_model_.view_at(i) == view_with_locked_position) {
       SetIdealBoundsForViewToGridIndex(i, open_folder_info_->grid_index);
       continue;
     }
@@ -1409,7 +1412,8 @@
       view_index = view_structure_.GetIndexFromModelIndex(slot_index);
     }
 
-    item_view->SetMostRecentGridIndex(view_index, cols_);
+    if (i < view_model_.view_size())
+      view_model_.view_at(i)->SetMostRecentGridIndex(view_index, cols_);
     SetIdealBoundsForViewToGridIndex(i, view_index);
     ++slot_index;
   }
diff --git a/ash/app_list/views/continue_task_view.cc b/ash/app_list/views/continue_task_view.cc
index 724ea09..b243635 100644
--- a/ash/app_list/views/continue_task_view.cc
+++ b/ash/app_list/views/continue_task_view.cc
@@ -23,7 +23,6 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/style_util.h"
 #include "base/bind.h"
 #include "base/strings/string_util.h"
@@ -44,6 +43,7 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/vector_icons.h"
@@ -391,9 +391,9 @@
   SetBackground(
       views::CreateSolidBackground(ColorProvider::Get()->GetBaseLayerColor(
           ColorProvider::BaseLayerType::kTransparent60)));
-  SetBorder(std::make_unique<HighlightBorder>(
+  SetBorder(std::make_unique<views::HighlightBorder>(
       GetCornerRadius(/*tablet_mode=*/true),
-      HighlightBorder::Type::kHighlightBorder2,
+      views::HighlightBorder::Type::kHighlightBorder2,
       /*use_light_colors=*/false));
 }
 
diff --git a/ash/app_list/views/remove_query_confirmation_dialog.cc b/ash/app_list/views/remove_query_confirmation_dialog.cc
index 8c406d5..84b5a7ed 100644
--- a/ash/app_list/views/remove_query_confirmation_dialog.cc
+++ b/ash/app_list/views/remove_query_confirmation_dialog.cc
@@ -13,7 +13,6 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/pill_button.h"
 #include "base/bind.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -24,6 +23,7 @@
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/style/typography.h"
 #include "ui/views/view_class_properties.h"
@@ -150,8 +150,9 @@
       AshColorProvider::Get()->GetBaseLayerColor(
           AshColorProvider::BaseLayerType::kTransparent80),
       kDialogRoundedCornerRadius));
-  SetBorder(std::make_unique<HighlightBorder>(
-      kDialogRoundedCornerRadius, HighlightBorder::Type::kHighlightBorder1,
+  SetBorder(std::make_unique<views::HighlightBorder>(
+      kDialogRoundedCornerRadius,
+      views::HighlightBorder::Type::kHighlightBorder1,
       /*use_light_colors=*/false));
   title_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kTextColorPrimary));
diff --git a/ash/app_list/views/remove_task_feedback_dialog.cc b/ash/app_list/views/remove_task_feedback_dialog.cc
index da6f289..834f7e1 100644
--- a/ash/app_list/views/remove_task_feedback_dialog.cc
+++ b/ash/app_list/views/remove_task_feedback_dialog.cc
@@ -11,7 +11,6 @@
 #include "ash/public/cpp/view_shadow.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/pill_button.h"
 #include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
@@ -24,6 +23,7 @@
 #include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/button/radio_button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/box_layout_view.h"
 #include "ui/views/style/typography.h"
@@ -171,8 +171,9 @@
       AshColorProvider::Get()->GetBaseLayerColor(
           AshColorProvider::BaseLayerType::kTransparent80),
       kDialogRoundedCornerRadius));
-  SetBorder(std::make_unique<HighlightBorder>(
-      kDialogRoundedCornerRadius, HighlightBorder::Type::kHighlightBorder1,
+  SetBorder(std::make_unique<views::HighlightBorder>(
+      kDialogRoundedCornerRadius,
+      views::HighlightBorder::Type::kHighlightBorder1,
       /*use_light_colors=*/false));
 }
 
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 6c6b63b..38903bd 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -30,7 +30,6 @@
 #include "ash/search_box/search_box_view_delegate.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "base/notreached.h"
@@ -54,6 +53,7 @@
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/vector_icons.h"
 #include "ui/views/view.h"
@@ -360,9 +360,10 @@
 
 void SearchBoxView::OnPaintBorder(gfx::Canvas* canvas) {
   if (should_paint_highlight_border_) {
-    HighlightBorder::PaintBorderToCanvas(
-        canvas, GetContentsBounds(), gfx::RoundedCornersF(corner_radius_),
-        HighlightBorder::Type::kHighlightBorder1, false);
+    views::HighlightBorder::PaintBorderToCanvas(
+        canvas, *this, GetContentsBounds(),
+        gfx::RoundedCornersF(corner_radius_),
+        views::HighlightBorder::Type::kHighlightBorder1, false);
   }
 }
 
diff --git a/ash/components/arc/BUILD.gn b/ash/components/arc/BUILD.gn
index 796bb0a..8a67e5c4 100644
--- a/ash/components/arc/BUILD.gn
+++ b/ash/components/arc/BUILD.gn
@@ -97,6 +97,8 @@
     "sensor/arc_sensor_bridge.h",
     "storage_manager/arc_storage_manager.cc",
     "storage_manager/arc_storage_manager.h",
+    "system_ui/arc_system_ui_bridge.cc",
+    "system_ui/arc_system_ui_bridge.h",
     "timer/arc_timer_bridge.cc",
     "timer/arc_timer_bridge.h",
     "usb/usb_host_bridge.cc",
@@ -324,6 +326,8 @@
     "test/fake_snapshot_reboot_notification.h",
     "test/fake_storage_manager_instance.cc",
     "test/fake_storage_manager_instance.h",
+    "test/fake_system_ui_instance.cc",
+    "test/fake_system_ui_instance.h",
     "test/fake_timer_instance.cc",
     "test/fake_timer_instance.h",
     "test/fake_tracing_instance.cc",
@@ -414,6 +418,7 @@
     "rotation_lock/arc_rotation_lock_bridge_unittest.cc",
     "sensor/arc_iio_sensor_bridge_unittest.cc",
     "storage_manager/arc_storage_manager_unittest.cc",
+    "system_ui/arc_system_ui_bridge_unittest.cc",
     "timer/arc_timer_bridge_unittest.cc",
     "usb/usb_host_bridge_unittest.cc",
     "video_accelerator/arc_video_accelerator_util_unittest.cc",
diff --git a/ash/components/arc/mojom/BUILD.gn b/ash/components/arc/mojom/BUILD.gn
index fa83f00..c3d2aab 100644
--- a/ash/components/arc/mojom/BUILD.gn
+++ b/ash/components/arc/mojom/BUILD.gn
@@ -63,6 +63,7 @@
     "sensor_service.mojom",
     "sharesheet.mojom",
     "storage_manager.mojom",
+    "system_ui.mojom",
     "timer.mojom",
     "tracing.mojom",
     "tts.mojom",
diff --git a/ash/components/arc/mojom/arc_bridge.mojom b/ash/components/arc/mojom/arc_bridge.mojom
index d11395c..a3afd4cc 100644
--- a/ash/components/arc/mojom/arc_bridge.mojom
+++ b/ash/components/arc/mojom/arc_bridge.mojom
@@ -54,6 +54,7 @@
 import "ash/components/arc/mojom/sensor.mojom";
 import "ash/components/arc/mojom/sharesheet.mojom";
 import "ash/components/arc/mojom/storage_manager.mojom";
+import "ash/components/arc/mojom/system_ui.mojom";
 import "ash/components/arc/mojom/timer.mojom";
 import "ash/components/arc/mojom/tracing.mojom";
 import "ash/components/arc/mojom/tts.mojom";
@@ -64,9 +65,9 @@
 import "ash/components/arc/mojom/wallpaper.mojom";
 import "ash/components/arc/mojom/webapk.mojom";
 
-// Next MinVersion: 62
+// Next MinVersion: 63
 // Deprecated method IDs: 101, 105, 121, 132
-// Next method ID: 167
+// Next method ID: 168
 interface ArcBridgeHost {
   // Keep the entries alphabetical. In order to do so without breaking
   // compatibility with the ARC instance, explicitly assign each interface a
@@ -273,6 +274,10 @@
   [MinVersion=12] OnStorageManagerInstanceReady@118(
       pending_remote<StorageManagerInstance> instance_remote);
 
+  // Notifies Chrome that the SystemUIInstance interface is ready.
+  [MinVersion=62] OnSystemUIInstanceReady@167(
+      pending_remote<SystemUIInstance> instance_remote);
+
   // Notifies Chrome that the TimerInstance interface is ready.
   [MinVersion=36] OnTimerInstanceReady@141(
       pending_remote<TimerInstance> instance_remote);
diff --git a/ash/components/arc/mojom/system_ui.mojom b/ash/components/arc/mojom/system_ui.mojom
new file mode 100644
index 0000000..8b0bc47
--- /dev/null
+++ b/ash/components/arc/mojom/system_ui.mojom
@@ -0,0 +1,27 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Next MinVersion: 1
+
+module arc.mojom;
+
+// Theme Style Type, defaults to TONAL_SPOT
+[Extensible]
+enum ThemeStyleType {
+  [Default] TONAL_SPOT = 0,
+  VIBRANT = 1,
+  EXPRESSIVE = 2,
+  SPRITZ = 3,
+  RAINBOW = 4,
+  FRUIT_SALAD = 5,
+};
+
+// Next method ID: 2
+interface SystemUIInstance {
+  // Notifies ARC whether the Chrome dark theme is on/off.
+  SetDarkThemeStatus@0(bool active); // Migrated from DarkThemeInstance
+
+  // Sets the dynamic color "source" color (ARGB8888), and the theme style.
+  SetOverlayColor@1(uint32 source_color, ThemeStyleType theme_style);
+};
diff --git a/ash/components/arc/session/arc_bridge_host_impl.cc b/ash/components/arc/session/arc_bridge_host_impl.cc
index 0fa8a270..93a0ed6e 100644
--- a/ash/components/arc/session/arc_bridge_host_impl.cc
+++ b/ash/components/arc/session/arc_bridge_host_impl.cc
@@ -57,6 +57,7 @@
 #include "ash/components/arc/mojom/sensor.mojom.h"
 #include "ash/components/arc/mojom/sharesheet.mojom.h"
 #include "ash/components/arc/mojom/storage_manager.mojom.h"
+#include "ash/components/arc/mojom/system_ui.mojom.h"
 #include "ash/components/arc/mojom/timer.mojom.h"
 #include "ash/components/arc/mojom/tracing.mojom.h"
 #include "ash/components/arc/mojom/tts.mojom.h"
@@ -396,6 +397,12 @@
                   std::move(storage_manager_remote));
 }
 
+void ArcBridgeHostImpl::OnSystemUIInstanceReady(
+    mojo::PendingRemote<mojom::SystemUIInstance> system_ui_remote) {
+  OnInstanceReady(arc_bridge_service_->system_ui(),
+                  std::move(system_ui_remote));
+}
+
 void ArcBridgeHostImpl::OnTimerInstanceReady(
     mojo::PendingRemote<mojom::TimerInstance> timer_remote) {
   OnInstanceReady(arc_bridge_service_->timer(), std::move(timer_remote));
diff --git a/ash/components/arc/session/arc_bridge_host_impl.h b/ash/components/arc/session/arc_bridge_host_impl.h
index bfc968d..0fda8d14 100644
--- a/ash/components/arc/session/arc_bridge_host_impl.h
+++ b/ash/components/arc/session/arc_bridge_host_impl.h
@@ -165,6 +165,8 @@
   void OnStorageManagerInstanceReady(
       mojo::PendingRemote<mojom::StorageManagerInstance> storage_manager_remote)
       override;
+  void OnSystemUIInstanceReady(
+      mojo::PendingRemote<mojom::SystemUIInstance> system_ui_remote) override;
   void OnTimerInstanceReady(
       mojo::PendingRemote<mojom::TimerInstance> timer_remote) override;
   void OnTracingInstanceReady(
diff --git a/ash/components/arc/session/arc_bridge_service.h b/ash/components/arc/session/arc_bridge_service.h
index e9385288..adf9e28 100644
--- a/ash/components/arc/session/arc_bridge_service.h
+++ b/ash/components/arc/session/arc_bridge_service.h
@@ -102,6 +102,7 @@
 class SmartCardManagerHost;
 class SmartCardManagerInstance;
 class StorageManagerInstance;
+class SystemUIInstance;
 class TimerHost;
 class TimerInstance;
 class TracingInstance;
@@ -321,6 +322,7 @@
   ConnectionHolder<mojom::StorageManagerInstance>* storage_manager() {
     return &storage_manager_;
   }
+  ConnectionHolder<mojom::SystemUIInstance>* system_ui() { return &system_ui_; }
   ConnectionHolder<mojom::TimerInstance, mojom::TimerHost>* timer() {
     return &timer_;
   }
@@ -418,6 +420,7 @@
   ConnectionHolder<mojom::SmartCardManagerInstance, mojom::SmartCardManagerHost>
       smart_card_manager_;
   ConnectionHolder<mojom::StorageManagerInstance> storage_manager_;
+  ConnectionHolder<mojom::SystemUIInstance> system_ui_;
   ConnectionHolder<mojom::TimerInstance, mojom::TimerHost> timer_;
   ConnectionHolder<mojom::TracingInstance> tracing_;
   ConnectionHolder<mojom::TtsInstance, mojom::TtsHost> tts_;
diff --git a/ash/components/arc/system_ui/arc_system_ui_bridge.cc b/ash/components/arc/system_ui/arc_system_ui_bridge.cc
new file mode 100644
index 0000000..13a86fb
--- /dev/null
+++ b/ash/components/arc/system_ui/arc_system_ui_bridge.cc
@@ -0,0 +1,117 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/arc/system_ui/arc_system_ui_bridge.h"
+
+#include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
+#include "ash/components/arc/mojom/dark_theme.mojom.h"
+#include "ash/components/arc/session/arc_bridge_service.h"
+#include "ash/components/arc/session/arc_service_manager.h"
+#include "ash/components/arc/session/connection_holder.h"
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/style/color_provider.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace arc {
+
+namespace {
+
+// Singleton factory for ArcSystemUIBridge.
+class ArcSystemUIBridgeFactory
+    : public internal::ArcBrowserContextKeyedServiceFactoryBase<
+          ArcSystemUIBridge,
+          ArcSystemUIBridgeFactory> {
+ public:
+  // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
+  static constexpr const char* kName = "ArcSystemUIBridgeFactory";
+
+  static ArcSystemUIBridgeFactory* GetInstance() {
+    return base::Singleton<ArcSystemUIBridgeFactory>::get();
+  }
+
+ private:
+  friend base::DefaultSingletonTraits<ArcSystemUIBridgeFactory>;
+  ArcSystemUIBridgeFactory() = default;
+  ~ArcSystemUIBridgeFactory() override = default;
+};
+
+}  // namespace
+
+// static
+ArcSystemUIBridge* ArcSystemUIBridge::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return ArcSystemUIBridgeFactory::GetForBrowserContext(context);
+}
+
+// static
+ArcSystemUIBridge* ArcSystemUIBridge::GetForBrowserContextForTesting(
+    content::BrowserContext* context) {
+  return ArcSystemUIBridgeFactory::GetForBrowserContextForTesting(context);
+}
+
+ArcSystemUIBridge::ArcSystemUIBridge(content::BrowserContext* context,
+                                     ArcBridgeService* bridge_service)
+    : arc_bridge_service_(bridge_service) {
+  ash::ColorProvider* provider = ash::ColorProvider::Get();
+  if (provider)  // for unit tests
+    provider->AddObserver(this);
+  arc_bridge_service_->system_ui()->AddObserver(this);
+}
+
+ArcSystemUIBridge::~ArcSystemUIBridge() {
+  ash::ColorProvider* provider = ash::ColorProvider::Get();
+  if (provider)  // for unit tests
+    provider->RemoveObserver(this);
+  arc_bridge_service_->system_ui()->RemoveObserver(this);
+}
+
+void ArcSystemUIBridge::OnConnectionReady() {
+  ash::ColorProvider* provider = ash::ColorProvider::Get();
+  bool dark_theme_status = false;
+  // Checking to see if the flag is enabled because provider returns dark
+  // mode when the flag is default.
+  if (provider && ash::features::IsDarkLightModeEnabled())
+    dark_theme_status = provider->IsDarkModeEnabled();
+
+  if (!ArcSystemUIBridge::SendDeviceDarkThemeState(dark_theme_status)) {
+    LOG(ERROR) << "OnConnectionReady failed to get System UI instance for "
+                  "initial dark theme status : "
+               << dark_theme_status;
+  }
+}
+
+void ArcSystemUIBridge::OnColorModeChanged(bool dark_theme_status) {
+  if (!ArcSystemUIBridge::SendDeviceDarkThemeState(dark_theme_status)) {
+    LOG(ERROR) << "OnColorModeChanged failed to get System UI instance for "
+                  "the change in dark theme status to : "
+               << dark_theme_status;
+  }
+}
+
+bool ArcSystemUIBridge::SendDeviceDarkThemeState(bool dark_theme_status) {
+  mojom::SystemUIInstance* system_ui_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service_->system_ui(), SetDarkThemeStatus);
+
+  if (!system_ui_instance)
+    return false;
+  system_ui_instance->SetDarkThemeStatus(dark_theme_status);
+  return true;
+}
+
+bool ArcSystemUIBridge::SendOverlayColor(uint32_t source_color,
+                                         mojom::ThemeStyleType theme_style) {
+  mojom::SystemUIInstance* system_ui_instance = ARC_GET_INSTANCE_FOR_METHOD(
+      arc_bridge_service_->system_ui(), SetOverlayColor);
+
+  if (!system_ui_instance)
+    return false;
+  system_ui_instance->SetOverlayColor(source_color, theme_style);
+  return true;
+}
+
+}  // namespace arc
diff --git a/ash/components/arc/system_ui/arc_system_ui_bridge.h b/ash/components/arc/system_ui/arc_system_ui_bridge.h
new file mode 100644
index 0000000..f9769a2
--- /dev/null
+++ b/ash/components/arc/system_ui/arc_system_ui_bridge.h
@@ -0,0 +1,67 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_ARC_SYSTEM_UI_ARC_SYSTEM_UI_BRIDGE_H_
+#define ASH_COMPONENTS_ARC_SYSTEM_UI_ARC_SYSTEM_UI_BRIDGE_H_
+
+#include "ash/components/arc/mojom/system_ui.mojom-shared.h"
+#include "ash/components/arc/mojom/system_ui.mojom.h"
+#include "ash/components/arc/session/connection_observer.h"
+#include "ash/public/cpp/style/color_mode_observer.h"
+#include "ash/public/cpp/style/color_provider.h"
+#include "base/gtest_prod_util.h"
+#include "base/threading/thread_checker.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class notifies the Chrome OS side dark theme state to Android.
+class ArcSystemUIBridge : public KeyedService,
+                          public ConnectionObserver<mojom::SystemUIInstance>,
+                          public ash::ColorModeObserver {
+ public:
+  // Returns singleton instance for the given BrowserContext,
+  // or nullptr if the browser |context| is not allowed to use ARC.
+  static ArcSystemUIBridge* GetForBrowserContext(
+      content::BrowserContext* context);
+
+  static ArcSystemUIBridge* GetForBrowserContextForTesting(
+      content::BrowserContext* context);
+
+  ArcSystemUIBridge(content::BrowserContext* context,
+                    ArcBridgeService* bridge_service);
+  ~ArcSystemUIBridge() override;
+
+  ArcSystemUIBridge(const ArcSystemUIBridge&) = delete;
+  ArcSystemUIBridge& operator=(const ArcSystemUIBridge&) = delete;
+
+  // ConnectionObserver<mojom::SystemUIInstance> override:
+  void OnConnectionReady() override;
+
+  // ash::ColorModeObserver override:
+  void OnColorModeChanged(bool dark_theme_status) override;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(ArcSystemUIBridgeTest, SendOverlayColor);
+  // Sends the device dark theme state to Android.
+  bool SendDeviceDarkThemeState(bool dark_theme_status);
+
+  // Sends the device overlay color and the {@link mojom::ThemeStyleType}.
+  bool SendOverlayColor(uint32_t source_color,
+                        mojom::ThemeStyleType theme_style);
+
+  THREAD_CHECKER(thread_checker_);
+
+  ArcBridgeService* const arc_bridge_service_;  // Owned by ArcServiceManager.
+};
+
+}  // namespace arc
+
+#endif  // ASH_COMPONENTS_ARC_SYSTEM_UI_ARC_SYSTEM_UI_BRIDGE_H_
diff --git a/ash/components/arc/system_ui/arc_system_ui_bridge_unittest.cc b/ash/components/arc/system_ui/arc_system_ui_bridge_unittest.cc
new file mode 100644
index 0000000..ce07006
--- /dev/null
+++ b/ash/components/arc/system_ui/arc_system_ui_bridge_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/arc/system_ui/arc_system_ui_bridge.h"
+
+#include <memory>
+
+#include "ash/components/arc/session/arc_bridge_service.h"
+#include "ash/components/arc/session/arc_service_manager.h"
+#include "ash/components/arc/test/connection_holder_util.h"
+#include "ash/components/arc/test/fake_system_ui_instance.h"
+#include "ash/components/arc/test/test_browser_context.h"
+#include "base/logging.h"
+#include "base/test/mock_log.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+namespace arc {
+
+#define EXPECT_ERROR_LOG(matcher)                                \
+  if (DLOG_IS_ON(ERROR)) {                                       \
+    EXPECT_CALL(log_, Log(logging::LOG_ERROR, _, _, _, matcher)) \
+        .WillOnce(testing::Return(true)); /* suppress logging */ \
+  }
+class ArcSystemUIBridgeTest : public testing::Test {
+ protected:
+  ArcSystemUIBridgeTest()
+      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP),
+        bridge_(ArcSystemUIBridge::GetForBrowserContextForTesting(&context_)) {
+    ArcServiceManager::Get()->arc_bridge_service()->system_ui()->SetInstance(
+        &system_ui_instance_);
+    WaitForInstanceReady(
+        ArcServiceManager::Get()->arc_bridge_service()->system_ui());
+  }
+
+  ~ArcSystemUIBridgeTest() override {
+    ArcServiceManager::Get()->arc_bridge_service()->system_ui()->CloseInstance(
+        &system_ui_instance_);
+    bridge_->Shutdown();
+  }
+
+  explicit ArcSystemUIBridgeTest(const ArcSystemUIBridge&) = delete;
+  ArcSystemUIBridgeTest& operator=(const ArcSystemUIBridge&) = delete;
+
+  content::BrowserTaskEnvironment task_environment_;
+  ArcServiceManager arc_service_manager_;
+  TestBrowserContext context_;
+  FakeSystemUIInstance system_ui_instance_;
+  ArcSystemUIBridge* const bridge_;
+  base::test::MockLog log_;
+};
+
+TEST_F(ArcSystemUIBridgeTest, ConstructDestruct) {}
+
+TEST_F(ArcSystemUIBridgeTest, OnColorModeChanged) {
+  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
+  bridge_->OnColorModeChanged(true);
+  EXPECT_TRUE(system_ui_instance_.dark_theme_status());
+  ArcServiceManager::Get()->arc_bridge_service()->system_ui()->CloseInstance(
+      &system_ui_instance_);
+  EXPECT_ERROR_LOG(testing::HasSubstr("OnColorModeChanged failed"));
+  log_.StartCapturingLogs();
+  bridge_->OnColorModeChanged(true);
+}
+
+TEST_F(ArcSystemUIBridgeTest, OnConnectionReady) {
+  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
+  bridge_->OnColorModeChanged(true);
+  EXPECT_TRUE(system_ui_instance_.dark_theme_status());
+  bridge_->OnConnectionReady();
+  EXPECT_FALSE(system_ui_instance_.dark_theme_status());
+  ArcServiceManager::Get()->arc_bridge_service()->system_ui()->CloseInstance(
+      &system_ui_instance_);
+  EXPECT_ERROR_LOG(testing::HasSubstr("OnConnectionReady failed"));
+  log_.StartCapturingLogs();
+  bridge_->OnConnectionReady();
+}
+
+TEST_F(ArcSystemUIBridgeTest, SendOverlayColor) {
+  EXPECT_EQ((uint32_t)0, system_ui_instance_.source_color());
+  EXPECT_EQ(mojom::ThemeStyleType::TONAL_SPOT,
+            system_ui_instance_.theme_style());
+  bridge_->SendOverlayColor(50, mojom::ThemeStyleType::EXPRESSIVE);
+  EXPECT_EQ((uint32_t)50, system_ui_instance_.source_color());
+  EXPECT_EQ(mojom::ThemeStyleType::EXPRESSIVE,
+            system_ui_instance_.theme_style());
+}
+
+}  // namespace arc
diff --git a/ash/components/arc/test/fake_arc_bridge_host.cc b/ash/components/arc/test/fake_arc_bridge_host.cc
index 99b1689..917ad7f 100644
--- a/ash/components/arc/test/fake_arc_bridge_host.cc
+++ b/ash/components/arc/test/fake_arc_bridge_host.cc
@@ -52,6 +52,7 @@
 #include "ash/components/arc/mojom/sensor.mojom.h"
 #include "ash/components/arc/mojom/sharesheet.mojom.h"
 #include "ash/components/arc/mojom/storage_manager.mojom.h"
+#include "ash/components/arc/mojom/system_ui.mojom.h"
 #include "ash/components/arc/mojom/timer.mojom.h"
 #include "ash/components/arc/mojom/tracing.mojom.h"
 #include "ash/components/arc/mojom/tts.mojom.h"
@@ -231,6 +232,9 @@
     mojo::PendingRemote<mojom::StorageManagerInstance> storage_manager_remote) {
 }
 
+void FakeArcBridgeHost::OnSystemUIInstanceReady(
+    mojo::PendingRemote<mojom::SystemUIInstance> system_ui_remote) {}
+
 void FakeArcBridgeHost::OnTimerInstanceReady(
     mojo::PendingRemote<mojom::TimerInstance> timer_remote) {}
 
diff --git a/ash/components/arc/test/fake_arc_bridge_host.h b/ash/components/arc/test/fake_arc_bridge_host.h
index 2e66db9..766defc 100644
--- a/ash/components/arc/test/fake_arc_bridge_host.h
+++ b/ash/components/arc/test/fake_arc_bridge_host.h
@@ -143,6 +143,8 @@
   void OnStorageManagerInstanceReady(
       mojo::PendingRemote<mojom::StorageManagerInstance> storage_manager_remote)
       override;
+  void OnSystemUIInstanceReady(
+      mojo::PendingRemote<mojom::SystemUIInstance> system_ui_remote) override;
   void OnTimerInstanceReady(
       mojo::PendingRemote<mojom::TimerInstance> timer_remote) override;
   void OnTracingInstanceReady(
diff --git a/ash/components/arc/test/fake_system_ui_instance.cc b/ash/components/arc/test/fake_system_ui_instance.cc
new file mode 100644
index 0000000..d12c05ee
--- /dev/null
+++ b/ash/components/arc/test/fake_system_ui_instance.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/arc/test/fake_system_ui_instance.h"
+
+#include "base/callback_helpers.h"
+
+namespace arc {
+
+FakeSystemUIInstance::FakeSystemUIInstance() = default;
+
+FakeSystemUIInstance::~FakeSystemUIInstance() = default;
+
+void FakeSystemUIInstance::SetDarkThemeStatus(bool darkThemeStatus) {
+  dark_theme_status_ = darkThemeStatus;
+}
+
+void FakeSystemUIInstance::SetOverlayColor(uint32_t sourceColor,
+                                           mojom::ThemeStyleType themeStyle) {
+  source_color_ = sourceColor;
+  theme_style_ = themeStyle;
+}
+
+}  // namespace arc
diff --git a/ash/components/arc/test/fake_system_ui_instance.h b/ash/components/arc/test/fake_system_ui_instance.h
new file mode 100644
index 0000000..15878a96
--- /dev/null
+++ b/ash/components/arc/test/fake_system_ui_instance.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_ARC_TEST_FAKE_SYSTEM_UI_INSTANCE_H_
+#define ASH_COMPONENTS_ARC_TEST_FAKE_SYSTEM_UI_INSTANCE_H_
+
+#include "ash/components/arc/mojom/system_ui.mojom-shared.h"
+#include "ash/components/arc/mojom/system_ui.mojom.h"
+
+namespace arc {
+
+class FakeSystemUIInstance : public mojom::SystemUIInstance {
+ public:
+  FakeSystemUIInstance();
+  FakeSystemUIInstance(const FakeSystemUIInstance&) = delete;
+  FakeSystemUIInstance& operator=(const FakeSystemUIInstance&) = delete;
+  ~FakeSystemUIInstance() override;
+
+  bool dark_theme_status() const { return dark_theme_status_; }
+
+  uint32_t source_color() const { return source_color_; }
+
+  mojom::ThemeStyleType theme_style() const { return theme_style_; }
+
+  // mojom::SystemUIInstance override:
+  void SetDarkThemeStatus(bool darkThemeStatus) override;
+
+  // mojom::SystemUIInstance override:
+  void SetOverlayColor(uint32_t sourceColor,
+                       mojom::ThemeStyleType themeStyle) override;
+
+ private:
+  bool dark_theme_status_ = false;
+  uint32_t source_color_ = 0;
+  mojom::ThemeStyleType theme_style_ = mojom::ThemeStyleType::TONAL_SPOT;
+};
+
+}  // namespace arc
+
+#endif  // ASH_COMPONENTS_ARC_TEST_FAKE_SYSTEM_UI_INSTANCE_H_
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 4c59166..f0b4d64 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1298,7 +1298,7 @@
 // screen, removes the Smart Lock subpage in settings, and shows a one-time
 // notification for users who previously had this feature enabled.
 const base::Feature kSmartLockSignInRemoved{"SmartLockSignInRemoved",
-                                            base::FEATURE_DISABLED_BY_DEFAULT};
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Replaces Smart Lock UI in lock screen password box with new UI similar to
 // fingerprint auth. Adds Smart Lock to "Lock screen and sign-in" section of
@@ -1353,6 +1353,11 @@
 const base::Feature kTelemetryExtension{"TelemetryExtension",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables Terminal System App to load from Downloads for developer testing.
+// Only works in dev and canary channels.
+const base::Feature kTerminalDev{"TerminalDev",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables SSH tabs in the Terminal System App.
 const base::Feature kTerminalSSH{"TerminalSSH",
                                  base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index acb7b37d..3474dd46 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -525,6 +525,7 @@
 extern const base::Feature kSystemProxyForSystemServices;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTabClusterUI;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTelemetryExtension;
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTerminalDev;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kTerminalSSH;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kTerminalTmuxIntegration;
diff --git a/ash/shelf/hotseat_widget.cc b/ash/shelf/hotseat_widget.cc
index 417a5cc..399a3d3 100644
--- a/ash/shelf/hotseat_widget.cc
+++ b/ash/shelf/hotseat_widget.cc
@@ -21,7 +21,6 @@
 #include "ash/shelf/shelf_navigation_widget.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
-#include "ash/style/highlight_border.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -39,6 +38,7 @@
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/background.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/view_targeter_delegate.h"
 #include "ui/views/widget/widget_delegate.h"
@@ -587,9 +587,10 @@
       rounded_corners) {
     translucent_background_->layer()->SetRoundedCornerRadius(rounded_corners);
     if (features::IsDarkLightModeEnabled()) {
-      translucent_background_->SetBorder(std::make_unique<HighlightBorder>(
-          radius, HighlightBorder::Type::kHighlightBorder1,
-          /*use_light_colors=*/true));
+      translucent_background_->SetBorder(
+          std::make_unique<views::HighlightBorder>(
+              radius, views::HighlightBorder::Type::kHighlightBorder1,
+              /*use_light_colors=*/!features::IsDarkLightModeEnabled()));
     }
   }
 
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index ae65660..c03fd14 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -267,6 +267,11 @@
   return absl::nullopt;
 }
 
+bool IsInImmersiveFullscreen() {
+  WindowState* active_window = WindowState::ForActiveWindow();
+  return active_window && active_window->IsInImmersiveFullscreen();
+}
+
 // Forwards gesture events to ShelfLayoutManager to hide the hotseat
 // when it is kExtended.
 class HotseatEventHandler : public ui::EventHandler,
@@ -2388,9 +2393,10 @@
   }
 
   // Clamshell ProductivityLauncher does not support shelf drags unless autohide
-  // is enabled.
+  // is enabled or the shelf is autohidden for immersive fullscreen.
   if (!is_tablet_mode && features::IsProductivityLauncherEnabled() &&
-      CalculateShelfVisibility() != SHELF_AUTO_HIDE) {
+      CalculateShelfVisibility() != SHELF_AUTO_HIDE &&
+      !IsInImmersiveFullscreen()) {
     return false;
   }
 
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 9a6a1d2..df07e6b 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1560,6 +1560,22 @@
   }
 }
 
+// Tests shelf gesture drags with ProductivityLauncher enabled. This test can be
+// deleted when ProductivityLauncher is on by default, because it is covered by
+// the "bottom" case above.
+TEST_F(ShelfLayoutManagerTest, GestureDragForProductivityLauncher) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kProductivityLauncher);
+
+  ui::GestureConfiguration::GetInstance()
+      ->set_max_touch_move_in_pixels_for_click(0);
+  ASSERT_EQ(GetPrimaryShelf()->alignment(), ShelfAlignment::kBottom);
+  gfx::Rect shelf_bounds = GetVisibleShelfWidgetBoundsInScreen();
+  gfx::Point bottom_center = shelf_bounds.bottom_center();
+  bottom_center.Offset(0, -1);  // Make sure the point is inside shelf.
+  RunGestureDragTests(bottom_center, shelf_bounds.top_center());
+}
+
 TEST_F(ShelfLayoutManagerTest, ShelfDragDisabledForProductivityLauncher) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kProductivityLauncher);
diff --git a/ash/shelf/shelf_navigation_widget.cc b/ash/shelf/shelf_navigation_widget.cc
index 90de30c..6571b7f 100644
--- a/ash/shelf/shelf_navigation_widget.cc
+++ b/ash/shelf/shelf_navigation_widget.cc
@@ -17,7 +17,6 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/style/highlight_border.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/bind.h"
@@ -39,6 +38,7 @@
 #include "ui/gfx/geometry/transform_util.h"
 #include "ui/views/animation/bounds_animator.h"
 #include "ui/views/background.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/view.h"
 #include "ui/wm/core/coordinate_conversion.h"
 
@@ -146,7 +146,8 @@
 
 class BackgroundLayerDelegate : public ui::LayerDelegate {
  public:
-  explicit BackgroundLayerDelegate(ui::Layer* layer) : layer_(layer) {}
+  BackgroundLayerDelegate(ui::Layer* layer, views::View* shelf_view)
+      : layer_(layer), shelf_view_(shelf_view) {}
   BackgroundLayerDelegate(const BackgroundLayerDelegate&) = delete;
   BackgroundLayerDelegate& operator=(const BackgroundLayerDelegate&) = delete;
   ~BackgroundLayerDelegate() override {}
@@ -177,9 +178,9 @@
     if (!Shell::Get()->IsInTabletMode() || ShelfConfig::Get()->is_in_app())
       return;
 
-    HighlightBorder::PaintBorderToCanvas(
-        canvas, gfx::Rect(layer_->size()), corner_radii,
-        HighlightBorder::Type::kHighlightBorder2, false);
+    views::HighlightBorder::PaintBorderToCanvas(
+        canvas, *shelf_view_, gfx::Rect(layer_->size()), corner_radii,
+        views::HighlightBorder::Type::kHighlightBorder2, false);
   }
 
   void OnDeviceScaleFactorChanged(float old_device_scale_factor,
@@ -188,6 +189,8 @@
   }
 
   ui::Layer* const layer_;
+  views::View* const shelf_view_;
+
   // The background color of `layer_`. Note that this value has to be updated by
   // SetBackgroundColor() and the default value should never be drawn.
   SkColor background_color_ = SK_ColorRED;
@@ -363,8 +366,8 @@
   SetOwnedByWidget(true);
 
   if (features::IsDarkLightModeEnabled()) {
-    background_delegate_ =
-        std::make_unique<BackgroundLayerDelegate>(&opaque_background_);
+    background_delegate_ = std::make_unique<BackgroundLayerDelegate>(
+        &opaque_background_, shelf_view);
     opaque_background_.set_delegate(background_delegate_.get());
     opaque_background_.SetFillsBoundsOpaquely(false);
   }
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 2b6ed8a..d5904d6 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -32,7 +32,6 @@
 #include "ash/shelf/shelf_view.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/work_area_insets.h"
@@ -48,6 +47,7 @@
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/views/accessible_pane_view.h"
 #include "ui/views/focus/focus_search.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -98,8 +98,12 @@
 class ShelfBackgroundLayerDelegate : public ui::LayerOwner,
                                      public ui::LayerDelegate {
  public:
-  ShelfBackgroundLayerDelegate(Shelf* shelf, bool draw_highlight_border)
-      : shelf_(shelf), draw_highlight_border_(draw_highlight_border) {}
+  ShelfBackgroundLayerDelegate(Shelf* shelf,
+                               views::View* owner_view,
+                               bool draw_highlight_border)
+      : shelf_(shelf),
+        owner_view_(owner_view),
+        draw_highlight_border_(draw_highlight_border) {}
 
   ShelfBackgroundLayerDelegate(const ShelfBackgroundLayerDelegate&) = delete;
   ShelfBackgroundLayerDelegate& operator=(const ShelfBackgroundLayerDelegate&) =
@@ -132,7 +136,7 @@
 
   // Sets the highlight border type to use if shelf uses highlight border.
   // No-op if `draw_highlight_border_` is false.
-  void SetBorderType(HighlightBorder::Type type) {
+  void SetBorderType(views::HighlightBorder::Type type) {
     if (!draw_highlight_border_)
       return;
 
@@ -176,9 +180,9 @@
     if (login_shelf_view_ && login_shelf_view_->GetVisible())
       return;
 
-    HighlightBorder::PaintBorderToCanvas(canvas, gfx::Rect(layer()->size()),
-                                         gfx::RoundedCornersF(corner_radius_),
-                                         highlight_border_type_, false);
+    views::HighlightBorder::PaintBorderToCanvas(
+        canvas, *owner_view_, gfx::Rect(layer()->size()),
+        gfx::RoundedCornersF(corner_radius_), highlight_border_type_, false);
   }
 
   void OnDeviceScaleFactorChanged(float old_device_scale_factor,
@@ -187,13 +191,14 @@
   }
 
   Shelf* const shelf_;
+  views::View* const owner_view_;
   const bool draw_highlight_border_;
 
   LoginShelfView* login_shelf_view_ = nullptr;
   SkColor background_color_;
   float corner_radius_ = 0.0f;
-  HighlightBorder::Type highlight_border_type_ =
-      HighlightBorder::Type::kHighlightBorder1;
+  views::HighlightBorder::Type highlight_border_type_ =
+      views::HighlightBorder::Type::kHighlightBorder1;
 };
 
 }  // namespace
@@ -323,6 +328,7 @@
     : shelf_widget_(shelf_widget),
       opaque_background_(
           shelf,
+          this,
           /*draw_highlight_border=*/features::IsDarkLightModeEnabled()),
       animating_background_(ui::LAYER_SOLID_COLOR),
       animating_drag_handle_(ui::LAYER_SOLID_COLOR) {
@@ -853,10 +859,10 @@
 
   if (new_state == HotseatState::kExtended) {
     delegate_view_->opaque_background()->SetBorderType(
-        HighlightBorder::Type::kHighlightBorder2);
+        views::HighlightBorder::Type::kHighlightBorder2);
   } else {
     delegate_view_->opaque_background()->SetBorderType(
-        HighlightBorder::Type::kHighlightBorder1);
+        views::HighlightBorder::Type::kHighlightBorder1);
   }
 }
 
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc
index c2f26b0..527e58cb 100644
--- a/ash/style/ash_color_mixer.cc
+++ b/ash/style/ash_color_mixer.cc
@@ -5,6 +5,7 @@
 #include "ash/style/ash_color_mixer.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/public/cpp/style/scoped_light_mode_as_default.h"
 #include "ash/style/ash_color_provider.h"
 #include "ui/color/color_id.h"
 #include "ui/color/color_mixer.h"
@@ -15,11 +16,39 @@
 
 void AddAshColorMixer(ui::ColorProvider* provider,
                       const ui::ColorProviderManager::Key& key) {
-  if (!features::IsDarkLightModeEnabled())
-    return;
-
   auto* ash_color_provider = AshColorProvider::Get();
   ui::ColorMixer& mixer = provider->AddMixer();
+
+  mixer[ui::kColorAshSystemUIBorderColor1] = {
+      ash_color_provider->GetControlsLayerColor(
+          ash::AshColorProvider::ControlsLayerType::kBorderColor1)};
+  mixer[ui::kColorAshSystemUIBorderColor2] = {
+      ash_color_provider->GetControlsLayerColor(
+          ash::AshColorProvider::ControlsLayerType::kBorderColor2)};
+  mixer[ui::kColorAshSystemUIHighlightColor1] = {
+      ash_color_provider->GetControlsLayerColor(
+          ash::AshColorProvider::ControlsLayerType::kHighlightColor1)};
+  mixer[ui::kColorAshSystemUIHighlightColor2] = {
+      ash_color_provider->GetControlsLayerColor(
+          ash::AshColorProvider::ControlsLayerType::kHighlightColor2)};
+
+  if (!features::IsDarkLightModeEnabled()) {
+    ash::ScopedLightModeAsDefault scoped_light_mode_as_default;
+    mixer[ui::kColorAshSystemUILightBorderColor1] = {
+        ash_color_provider->GetControlsLayerColor(
+            ash::AshColorProvider::ControlsLayerType::kBorderColor1)};
+    mixer[ui::kColorAshSystemUILightBorderColor2] = {
+        ash_color_provider->GetControlsLayerColor(
+            ash::AshColorProvider::ControlsLayerType::kBorderColor1)};
+    mixer[ui::kColorAshSystemUILightHighlightColor1] = {
+        ash_color_provider->GetControlsLayerColor(
+            ash::AshColorProvider::ControlsLayerType::kHighlightColor1)};
+    mixer[ui::kColorAshSystemUILightHighlightColor2] = {
+        ash_color_provider->GetControlsLayerColor(
+            ash::AshColorProvider::ControlsLayerType::kHighlightColor2)};
+    return;
+  }
+
   mixer[ui::kColorAshSystemUIMenuBackground] = {
       ash_color_provider->GetBaseLayerColor(
           AshColorProvider::BaseLayerType::kTransparent80)};
diff --git a/ash/style/dark_mode_controller.h b/ash/style/dark_mode_controller.h
index 94a8bb3..98b3159 100644
--- a/ash/style/dark_mode_controller.h
+++ b/ash/style/dark_mode_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_STYLE_DARK_MODE_CONTROLLER_H_
 #define ASH_STYLE_DARK_MODE_CONTROLLER_H_
 
+#include "ash/ash_export.h"
 #include "ash/system/scheduled_feature/scheduled_feature.h"
 #include "components/prefs/pref_registry_simple.h"
 
@@ -15,7 +16,7 @@
 // DarkModeController handles automatic scheduling of dark mode to turn it on
 // at sunset and off at sunrise. However, it does not support custom start
 // and end times for scheduling.
-class DarkModeController : public ScheduledFeature {
+class ASH_EXPORT DarkModeController : public ScheduledFeature {
  public:
   DarkModeController();
   DarkModeController(const DarkModeController&) = delete;
diff --git a/ash/style/system_toast_style.cc b/ash/style/system_toast_style.cc
index 91b3471c..9df39e1 100644
--- a/ash/style/system_toast_style.cc
+++ b/ash/style/system_toast_style.cc
@@ -10,7 +10,6 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/pill_button.h"
 #include "ash/system/toast/toast_overlay.h"
 #include "ash/wm/work_area_insets.h"
@@ -25,6 +24,7 @@
 #include "ui/views/background.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace ash {
@@ -163,8 +163,8 @@
   SetBackground(views::CreateRoundedRectBackground(GetBackgroundColor(),
                                                    toast_corner_radius));
   if (features::IsDarkLightModeEnabled()) {
-    SetBorder(std::make_unique<HighlightBorder>(
-        toast_corner_radius, HighlightBorder::Type::kHighlightBorder1,
+    SetBorder(std::make_unique<views::HighlightBorder>(
+        toast_corner_radius, views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   }
 }
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller.cc b/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
index 4ea7aa5..8b9a874a 100644
--- a/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller.cc
@@ -49,6 +49,12 @@
 }
 
 void DarkModeFeaturePodController::OnIconPressed() {
+  // Toggling Dark theme feature pod button inside quick settings should cancel
+  // auto scheduling. This ensures that on and off states of the pod button
+  // match the non-scheduled states of Dark and Light buttons in
+  // personalization hub respectively.
+  ash::Shell::Get()->dark_mode_controller()->SetAutoScheduleEnabled(
+      /*enabled=*/false);
   AshColorProvider::Get()->ToggleColorMode();
 }
 
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller.h b/ash/system/dark_mode/dark_mode_feature_pod_controller.h
index 82219c7..bc2231e 100644
--- a/ash/system/dark_mode/dark_mode_feature_pod_controller.h
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller.h
@@ -5,6 +5,7 @@
 #ifndef ASH_SYSTEM_DARK_MODE_DARK_MODE_FEATURE_POD_CONTROLLER_H_
 #define ASH_SYSTEM_DARK_MODE_DARK_MODE_FEATURE_POD_CONTROLLER_H_
 
+#include "ash/ash_export.h"
 #include "ash/public/cpp/style/color_mode_observer.h"
 #include "ash/system/unified/feature_pod_controller_base.h"
 
@@ -13,8 +14,8 @@
 class UnifiedSystemTrayController;
 
 // Controller of a feature pod button that toggles dark mode for ash.
-class DarkModeFeaturePodController : public FeaturePodControllerBase,
-                                     public ColorModeObserver {
+class ASH_EXPORT DarkModeFeaturePodController : public FeaturePodControllerBase,
+                                                public ColorModeObserver {
  public:
   explicit DarkModeFeaturePodController(
       UnifiedSystemTrayController* tray_controller);
diff --git a/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc b/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc
new file mode 100644
index 0000000..90b1645
--- /dev/null
+++ b/ash/system/dark_mode/dark_mode_feature_pod_controller_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/dark_mode/dark_mode_feature_pod_controller.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
+#include "ash/style/ash_color_provider.h"
+#include "ash/style/dark_mode_controller.h"
+#include "ash/system/unified/feature_pod_button.h"
+#include "ash/system/unified/unified_system_tray.h"
+#include "ash/system/unified/unified_system_tray_bubble.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/scoped_feature_list.h"
+#include "chromeos/constants/chromeos_features.h"
+
+namespace ash {
+
+using DarkModeFeaturePodControllerTest = AshTestBase;
+
+// Tests that toggling dark mode from the system tray disables auto scheduling
+// and switches the color mode properly.
+TEST_F(DarkModeFeaturePodControllerTest, ToggleDarkMode) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(chromeos::features::kDarkLightMode);
+
+  AshColorProvider* provider = AshColorProvider::Get();
+  provider->OnActiveUserPrefServiceChanged(
+      Shell::Get()->session_controller()->GetActivePrefService());
+
+  UnifiedSystemTray* system_tray = GetPrimaryUnifiedSystemTray();
+  system_tray->ShowBubble();
+  std::unique_ptr<DarkModeFeaturePodController>
+      dark_mode_feature_pod_controller =
+          std::make_unique<DarkModeFeaturePodController>(
+              system_tray->bubble()->controller_for_test());
+
+  std::unique_ptr<FeaturePodButton> button(
+      dark_mode_feature_pod_controller->CreateButton());
+
+  // Enable dark mode auto scheduling.
+  DarkModeController* controller = Shell::Get()->dark_mode_controller();
+  controller->SetAutoScheduleEnabled(true);
+  EXPECT_TRUE(controller->GetAutoScheduleEnabled());
+
+  // Check that the statuses of toggle and dark mode are consistent.
+  bool dark_mode_enabled = provider->IsDarkModeEnabled();
+  EXPECT_EQ(dark_mode_enabled, button->IsToggled());
+
+  // Pressing the dark mode button should disable the scheduling and switch the
+  // dark mode status.
+  dark_mode_feature_pod_controller->OnIconPressed();
+  EXPECT_FALSE(controller->GetAutoScheduleEnabled());
+  EXPECT_EQ(!dark_mode_enabled, provider->IsDarkModeEnabled());
+  EXPECT_EQ(!dark_mode_enabled, button->IsToggled());
+
+  // Pressing the dark mode button again should only switch the dark mode status
+  // while maintaining the disabled status of scheduling.
+  dark_mode_feature_pod_controller->OnIconPressed();
+  EXPECT_FALSE(controller->GetAutoScheduleEnabled());
+  EXPECT_EQ(dark_mode_enabled, provider->IsDarkModeEnabled());
+  EXPECT_EQ(dark_mode_enabled, button->IsToggled());
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/machine_learning/OWNERS b/ash/system/machine_learning/OWNERS
index a7df7fe..a8c032aa 100644
--- a/ash/system/machine_learning/OWNERS
+++ b/ash/system/machine_learning/OWNERS
@@ -1,3 +1,2 @@
-jiameng@chromium.org
 wrong@chromium.org
 tby@chromium.org
diff --git a/ash/system/network/fake_network_list_mobile_header_view.cc b/ash/system/network/fake_network_list_mobile_header_view.cc
new file mode 100644
index 0000000..4111c96
--- /dev/null
+++ b/ash/system/network/fake_network_list_mobile_header_view.cc
@@ -0,0 +1,32 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/network/fake_network_list_mobile_header_view.h"
+
+#include "ash/system/network/network_list_mobile_header_view.h"
+#include "ash/system/network/network_list_network_header_view.h"
+
+namespace ash {
+
+FakeNetworkListMobileHeaderView::FakeNetworkListMobileHeaderView(
+    NetworkListNetworkHeaderView::Delegate* delegate)
+    : NetworkListMobileHeaderView(delegate) {}
+
+FakeNetworkListMobileHeaderView::~FakeNetworkListMobileHeaderView() = default;
+
+void FakeNetworkListMobileHeaderView::SetToggleState(bool enabled,
+                                                     bool visible) {
+  is_toggle_enabled_ = enabled;
+  is_toggle_visible_ = visible;
+  set_toggle_state_count_++;
+};
+
+void FakeNetworkListMobileHeaderView::SetAddESimButtonState(bool enabled,
+                                                            bool visible) {
+  is_add_esim_enabled_ = enabled;
+  is_add_esim_visible_ = visible;
+  set_add_esim_button_state_count_++;
+};
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/fake_network_list_mobile_header_view.h b/ash/system/network/fake_network_list_mobile_header_view.h
new file mode 100644
index 0000000..d8dcbde
--- /dev/null
+++ b/ash/system/network/fake_network_list_mobile_header_view.h
@@ -0,0 +1,60 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_NETWORK_FAKE_NETWORK_LIST_MOBILE_HEADER_VIEW_H_
+#define ASH_SYSTEM_NETWORK_FAKE_NETWORK_LIST_MOBILE_HEADER_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/network/network_list_mobile_header_view.h"
+#include "ash/system/network/network_list_network_header_view.h"
+#include "ash/system/tray/tri_view.h"
+
+namespace ash {
+
+// Fake implementation of NetworkListMobileHeaderView
+class ASH_EXPORT FakeNetworkListMobileHeaderView
+    : public NetworkListMobileHeaderView {
+ public:
+  explicit FakeNetworkListMobileHeaderView(
+      NetworkListNetworkHeaderView::Delegate* delegate);
+  FakeNetworkListMobileHeaderView(const FakeNetworkListMobileHeaderView&) =
+      delete;
+  FakeNetworkListMobileHeaderView& operator=(
+      const FakeNetworkListMobileHeaderView&) = delete;
+  ~FakeNetworkListMobileHeaderView() override;
+
+  bool is_toggle_enabled() { return is_toggle_enabled_; }
+
+  bool is_toggle_visible() { return is_toggle_visible_; }
+
+  size_t set_toggle_state_count() { return set_toggle_state_count_; }
+
+  bool is_add_esim_enabled() { return is_add_esim_enabled_; }
+
+  bool is_add_esim_visible() { return is_add_esim_visible_; }
+
+  size_t set_add_esim_button_state_count() {
+    return set_add_esim_button_state_count_;
+  }
+
+ private:
+  // NetworkListNetworkHeaderView:
+  void SetToggleState(bool enabled, bool visible) override;
+
+  // NetworkListMobileHeaderView:
+  void SetAddESimButtonState(bool enabled, bool visible) override;
+
+  bool is_toggle_enabled_;
+  bool is_toggle_visible_;
+  size_t set_toggle_state_count_;
+
+  bool is_add_esim_enabled_;
+  bool is_add_esim_visible_;
+  size_t set_add_esim_button_state_count_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_NETWORK_FAKE_NETWORK_LIST_MOBIL_HEADER_VIEW_H_
diff --git a/ash/system/network/fake_network_list_wifi_header_view.cc b/ash/system/network/fake_network_list_wifi_header_view.cc
new file mode 100644
index 0000000..a0a91d8
--- /dev/null
+++ b/ash/system/network/fake_network_list_wifi_header_view.cc
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/network/fake_network_list_wifi_header_view.h"
+
+#include "ash/system/network/network_list_network_header_view.h"
+#include "ash/system/network/network_list_wifi_header_view.h"
+
+namespace ash {
+
+FakeNetworkListWifiHeaderView::FakeNetworkListWifiHeaderView(
+    NetworkListNetworkHeaderView::Delegate* delegate)
+    : NetworkListWifiHeaderView(delegate) {}
+
+FakeNetworkListWifiHeaderView::~FakeNetworkListWifiHeaderView() = default;
+
+void FakeNetworkListWifiHeaderView::SetToggleState(bool enabled, bool visible) {
+  is_toggle_enabled_ = enabled;
+  is_toggle_visible_ = visible;
+  set_toggle_state_count_++;
+};
+
+void FakeNetworkListWifiHeaderView::SetJoinWifiButtonState(bool enabled,
+                                                           bool visible) {
+  is_join_wifi_enabled_ = enabled;
+  is_join_wifi_visible_ = visible;
+  set_join_wifi_button_state_count_++;
+};
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/fake_network_list_wifi_header_view.h b/ash/system/network/fake_network_list_wifi_header_view.h
new file mode 100644
index 0000000..7fc1c9a8e
--- /dev/null
+++ b/ash/system/network/fake_network_list_wifi_header_view.h
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_NETWORK_FAKE_NETWORK_LIST_WIFI_HEADER_VIEW_H_
+#define ASH_SYSTEM_NETWORK_FAKE_NETWORK_LIST_WIFI_HEADER_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/network/network_list_network_header_view.h"
+#include "ash/system/network/network_list_wifi_header_view.h"
+#include "ash/system/tray/tri_view.h"
+
+namespace ash {
+
+// Fake implementation of NetworkListWifiHeaderView
+class ASH_EXPORT FakeNetworkListWifiHeaderView
+    : public NetworkListWifiHeaderView {
+ public:
+  explicit FakeNetworkListWifiHeaderView(
+      NetworkListNetworkHeaderView::Delegate* delegate);
+  FakeNetworkListWifiHeaderView(const FakeNetworkListWifiHeaderView&) = delete;
+  FakeNetworkListWifiHeaderView& operator=(
+      const FakeNetworkListWifiHeaderView&) = delete;
+  ~FakeNetworkListWifiHeaderView() override;
+
+  bool is_toggle_enabled() { return is_toggle_enabled_; }
+
+  bool is_toggle_visible() { return is_toggle_visible_; }
+
+  size_t set_toggle_state_count() { return set_toggle_state_count_; }
+
+  bool is_join_wifi_enabled() { return is_join_wifi_enabled_; }
+
+  bool is_join_wifi_visible() { return is_join_wifi_visible_; }
+
+  size_t set_join_wifi_button_state_count() {
+    return set_join_wifi_button_state_count_;
+  }
+
+ private:
+  // NetworkListNetworkHeaderView:
+  void SetToggleState(bool enabled, bool visible) override;
+
+  // NetworkListWifiHeaderView:
+  void SetJoinWifiButtonState(bool enabled, bool visible) override;
+
+  bool is_toggle_enabled_;
+  bool is_toggle_visible_;
+  size_t set_toggle_state_count_;
+
+  bool is_join_wifi_enabled_;
+  bool is_join_wifi_visible_;
+  size_t set_join_wifi_button_state_count_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_NETWORK_FAKE_NETWORK_LIST_WIFI_HEADER_VIEW_H_
diff --git a/ash/system/network/network_list_mobile_header_view.cc b/ash/system/network/network_list_mobile_header_view.cc
index e1db8f29..75e497ac 100644
--- a/ash/system/network/network_list_mobile_header_view.cc
+++ b/ash/system/network/network_list_mobile_header_view.cc
@@ -4,105 +4,16 @@
 
 #include "ash/system/network/network_list_mobile_header_view.h"
 
-#include "ash/ash_export.h"
-#include "ash/constants/ash_features.h"
-#include "ash/public/cpp/system_tray_client.h"
-#include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/style/icon_button.h"
-#include "ash/system/model/system_tray_model.h"
 #include "ash/system/network/network_list_network_header_view.h"
-#include "ash/system/network/tray_network_state_model.h"
-#include "ash/system/tray/tray_popup_utils.h"
-#include "ash/system/tray/tri_view.h"
-#include "base/memory/weak_ptr.h"
-#include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
-#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
-#include "components/onc/onc_constants.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/views/controls/image_view.h"
-#include "ui/views/view.h"
 
 namespace ash {
-namespace {
-
-using chromeos::network_config::mojom::DeviceStateProperties;
-using chromeos::network_config::mojom::DeviceStateType;
-using chromeos::network_config::mojom::NetworkType;
-
-int GetAddESimTooltipMessageId() {
-  const DeviceStateProperties* cellular_device =
-      Shell::Get()->system_tray_model()->network_state_model()->GetDevice(
-          NetworkType::kCellular);
-  if (!cellular_device)
-    return 0;
-
-  switch (cellular_device->inhibit_reason) {
-    case chromeos::network_config::mojom::InhibitReason::kInstallingProfile:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_INSTALLING_PROFILE;
-    case chromeos::network_config::mojom::InhibitReason::kRenamingProfile:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_RENAMING_PROFILE;
-    case chromeos::network_config::mojom::InhibitReason::kRemovingProfile:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_REMOVING_PROFILE;
-    case chromeos::network_config::mojom::InhibitReason::kConnectingToProfile:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_CONNECTING_TO_PROFILE;
-    case chromeos::network_config::mojom::InhibitReason::kRefreshingProfileList:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_REFRESHING_PROFILE_LIST;
-    case chromeos::network_config::mojom::InhibitReason::kNotInhibited:
-      return IDS_ASH_STATUS_TRAY_ADD_CELLULAR_LABEL;
-    case chromeos::network_config::mojom::InhibitReason::kResettingEuiccMemory:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_RESETTING_ESIM;
-    case chromeos::network_config::mojom::InhibitReason::kDisablingProfile:
-      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_DISABLING_PROFILE;
-  }
-}
-
-}  // namespace
 
 NetworkListMobileHeaderView::NetworkListMobileHeaderView(
     NetworkListNetworkHeaderView::Delegate* delegate)
     : NetworkListNetworkHeaderView(delegate,
-                                   IDS_ASH_STATUS_TRAY_NETWORK_MOBILE) {
-  AddExtraButtons();
-}
+                                   IDS_ASH_STATUS_TRAY_NETWORK_MOBILE) {}
 
 NetworkListMobileHeaderView::~NetworkListMobileHeaderView() = default;
 
-void NetworkListMobileHeaderView::AddExtraButtons() {
-  // The button navigates to Settings, only add it if this can occur.
-  if (!TrayPopupUtils::CanOpenWebUISettings())
-    return;
-
-  const gfx::VectorIcon& icon = base::i18n::IsRTL() ? kAddCellularNetworkRtlIcon
-                                                    : kAddCellularNetworkIcon;
-  std::unique_ptr<IconButton> add_esim_button = std::make_unique<IconButton>(
-      base::BindRepeating(&NetworkListMobileHeaderView::AddESimButtonPressed,
-                          weak_factory_.GetWeakPtr()),
-      IconButton::Type::kSmall, &icon, GetAddESimTooltipMessageId());
-  add_esim_button.get()->SetID(kAddESimButtonId);
-  add_esim_button_ = add_esim_button.get();
-  container()->AddView(TriView::Container::END, add_esim_button.release());
-};
-
-void NetworkListMobileHeaderView::OnToggleToggled(bool is_on) {
-  delegate()->OnMobileToggleClicked(is_on);
-}
-
-void NetworkListMobileHeaderView::AddESimButtonPressed() {
-  Shell::Get()->system_tray_model()->client()->ShowNetworkCreate(
-      ::onc::network_type::kCellular);
-}
-
-void NetworkListMobileHeaderView::SetAddESimButtonState(bool enabled,
-                                                        bool visible) {
-  if (!add_esim_button_)
-    return;
-
-  add_esim_button_->SetVisible(visible);
-  add_esim_button_->SetEnabled(enabled);
-  add_esim_button_->SetTooltipText(
-      l10n_util::GetStringUTF16(GetAddESimTooltipMessageId()));
-}
-
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/network_list_mobile_header_view.h b/ash/system/network/network_list_mobile_header_view.h
index 8142805..d782704d 100644
--- a/ash/system/network/network_list_mobile_header_view.h
+++ b/ash/system/network/network_list_mobile_header_view.h
@@ -6,15 +6,11 @@
 #define ASH_SYSTEM_NETWORK_NETWORK_LIST_MOBILE_HEADER_VIEW_H_
 
 #include "ash/ash_export.h"
-#include "ash/style/icon_button.h"
 #include "ash/system/network/network_list_network_header_view.h"
-#include "ash/system/tray/tri_view.h"
-#include "base/memory/weak_ptr.h"
-#include "ui/views/view.h"
 
 namespace ash {
 
-// This class is the implementation of the network list header for Mobile
+// This class is the interface used to create network list header for Mobile
 // networks, and is responsible for the creation of mobile-specific buttons.
 class ASH_EXPORT NetworkListMobileHeaderView
     : public NetworkListNetworkHeaderView {
@@ -26,25 +22,7 @@
       delete;
   ~NetworkListMobileHeaderView() override;
 
- private:
-  friend class NetworkListMobileHeaderViewTest;
-
-  // Used for testing.
-  static constexpr int kAddESimButtonId =
-      NetworkListNetworkHeaderView::kToggleButtonId + 1;
-
-  // NetworkListNetworkHeaderView:
-  void AddExtraButtons() override;
-  void OnToggleToggled(bool is_on) override;
-
-  void AddESimButtonPressed();
-  void SetAddESimButtonState(bool enabled, bool visible);
-
-  // Button that navigates to the Settings mobile data subpage with the eSIM
-  // setup dialog open. This is null when the device is not eSIM-capable.
-  IconButton* add_esim_button_ = nullptr;
-
-  base::WeakPtrFactory<NetworkListMobileHeaderView> weak_factory_{this};
+  virtual void SetAddESimButtonState(bool enabled, bool visible) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/system/network/network_list_mobile_header_view_impl.cc b/ash/system/network/network_list_mobile_header_view_impl.cc
new file mode 100644
index 0000000..5e418be
--- /dev/null
+++ b/ash/system/network/network_list_mobile_header_view_impl.cc
@@ -0,0 +1,109 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/network/network_list_mobile_header_view_impl.h"
+
+#include "ash/ash_export.h"
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/system_tray_client.h"
+#include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/network/network_list_mobile_header_view.h"
+#include "ash/system/network/network_list_network_header_view.h"
+#include "ash/system/network/tray_network_state_model.h"
+#include "ash/system/tray/tray_popup_utils.h"
+#include "ash/system/tray/tri_view.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
+#include "components/onc/onc_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/view.h"
+
+namespace ash {
+namespace {
+
+using chromeos::network_config::mojom::DeviceStateProperties;
+using chromeos::network_config::mojom::DeviceStateType;
+using chromeos::network_config::mojom::NetworkType;
+
+int GetAddESimTooltipMessageId() {
+  const DeviceStateProperties* cellular_device =
+      Shell::Get()->system_tray_model()->network_state_model()->GetDevice(
+          NetworkType::kCellular);
+  if (!cellular_device)
+    return 0;
+
+  switch (cellular_device->inhibit_reason) {
+    case chromeos::network_config::mojom::InhibitReason::kInstallingProfile:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_INSTALLING_PROFILE;
+    case chromeos::network_config::mojom::InhibitReason::kRenamingProfile:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_RENAMING_PROFILE;
+    case chromeos::network_config::mojom::InhibitReason::kRemovingProfile:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_REMOVING_PROFILE;
+    case chromeos::network_config::mojom::InhibitReason::kConnectingToProfile:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_CONNECTING_TO_PROFILE;
+    case chromeos::network_config::mojom::InhibitReason::kRefreshingProfileList:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_REFRESHING_PROFILE_LIST;
+    case chromeos::network_config::mojom::InhibitReason::kNotInhibited:
+      return IDS_ASH_STATUS_TRAY_ADD_CELLULAR_LABEL;
+    case chromeos::network_config::mojom::InhibitReason::kResettingEuiccMemory:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_RESETTING_ESIM;
+    case chromeos::network_config::mojom::InhibitReason::kDisablingProfile:
+      return IDS_ASH_STATUS_TRAY_INHIBITED_CELLULAR_DISABLING_PROFILE;
+  }
+}
+
+}  // namespace
+
+NetworkListMobileHeaderViewImpl::NetworkListMobileHeaderViewImpl(
+    NetworkListNetworkHeaderView::Delegate* delegate)
+    : NetworkListMobileHeaderView(delegate) {
+  AddExtraButtons();
+}
+
+NetworkListMobileHeaderViewImpl::~NetworkListMobileHeaderViewImpl() = default;
+
+void NetworkListMobileHeaderViewImpl::AddExtraButtons() {
+  // The button navigates to Settings, only add it if this can occur.
+  if (!TrayPopupUtils::CanOpenWebUISettings())
+    return;
+
+  const gfx::VectorIcon& icon = base::i18n::IsRTL() ? kAddCellularNetworkRtlIcon
+                                                    : kAddCellularNetworkIcon;
+  std::unique_ptr<IconButton> add_esim_button = std::make_unique<IconButton>(
+      base::BindRepeating(
+          &NetworkListMobileHeaderViewImpl::AddESimButtonPressed,
+          weak_factory_.GetWeakPtr()),
+      IconButton::Type::kSmall, &icon, GetAddESimTooltipMessageId());
+  add_esim_button.get()->SetID(kAddESimButtonId);
+  add_esim_button_ = add_esim_button.get();
+  container()->AddView(TriView::Container::END, add_esim_button.release());
+};
+
+void NetworkListMobileHeaderViewImpl::OnToggleToggled(bool is_on) {
+  delegate()->OnMobileToggleClicked(is_on);
+}
+
+void NetworkListMobileHeaderViewImpl::AddESimButtonPressed() {
+  Shell::Get()->system_tray_model()->client()->ShowNetworkCreate(
+      ::onc::network_type::kCellular);
+}
+
+void NetworkListMobileHeaderViewImpl::SetAddESimButtonState(bool enabled,
+                                                            bool visible) {
+  if (!add_esim_button_)
+    return;
+
+  add_esim_button_->SetVisible(visible);
+  add_esim_button_->SetEnabled(enabled);
+  add_esim_button_->SetTooltipText(
+      l10n_util::GetStringUTF16(GetAddESimTooltipMessageId()));
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/network_list_mobile_header_view_impl.h b/ash/system/network/network_list_mobile_header_view_impl.h
new file mode 100644
index 0000000..5dfc90e
--- /dev/null
+++ b/ash/system/network/network_list_mobile_header_view_impl.h
@@ -0,0 +1,55 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_NETWORK_NETWORK_LIST_MOBILE_HEADER_VIEW_IMPL_H_
+#define ASH_SYSTEM_NETWORK_NETWORK_LIST_MOBILE_HEADER_VIEW_IMPL_H_
+
+#include "ash/ash_export.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/network/network_list_mobile_header_view.h"
+#include "ash/system/network/network_list_network_header_view.h"
+#include "ash/system/tray/tri_view.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+// Implementation of NetworkListMobileHeaderView.
+class ASH_EXPORT NetworkListMobileHeaderViewImpl
+    : public NetworkListMobileHeaderView {
+ public:
+  explicit NetworkListMobileHeaderViewImpl(
+      NetworkListNetworkHeaderView::Delegate* delegate);
+  NetworkListMobileHeaderViewImpl(const NetworkListMobileHeaderViewImpl&) =
+      delete;
+  NetworkListMobileHeaderViewImpl& operator=(
+      const NetworkListMobileHeaderViewImpl&) = delete;
+  ~NetworkListMobileHeaderViewImpl() override;
+
+ private:
+  friend class NetworkListMobileHeaderViewTest;
+
+  // Used for testing.
+  static constexpr int kAddESimButtonId =
+      NetworkListNetworkHeaderView::kToggleButtonId + 1;
+
+  // NetworkListNetworkHeaderView:
+  void AddExtraButtons() override;
+  void OnToggleToggled(bool is_on) override;
+
+  // NetworkListMobileHeaderView:
+  void SetAddESimButtonState(bool enabled, bool visible) override;
+
+  void AddESimButtonPressed();
+
+  // Button that navigates to the Settings mobile data subpage with the eSIM
+  // setup dialog open. This is null when the device is not eSIM-capable.
+  IconButton* add_esim_button_ = nullptr;
+
+  base::WeakPtrFactory<NetworkListMobileHeaderViewImpl> weak_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_NETWORK_NETWORK_LIST_MOBILE_HEADER_VIEW_IMPL_H_
diff --git a/ash/system/network/network_list_mobile_header_view_unittest.cc b/ash/system/network/network_list_mobile_header_view_unittest.cc
index b8b4337e..123a0332 100644
--- a/ash/system/network/network_list_mobile_header_view_unittest.cc
+++ b/ash/system/network/network_list_mobile_header_view_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/network/network_list_mobile_header_view.h"
+#include "ash/system/network/network_list_mobile_header_view_impl.h"
 
 #include <memory>
 
@@ -69,9 +69,9 @@
   }
 
   void Init() {
-    std::unique_ptr<NetworkListMobileHeaderView>
+    std::unique_ptr<NetworkListMobileHeaderViewImpl>
         network_list_mobile_header_view =
-            std::make_unique<NetworkListMobileHeaderView>(
+            std::make_unique<NetworkListMobileHeaderViewImpl>(
                 &fake_network_list_network_header_delegate_);
 
     widget_ = CreateFramelessTestWidget();
@@ -106,7 +106,7 @@
 
   IconButton* GetAddEsimButton() {
     return FindViewById<IconButton*>(
-        NetworkListMobileHeaderView::kAddESimButtonId);
+        NetworkListMobileHeaderViewImpl::kAddESimButtonId);
   }
 
   TrayToggleButton* GetToggleButton() {
@@ -136,7 +136,7 @@
   base::test::ScopedFeatureList feature_list_;
   FakeNetworkListNetworkHeaderViewDelegate
       fake_network_list_network_header_delegate_;
-  NetworkListMobileHeaderView* network_list_mobile_header_view_;
+  NetworkListMobileHeaderViewImpl* network_list_mobile_header_view_;
 };
 
 TEST_F(NetworkListMobileHeaderViewTest, HeaderLabel) {
diff --git a/ash/system/network/network_list_wifi_header_view.cc b/ash/system/network/network_list_wifi_header_view.cc
index 0dd2c14..da9f4a52 100644
--- a/ash/system/network/network_list_wifi_header_view.cc
+++ b/ash/system/network/network_list_wifi_header_view.cc
@@ -4,74 +4,16 @@
 
 #include "ash/system/network/network_list_wifi_header_view.h"
 
-#include "ash/constants/ash_features.h"
-#include "ash/public/cpp/system_tray_client.h"
-#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/style/icon_button.h"
-#include "ash/system/model/system_tray_model.h"
 #include "ash/system/network/network_list_network_header_view.h"
-#include "ash/system/network/tray_network_state_model.h"
-#include "ash/system/tray/tri_view.h"
-#include "base/memory/weak_ptr.h"
-#include "base/metrics/user_metrics.h"
-#include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
-#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
-#include "components/onc/onc_constants.h"
-#include "components/vector_icons/vector_icons.h"
-#include "ui/views/controls/image_view.h"
-#include "ui/views/view.h"
 
 namespace ash {
-namespace {
-using chromeos::network_config::mojom::DeviceStateType;
-using chromeos::network_config::mojom::NetworkType;
-}  // namespace
 
 NetworkListWifiHeaderView::NetworkListWifiHeaderView(
     NetworkListNetworkHeaderView::Delegate* delegate)
     : NetworkListNetworkHeaderView(delegate, IDS_ASH_STATUS_TRAY_NETWORK_WIFI) {
-  AddExtraButtons();
 }
 
 NetworkListWifiHeaderView::~NetworkListWifiHeaderView() = default;
 
-void NetworkListWifiHeaderView::AddExtraButtons() {
-  std::unique_ptr<IconButton> join_wifi_button = std::make_unique<IconButton>(
-      base::BindRepeating(&NetworkListWifiHeaderView::JoinWifiButtonPressed,
-                          weak_factory_.GetWeakPtr()),
-      IconButton::Type::kSmall, &vector_icons::kWifiAddIcon,
-      IDS_ASH_STATUS_TRAY_OTHER_WIFI);
-
-  join_wifi_button.get()->SetID(kJoinWifiButtonId);
-  join_wifi_button_ = join_wifi_button.get();
-  container()->AddView(TriView::Container::END, join_wifi_button.release());
-};
-
-void NetworkListWifiHeaderView::SetToggleState(bool enabled, bool is_on) {
-  join_wifi_button_->SetEnabled(enabled && is_on);
-  NetworkListNetworkHeaderView::SetToggleState(enabled, is_on);
-}
-
-void NetworkListWifiHeaderView::OnToggleToggled(bool is_on) {
-  // |join_wifi_button_| state is not updated here, it will be updated when
-  // WiFi device state changes.
-  delegate()->OnWifiToggleClicked(is_on);
-}
-
-void NetworkListWifiHeaderView::JoinWifiButtonPressed() {
-  base::RecordAction(base::UserMetricsAction("StatusArea_Network_JoinOther"));
-  Shell::Get()->system_tray_model()->client()->ShowNetworkCreate(
-      ::onc::network_type::kWiFi);
-}
-
-void NetworkListWifiHeaderView::SetJoinWifiButtonState(bool enabled,
-                                                       bool visible) {
-  if (!join_wifi_button_)
-    return;
-
-  join_wifi_button_->SetEnabled(enabled);
-  join_wifi_button_->SetVisible(visible);
-}
-
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/network_list_wifi_header_view.h b/ash/system/network/network_list_wifi_header_view.h
index 3426798..1443721 100644
--- a/ash/system/network/network_list_wifi_header_view.h
+++ b/ash/system/network/network_list_wifi_header_view.h
@@ -6,15 +6,11 @@
 #define ASH_SYSTEM_NETWORK_NETWORK_LIST_WIFI_HEADER_VIEW_H_
 
 #include "ash/ash_export.h"
-#include "ash/style/icon_button.h"
 #include "ash/system/network/network_list_network_header_view.h"
-#include "ash/system/tray/tri_view.h"
-#include "base/memory/weak_ptr.h"
-#include "ui/views/view.h"
 
 namespace ash {
 
-// This class is the implementation of the network list header for Wifi
+// This class is the interface used to create network list header for Wifi
 // networks, and is responsible for the creation of wifi-specific buttons.
 class ASH_EXPORT NetworkListWifiHeaderView
     : public NetworkListNetworkHeaderView {
@@ -26,25 +22,7 @@
       delete;
   ~NetworkListWifiHeaderView() override;
 
- private:
-  friend class NetworkListWifiHeaderViewTest;
-
-  // Used for testing.
-  static constexpr int kJoinWifiButtonId =
-      NetworkListNetworkHeaderView::kToggleButtonId + 1;
-
-  // NetworkListNetworkHeaderView:
-  void AddExtraButtons() override;
-  void SetToggleState(bool enabled, bool is_on) override;
-  void OnToggleToggled(bool is_on) override;
-
-  void JoinWifiButtonPressed();
-  void SetJoinWifiButtonState(bool enabled, bool visible);
-
-  // A button to invoke "Join Wi-Fi network" dialog.
-  IconButton* join_wifi_button_ = nullptr;
-
-  base::WeakPtrFactory<NetworkListWifiHeaderView> weak_factory_{this};
+  virtual void SetJoinWifiButtonState(bool enabled, bool visible) = 0;
 };
 
 }  // namespace ash
diff --git a/ash/system/network/network_list_wifi_header_view_impl.cc b/ash/system/network/network_list_wifi_header_view_impl.cc
new file mode 100644
index 0000000..1328b6f3
--- /dev/null
+++ b/ash/system/network/network_list_wifi_header_view_impl.cc
@@ -0,0 +1,78 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/network/network_list_wifi_header_view_impl.h"
+
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/system_tray_client.h"
+#include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/model/system_tray_model.h"
+#include "ash/system/network/network_list_network_header_view.h"
+#include "ash/system/network/network_list_wifi_header_view.h"
+#include "ash/system/network/tray_network_state_model.h"
+#include "ash/system/tray/tri_view.h"
+#include "base/memory/weak_ptr.h"
+#include "base/metrics/user_metrics.h"
+#include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
+#include "components/onc/onc_constants.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/view.h"
+
+namespace ash {
+namespace {
+using chromeos::network_config::mojom::DeviceStateType;
+using chromeos::network_config::mojom::NetworkType;
+}  // namespace
+
+NetworkListWifiHeaderViewImpl::NetworkListWifiHeaderViewImpl(
+    NetworkListNetworkHeaderView::Delegate* delegate)
+    : NetworkListWifiHeaderView(delegate) {
+  AddExtraButtons();
+}
+
+NetworkListWifiHeaderViewImpl::~NetworkListWifiHeaderViewImpl() = default;
+
+void NetworkListWifiHeaderViewImpl::AddExtraButtons() {
+  std::unique_ptr<IconButton> join_wifi_button = std::make_unique<IconButton>(
+      base::BindRepeating(&NetworkListWifiHeaderViewImpl::JoinWifiButtonPressed,
+                          weak_factory_.GetWeakPtr()),
+      IconButton::Type::kSmall, &vector_icons::kWifiAddIcon,
+      IDS_ASH_STATUS_TRAY_OTHER_WIFI);
+
+  join_wifi_button.get()->SetID(kJoinWifiButtonId);
+  join_wifi_button_ = join_wifi_button.get();
+  container()->AddView(TriView::Container::END, join_wifi_button.release());
+};
+
+void NetworkListWifiHeaderViewImpl::SetToggleState(bool enabled, bool is_on) {
+  join_wifi_button_->SetEnabled(enabled && is_on);
+  NetworkListNetworkHeaderView::SetToggleState(enabled, is_on);
+}
+
+void NetworkListWifiHeaderViewImpl::OnToggleToggled(bool is_on) {
+  // |join_wifi_button_| state is not updated here, it will be updated when
+  // WiFi device state changes.
+  delegate()->OnWifiToggleClicked(is_on);
+}
+
+void NetworkListWifiHeaderViewImpl::JoinWifiButtonPressed() {
+  base::RecordAction(base::UserMetricsAction("StatusArea_Network_JoinOther"));
+  Shell::Get()->system_tray_model()->client()->ShowNetworkCreate(
+      ::onc::network_type::kWiFi);
+}
+
+void NetworkListWifiHeaderViewImpl::SetJoinWifiButtonState(bool enabled,
+                                                           bool visible) {
+  if (!join_wifi_button_)
+    return;
+
+  join_wifi_button_->SetEnabled(enabled);
+  join_wifi_button_->SetVisible(visible);
+}
+
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/network/network_list_wifi_header_view_impl.h b/ash/system/network/network_list_wifi_header_view_impl.h
new file mode 100644
index 0000000..8508bd9
--- /dev/null
+++ b/ash/system/network/network_list_wifi_header_view_impl.h
@@ -0,0 +1,53 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_SYSTEM_NETWORK_NETWORK_LIST_WIFI_HEADER_VIEW_IMPL_H_
+#define ASH_SYSTEM_NETWORK_NETWORK_LIST_WIFI_HEADER_VIEW_IMPL_H_
+
+#include "ash/ash_export.h"
+#include "ash/style/icon_button.h"
+#include "ash/system/network/network_list_wifi_header_view.h"
+#include "ash/system/tray/tri_view.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+// Implementation of NetworkListWifiHeaderView.
+class ASH_EXPORT NetworkListWifiHeaderViewImpl
+    : public NetworkListWifiHeaderView {
+ public:
+  explicit NetworkListWifiHeaderViewImpl(
+      NetworkListNetworkHeaderView::Delegate* delegate);
+  NetworkListWifiHeaderViewImpl(const NetworkListWifiHeaderViewImpl&) = delete;
+  NetworkListWifiHeaderViewImpl& operator=(
+      const NetworkListWifiHeaderViewImpl&) = delete;
+  ~NetworkListWifiHeaderViewImpl() override;
+
+ private:
+  friend class NetworkListWifiHeaderViewTest;
+
+  // Used for testing.
+  static constexpr int kJoinWifiButtonId =
+      NetworkListNetworkHeaderView::kToggleButtonId + 1;
+
+  // NetworkListNetworkHeaderView:
+  void AddExtraButtons() override;
+  void SetToggleState(bool enabled, bool is_on) override;
+  void OnToggleToggled(bool is_on) override;
+
+  // NetworkListWifiHeaderView:
+  void SetJoinWifiButtonState(bool enabled, bool visible) override;
+
+  void JoinWifiButtonPressed();
+
+  // A button to invoke "Join Wi-Fi network" dialog.
+  IconButton* join_wifi_button_ = nullptr;
+
+  base::WeakPtrFactory<NetworkListWifiHeaderViewImpl> weak_factory_{this};
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_NETWORK_NETWORK_LIST_WIFI_HEADER_VIEW_IMPL_H_
diff --git a/ash/system/network/network_list_wifi_header_view_unittest.cc b/ash/system/network/network_list_wifi_header_view_unittest.cc
index b56737a7..c9bd696 100644
--- a/ash/system/network/network_list_wifi_header_view_unittest.cc
+++ b/ash/system/network/network_list_wifi_header_view_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/system/network/network_list_wifi_header_view.h"
+#include "ash/system/network/network_list_wifi_header_view_impl.h"
 
 #include <memory>
 
@@ -36,9 +36,10 @@
     AshTestBase::SetUp();
     feature_list_.InitAndEnableFeature(features::kQuickSettingsNetworkRevamp);
 
-    std::unique_ptr<NetworkListWifiHeaderView> network_list_wifi_header_view =
-        std::make_unique<NetworkListWifiHeaderView>(
-            &fake_network_list_network_header_delegate_);
+    std::unique_ptr<NetworkListWifiHeaderViewImpl>
+        network_list_wifi_header_view =
+            std::make_unique<NetworkListWifiHeaderViewImpl>(
+                &fake_network_list_network_header_delegate_);
 
     widget_ = CreateFramelessTestWidget();
     widget_->SetFullscreen(true);
@@ -66,7 +67,7 @@
 
   IconButton* GetJoinWifiButton() {
     return FindViewById<IconButton*>(
-        NetworkListWifiHeaderView::kJoinWifiButtonId);
+        NetworkListWifiHeaderViewImpl::kJoinWifiButtonId);
   }
 
   TrayToggleButton* GetToggleButton() {
@@ -84,7 +85,7 @@
     return &fake_network_list_network_header_delegate_;
   }
 
-  NetworkListWifiHeaderView* network_list_wifi_header_view() {
+  NetworkListWifiHeaderViewImpl* network_list_wifi_header_view() {
     return network_list_wifi_header_view_;
   }
 
@@ -100,7 +101,7 @@
   base::test::ScopedFeatureList feature_list_;
   FakeNetworkListNetworkHeaderViewDelegate
       fake_network_list_network_header_delegate_;
-  NetworkListWifiHeaderView* network_list_wifi_header_view_;
+  NetworkListWifiHeaderViewImpl* network_list_wifi_header_view_;
 };
 
 TEST_F(NetworkListWifiHeaderViewTest, HeaderLabel) {
diff --git a/ash/system/power/power_button_menu_view.cc b/ash/system/power/power_button_menu_view.cc
index 50ba8dec..f98ea40 100644
--- a/ash/system/power/power_button_menu_view.cc
+++ b/ash/system/power/power_button_menu_view.cc
@@ -18,7 +18,6 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/style/system_shadow.h"
 #include "ash/system/power/power_button_menu_item_view.h"
 #include "ash/system/power/power_button_menu_metrics_type.h"
@@ -36,6 +35,7 @@
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/background.h"
+#include "ui/views/highlight_border.h"
 
 namespace ash {
 
@@ -89,8 +89,8 @@
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetPaintToLayer();
   if (features::IsDarkLightModeEnabled()) {
-    SetBorder(std::make_unique<HighlightBorder>(
-        kMenuCornerRadius, HighlightBorder::Type::kHighlightBorder1,
+    SetBorder(std::make_unique<views::HighlightBorder>(
+        kMenuCornerRadius, views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   }
   layer()->SetFillsBoundsOpaquely(false);
diff --git a/ash/system/time/calendar_event_list_view.cc b/ash/system/time/calendar_event_list_view.cc
index 087a3df..0b339e4 100644
--- a/ash/system/time/calendar_event_list_view.cc
+++ b/ash/system/time/calendar_event_list_view.cc
@@ -4,12 +4,12 @@
 
 #include "ash/system/time/calendar_event_list_view.h"
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/ash_typography.h"
 #include "ash/public/cpp/system_tray_client.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/system/model/system_tray_model.h"
 #include "ash/system/time/calendar_utils.h"
 #include "ash/system/time/calendar_view_controller.h"
@@ -25,6 +25,7 @@
 #include "ui/views/background.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/scroll_view.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/vector_icons.h"
 #include "ui/views/view.h"
@@ -60,10 +61,10 @@
     SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER);
     label()->SetBorder(views::CreateEmptyBorder(kOpenGoogleCalendarInsets));
     label()->SetTextContext(CONTEXT_CALENDAR_DATE);
-    SetBorder(std::make_unique<HighlightBorder>(
+    SetBorder(std::make_unique<views::HighlightBorder>(
         GetPreferredSize().height() / 2,
-        HighlightBorder::Type::kHighlightBorder1,
-        /*use_light_colors=*/true));
+        views::HighlightBorder::Type::kHighlightBorder1,
+        /*use_light_colors=*/!features::IsDarkLightModeEnabled()));
     SetTooltipText(
         l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_NO_EVENT_BUTTON_TOOL_TIP));
   }
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 0a4100fb..1d79338c 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -16,7 +16,6 @@
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/system/unified/unified_system_tray_view.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -37,6 +36,7 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/skia_conversions.h"
 #include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/painter.h"
 #include "ui/views/views_delegate.h"
@@ -517,8 +517,8 @@
     return;
 
   if (features::IsDarkLightModeEnabled()) {
-    SetBorder(std::make_unique<HighlightBorder>(
-        params_.corner_radius, HighlightBorder::Type::kHighlightBorder1,
+    SetBorder(std::make_unique<views::HighlightBorder>(
+        params_.corner_radius, views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
     set_color(AshColorProvider::Get()->GetBaseLayerColor(
         AshColorProvider::BaseLayerType::kTransparent80));
diff --git a/ash/system/tray/tray_container.cc b/ash/system/tray/tray_container.cc
index 06f5349..85814e4 100644
--- a/ash/system/tray/tray_container.cc
+++ b/ash/system/tray/tray_container.cc
@@ -11,12 +11,12 @@
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
-#include "ash/style/highlight_border.h"
 #include "ash/system/tray/tray_background_view.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ui/compositor/layer.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/views/border.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace ash {
@@ -112,11 +112,11 @@
   const gfx::RoundedCornersF rounded_corners =
       tray_background_view_->GetRoundedCorners();
 
-  HighlightBorder::PaintBorderToCanvas(
-      canvas,
+  views::HighlightBorder::PaintBorderToCanvas(
+      canvas, *this,
       gfx::Rect(gfx::PointAtOffsetFromOrigin(bounds_origin),
                 background_bounds.size()),
-      rounded_corners, HighlightBorder::Type::kHighlightBorder2,
+      rounded_corners, views::HighlightBorder::Type::kHighlightBorder2,
       /*use_light_colors=*/false);
 }
 
diff --git a/ash/webui/common/resources/navigation_view_panel.js b/ash/webui/common/resources/navigation_view_panel.js
index 18900ab..2497e78 100644
--- a/ash/webui/common/resources/navigation_view_panel.js
+++ b/ash/webui/common/resources/navigation_view_panel.js
@@ -187,6 +187,27 @@
     this.push('selectorItems_', selectorItem);
   }
 
+  /**
+   * Removes a section from the top level navigation. If the section is
+   * currently selected, the selection will be reset to the top item.
+   *
+   * @param {string} id The ID of the section to remove.
+   */
+  removeSelectorById(id) {
+    const index =
+        this.selectorItems_.findIndex((selector) => selector.id === id);
+    if (index < 0) {
+      throw new Error('Cannot find selector with ID "' + id + '" to remove.');
+    }
+    if (this.selectorItems_.length === 1) {
+      throw new Error('Removing the last selector is not supported.');
+    }
+    this.splice('selectorItems_', index, 1);
+    if (this.selectedItem && this.selectedItem.id === id) {
+      this.selectedItem = this.selectorItems_[0];
+    }
+  }
+
   /** @protected */
   selectedItemChanged_() {
     if (!this.selectedItem) {
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc
index 15cf054..972aa4c3 100644
--- a/ash/wm/splitview/split_view_divider.cc
+++ b/ash/wm/splitview/split_view_divider.cc
@@ -12,7 +12,6 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/splitview/split_view_controller.h"
@@ -29,6 +28,7 @@
 #include "ui/compositor/layer_type.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/views/background.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/view.h"
 #include "ui/views/view_targeter_delegate.h"
 #include "ui/views/widget/widget.h"
@@ -202,8 +202,8 @@
             AshColorProvider::BaseLayerType::kOpaque));
 
     if (chromeos::features::IsDarkLightModeEnabled()) {
-      divider_view_->SetBorder(std::make_unique<HighlightBorder>(
-          /*corner_radius=*/0, HighlightBorder::Type::kHighlightBorder1,
+      divider_view_->SetBorder(std::make_unique<views::HighlightBorder>(
+          /*corner_radius=*/0, views::HighlightBorder::Type::kHighlightBorder1,
           /*use_light_colors=*/false));
     }
   }
diff --git a/ash/wm/splitview/split_view_drag_indicators.cc b/ash/wm/splitview/split_view_drag_indicators.cc
index ebf1024..6ff2493 100644
--- a/ash/wm/splitview/split_view_drag_indicators.cc
+++ b/ash/wm/splitview/split_view_drag_indicators.cc
@@ -17,7 +17,6 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/default_color_constants.h"
 #include "ash/style/default_colors.h"
-#include "ash/style/highlight_border.h"
 #include "ash/utility/haptics_util.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_window_drag_controller.h"
@@ -38,6 +37,7 @@
 #include "ui/events/devices/haptic_touchpad_effects.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
@@ -235,9 +235,9 @@
         2, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::NORMAL));
 
     if (chromeos::features::IsDarkLightModeEnabled()) {
-      label_parent_->SetBorder(std::make_unique<HighlightBorder>(
+      label_parent_->SetBorder(std::make_unique<views::HighlightBorder>(
           /*corner_radius=*/kSplitviewLabelRoundRectRadiusDp,
-          HighlightBorder::Type::kHighlightBorder1,
+          views::HighlightBorder::Type::kHighlightBorder1,
           /*use_light_colors=*/false));
     }
   }
diff --git a/ash/wm/splitview/split_view_highlight_view.cc b/ash/wm/splitview/split_view_highlight_view.cc
index 8217843..7dd60d1 100644
--- a/ash/wm/splitview/split_view_highlight_view.cc
+++ b/ash/wm/splitview/split_view_highlight_view.cc
@@ -8,13 +8,13 @@
 #include "ash/shell.h"
 #include "ash/style/default_color_constants.h"
 #include "ash/style/default_colors.h"
-#include "ash/style/highlight_border.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "base/i18n/rtl.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_type.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
 #include "ui/views/background.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/view.h"
 #include "ui/views/view_observer.h"
 #include "ui/views/widget/widget.h"
@@ -78,9 +78,9 @@
   background()->SetNativeControlColor(
       DeprecatedGetBackgroundColor(kSplitviewHighlightViewBackgroundColor));
   if (chromeos::features::IsDarkLightModeEnabled()) {
-    SetBorder(std::make_unique<HighlightBorder>(
+    SetBorder(std::make_unique<views::HighlightBorder>(
         kHighlightScreenRoundRectRadius,
-        HighlightBorder::Type::kHighlightBorder1,
+        views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   }
 }
@@ -192,9 +192,9 @@
           ? kSplitviewHighlightViewBackgroundColor
           : kSplitviewHighlightViewBackgroundCannotSnapColor));
   if (chromeos::features::IsDarkLightModeEnabled()) {
-    SetBorder(std::make_unique<HighlightBorder>(
+    SetBorder(std::make_unique<views::HighlightBorder>(
         kHighlightScreenRoundRectRadius,
-        HighlightBorder::Type::kHighlightBorder1,
+        views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   }
 
diff --git a/ash/wm/window_cycle/window_cycle_view.cc b/ash/wm/window_cycle/window_cycle_view.cc
index 121be3c..fc177438 100644
--- a/ash/wm/window_cycle/window_cycle_view.cc
+++ b/ash/wm/window_cycle/window_cycle_view.cc
@@ -10,7 +10,6 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/style/highlight_border.h"
 #include "ash/wm/window_cycle/window_cycle_controller.h"
 #include "ash/wm/window_cycle/window_cycle_item_view.h"
 #include "base/bind.h"
@@ -39,6 +38,7 @@
 #include "ui/gfx/text_constants.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/view.h"
 
@@ -602,8 +602,9 @@
       AshColorProvider::Get()->GetBaseLayerColor(
           AshColorProvider::BaseLayerType::kTransparent80));
   if (chromeos::features::IsDarkLightModeEnabled()) {
-    SetBorder(std::make_unique<HighlightBorder>(
-        kBackgroundCornerRadius, HighlightBorder::Type::kHighlightBorder1,
+    SetBorder(std::make_unique<views::HighlightBorder>(
+        kBackgroundCornerRadius,
+        views::HighlightBorder::Type::kHighlightBorder1,
         /*use_light_colors=*/false));
   }
 }
diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc
index 0d01e92..9bf3842 100644
--- a/ash/wm/workspace/phantom_window_controller.cc
+++ b/ash/wm/workspace/phantom_window_controller.cc
@@ -6,6 +6,7 @@
 
 #include <math.h>
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/style/scoped_light_mode_as_default.h"
 #include "ash/root_window_controller.h"
@@ -13,7 +14,6 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/default_color_constants.h"
 #include "ash/style/default_colors.h"
-#include "ash/style/highlight_border.h"
 #include "ash/wm/window_util.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -26,6 +26,7 @@
 #include "ui/views/background.h"
 #include "ui/views/border.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
@@ -224,9 +225,10 @@
           AshColorProvider::ShieldLayerType::kShield20,
           kSplitviewPhantomWindowColor),
       kPhantomWindowCornerRadius));
-  phantom_view->SetBorder(std::make_unique<HighlightBorder>(
-      kPhantomWindowCornerRadius, HighlightBorder::Type::kHighlightBorder1,
-      /*use_light_colors=*/true));
+  phantom_view->SetBorder(std::make_unique<views::HighlightBorder>(
+      kPhantomWindowCornerRadius,
+      views::HighlightBorder::Type::kHighlightBorder1,
+      /*use_light_colors=*/!features::IsDarkLightModeEnabled()));
 
   return phantom_widget;
 }
@@ -265,8 +267,8 @@
   maximize_cue->layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
   const gfx::RoundedCornersF radii(kMaximizeCueHeight / 2);
   maximize_cue->layer()->SetRoundedCornerRadius(radii);
-  maximize_cue->SetBorder(std::make_unique<HighlightBorder>(
-      kMaximizeCueHeight / 2, HighlightBorder::Type::kHighlightBorder1,
+  maximize_cue->SetBorder(std::make_unique<views::HighlightBorder>(
+      kMaximizeCueHeight / 2, views::HighlightBorder::Type::kHighlightBorder1,
       /*use_light_colors=*/false));
 
   // Set layout of cue view and add a label to the view.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index f0a01d4..5d33661 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3955,6 +3955,13 @@
       "android/java/src/org/chromium/base/JniException.java",
       "android/java/src/org/chromium/base/JniStaticTestMocker.java",
       "android/java/src/org/chromium/base/NativeLibraryLoadedStatus.java",
+      "android/java/src/org/chromium/base/annotations/AccessedByNative.java",
+      "android/java/src/org/chromium/base/annotations/CalledByNative.java",
+      "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
+      "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
+      "android/java/src/org/chromium/base/annotations/JNINamespace.java",
+      "android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java",
+      "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
       "android/java/src/org/chromium/base/annotations/NativeMethods.java",
     ]
 
@@ -4058,13 +4065,6 @@
       "android/java/src/org/chromium/base/UserData.java",
       "android/java/src/org/chromium/base/UserDataHost.java",
       "android/java/src/org/chromium/base/WrappedClassLoader.java",
-      "android/java/src/org/chromium/base/annotations/AccessedByNative.java",
-      "android/java/src/org/chromium/base/annotations/CalledByNative.java",
-      "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
-      "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java",
-      "android/java/src/org/chromium/base/annotations/JNINamespace.java",
-      "android/java/src/org/chromium/base/annotations/JniIgnoreNatives.java",
-      "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
       "android/java/src/org/chromium/base/compat/ApiHelperForM.java",
       "android/java/src/org/chromium/base/compat/ApiHelperForN.java",
       "android/java/src/org/chromium/base/compat/ApiHelperForO.java",
diff --git a/base/bind.h b/base/bind.h
index 8feb0c59..318ce32 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -13,7 +13,6 @@
 #include "base/bind_internal.h"
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
-#include "base/template_util.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_APPLE) && !HAS_FEATURE(objc_arc)
@@ -69,7 +68,7 @@
                 "BindOnce requires non-const rvalue for OnceCallback binding."
                 " I.e.: base::BindOnce(std::move(callback)).");
   static_assert(
-      conjunction<
+      std::conjunction<
           internal::AssertBindArgIsNotBasePassed<std::decay_t<Args>>...>::value,
       "Use std::move() instead of base::Passed() with base::BindOnce()");
 
diff --git a/base/bind_internal.h b/base/bind_internal.h
index 60607efa..632b64e 100644
--- a/base/bind_internal.h
+++ b/base/bind_internal.h
@@ -21,7 +21,6 @@
 #include "base/memory/raw_scoped_refptr_mismatch_checker.h"
 #include "base/memory/weak_ptr.h"
 #include "base/notreached.h"
-#include "base/template_util.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_APPLE) && !HAS_FEATURE(objc_arc)
@@ -371,7 +370,7 @@
 // raw pointer to a RefCounted type.
 template <typename... Ts>
 struct HasRefCountedTypeAsRawPtr
-    : disjunction<NeedsScopedRefptrButGetsRawPtr<Ts>...> {};
+    : std::disjunction<NeedsScopedRefptrButGetsRawPtr<Ts>...> {};
 
 // ForceVoidReturn<>
 //
@@ -912,7 +911,7 @@
 // This stores all the state passed into Bind().
 template <typename Functor, typename... BoundArgs>
 struct BindState final : BindStateBase {
-  using IsCancellable = bool_constant<
+  using IsCancellable = std::bool_constant<
       CallbackCancellationTraits<Functor,
                                  std::tuple<BoundArgs...>>::is_cancellable>;
   template <typename ForwardFunctor, typename... ForwardBoundArgs>
diff --git a/base/containers/contiguous_iterator.h b/base/containers/contiguous_iterator.h
index f8d0150..84502c5 100644
--- a/base/containers/contiguous_iterator.h
+++ b/base/containers/contiguous_iterator.h
@@ -12,7 +12,6 @@
 #include <vector>
 
 #include "base/containers/checked_iterators.h"
-#include "base/template_util.h"
 
 namespace base {
 
@@ -28,8 +27,8 @@
 
 template <typename T, typename StringT = std::basic_string<iter_value_t<T>>>
 struct IsStringIterImpl
-    : disjunction<std::is_same<T, typename StringT::const_iterator>,
-                  std::is_same<T, typename StringT::iterator>> {};
+    : std::disjunction<std::is_same<T, typename StringT::const_iterator>,
+                       std::is_same<T, typename StringT::iterator>> {};
 
 // An iterator to std::basic_string is contiguous.
 // Reference: https://wg21.link/basic.string.general#2
@@ -38,22 +37,24 @@
 // `static_assert(is_trivial_v<value_type>)` inside libc++'s std::basic_string.
 template <typename T>
 struct IsStringIter
-    : conjunction<std::is_trivial<iter_value_t<T>>, IsStringIterImpl<T>> {};
+    : std::conjunction<std::is_trivial<iter_value_t<T>>, IsStringIterImpl<T>> {
+};
 
 // An iterator to std::array is contiguous.
 // Reference: https://wg21.link/array.overview#1
 template <typename T, typename ArrayT = std::array<iter_value_t<T>, 1>>
 struct IsArrayIter
-    : disjunction<std::is_same<T, typename ArrayT::const_iterator>,
-                  std::is_same<T, typename ArrayT::iterator>> {};
+    : std::disjunction<std::is_same<T, typename ArrayT::const_iterator>,
+                       std::is_same<T, typename ArrayT::iterator>> {};
 
 // An iterator to a non-bool std::vector is contiguous.
 // Reference: https://wg21.link/vector.overview#2
 template <typename T, typename VectorT = std::vector<iter_value_t<T>>>
 struct IsVectorIter
-    : conjunction<negation<std::is_same<iter_value_t<T>, bool>>,
-                  disjunction<std::is_same<T, typename VectorT::const_iterator>,
-                              std::is_same<T, typename VectorT::iterator>>> {};
+    : std::conjunction<
+          std::negation<std::is_same<iter_value_t<T>, bool>>,
+          std::disjunction<std::is_same<T, typename VectorT::const_iterator>,
+                           std::is_same<T, typename VectorT::iterator>>> {};
 
 // The result of passing a std::valarray to std::begin is a contiguous iterator.
 // Note: Since all common standard library implementations (i.e. libc++,
@@ -67,20 +68,21 @@
 // base's CheckedContiguousIterator is a contiguous iterator.
 template <typename T, typename ValueT = iter_value_t<T>>
 struct IsCheckedContiguousIter
-    : disjunction<std::is_same<T, base::CheckedContiguousConstIterator<ValueT>>,
-                  std::is_same<T, base::CheckedContiguousIterator<ValueT>>> {};
+    : std::disjunction<
+          std::is_same<T, base::CheckedContiguousConstIterator<ValueT>>,
+          std::is_same<T, base::CheckedContiguousIterator<ValueT>>> {};
 
 // Check that the iterator points to an actual object, and is one of the
 // iterator types mentioned above.
 template <typename T>
 struct IsContiguousIteratorImpl
-    : conjunction<PointsToObject<T>,
-                  disjunction<IsPointer<T>,
-                              IsStringIter<T>,
-                              IsArrayIter<T>,
-                              IsVectorIter<T>,
-                              IsValueArrayIter<T>,
-                              IsCheckedContiguousIter<T>>> {};
+    : std::conjunction<PointsToObject<T>,
+                       std::disjunction<IsPointer<T>,
+                                        IsStringIter<T>,
+                                        IsArrayIter<T>,
+                                        IsVectorIter<T>,
+                                        IsValueArrayIter<T>,
+                                        IsCheckedContiguousIter<T>>> {};
 
 }  // namespace internal
 
diff --git a/base/containers/span.h b/base/containers/span.h
index 6d539c754..6d5fed1 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -19,7 +19,6 @@
 #include "base/containers/checked_iterators.h"
 #include "base/containers/contiguous_iterator.h"
 #include "base/cxx20_to_address.h"
-#include "base/template_util.h"
 
 namespace base {
 
@@ -56,7 +55,7 @@
 struct IsSpanImpl<span<T, Extent>> : std::true_type {};
 
 template <typename T>
-using IsNotSpan = negation<IsSpanImpl<std::decay_t<T>>>;
+using IsNotSpan = std::negation<IsSpanImpl<std::decay_t<T>>>;
 
 template <typename T>
 struct IsStdArrayImpl : std::false_type {};
@@ -65,10 +64,10 @@
 struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
 
 template <typename T>
-using IsNotStdArray = negation<IsStdArrayImpl<std::decay_t<T>>>;
+using IsNotStdArray = std::negation<IsStdArrayImpl<std::decay_t<T>>>;
 
 template <typename T>
-using IsNotCArray = negation<std::is_array<std::remove_reference_t<T>>>;
+using IsNotCArray = std::negation<std::is_array<std::remove_reference_t<T>>>;
 
 template <typename From, typename To>
 using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
@@ -79,8 +78,8 @@
 
 template <typename Iter, typename T>
 using EnableIfCompatibleContiguousIterator = std::enable_if_t<
-    conjunction<IsContiguousIterator<Iter>,
-                IteratorHasConvertibleReferenceType<Iter, T>>::value>;
+    std::conjunction<IsContiguousIterator<Iter>,
+                     IteratorHasConvertibleReferenceType<Iter, T>>::value>;
 
 template <typename Container, typename T>
 using ContainerHasConvertibleData = IsLegalDataConversion<
@@ -106,11 +105,11 @@
 // SFINAE check if Container can be converted to a span<T>.
 template <typename Container, typename T>
 using IsSpanCompatibleContainer =
-    conjunction<IsNotSpan<Container>,
-                IsNotStdArray<Container>,
-                IsNotCArray<Container>,
-                ContainerHasConvertibleData<Container, T>,
-                ContainerHasIntegralSize<Container>>;
+    std::conjunction<IsNotSpan<Container>,
+                     IsNotStdArray<Container>,
+                     IsNotCArray<Container>,
+                     ContainerHasConvertibleData<Container, T>,
+                     ContainerHasIntegralSize<Container>>;
 
 template <typename Container, typename T>
 using EnableIfSpanCompatibleContainer =
diff --git a/base/memory/raw_scoped_refptr_mismatch_checker.h b/base/memory/raw_scoped_refptr_mismatch_checker.h
index 9e50458..1a1ff00 100644
--- a/base/memory/raw_scoped_refptr_mismatch_checker.h
+++ b/base/memory/raw_scoped_refptr_mismatch_checker.h
@@ -35,8 +35,8 @@
 // pointer type and are convertible to a RefCounted(Base|ThreadSafeBase) type.
 template <typename T>
 struct NeedsScopedRefptrButGetsRawPtr
-    : conjunction<std::is_pointer<T>,
-                  IsRefCountedType<std::remove_pointer_t<T>>> {
+    : std::conjunction<std::is_pointer<T>,
+                       IsRefCountedType<std::remove_pointer_t<T>>> {
   static_assert(!std::is_reference<T>::value,
                 "NeedsScopedRefptrButGetsRawPtr requires non-reference type.");
 };
diff --git a/base/parameter_pack.h b/base/parameter_pack.h
index d6871454..36ec9d8 100644
--- a/base/parameter_pack.h
+++ b/base/parameter_pack.h
@@ -11,9 +11,6 @@
 #include <tuple>
 #include <type_traits>
 
-#include "base/template_util.h"
-#include "build/build_config.h"
-
 namespace base {
 
 // Checks if any of the elements in |ilist| is true.
@@ -53,16 +50,18 @@
 struct ParameterPack {
   // Checks if |Type| occurs in the parameter pack.
   template <typename Type>
-  using HasType = bool_constant<any_of({std::is_same<Type, Ts>::value...})>;
+  using HasType =
+      std::bool_constant<any_of({std::is_same<Type, Ts>::value...})>;
 
   // Checks if the parameter pack only contains |Type|.
   template <typename Type>
-  using OnlyHasType = bool_constant<all_of({std::is_same<Type, Ts>::value...})>;
+  using OnlyHasType =
+      std::bool_constant<all_of({std::is_same<Type, Ts>::value...})>;
 
   // Checks if |Type| occurs only once in the parameter pack.
   template <typename Type>
   using IsUniqueInPack =
-      bool_constant<count({std::is_same<Type, Ts>::value...}, true) == 1>;
+      std::bool_constant<count({std::is_same<Type, Ts>::value...}, true) == 1>;
 
   // Returns the zero-based index of |Type| within |Pack...| or |pack_npos| if
   // it's not within the pack.
@@ -83,7 +82,7 @@
 
   // Checks if every type in the parameter pack is the same.
   using IsAllSameType =
-      bool_constant<all_of({std::is_same<NthType<0>, Ts>::value...})>;
+      std::bool_constant<all_of({std::is_same<NthType<0>, Ts>::value...})>;
 };
 
 }  // namespace base
diff --git a/base/template_util.h b/base/template_util.h
index e319a7ed..b1bf266 100644
--- a/base/template_util.h
+++ b/base/template_util.h
@@ -193,50 +193,6 @@
   static constexpr bool value = true;
 };
 
-// C++14 implementation of C++17's std::bool_constant.
-//
-// Reference: https://en.cppreference.com/w/cpp/types/integral_constant
-// Specification: https://wg21.link/meta.type.synop
-template <bool B>
-using bool_constant = std::integral_constant<bool, B>;
-
-// C++14 implementation of C++17's std::conjunction.
-//
-// Reference: https://en.cppreference.com/w/cpp/types/conjunction
-// Specification: https://wg21.link/meta.logical#1.itemdecl:1
-template <typename...>
-struct conjunction : std::true_type {};
-
-template <typename B1>
-struct conjunction<B1> : B1 {};
-
-template <typename B1, typename... Bn>
-struct conjunction<B1, Bn...>
-    : std::conditional_t<static_cast<bool>(B1::value), conjunction<Bn...>, B1> {
-};
-
-// C++14 implementation of C++17's std::disjunction.
-//
-// Reference: https://en.cppreference.com/w/cpp/types/disjunction
-// Specification: https://wg21.link/meta.logical#itemdecl:2
-template <typename...>
-struct disjunction : std::false_type {};
-
-template <typename B1>
-struct disjunction<B1> : B1 {};
-
-template <typename B1, typename... Bn>
-struct disjunction<B1, Bn...>
-    : std::conditional_t<static_cast<bool>(B1::value), B1, disjunction<Bn...>> {
-};
-
-// C++14 implementation of C++17's std::negation.
-//
-// Reference: https://en.cppreference.com/w/cpp/types/negation
-// Specification: https://wg21.link/meta.logical#itemdecl:3
-template <typename B>
-struct negation : bool_constant<!static_cast<bool>(B::value)> {};
-
 namespace internal {
 
 // The indirection with std::is_enum<T> is required, because instantiating
@@ -246,7 +202,7 @@
 
 template <typename T>
 struct IsScopedEnumImpl<T, /*std::is_enum<T>::value=*/true>
-    : negation<std::is_convertible<T, std::underlying_type_t<T>>> {};
+    : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {};
 
 }  // namespace internal
 
diff --git a/base/template_util_unittest.cc b/base/template_util_unittest.cc
index 7f78f21..c9dc4b05 100644
--- a/base/template_util_unittest.cc
+++ b/base/template_util_unittest.cc
@@ -115,75 +115,6 @@
     !base::is_trivially_copy_constructible<std::vector<NoCopy>>::value,
     "is_trivially_copy_constructible<std::vector<T>> must be compiled.");
 
-using TrueT = std::true_type;
-using FalseT = std::false_type;
-
-// bool_constant
-static_assert(std::is_same<TrueT, bool_constant<true>>::value, "");
-static_assert(std::is_same<FalseT, bool_constant<false>>::value, "");
-
-struct True {
-  static constexpr bool value = true;
-};
-
-struct False {
-  static constexpr bool value = false;
-};
-
-// conjunction
-static_assert(conjunction<>::value, "");
-static_assert(conjunction<TrueT>::value, "");
-static_assert(!conjunction<FalseT>::value, "");
-
-static_assert(conjunction<TrueT, TrueT>::value, "");
-static_assert(!conjunction<TrueT, FalseT>::value, "");
-static_assert(!conjunction<FalseT, TrueT>::value, "");
-static_assert(!conjunction<FalseT, FalseT>::value, "");
-
-static_assert(conjunction<TrueT, TrueT, TrueT>::value, "");
-static_assert(!conjunction<TrueT, TrueT, FalseT>::value, "");
-static_assert(!conjunction<TrueT, FalseT, TrueT>::value, "");
-static_assert(!conjunction<TrueT, FalseT, FalseT>::value, "");
-static_assert(!conjunction<FalseT, TrueT, TrueT>::value, "");
-static_assert(!conjunction<FalseT, TrueT, FalseT>::value, "");
-static_assert(!conjunction<FalseT, FalseT, TrueT>::value, "");
-static_assert(!conjunction<FalseT, FalseT, FalseT>::value, "");
-
-static_assert(conjunction<True>::value, "");
-static_assert(!conjunction<False>::value, "");
-
-// disjunction
-static_assert(!disjunction<>::value, "");
-static_assert(disjunction<TrueT>::value, "");
-static_assert(!disjunction<FalseT>::value, "");
-
-static_assert(disjunction<TrueT, TrueT>::value, "");
-static_assert(disjunction<TrueT, FalseT>::value, "");
-static_assert(disjunction<FalseT, TrueT>::value, "");
-static_assert(!disjunction<FalseT, FalseT>::value, "");
-
-static_assert(disjunction<TrueT, TrueT, TrueT>::value, "");
-static_assert(disjunction<TrueT, TrueT, FalseT>::value, "");
-static_assert(disjunction<TrueT, FalseT, TrueT>::value, "");
-static_assert(disjunction<TrueT, FalseT, FalseT>::value, "");
-static_assert(disjunction<FalseT, TrueT, TrueT>::value, "");
-static_assert(disjunction<FalseT, TrueT, FalseT>::value, "");
-static_assert(disjunction<FalseT, FalseT, TrueT>::value, "");
-static_assert(!disjunction<FalseT, FalseT, FalseT>::value, "");
-
-static_assert(disjunction<True>::value, "");
-static_assert(!disjunction<False>::value, "");
-
-// negation
-static_assert(!negation<TrueT>::value, "");
-static_assert(negation<FalseT>::value, "");
-
-static_assert(!negation<True>::value, "");
-static_assert(negation<False>::value, "");
-
-static_assert(negation<negation<TrueT>>::value, "");
-static_assert(!negation<negation<FalseT>>::value, "");
-
 // is_scoped_enum
 TEST(TemplateUtil, IsScopedEnum) {
   static_assert(!is_scoped_enum<int>::value, "");
diff --git a/base/traits_bag.h b/base/traits_bag.h
index fb4e4a6..89d172d9 100644
--- a/base/traits_bag.h
+++ b/base/traits_bag.h
@@ -11,7 +11,6 @@
 #include <utility>
 
 #include "base/parameter_pack.h"
-#include "base/template_util.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 // A bag of Traits (structs / enums / etc...) can be an elegant alternative to
@@ -199,8 +198,8 @@
 
 // Note EmptyTrait is always regarded as valid to support filtering.
 template <class ValidTraits, class T>
-using IsValidTrait = disjunction<std::is_constructible<ValidTraits, T>,
-                                 std::is_same<T, EmptyTrait>>;
+using IsValidTrait = std::disjunction<std::is_constructible<ValidTraits, T>,
+                                      std::is_same<T, EmptyTrait>>;
 
 // Tests whether a given trait type is valid or invalid by testing whether it is
 // convertible to the provided ValidTraits type. To use, define a ValidTraits
@@ -220,7 +219,7 @@
 // };
 template <class ValidTraits, class... ArgTypes>
 using AreValidTraits =
-    bool_constant<all_of({IsValidTrait<ValidTraits, ArgTypes>::value...})>;
+    std::bool_constant<all_of({IsValidTrait<ValidTraits, ArgTypes>::value...})>;
 
 // Helper to make getting an enum from a trait more readable.
 template <typename Enum, typename... Args>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java
index 2adac55..04d0da3c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/suggestions/SwitchToTabTest.java
@@ -58,7 +58,6 @@
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.MenuUtils;
 import org.chromium.chrome.test.util.OmniboxTestUtils;
-import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.omnibox.AutocompleteMatch;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.content_public.browser.test.util.TestTouchUtils;
@@ -266,7 +265,6 @@
 
     @Test
     @MediumTest
-    @EnableFeatures("OmniboxTabSwitchSuggestions")
     public void testSwitchToTabSuggestion() throws InterruptedException {
         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
                 InstrumentationRegistry.getInstrumentation().getContext(),
@@ -294,7 +292,6 @@
     @Test
     @MediumTest
     @MinAndroidSdkLevel(Build.VERSION_CODES.N)
-    @EnableFeatures("OmniboxTabSwitchSuggestions")
     @CommandLineFlags.Add(ChromeSwitches.DISABLE_TAB_MERGING_FOR_TESTING)
     @FlakyTest(message = "https://crbug.com/1291136")
     public void testSwitchToTabSuggestionWhenIncognitoTabOnTop() throws InterruptedException {
@@ -334,7 +331,6 @@
 
     @Test
     @MediumTest
-    @EnableFeatures("OmniboxTabSwitchSuggestions")
     public void testNoSwitchToIncognitoTabFromNormalModel() throws InterruptedException {
         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
                 InstrumentationRegistry.getInstrumentation().getContext(),
@@ -362,7 +358,6 @@
 
     @Test
     @MediumTest
-    @EnableFeatures("OmniboxTabSwitchSuggestions")
     public void testSwitchToTabInSearchActivity() throws InterruptedException {
         mTestServer = EmbeddedTestServer.createAndStartHTTPSServer(
                 InstrumentationRegistry.getInstrumentation().getContext(),
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index de000f7..0d29ae6 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -981,7 +981,7 @@
       </message>
 
       <!-- ProcessSingleton -->
-      <if expr="is_linux or is_macosx">
+      <if expr="is_posix">
         <message name="IDS_PROFILE_IN_USE_POSIX" desc="Message shown when the browser cannot start because the profile is in use on a different host.">
           The profile appears to be in use by another Chromium process (<ph name="PROCESS_ID">$1<ex>12345</ex></ph>) on another computer (<ph name="HOST_NAME">$2<ex>example.com</ex></ph>). Chromium has locked the profile so that it doesn't get corrupted. If you are sure no other processes are using this profile, you can unlock the profile and relaunch Chromium.
         </message>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 0ccb94d..c57889d4 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1048,7 +1048,7 @@
       </message>
 
       <!-- ProcessSingleton -->
-      <if expr="is_linux or is_macosx">
+      <if expr="is_posix">
         <message name="IDS_PROFILE_IN_USE_POSIX" desc="Message shown when the browser cannot start because the profile is in use on a different host.">
           The profile appears to be in use by another Google Chrome process (<ph name="PROCESS_ID">$1<ex>12345</ex></ph>) on another computer (<ph name="HOST_NAME">$2<ex>example.com</ex></ph>).  Chrome has locked the profile so that it doesn't get corrupted.  If you are sure no other processes are using this profile, you can unlock the profile and relaunch Chrome.
         </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 83ed56ff..72d20d6 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -992,53 +992,33 @@
     {"Action Chips on URLs", kJourneysOmniboxActionOnURLsParams,
      std::size(kJourneysOmniboxActionOnURLsParams), nullptr},
 };
-const FeatureEntry::FeatureParam
-    kJourneysOnDeviceClusteringLabelingNoContentClusteringParams[] = {
-        {"should_label_clusters", "true"},
-        {"labels_from_entities", "true"},
-        {"content_clustering_enabled", "false"},
+const FeatureEntry::FeatureParam kJourneysLabelsWithEntitiesParams[] = {
+    {"labels_from_entities", "true"},
 };
 const FeatureEntry::FeatureParam
-    kJourneysOnDeviceClusteringLabelingNoHostnamesNoContentClusteringParams[] =
-        {
-            {"should_label_clusters", "true"},
-            {"labels_from_hostnames", "false"},
-            {"labels_from_entities", "true"},
-            {"content_clustering_enabled", "false"},
+    kJourneysLabelsWithEntitiesNoHostnamesParams[] = {
+        {"labels_from_hostnames", "false"},
+        {"labels_from_entities", "true"},
+};
+const FeatureEntry::FeatureVariation kJourneysLabelsVariations[] = {
+    {"With Entities", kJourneysLabelsWithEntitiesParams,
+     std::size(kJourneysLabelsWithEntitiesParams), nullptr},
+    {"With Entities, No Hostnames",
+     kJourneysLabelsWithEntitiesNoHostnamesParams,
+     std::size(kJourneysLabelsWithEntitiesNoHostnamesParams), nullptr},
 };
 const FeatureEntry::FeatureParam
     kJourneysOnDeviceClusteringNoContentClusteringParams[] = {
-        {"should_label_clusters", "false"},
         {"content_clustering_enabled", "false"},
 };
 const FeatureEntry::FeatureParam
-    kJourneysOnDeviceClusteringLabelingWithContentClusteringParams[] = {
-        {"should_label_clusters", "true"},
-        {"labels_from_entities", "true"},
-        {"content_clustering_enabled", "true"},
-};
-const FeatureEntry::FeatureParam
     kJourneysOnDeviceClusteringContentClusteringParams[] = {
-        {"should_label_clusters", "false"},
         {"content_clustering_enabled", "true"},
 };
 const FeatureEntry::FeatureVariation kJourneysOnDeviceClusteringVariations[] = {
-    {"Label Clusters and No Content Clustering",
-     kJourneysOnDeviceClusteringLabelingNoContentClusteringParams,
-     std::size(kJourneysOnDeviceClusteringLabelingNoContentClusteringParams),
-     nullptr},
-    {"Label Clusters, No Hostnames, & No Content Clustering",
-     kJourneysOnDeviceClusteringLabelingNoHostnamesNoContentClusteringParams,
-     std::size(
-         kJourneysOnDeviceClusteringLabelingNoHostnamesNoContentClusteringParams),
-     nullptr},
     {"No Content Clustering",
      kJourneysOnDeviceClusteringNoContentClusteringParams,
      std::size(kJourneysOnDeviceClusteringNoContentClusteringParams), nullptr},
-    {"Label Clusters and Content Clustering",
-     kJourneysOnDeviceClusteringLabelingWithContentClusteringParams,
-     std::size(kJourneysOnDeviceClusteringLabelingWithContentClusteringParams),
-     nullptr},
     {"Content Clustering", kJourneysOnDeviceClusteringContentClusteringParams,
      std::size(kJourneysOnDeviceClusteringContentClusteringParams), nullptr},
 };
@@ -3902,6 +3882,9 @@
     {"crostini-reset-lxd-db", flag_descriptions::kCrostiniResetLxdDbName,
      flag_descriptions::kCrostiniResetLxdDbDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kCrostiniResetLxdDb)},
+    {"terminal-dev", flag_descriptions::kTerminalDevName,
+     flag_descriptions::kTerminalDevDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kTerminalDev)},
     {"terminal-ssh", flag_descriptions::kTerminalSSHName,
      flag_descriptions::kTerminalSSHDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kTerminalSSH)},
@@ -4809,10 +4792,6 @@
      flag_descriptions::kOmniboxMostVisitedTilesName,
      flag_descriptions::kOmniboxMostVisitedTilesDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(omnibox::kMostVisitedTiles)},
-    {"omnibox-tab-switch-suggestions",
-     flag_descriptions::kOmniboxTabSwitchSuggestionsName,
-     flag_descriptions::kOmniboxTabSwitchSuggestionsDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(omnibox::kOmniboxTabSwitchSuggestions)},
     {"omnibox-pedals-android-batch1",
      flag_descriptions::kOmniboxPedalsAndroidBatch1Name,
      flag_descriptions::kOmniboxPedalsAndroidBatch1Description, kOsAndroid,
@@ -4870,10 +4849,6 @@
      flag_descriptions::kOmniboxShortBookmarkSuggestionsName,
      flag_descriptions::kOmniboxShortBookmarkSuggestionsDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kShortBookmarkSuggestions)},
-    {"omnibox-tab-switch-suggestions",
-     flag_descriptions::kOmniboxTabSwitchSuggestionsName,
-     flag_descriptions::kOmniboxTabSwitchSuggestionsDescription, kOsDesktop,
-     FEATURE_VALUE_TYPE(omnibox::kOmniboxTabSwitchSuggestions)},
     {"omnibox-pedals-batch3-nonenglish",
      flag_descriptions::kOmniboxPedalsBatch3NonEnglishName,
      flag_descriptions::kOmniboxPedalsBatch3NonEnglishDescription, kOsDesktop,
@@ -5062,6 +5037,12 @@
                                     kJourneysVariations,
                                     "HistoryJourneys")},
 
+    {"history-journeys-labels", flag_descriptions::kJourneysLabelsName,
+     flag_descriptions::kJourneysLabelsDescription, kOsDesktop | kOsAndroid,
+     FEATURE_WITH_PARAMS_VALUE_TYPE(history_clusters::internal::kJourneysLabels,
+                                    kJourneysLabelsVariations,
+                                    "HistoryJourneysLabels")},
+
     {"history-journeys-omnibox-action",
      flag_descriptions::kJourneysOmniboxActionName,
      flag_descriptions::kJourneysOmniboxActionDescription,
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index 31a8a5f..c2706baf 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -22,6 +22,7 @@
 #include "ui/android/color_utils_android.h"
 #include "ui/gfx/android/java_bitmap.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
@@ -153,7 +154,8 @@
   DCHECK_EQ(origin, origin.DeprecatedGetOriginAsURL());
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jstring> java_origin =
-      base::android::ConvertUTF8ToJavaString(env, origin.spec());
+      base::android::ConvertUTF8ToJavaString(
+          env, url::Origin::Create(origin).Serialize());
   return Java_ShortcutHelper_doesOriginContainAnyInstalledWebApk(env,
                                                                  java_origin);
 }
@@ -163,7 +165,8 @@
   DCHECK_EQ(origin, origin.DeprecatedGetOriginAsURL());
   JNIEnv* env = base::android::AttachCurrentThread();
   base::android::ScopedJavaLocalRef<jstring> java_origin =
-      base::android::ConvertUTF8ToJavaString(env, origin.spec());
+      base::android::ConvertUTF8ToJavaString(
+          env, url::Origin::Create(origin).Serialize());
   return Java_ShortcutHelper_doesOriginContainAnyInstalledTwa(env, java_origin);
 }
 
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index 59828be..84a2bde 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -166,8 +166,10 @@
   autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       portal_contents,
       autofill::ChromeAutofillClient::FromWebContents(portal_contents),
-      g_browser_process->GetApplicationLocale(),
-      autofill::BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      base::BindRepeating(
+          &autofill::BrowserDriverInitHook,
+          autofill::ChromeAutofillClient::FromWebContents(portal_contents),
+          g_browser_process->GetApplicationLocale()));
   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       portal_contents,
       autofill::ChromeAutofillClient::FromWebContents(portal_contents));
diff --git a/chrome/browser/android/webapk/webapk_install_service.cc b/chrome/browser/android/webapk/webapk_install_service.cc
index 203f1392..18077d8 100644
--- a/chrome/browser/android/webapk/webapk_install_service.cc
+++ b/chrome/browser/android/webapk/webapk_install_service.cc
@@ -128,10 +128,12 @@
       shortcut_info.manifest_url, shortcut_info.url, shortcut_info.short_name,
       primary_icon, is_primary_icon_maskable, result, webapk_package_name);
 
-  // If the install didn't definitely fail, we don't add a shortcut. This could
-  // happen if Play was busy with another install and this one is still queued
-  // (and hence might succeed in the future).
-  if (result != webapps::WebApkInstallResult::PROBABLE_FAILURE) {
+  // If WebAPK install failed, try adding a shortcut instead.
+  // If the install didn't definitely fail (i.e. PROBABLE_FAILURE), we don't add
+  // a shortcut. This could happen if Play was busy with another install and
+  // this one is still queued (and hence might succeed in the future).
+  if (result != webapps::WebApkInstallResult::SUCCESS &&
+      result != webapps::WebApkInstallResult::PROBABLE_FAILURE) {
     if (!web_contents)
       return;
 
diff --git a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
index c4c29e1..b1a564c 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_unittest.cc
@@ -287,8 +287,6 @@
   void SetUp() override {
     proxy_ = AppServiceProxyFactory::GetForProfile(&profile_);
 
-    auto* const provider = web_app::FakeWebAppProvider::Get(&profile_);
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(&profile_);
   }
 
diff --git a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
index 533dfce5..40b311e 100644
--- a/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/arc_apps_unittest.cc
@@ -147,8 +147,6 @@
     arc_bridge_service->file_system()->SetInstance(file_system_instance());
     arc::WaitForInstanceReady(arc_bridge_service->file_system());
 
-    auto* const provider = web_app::FakeWebAppProvider::Get(&profile_);
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
 
     app_service_test_.SetUp(&profile_);
diff --git a/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc b/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
index 035539d..462de8c5 100644
--- a/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
+++ b/chrome/browser/apps/app_service/webapk/webapk_install_task_unittest.cc
@@ -109,8 +109,6 @@
     testing::Test::SetUp();
     app_service_test_.SetUp(&profile_);
 
-    auto* const provider = web_app::FakeWebAppProvider::Get(&profile_);
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
 
     // Disable WebApkManager by policy. This allows us to unit test
diff --git a/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc b/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc
index 80fc4dd..1e36f33 100644
--- a/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc
+++ b/chrome/browser/apps/app_service/webapk/webapk_manager_unittest.cc
@@ -71,8 +71,6 @@
 
   void SetUp() override {
     testing::Test::SetUp();
-    auto* const provider = web_app::FakeWebAppProvider::Get(&profile_);
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
diff --git a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc
index 84aec30..4fe745f 100644
--- a/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/web_app_shim_manager_delegate_mac_unittest.cc
@@ -102,9 +102,6 @@
             /*protocol_handler_manager=*/nullptr,
             /*url_handler_manager*/ nullptr));
 
-    // FakeWebAppProvider should not wait for a test extension system, that is
-    // never started, to be ready.
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
 
     // Install a dummy app
diff --git a/chrome/browser/ash/crosapi/move_migrator.cc b/chrome/browser/ash/crosapi/move_migrator.cc
index 4a571b4a..6c5df67 100644
--- a/chrome/browser/ash/crosapi/move_migrator.cc
+++ b/chrome/browser/ash/crosapi/move_migrator.cc
@@ -617,6 +617,12 @@
 
   for (base::FilePath path = e.Next(); !path.empty(); path = e.Next()) {
     base::FilePath ash_path = original_profile_dir.Append(path.BaseName());
+    if (base::DirectoryExists(ash_path) && !DeletePathRecursively(ash_path)) {
+      PLOG(ERROR) << "Failed deleting " << ash_path.value();
+      return {TaskStatus::kMoveSplitItemsToOriginalDirMoveSplitItemsFailed,
+              errno};
+    }
+
     if (!base::Move(path, ash_path)) {
       PLOG(ERROR) << "Failed moving " << path.value() << " to "
                   << ash_path.value();
diff --git a/chrome/browser/ash/input_method/fake_suggestion_handler.cc b/chrome/browser/ash/input_method/fake_suggestion_handler.cc
index 15bb8844..4a5b3e7 100644
--- a/chrome/browser/ash/input_method/fake_suggestion_handler.cc
+++ b/chrome/browser/ash/input_method/fake_suggestion_handler.cc
@@ -42,7 +42,9 @@
 }
 
 void FakeSuggestionHandler::OnSuggestionsChanged(
-    const std::vector<std::string>& suggestions) {}
+    const std::vector<std::string>& suggestions) {
+  last_on_suggestion_changed_event_suggestions_ = suggestions;
+}
 
 bool FakeSuggestionHandler::SetButtonHighlighted(
     int context_id,
@@ -54,7 +56,9 @@
 }
 
 void FakeSuggestionHandler::ClickButton(
-    const ui::ime::AssistiveWindowButton& button) {}
+    const ui::ime::AssistiveWindowButton& button) {
+  last_clicked_button_ = button.id;
+}
 
 bool FakeSuggestionHandler::AcceptSuggestionCandidate(
     int context_id,
diff --git a/chrome/browser/ash/input_method/fake_suggestion_handler.h b/chrome/browser/ash/input_method/fake_suggestion_handler.h
index b1faa2f..9f3923a 100644
--- a/chrome/browser/ash/input_method/fake_suggestion_handler.h
+++ b/chrome/browser/ash/input_method/fake_suggestion_handler.h
@@ -54,11 +54,15 @@
   bool GetShowingSuggestion() { return showing_suggestion_; }
   bool GetAcceptedSuggestion() { return accepted_suggestion_; }
   bool GetDismissedSuggestion() { return dismissed_suggestion_; }
-  std::vector<std::u16string> GetAnnouncements() { return announcements_; }
   bool GetHighlightedSuggestion() { return highlighted_suggestion_; }
+  std::vector<std::u16string> GetAnnouncements() { return announcements_; }
+  std::vector<std::string> GetLastOnSuggestionChangedEventSuggestions() {
+    return last_on_suggestion_changed_event_suggestions_;
+  }
   ui::ime::SuggestionDetails GetLastSuggestionDetails() {
     return last_suggestion_details_;
   }
+  ui::ime::ButtonId GetLastClickedButton() { return last_clicked_button_; };
 
  private:
   int context_id_ = 0;
@@ -69,7 +73,9 @@
   bool dismissed_suggestion_ = false;
   bool highlighted_suggestion_ = false;
   std::vector<std::u16string> announcements_;
+  std::vector<std::string> last_on_suggestion_changed_event_suggestions_;
   ui::ime::SuggestionDetails last_suggestion_details_;
+  ui::ime::ButtonId last_clicked_button_ = ui::ime::ButtonId::kNone;
 };
 
 }  // namespace input_method
diff --git a/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc b/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc
index f983143b..383df4c 100644
--- a/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc
+++ b/chrome/browser/ash/input_method/personal_info_suggester_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ash/input_method/fake_suggestion_handler.h"
 #include "chrome/browser/ash/input_method/ui/suggestion_details.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #include "chrome/test/base/testing_profile.h"
@@ -20,121 +21,18 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 
 namespace ash {
 namespace input_method {
-namespace {
 
 using ime::TextSuggestion;
 using ime::TextSuggestionMode;
 using ime::TextSuggestionType;
-
-// TODO(crbug/1201529): Update this unit test to use `FakeSuggestionHandler`
-// instead.
-class TestSuggestionHandler : public SuggestionHandlerInterface {
- public:
-  bool DismissSuggestion(int context_id, std::string* error) override {
-    suggestion_text_ = base::EmptyString16();
-    previous_suggestions_.clear();
-    confirmed_length_ = 0;
-    suggestion_accepted_ = false;
-    return true;
-  }
-
-  bool SetSuggestion(int context_id,
-                     const ui::ime::SuggestionDetails& details,
-                     std::string* error) override {
-    suggestion_text_ = details.text;
-    confirmed_length_ = details.confirmed_length;
-    show_accept_annotation_ = details.show_accept_annotation;
-    show_setting_link_ = details.show_setting_link;
-    return true;
-  }
-
-  bool AcceptSuggestion(int context_id, std::string* error) override {
-    suggestion_text_ = base::EmptyString16();
-    confirmed_length_ = 0;
-    suggestion_accepted_ = true;
-    return true;
-  }
-
-  void VerifySuggestion(const std::u16string text,
-                        const size_t confirmed_length) {
-    EXPECT_EQ(suggestion_text_, text);
-    EXPECT_EQ(confirmed_length_, confirmed_length);
-    suggestion_text_ = base::EmptyString16();
-    confirmed_length_ = 0;
-  }
-
-  void VerifySuggestionDispatchedToExtension(
-      const std::vector<std::string>& suggestions) {
-    EXPECT_THAT(previous_suggestions_, testing::ContainerEq(suggestions));
-  }
-
-  void OnSuggestionsChanged(
-      const std::vector<std::string>& suggestions) override {
-    previous_suggestions_ = suggestions;
-  }
-
-  void ClickButton(const ui::ime::AssistiveWindowButton& button) override {
-    button_clicked_ = button.id;
-  }
-
-  bool SetButtonHighlighted(int context_id,
-                            const ui::ime::AssistiveWindowButton& button,
-                            bool highlighted,
-                            std::string* error) override {
-    return false;
-  }
-
-  bool AcceptSuggestionCandidate(int context_id,
-                                 const std::u16string& candidate,
-                                 std::string* error) override {
-    return false;
-  }
-
-  bool SetAssistiveWindowProperties(
-      int context_id,
-      const AssistiveWindowProperties& assistive_window,
-      std::string* error) override {
-    return false;
-  }
-
-  void VerifyShowAnnotation(const bool show_accept_annotation) {
-    EXPECT_EQ(show_accept_annotation_, show_accept_annotation);
-  }
-  void VerifyShowSettingLink(const bool show_setting_link) {
-    EXPECT_EQ(show_setting_link_, show_setting_link);
-  }
-  void VerifyButtonClicked(const ui::ime::ButtonId id) {
-    EXPECT_EQ(button_clicked_, id);
-  }
-
-  bool IsSuggestionAccepted() { return suggestion_accepted_; }
-
-  void Announce(const std::u16string& message) override {
-    announced_text_ = message;
-  }
-
-  void VerifyAnnouncement(const std::u16string& expected_text) {
-    EXPECT_EQ(announced_text_, expected_text);
-  }
-
- private:
-  std::u16string suggestion_text_;
-  size_t confirmed_length_ = 0;
-  bool show_accept_annotation_ = false;
-  bool show_setting_link_ = false;
-  bool suggestion_accepted_ = false;
-  ui::ime::ButtonId button_clicked_ = ui::ime::ButtonId::kNone;
-  std::vector<std::string> previous_suggestions_;
-  std::u16string announced_text_ = u"";
-};
-
-}  // namespace
+using ::testing::ElementsAre;
 
 class PersonalInfoSuggesterTest : public testing::Test {
  protected:
@@ -144,7 +42,7 @@
 
   void SetUp() override {
     profile_ = std::make_unique<TestingProfile>();
-    suggestion_handler_ = std::make_unique<TestSuggestionHandler>();
+    suggestion_handler_ = std::make_unique<FakeSuggestionHandler>();
 
     personal_data_ = std::make_unique<autofill::TestPersonalDataManager>();
     personal_data_->SetPrefService(autofill_client_.GetPrefs());
@@ -167,7 +65,7 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
   std::unique_ptr<TestingProfile> profile_;
-  std::unique_ptr<TestSuggestionHandler> suggestion_handler_;
+  std::unique_ptr<FakeSuggestionHandler> suggestion_handler_;
   std::unique_ptr<PersonalInfoSuggester> suggester_;
   std::unique_ptr<ChromeKeyboardControllerClient>
       chrome_keyboard_controller_client_;
@@ -192,16 +90,23 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
+
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"My email is: ", 13, 13);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"hi, my email: ", 14, 14);
-  suggestion_handler_->VerifySuggestion(email_, 0);
-}
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
+}  // namespace
 
 TEST_F(PersonalInfoSuggesterTest, SuggestsEmailWithMultilineText) {
   base::test::ScopedFeatureList feature_list;
@@ -212,11 +117,15 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"\nmy email is ", 13, 13);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"Hey\nMan\nmy email is ", 20, 20);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 }
 
@@ -229,14 +138,14 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"\nmy email is \n", 14, 14);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"\nmy email is \n ", 15, 15);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"Hey\nMan\nmy email is \nhey ", 25,
                                             25);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestWhenContainsCursorSelection) {
@@ -248,7 +157,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 10);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestWhenStringDoesntEndWithSpace) {
@@ -260,7 +169,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is", 11, 11);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestWhenCursorNotEndOfLine) {
@@ -272,7 +181,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 11, 11);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, SuggestWhenEndOfLineWhenNewLineExist) {
@@ -285,7 +194,9 @@
 
   suggester_->TrySuggestWithSurroundingText(u"my email is \nBOTTOM TEXT", 12,
                                             12);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestEmailWhenFlagIsDisabled) {
@@ -297,7 +208,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestEmailWhenPrefixDoesNotMatch) {
@@ -309,10 +220,10 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is John", 16, 16);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"our email is: ", 14, 14);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestWhenVirtualKeyboardEnabled) {
@@ -325,7 +236,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest,
@@ -339,8 +250,8 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifySuggestionDispatchedToExtension(
-      std::vector<std::string>{base::UTF16ToUTF8(email_)});
+  EXPECT_THAT(suggestion_handler_->GetLastOnSuggestionChangedEventSuggestions(),
+              ElementsAre(base::UTF16ToUTF8(email_)));
 }
 
 TEST_F(PersonalInfoSuggesterTest, SuggestsNames) {
@@ -358,19 +269,29 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my first name is ", 17, 17);
-  suggestion_handler_->VerifySuggestion(first_name_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), first_name_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
+
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my last name is: ", 17, 17);
-  suggestion_handler_->VerifySuggestion(last_name_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), last_name_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my name is ", 11, 11);
-  suggestion_handler_->VerifySuggestion(full_name_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), full_name_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
+
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"Hmm... my FULL name: ", 21, 21);
-  suggestion_handler_->VerifySuggestion(full_name_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), full_name_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
 }
 
 TEST_F(PersonalInfoSuggesterTest, SuggestsNamesButInsufficientData) {
@@ -406,13 +327,13 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my first name is ", 17, 17);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"my last name is: ", 17, 17);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"my name is ", 11, 11);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestNamesWhenPrefixDoesNotMatch) {
@@ -430,16 +351,16 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"our first name is ", 18, 18);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"our last name is: ", 18, 18);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"our name is ", 12, 12);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"our full name: ", 15, 15);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, SuggestsAddress) {
@@ -464,23 +385,34 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my address is ", 14, 14);
-  suggestion_handler_->VerifySuggestion(address_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), address_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
+
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"our address is: ", 16, 16);
-  suggestion_handler_->VerifySuggestion(address_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), address_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my shipping address: ", 21, 21);
-  suggestion_handler_->VerifySuggestion(address_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), address_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"our billing address is ", 23, 23);
-  suggestion_handler_->VerifySuggestion(address_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), address_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my current address: ", 20, 20);
-  suggestion_handler_->VerifySuggestion(address_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), address_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestAddressWhenFlagIsDisabled) {
@@ -505,7 +437,7 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my address is ", 14, 14);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestAddressWhenPrefixDoesNotMatch) {
@@ -530,13 +462,13 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my address ", 11, 11);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"my last address is: ", 20, 20);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"our address number is ", 22, 22);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, SuggestsPhoneNumber) {
@@ -552,23 +484,33 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my phone number is ", 19, 19);
-  suggestion_handler_->VerifySuggestion(phone_number_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), phone_number_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my number is ", 13, 13);
-  suggestion_handler_->VerifySuggestion(phone_number_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), phone_number_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my mobile number is: ", 21, 21);
-  suggestion_handler_->VerifySuggestion(phone_number_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), phone_number_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my number: ", 11, 11);
-  suggestion_handler_->VerifySuggestion(phone_number_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), phone_number_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
 
   suggester_->TrySuggestWithSurroundingText(u"my telephone number is ", 23, 23);
-  suggestion_handler_->VerifySuggestion(phone_number_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), phone_number_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntSuggestPhoneNumberWhenFlagIsDisabled) {
@@ -584,7 +526,7 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"my phone number is ", 20, 20);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest,
@@ -601,16 +543,16 @@
   personal_data_->AddProfile(autofill_profile);
 
   suggester_->TrySuggestWithSurroundingText(u"our phone number is ", 20, 20);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"my number ", 10, 10);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"my number phone is: ", 20, 20);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 
   suggester_->TrySuggestWithSurroundingText(u"my phone phone: ", 16, 16);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, AcceptsSuggestionWithDownEnter) {
@@ -625,8 +567,9 @@
   SendKeyboardEvent(ui::DomCode::ARROW_DOWN);
   SendKeyboardEvent(ui::DomCode::ENTER);
 
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
-  EXPECT_TRUE(suggestion_handler_->IsSuggestionAccepted());
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
+
+  EXPECT_TRUE(suggestion_handler_->GetAcceptedSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, AcceptsSuggestionWithUpEnter) {
@@ -644,8 +587,9 @@
   SendKeyboardEvent(ui::DomCode::ARROW_UP);
   SendKeyboardEvent(ui::DomCode::ENTER);
 
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
-  EXPECT_TRUE(suggestion_handler_->IsSuggestionAccepted());
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
+
+  EXPECT_TRUE(suggestion_handler_->GetAcceptedSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, DismissesSuggestion) {
@@ -661,8 +605,9 @@
 
   suggester_->TrySuggestWithSurroundingText(u"my name is ", 11, 11);
   SendKeyboardEvent(ui::DomCode::ESCAPE);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
-  EXPECT_FALSE(suggestion_handler_->IsSuggestionAccepted());
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
+
+  EXPECT_FALSE(suggestion_handler_->GetAcceptedSuggestion());
 }
 
 TEST_F(PersonalInfoSuggesterTest, SuggestsWithConfirmedLength) {
@@ -679,7 +624,9 @@
 
   suggester_->TrySuggestWithSurroundingText(u"my phone number is ", 19, 19);
   suggester_->TrySuggestWithSurroundingText(u"my phone number is 16", 21, 21);
-  suggestion_handler_->VerifySuggestion(phone_number_, 2);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), phone_number_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 2);
 }
 
 TEST_F(PersonalInfoSuggesterTest, AnnouncesSpokenFeedbackWhenChromeVoxIsOn) {
@@ -694,23 +641,26 @@
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
   task_environment_.FastForwardBy(base::Milliseconds(200));
-  suggestion_handler_->VerifyAnnouncement(
-      u"Personal info suggested. Press down arrow to access; escape to "
-      u"ignore.");
+  EXPECT_EQ(suggestion_handler_->GetAnnouncements().back(),
+            u"Personal info suggested. Press down "
+            u"arrow to access; escape to ignore.");
 
   SendKeyboardEvent(ui::DomCode::ARROW_DOWN);
   SendKeyboardEvent(ui::DomCode::ENTER);
   task_environment_.FastForwardBy(base::Milliseconds(200));
-  suggestion_handler_->VerifyAnnouncement(u"Suggestion inserted.");
+  EXPECT_EQ(suggestion_handler_->GetAnnouncements().back(),
+            u"Suggestion inserted.");
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
   task_environment_.FastForwardBy(base::Milliseconds(1500));
-  suggestion_handler_->VerifyAnnouncement(
-      u"Personal info suggested. Press down arrow to access; escape to "
-      u"ignore.");
+  EXPECT_THAT(suggestion_handler_->GetAnnouncements().back(),
+              u"Personal info suggested. Press down arrow to access; escape to "
+              u"ignore.");
+
   SendKeyboardEvent(ui::DomCode::ESCAPE);
   task_environment_.FastForwardBy(base::Milliseconds(200));
-  suggestion_handler_->VerifyAnnouncement(u"Suggestion dismissed.");
+  EXPECT_EQ(suggestion_handler_->GetAnnouncements().back(),
+            u"Suggestion dismissed.");
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntShowAnnotationAfterMaxAcceptanceCount) {
@@ -723,10 +673,12 @@
     suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
     SendKeyboardEvent(ui::DomCode::ARROW_DOWN);
     SendKeyboardEvent(ui::DomCode::ENTER);
-    suggestion_handler_->VerifyShowAnnotation(true);
+    EXPECT_TRUE(
+        suggestion_handler_->GetLastSuggestionDetails().show_accept_annotation);
   }
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifyShowAnnotation(false);
+  EXPECT_FALSE(
+      suggestion_handler_->GetLastSuggestionDetails().show_accept_annotation);
 }
 
 TEST_F(PersonalInfoSuggesterTest, ShowsSettingLink) {
@@ -743,10 +695,12 @@
     suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
     // Dismiss suggestion.
     SendKeyboardEvent(ui::DomCode::ESCAPE);
-    suggestion_handler_->VerifyShowSettingLink(true);
+    EXPECT_TRUE(
+        suggestion_handler_->GetLastSuggestionDetails().show_setting_link);
   }
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifyShowSettingLink(false);
+  EXPECT_FALSE(
+      suggestion_handler_->GetLastSuggestionDetails().show_setting_link);
 }
 
 TEST_F(PersonalInfoSuggesterTest, DoesntShowSettingLinkAfterAcceptance) {
@@ -758,13 +712,16 @@
   DictionaryPrefUpdate update(profile_->GetPrefs(),
                               prefs::kAssistiveInputFeatureSettings);
   update->SetIntKey(kPersonalInfoSuggesterShowSettingCount, 0);
+
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifyShowSettingLink(true);
+  EXPECT_TRUE(
+      suggestion_handler_->GetLastSuggestionDetails().show_setting_link);
   // Accept suggestion.
   SendKeyboardEvent(ui::DomCode::ARROW_DOWN);
   SendKeyboardEvent(ui::DomCode::ENTER);
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifyShowSettingLink(false);
+  EXPECT_FALSE(
+      suggestion_handler_->GetLastSuggestionDetails().show_setting_link);
 }
 
 TEST_F(PersonalInfoSuggesterTest, ClicksSettingsWithDownDownEnter) {
@@ -784,8 +741,8 @@
   SendKeyboardEvent(ui::DomCode::ARROW_DOWN);
   SendKeyboardEvent(ui::DomCode::ENTER);
 
-  suggestion_handler_->VerifyButtonClicked(
-      ui::ime::ButtonId::kSmartInputsSettingLink);
+  EXPECT_EQ(suggestion_handler_->GetLastClickedButton(),
+            ui::ime::ButtonId::kSmartInputsSettingLink);
 }
 
 TEST_F(PersonalInfoSuggesterTest, ClicksSettingsWithUpEnter) {
@@ -804,8 +761,8 @@
   SendKeyboardEvent(ui::DomCode::ARROW_UP);
   SendKeyboardEvent(ui::DomCode::ENTER);
 
-  suggestion_handler_->VerifyButtonClicked(
-      ui::ime::ButtonId::kSmartInputsSettingLink);
+  EXPECT_EQ(suggestion_handler_->GetLastClickedButton(),
+            ui::ime::ButtonId::kSmartInputsSettingLink);
 }
 
 TEST_F(PersonalInfoSuggesterTest,
@@ -818,7 +775,9 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   EXPECT_TRUE(suggester_->HasSuggestions());
 }
 
@@ -832,7 +791,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"", 0, 0);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
   EXPECT_FALSE(suggester_->HasSuggestions());
 }
 
@@ -846,7 +805,9 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"my email is ", 12, 12);
-  suggestion_handler_->VerifySuggestion(email_, 0);
+  EXPECT_TRUE(suggestion_handler_->GetShowingSuggestion());
+  EXPECT_EQ(suggestion_handler_->GetSuggestionText(), email_);
+  EXPECT_EQ(suggestion_handler_->GetConfirmedLength(), 0);
   EXPECT_EQ(suggester_->GetSuggestions(),
             (std::vector<TextSuggestion>{TextSuggestion{
                 .mode = TextSuggestionMode::kPrediction,
@@ -864,7 +825,7 @@
   profile_->set_profile_name(base::UTF16ToUTF8(email_));
 
   suggester_->TrySuggestWithSurroundingText(u"", 0, 0);
-  suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0);
+  EXPECT_FALSE(suggestion_handler_->GetShowingSuggestion());
   EXPECT_TRUE(suggester_->GetSuggestions().empty());
 }
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h b/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h
index b8570d73..f264b6ff 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h
@@ -54,6 +54,8 @@
 
  protected:
   // Exposed for testing.
+  // TODO(b/227674947): Delete LaunchEasyUnlockSettings after Sign in with Smart
+  // Lock is removed.
   virtual void LaunchEasyUnlockSettings();
   virtual void LaunchMultiDeviceSettings();
   virtual void LockScreen();
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller_unittest.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller_unittest.cc
index 109f5cc..45dbf36 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller_unittest.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller_unittest.cc
@@ -76,6 +76,8 @@
           test_profile));
 
   // Check returns false when kSmartLockSignInRemoved isn't enabled.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(ash::features::kSmartLockSignInRemoved);
   pref_service->SetBoolean(
       proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled, true);
   pref_service->SetBoolean(prefs::kHasSeenSmartLockSignInRemovedNotification,
@@ -86,8 +88,8 @@
 
   // Check returns false when kHasSeenSmartLockSignInRemovedNotification is
   // true.
-  base::test::ScopedFeatureList feature_list(
-      ash::features::kSmartLockSignInRemoved);
+  feature_list.Reset();
+  feature_list.InitAndEnableFeature(ash::features::kSmartLockSignInRemoved);
   pref_service->SetBoolean(prefs::kHasSeenSmartLockSignInRemovedNotification,
                            true);
   ASSERT_FALSE(
@@ -142,6 +144,17 @@
   EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
 
   // Clicking notification button should launch settings.
+  EXPECT_CALL(*notification_controller_, LaunchMultiDeviceSettings());
+  notification->delegate()->Click(0, absl::nullopt);
+
+  // Clicking the notification itself should also launch settings.
+  EXPECT_CALL(*notification_controller_, LaunchMultiDeviceSettings());
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
+
+  // When kSmartLockSignInRemoved is disabled, LaunchEasyUnlockSettings() should
+  // be called instead.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(ash::features::kSmartLockSignInRemoved);
   EXPECT_CALL(*notification_controller_, LaunchEasyUnlockSettings());
   notification->delegate()->Click(0, absl::nullopt);
 
@@ -161,16 +174,23 @@
   ASSERT_EQ(2u, notification->buttons().size());
   EXPECT_EQ(message_center::SYSTEM_PRIORITY, notification->priority());
 
+  // Clicking the notification itself should do nothing.
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
+
   // Clicking 1st notification button should lock screen settings.
   EXPECT_CALL(*notification_controller_, LockScreen());
   notification->delegate()->Click(0, absl::nullopt);
 
   // Clicking 2nd notification button should launch settings.
-  EXPECT_CALL(*notification_controller_, LaunchEasyUnlockSettings());
+  EXPECT_CALL(*notification_controller_, LaunchMultiDeviceSettings());
   notification->delegate()->Click(1, absl::nullopt);
 
-  // Clicking the notification itself should do nothing.
-  notification->delegate()->Click(absl::nullopt, absl::nullopt);
+  // When kSmartLockSignInRemoved is disabled, LaunchEasyUnlockSettings() should
+  // be called instead.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(ash::features::kSmartLockSignInRemoved);
+  EXPECT_CALL(*notification_controller_, LaunchEasyUnlockSettings());
+  notification->delegate()->Click(1, absl::nullopt);
 }
 
 TEST_F(EasyUnlockNotificationControllerTest,
@@ -189,6 +209,18 @@
   EXPECT_NE(std::string::npos, notification->message().find(kPhoneName16));
 
   // Clicking notification button should launch settings.
+  EXPECT_CALL(*notification_controller_, LaunchMultiDeviceSettings());
+  notification->delegate()->Click(0, absl::nullopt);
+
+  // Clicking the notification itself should also launch settings.
+  EXPECT_CALL(*notification_controller_, LaunchMultiDeviceSettings());
+  notification->delegate()->Click(absl::nullopt, absl::nullopt);
+
+  // When kSmartLockSignInRemoved is disabled, LaunchEasyUnlockSettings() should
+  // be called instead.
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(ash::features::kSmartLockSignInRemoved);
+  // Clicking notification button should launch settings.
   EXPECT_CALL(*notification_controller_, LaunchEasyUnlockSettings());
   notification->delegate()->Click(0, absl::nullopt);
 
diff --git a/chrome/browser/ash/note_taking_helper_unittest.cc b/chrome/browser/ash/note_taking_helper_unittest.cc
index fff9869..253dbe8 100644
--- a/chrome/browser/ash/note_taking_helper_unittest.cc
+++ b/chrome/browser/ash/note_taking_helper_unittest.cc
@@ -297,10 +297,6 @@
   }
 
   void InitWebAppProvider() {
-    auto* provider = web_app::FakeWebAppProvider::Get(profile());
-    // FakeWebAppProvider should not wait for a test extension system, that is
-    // never started, to be ready.
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
diff --git a/chrome/browser/ash/release_notes/release_notes_storage.cc b/chrome/browser/ash/release_notes/release_notes_storage.cc
index bb470ea6..aa8791b 100644
--- a/chrome/browser/ash/release_notes/release_notes_storage.cc
+++ b/chrome/browser/ash/release_notes/release_notes_storage.cc
@@ -28,7 +28,7 @@
 // This stores the latest milestone with new Release Notes content. If the last
 // milestone the user has seen the notification is before this, a new
 // notification will be shown.
-constexpr int kLastChromeVersionWithReleaseNotes = 102;
+constexpr int kLastChromeVersionWithReleaseNotes = 103;
 constexpr int kTimesToShowSuggestionChip = 3;
 
 int GetMilestone() {
diff --git a/chrome/browser/ash/web_applications/terminal_source.cc b/chrome/browser/ash/web_applications/terminal_source.cc
index 4af8378..8ac46c5 100644
--- a/chrome/browser/ash/web_applications/terminal_source.cc
+++ b/chrome/browser/ash/web_applications/terminal_source.cc
@@ -18,11 +18,14 @@
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/crostini/crostini_pref_names.h"
 #include "chrome/browser/ash/crostini/crostini_terminal.h"
+#include "chrome/browser/ash/file_manager/path_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/common/channel_info.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/prefs/pref_service.h"
+#include "components/version_info/channel.h"
 #include "net/base/mime_util.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "third_party/zlib/google/compression_utils.h"
@@ -33,18 +36,34 @@
     FILE_PATH_LITERAL("/usr/share/chromeos-assets/crosh_builtin");
 constexpr char kDefaultMime[] = "text/html";
 
-void ReadFile(const std::string& relative_path,
-              content::URLDataSource::GotDataCallback callback) {
-  std::string content;
-  base::FilePath path = base::FilePath(kTerminalRoot).Append(relative_path);
-  // First look for uncompressed resource, then try for gzipped file.
-  bool result = base::ReadFileToString(path, &content);
+// Attempts to read |path| as plain file.  If read fails, attempts to read
+// |path|.gz and decompress. Returns true if either file is read ok.
+bool ReadUncompressedOrGzip(base::FilePath path, std::string* content) {
+  bool result = base::ReadFileToString(path, content);
   if (!result) {
     result =
-        base::ReadFileToString(base::FilePath(path.value() + ".gz"), &content);
-    std::string uncompressed;
-    result = compression::GzipUncompress(content, &uncompressed);
-    content = std::move(uncompressed);
+        base::ReadFileToString(base::FilePath(path.value() + ".gz"), content);
+    result = compression::GzipUncompress(*content, content);
+  }
+  return result;
+}
+
+void ReadFile(const base::FilePath downloads,
+              const std::string& relative_path,
+              content::URLDataSource::GotDataCallback callback) {
+  base::FilePath path;
+  std::string content;
+  bool result = false;
+
+  // If chrome://flags#terminal-dev set on dev channel, check Downloads.
+  if (chrome::GetChannel() <= version_info::Channel::DEV &&
+      base::FeatureList::IsEnabled(chromeos::features::kTerminalDev)) {
+    path = downloads.Append("crosh_builtin").Append(relative_path);
+    result = ReadUncompressedOrGzip(path, &content);
+  }
+  if (!result) {
+    path = base::FilePath(kTerminalRoot).Append(relative_path);
+    result = ReadUncompressedOrGzip(path, &content);
   }
 
   // Terminal gets files from /usr/share/chromeos-assets/crosh-builtin.
@@ -99,7 +118,8 @@
     : profile_(profile),
       source_(source),
       default_file_(default_file),
-      ssh_allowed_(ssh_allowed) {
+      ssh_allowed_(ssh_allowed),
+      downloads_(file_manager::util::GetDownloadsFolderForProfile(profile)) {
   auto* webui_allowlist = WebUIAllowlist::GetOrCreate(profile);
   const url::Origin terminal_origin = url::Origin::Create(GURL(source));
   CHECK(!terminal_origin.opaque());
@@ -110,6 +130,8 @@
         ContentSettingsType::SOUND}) {
     webui_allowlist->RegisterAutoGrantedPermission(terminal_origin, permission);
   }
+  webui_allowlist->RegisterAutoGrantedThirdPartyCookies(
+      terminal_origin, {ContentSettingsPattern::Wildcard()});
 }
 
 TerminalSource::~TerminalSource() = default;
@@ -118,12 +140,6 @@
   return source_;
 }
 
-#if !BUILDFLAG(OPTIMIZE_WEBUI)
-bool TerminalSource::AllowCaching() {
-  return false;
-}
-#endif
-
 void TerminalSource::StartDataRequest(
     const GURL& url,
     const content::WebContents::Getter& wc_getter,
@@ -141,7 +157,7 @@
 
   base::ThreadPool::PostTask(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
-      base::BindOnce(&ReadFile, path, std::move(callback)));
+      base::BindOnce(&ReadFile, downloads_, path, std::move(callback)));
 }
 
 std::string TerminalSource::GetMimeType(const std::string& path) {
diff --git a/chrome/browser/ash/web_applications/terminal_source.h b/chrome/browser/ash/web_applications/terminal_source.h
index 2bb9b29..6646785f87 100644
--- a/chrome/browser/ash/web_applications/terminal_source.h
+++ b/chrome/browser/ash/web_applications/terminal_source.h
@@ -9,8 +9,6 @@
 
 #include <string>
 
-#include "build/buildflag.h"
-#include "chrome/common/buildflags.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/template_expressions.h"
@@ -39,9 +37,6 @@
 
   // content::URLDataSource:
   std::string GetSource() override;
-#if !BUILDFLAG(OPTIMIZE_WEBUI)
-  bool AllowCaching() override;
-#endif
   void StartDataRequest(
       const GURL& url,
       const content::WebContents::Getter& wc_getter,
@@ -56,6 +51,7 @@
   std::string source_;
   std::string default_file_;
   const bool ssh_allowed_;
+  const base::FilePath downloads_;
   ui::TemplateReplacements replacements_;
 };
 
diff --git a/chrome/browser/autofill/autofill_browsertest.cc b/chrome/browser/autofill/autofill_browsertest.cc
index e1f18cf..653813d 100644
--- a/chrome/browser/autofill/autofill_browsertest.cc
+++ b/chrome/browser/autofill/autofill_browsertest.cc
@@ -792,11 +792,10 @@
     MockPrerenderBrowserAutofillManager(AutofillDriver* driver,
                                         AutofillClient* client,
                                         content::RenderFrameHost* rfh)
-        : BrowserAutofillManager(
-              driver,
-              client,
-              "en-US",
-              BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {
+        : BrowserAutofillManager(driver,
+                                 client,
+                                 "en-US",
+                                 EnableDownloadManager(false)) {
       // We need to set these expectations immediately to catch any premature
       // calls while prerendering.
       if (rfh->GetLifecycleState() ==
@@ -904,11 +903,10 @@
     MockFormSubmissionAutofillManager(AutofillDriver* driver,
                                       AutofillClient* client,
                                       content::RenderFrameHost* rhf)
-        : BrowserAutofillManager(
-              driver,
-              client,
-              "en-US",
-              BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {}
+        : BrowserAutofillManager(driver,
+                                 client,
+                                 "en-US",
+                                 EnableDownloadManager(false)) {}
     MOCK_METHOD(void,
                 OnFormSubmittedImpl,
                 (const FormData&, bool, mojom::SubmissionSource),
diff --git a/chrome/browser/autofill/content_autofill_driver_browsertest.cc b/chrome/browser/autofill/content_autofill_driver_browsertest.cc
index b5c02f77..8969cd7 100644
--- a/chrome/browser/autofill/content_autofill_driver_browsertest.cc
+++ b/chrome/browser/autofill/content_autofill_driver_browsertest.cc
@@ -96,8 +96,9 @@
         ContentAutofillDriverFactory::
             kContentAutofillDriverFactoryWebContentsUserDataKey);
     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
-        web_contents, &autofill_client(), "en-US",
-        BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+        web_contents, &autofill_client(),
+        base::BindRepeating(&autofill::BrowserDriverInitHook,
+                            &autofill_client(), "en-US"));
 
     // Serve both a.com and b.com (and any other domain).
     host_resolver()->AddRule("*", "127.0.0.1");
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index 1724892..ea26359 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1128,7 +1128,6 @@
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_LACROS)
     web_app_provider_ = web_app::FakeWebAppProvider::Get(profile_.get());
-    web_app_provider_->SkipAwaitingExtensionSystem();
     web_app_provider_->StartWithSubsystems();
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_LACROS)
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 3b5df61e..978c8df 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -336,6 +336,7 @@
     "//chromeos/network",
     "//chromeos/printing",
     "//chromeos/scanning",
+    "//chromeos/services/assistant:lib",
     "//chromeos/services/assistant/public/cpp",
     "//chromeos/services/bluetooth_config:in_process_bluetooth_config",
     "//chromeos/services/cros_healthd/public/cpp",
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 3c1034a..3d49e2af 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -139,6 +139,7 @@
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "chromeos/metrics/login_event_recorder.h"
 #include "chromeos/printing/printer_configuration.h"
+#include "chromeos/services/assistant/assistant_manager_service_impl.h"
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
@@ -3073,14 +3074,26 @@
     AutotestPrivateEnableAssistantAndWaitForReadyFunction() = default;
 
 AutotestPrivateEnableAssistantAndWaitForReadyFunction::
-    ~AutotestPrivateEnableAssistantAndWaitForReadyFunction() {
-  ash::AssistantState::Get()->RemoveObserver(this);
-}
+    ~AutotestPrivateEnableAssistantAndWaitForReadyFunction() = default;
 
 ExtensionFunction::ResponseAction
 AutotestPrivateEnableAssistantAndWaitForReadyFunction::Run() {
   DVLOG(1) << "AutotestPrivateEnableAssistantAndWaitForReadyFunction";
 
+  if (ash::AssistantState::Get()->assistant_status() ==
+      chromeos::assistant::AssistantStatus::READY) {
+    return RespondNow(Error("Assistant is already enabled."));
+  }
+
+  // We can set this callback only when assistant status is NOT_READY. We should
+  // call this before we try to enable Assistant to avoid causing some timing
+  // issue.
+  chromeos::assistant::AssistantManagerServiceImpl::
+      SetInitializedInternalCallbackForTesting(base::BindOnce(
+          &AutotestPrivateEnableAssistantAndWaitForReadyFunction::
+              OnInitializedInternal,
+          this));
+
   Profile* profile = Profile::FromBrowserContext(browser_context());
   const std::string& err_msg =
       SetWhitelistedPref(profile, chromeos::assistant::prefs::kAssistantEnabled,
@@ -3088,34 +3101,12 @@
   if (!err_msg.empty())
     return RespondNow(Error(err_msg));
 
-  // Asynchronously subscribe to status changes to avoid a possible segmentation
-  // fault caused by Respond() in the subscriber callback being called before
-  // RespondLater() below.
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AutotestPrivateEnableAssistantAndWaitForReadyFunction::
-                         SubscribeToStatusChanges,
-                     this));
-
-  // Prevent |this| from being freed before we get a response from the
-  // Assistant.
-  self_ = this;
-
   return RespondLater();
 }
 
 void AutotestPrivateEnableAssistantAndWaitForReadyFunction::
-    SubscribeToStatusChanges() {
-  // |AddObserver| will immediately trigger |OnAssistantStatusChanged|.
-  ash::AssistantState::Get()->AddObserver(this);
-}
-
-void AutotestPrivateEnableAssistantAndWaitForReadyFunction::
-    OnAssistantStatusChanged(chromeos::assistant::AssistantStatus status) {
-  if (status == chromeos::assistant::AssistantStatus::READY) {
-    Respond(NoArguments());
-    self_.reset();
-  }
+    OnInitializedInternal() {
+  Respond(NoArguments());
 }
 
 // AssistantInteractionHelper is a helper class used to interact with Assistant
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 9eaccce..1659cf4 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -738,8 +738,7 @@
 
 // Bring up the Assistant service, and wait until the ready signal is received.
 class AutotestPrivateEnableAssistantAndWaitForReadyFunction
-    : public ExtensionFunction,
-      public ash::AssistantStateObserver {
+    : public ExtensionFunction {
  public:
   AutotestPrivateEnableAssistantAndWaitForReadyFunction();
   DECLARE_EXTENSION_FUNCTION("autotestPrivate.enableAssistantAndWaitForReady",
@@ -749,15 +748,7 @@
   ~AutotestPrivateEnableAssistantAndWaitForReadyFunction() override;
   ResponseAction Run() override;
 
-  void SubscribeToStatusChanges();
-
-  // ash::AssistantStateObserver overrides:
-  void OnAssistantStatusChanged(
-      chromeos::assistant::AssistantStatus status) override;
-
-  // A reference to keep |this| alive while waiting for the Assistant to
-  // respond.
-  scoped_refptr<ExtensionFunction> self_;
+  void OnInitializedInternal();
 };
 
 // Send text query to Assistant and return response.
diff --git a/chrome/browser/download/chrome_download_manager_delegate.cc b/chrome/browser/download/chrome_download_manager_delegate.cc
index 03aaae76..d381ad5 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.cc
+++ b/chrome/browser/download/chrome_download_manager_delegate.cc
@@ -657,8 +657,7 @@
              download::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT)) {
       DVLOG(2) << __func__
                << "() SB service disabled. Marking download as DANGEROUS FILE";
-      if (ShouldBlockFile(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
-                          item)) {
+      if (ShouldBlockFile(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE)) {
         MaybeReportDangerousDownloadBlocked(
             download_prefs_->download_restriction(), "DANGEROUS_FILE_TYPE",
             item->GetTargetFilePath().AsUTF8Unsafe(), item);
@@ -1397,7 +1396,7 @@
       } else {
         item->OnAsyncScanningCompleted(danger_type);
       }
-    } else if (ShouldBlockFile(danger_type, item)) {
+    } else if (ShouldBlockFile(danger_type)) {
       // Specifying a dangerous type here would take precedence over the
       // blocking of the file. For BLOCKED_TOO_LARGE and
       // BLOCKED_PASSWORD_PROTECTED, we want to display more clear UX, so
@@ -1547,7 +1546,7 @@
 
     DownloadItemModel(item).SetDangerLevel(target_info->danger_level);
   }
-  if (ShouldBlockFile(target_info->danger_type, item)) {
+  if (ShouldBlockFile(target_info->danger_type)) {
     MaybeReportDangerousDownloadBlocked(
         download_prefs_->download_restriction(), "DANGEROUS_FILE_TYPE",
         target_info->target_path.AsUTF8Unsafe(), item);
@@ -1622,8 +1621,7 @@
 }
 
 bool ChromeDownloadManagerDelegate::ShouldBlockFile(
-    download::DownloadDangerType danger_type,
-    download::DownloadItem* item) const {
+    download::DownloadDangerType danger_type) const {
   DownloadPrefs::DownloadRestriction download_restriction =
       download_prefs_->download_restriction();
 
diff --git a/chrome/browser/download/chrome_download_manager_delegate.h b/chrome/browser/download/chrome_download_manager_delegate.h
index b4871ec..378990ed 100644
--- a/chrome/browser/download/chrome_download_manager_delegate.h
+++ b/chrome/browser/download/chrome_download_manager_delegate.h
@@ -182,8 +182,7 @@
 
   // Return true if the downloaded file should be blocked based on the current
   // download restriction pref and |danger_type|.
-  bool ShouldBlockFile(download::DownloadDangerType danger_type,
-                       download::DownloadItem* item) const;
+  bool ShouldBlockFile(download::DownloadDangerType danger_type) const;
 
  protected:
   virtual safe_browsing::DownloadProtectionService*
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api_interactive_uitest.cc b/chrome/browser/extensions/api/extension_action/extension_action_api_interactive_uitest.cc
index 6f13209..4151ae5 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api_interactive_uitest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api_interactive_uitest.cc
@@ -44,12 +44,8 @@
   // `extension`, and waits for a corresponding test success notification.
   void RunScriptTest(const std::string& script, const Extension& extension) {
     ResultCatcher result_catcher;
-    base::RunLoop run_loop;
-    auto callback = [&run_loop](base::Value value) { run_loop.Quit(); };
-    browsertest_util::ExecuteScriptInServiceWorker(
-        profile(), extension.id(), script,
-        base::BindLambdaForTesting(callback));
-    run_loop.Run();
+    browsertest_util::BackgroundScriptExecutor::ExecuteScript(
+        profile(), extension.id(), script);
     EXPECT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
   }
 
diff --git a/chrome/browser/extensions/api/scripting/scripting_apitest.cc b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
index 04b80d4a..c6b0eb1 100644
--- a/chrome/browser/extensions/api/scripting/scripting_apitest.cc
+++ b/chrome/browser/extensions/api/scripting/scripting_apitest.cc
@@ -391,18 +391,8 @@
 
   // A helper function to run the script in the worker context.
   auto run_script_in_worker = [this, extension](const std::string& script) {
-    base::RunLoop run_loop;
-    base::Value value_out;
-    auto callback = [&run_loop, &value_out](base::Value value) {
-      value_out = std::move(value);
-      run_loop.Quit();
-    };
-
-    browsertest_util::ExecuteScriptInServiceWorker(
-        profile(), extension->id(), script,
-        base::BindLambdaForTesting(callback));
-    run_loop.Run();
-    return value_out;
+    return browsertest_util::BackgroundScriptExecutor::ExecuteScript(
+        profile(), extension->id(), script);
   };
 
   auto get_default_result = [run_script_in_worker]() {
diff --git a/chrome/browser/extensions/browsertest_util_browsertest.cc b/chrome/browser/extensions/browsertest_util_browsertest.cc
index 70e3a5a..7ff6786 100644
--- a/chrome/browser/extensions/browsertest_util_browsertest.cc
+++ b/chrome/browser/extensions/browsertest_util_browsertest.cc
@@ -36,18 +36,10 @@
   ASSERT_TRUE(extension);
   ASSERT_TRUE(listener.WaitUntilSatisfied());
 
-  base::RunLoop run_loop;
-  base::Value value_out;
-  auto callback = [&run_loop, &value_out](base::Value value) {
-    value_out = std::move(value);
-    run_loop.Quit();
-  };
+  base::Value value = browsertest_util::BackgroundScriptExecutor::ExecuteScript(
+      profile(), extension->id(), "console.warn('script ran'); myTestFlag;");
 
-  browsertest_util::ExecuteScriptInServiceWorker(
-      profile(), extension->id(), "console.warn('script ran'); myTestFlag;",
-      base::BindLambdaForTesting(callback));
-  run_loop.Run();
-  EXPECT_THAT(value_out, base::test::IsJson(R"("HELLO!")"));
+  EXPECT_THAT(value, base::test::IsJson(R"("HELLO!")"));
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index 91d2453..c7580bf 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -55,8 +55,10 @@
   autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       host_contents(),
       autofill::ChromeAutofillClient::FromWebContents(host_contents()),
-      g_browser_process->GetApplicationLocale(),
-      autofill::BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      base::BindRepeating(
+          &autofill::BrowserDriverInitHook,
+          autofill::ChromeAutofillClient::FromWebContents(host_contents()),
+          g_browser_process->GetApplicationLocale()));
 
   // The popup itself cannot be zoomed, but we must specify a zoom level to use.
   // Otherwise, if a user zooms a page of the same extension, the popup would
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 2ed4301..8e1aa4f 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3537,18 +3537,23 @@
   },
   {
     "name": "history-journeys",
-    "owners": [ "tommycli", "chrome-journeys@google.com", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 105
+    "owners": [ "tommycli", "chrome-journeys@google.com" ],
+    "expiry_milestone": 110
+  },
+  {
+    "name": "history-journeys-labels",
+    "owners": [ "tommycli", "chrome-journeys@google.com" ],
+    "expiry_milestone": 110
   },
   {
     "name": "history-journeys-omnibox-action",
-    "owners": [ "tommycli", "chrome-journeys@google.com", "chrome-omnibox-team@google.com" ],
-    "expiry_milestone": 105
+    "owners": [ "tommycli", "chrome-journeys@google.com" ],
+    "expiry_milestone": 110
   },
   {
     "name": "history-journeys-on-device-clustering",
     "owners": [ "sophiechang", "chrome-journeys@google.com", "chrome-intelligence-core@google.com" ],
-    "expiry_milestone": 105
+    "expiry_milestone": 110
   },
   {
     "name": "http-cache-partitioning",
@@ -5659,6 +5664,14 @@
       "expiry_milestone":102
   },
   {
+    "name": "terminal-dev",
+    "owners": [ "joelhockey", "lxj@google.com", "//chrome/browser/ash/guest_os/OWNERS" ],
+    // This flag is used by CrOS Terminal developers for testing SSH on devices
+    // with valid machine certs and should not be removed.  Only works in dev
+    // and canary channels.
+    "expiry_milestone": -1
+  },
+  {
     "name": "terminal-ssh",
     "owners": [ "lxj@google.com", "joelhockey", "//chrome/browser/ash/guest_os/OWNERS" ],
     "expiry_milestone": 110
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json
index 503db26..d7a4d86c 100644
--- a/chrome/browser/flag-never-expire-list.json
+++ b/chrome/browser/flag-never-expire-list.json
@@ -104,6 +104,7 @@
   "show-touch-hud",
   "smooth-scrolling",
   "system-keyboard-lock",
+  "terminal-dev",
   "tint-composited-content",
   "top-chrome-touch-ui",
   "translate-force-trigger-on-english",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index fc3ef201..f3a81a5f6 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1570,6 +1570,10 @@
 const char kJourneysName[] = "History Journeys";
 const char kJourneysDescription[] = "Enables the History Journeys UI.";
 
+const char kJourneysLabelsName[] = "History Journeys Labels";
+const char kJourneysLabelsDescription[] =
+    "Enables labels for Journeys within the History Journeys UI.";
+
 const char kJourneysOmniboxActionName[] = "History Journeys Omnibox Action";
 const char kJourneysOmniboxActionDescription[] =
     "Enables the History Journeys Omnibox Action.";
@@ -1844,12 +1848,6 @@
     "is available will be retained as a whole and not be counted towards the "
     "limit.";
 
-const char kOmniboxTabSwitchSuggestionsName[] =
-    "Omnibox switch to tab suggestions";
-const char kOmniboxTabSwitchSuggestionsDescription[] =
-    "Enable URL suggestions to optionally take the user to a tab where a "
-    "website is already opened.";
-
 const char kOmniboxMaxZeroSuggestMatchesName[] =
     "Omnibox Max Zero Suggest Matches";
 const char kOmniboxMaxZeroSuggestMatchesDescription[] =
@@ -5330,6 +5328,11 @@
     "Enables the Quick Settings Network revamp, which updates Network Quick "
     "Settings UI and related infrastructure. See https://crbug.com/1169479.";
 
+const char kTerminalDevName[] = "Terminal dev";
+const char kTerminalDevDescription[] =
+    "Enables Terminal System App to load from Downloads for developer testing. "
+    "Only works in dev and canary channels.";
+
 const char kTerminalSSHName[] = "Terminal SSH tabs";
 const char kTerminalSSHDescription[] =
     "Enables SSH tabs in the Terminal System App.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f45281d..ba1469d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -883,6 +883,9 @@
 extern const char kJourneysName[];
 extern const char kJourneysDescription[];
 
+extern const char kJourneysLabelsName[];
+extern const char kJourneysLabelsDescription[];
+
 extern const char kJourneysOmniboxActionName[];
 extern const char kJourneysOmniboxActionDescription[];
 
@@ -1026,9 +1029,6 @@
 extern const char kOmniboxRetainSuggestionsWithHeadersName[];
 extern const char kOmniboxRetainSuggestionsWithHeadersDescription[];
 
-extern const char kOmniboxTabSwitchSuggestionsName[];
-extern const char kOmniboxTabSwitchSuggestionsDescription[];
-
 extern const char kOmniboxTrendingZeroPrefixSuggestionsOnNTPName[];
 extern const char kOmniboxTrendingZeroPrefixSuggestionsOnNTPDescription[];
 
@@ -3055,6 +3055,9 @@
 extern const char kQuickSettingsNetworkRevampName[];
 extern const char kQuickSettingsNetworkRevampDescription[];
 
+extern const char kTerminalDevName[];
+extern const char kTerminalDevDescription[];
+
 extern const char kTerminalSSHName[];
 extern const char kTerminalSSHDescription[];
 
diff --git a/chrome/browser/history_clusters/OWNERS b/chrome/browser/history_clusters/OWNERS
index d03b432..c6981c7 100644
--- a/chrome/browser/history_clusters/OWNERS
+++ b/chrome/browser/history_clusters/OWNERS
@@ -1 +1,5 @@
 file://components/history_clusters/OWNERS
+
+# This is for the common case of adding or renaming files. If you're doing
+# structural changes, use usual OWNERS rules.
+per-file BUILD.gn=*
diff --git a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java
index 5b0fa97..2787863 100644
--- a/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java
+++ b/chrome/browser/language/android/java/src/org/chromium/chrome/browser/language/AppLanguagePromoDialog.java
@@ -519,15 +519,18 @@
     private static boolean shouldShowPrompt() {
         // Skip feature and preference checks if forced on for testing.
         if (!ChromeFeatureList.isEnabled(ChromeFeatureList.FORCE_APP_LANGUAGE_PROMPT)) {
+            // Don't show if prompt has already been shown.
+            if (TranslateBridge.getAppLanguagePromptShown()) return false;
+            boolean hasULPMatch =
+                    LanguageBridge.isTopULPBaseLanguage(Locale.getDefault().toLanguageTag());
+            recordTopULPMatchStatus(hasULPMatch);
             // Don't show if not enabled.
             if (!ChromeFeatureList.isEnabled(ChromeFeatureList.APP_LANGUAGE_PROMPT)) return false;
             // Don't show if ULP match is enabled and the UI language matches the top ULP language.
             if (ChromeFeatureList.isEnabled(ChromeFeatureList.APP_LANGUAGE_PROMPT_ULP)
-                    && LanguageBridge.isTopULPBaseLanguage(Locale.getDefault().toLanguageTag())) {
+                    && hasULPMatch) {
                 return false;
             }
-            // Don't show if it has already been shown.
-            if (TranslateBridge.getAppLanguagePromptShown()) return false;
         }
 
         boolean isOnline = NetworkChangeNotifier.isOnline();
@@ -568,6 +571,11 @@
                 "LanguageSettings.AppLanguagePrompt.IsOnline", isOnline);
     }
 
+    private static void recordTopULPMatchStatus(boolean hasMatch) {
+        RecordHistogram.recordBooleanHistogram(
+                "LanguageSettings.AppLanguagePrompt.HasTopULPMatch", hasMatch);
+    }
+
     private static void recordOpenDuration(String type, long displayTime) {
         RecordHistogram.recordLongTimesHistogram100(
                 "LanguageSettings.AppLanguagePrompt.OpenDuration." + type, displayTime);
diff --git a/chrome/browser/metrics/cros_healthd_metrics_provider.cc b/chrome/browser/metrics/cros_healthd_metrics_provider.cc
index 3058ca8..a38df17 100644
--- a/chrome/browser/metrics/cros_healthd_metrics_provider.cc
+++ b/chrome/browser/metrics/cros_healthd_metrics_provider.cc
@@ -91,13 +91,13 @@
 
   auto tag = block_device_result->which();
   if (tag == chromeos::cros_healthd::mojom::NonRemovableBlockDeviceResult::Tag::
-                 ERROR) {
+                 kError) {
     DVLOG(1) << "cros_healthd: Error getting block device info: "
              << block_device_result->get_error()->msg;
     return;
   }
   DCHECK_EQ(tag, chromeos::cros_healthd::mojom::NonRemovableBlockDeviceResult::
-                     Tag::BLOCK_DEVICE_INFO);
+                     Tag::kBlockDeviceInfo);
 
   for (const auto& storage : block_device_result->get_block_device_info()) {
     SystemProfileProto::Hardware::InternalStorageDevice dev;
diff --git a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
index 9f3842a..5bba3ce 100644
--- a/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/prefetch/search_prefetch/search_prefetch_service.cc
@@ -196,7 +196,7 @@
 
     // The navigation prefetch should replace the existing prefetch.
     if (navigation_prefetch)
-      prefetches_.erase(search_terms);
+      DeletePrefetch(search_terms);
   }
 
   if (prefetches_.size() >= SearchPrefetchMaxAttemptsPerCachingDuration()) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 13e0bf5..a365b72 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -54,6 +54,8 @@
   "common/abstract_earcons.js",
   "common/background_bridge.js",
   "common/braille_interface.js",
+  "common/bridge_constants.js",
+  "common/bridge_helper.js",
   "common/extension_bridge.js",
   "common/key_sequence.js",
   "common/log_types.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index eb48cf9d..362d770 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -22,7 +22,7 @@
 import {RangeAutomationHandler} from './range_automation_handler.js';
 
 /**
- * @fileoverview The entry point for all ChromeVox2 related code for the
+ * @fileoverview The entry point for all ChromeVox related code for the
  * background page.
  */
 
@@ -32,9 +32,7 @@
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
 
-/**
- * ChromeVox2 background page.
- */
+/** ChromeVox background page. */
 export class Background extends ChromeVoxState {
   constructor() {
     super();
@@ -59,25 +57,13 @@
 
     Object.defineProperty(ChromeVox, 'typingEcho', {
       get() {
-        return parseInt(localStorage['typingEcho'], 10);
-      },
-      set(v) {
-        localStorage['typingEcho'] = v;
-      }
-    });
-
-    Object.defineProperty(ChromeVox, 'typingEcho', {
-      get() {
-        const typingEcho = parseInt(localStorage['typingEcho'], 10) || 0;
-        return typingEcho;
+        return parseInt(localStorage['typingEcho'], 10) || 0;
       },
       set(value) {
         localStorage['typingEcho'] = value;
       }
     });
 
-    ExtensionBridge.addMessageListener(this.onMessage_);
-
     /** @type {!BackgroundKeyboardHandler} @private */
     this.keyboardHandler_ = new BackgroundKeyboardHandler();
 
@@ -120,9 +106,7 @@
           ChromeVox.tts.speak(announceText.join(' '), QueueMode.FLUSH);
         });
     chrome.accessibilityPrivate.onCustomSpokenFeedbackToggled.addListener(
-        (enabled) => {
-          this.talkBackEnabled = enabled;
-        });
+        (enabled) => this.talkBackEnabled = enabled);
     chrome.accessibilityPrivate.onShowChromeVoxTutorial.addListener(() => {
       (new PanelCommand(PanelCommandType.TUTORIAL)).send();
     });
@@ -132,9 +116,7 @@
     sessionStorage.setItem('darkScreen', 'false');
   }
 
-  /**
-   * @override
-   */
+  /** @override */
   getCurrentRange() {
     if (this.currentRange_ && this.currentRange_.isValid()) {
       return this.currentRange_;
@@ -142,9 +124,7 @@
     return null;
   }
 
-  /**
-   * @override
-   */
+  /** @override */
   getCurrentRangeWithoutRecovery() {
     return this.currentRange_;
   }
@@ -297,48 +277,12 @@
     }
   }
 
-  /**
-   * Open the options page in a new tab.
-   */
-  showOptionsPage() {
-    const optionsPage = {url: '/chromevox/options/options.html'};
-    chrome.tabs.create(optionsPage);
-  }
-
-  /**
-   * @override
-   */
+  /** @override */
   onBrailleKeyEvent(evt, content) {
     return BrailleCommandHandler.onBrailleKeyEvent(evt, content);
   }
 
-  /**
-   * @param {Object} msg A message sent from a content script.
-   * @param {Port} port
-   * @private
-   */
-  onMessage_(msg, port) {
-    const target = msg['target'];
-    const action = msg['action'];
-
-    switch (target) {
-      case 'next':
-        if (action === 'getIsClassicEnabled') {
-          const url = msg['url'];
-          const isClassicEnabled = false;
-          port.postMessage({target: 'next', isClassicEnabled});
-        } else if (action === 'onCommand') {
-          CommandHandlerInterface.instance.onCommand(msg['command']);
-        } else if (action === 'flushNextUtterance') {
-          Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH);
-        }
-        break;
-    }
-  }
-
-  /**
-   * @override
-   */
+  /** @override */
   restoreLastValidRangeIfNeeded() {
     // Never restore range when TalkBack is enabled as commands such as
     // Search+Left, go directly to TalkBack.
@@ -346,7 +290,6 @@
       return;
     }
 
-
     if (!this.currentRange_ || !this.currentRange_.isValid()) {
       this.setCurrentRange(this.previousRange_);
     }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js
index 932f172..1263ae3 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/braille/braille_background.js
@@ -173,8 +173,8 @@
 /** @type {?BrailleBackground} */
 BrailleBackground.instance_ = null;
 
-BackgroundBridge.registerHandler(
-    /*target=*/ 'BrailleBackground', 'backTranslate',
+BridgeHelper.registerHandler(
+    BridgeTarget.BRAILLE_BACKGROUND, BridgeAction.BACK_TRANSLATE,
     (cells) => new Promise(resolve => {
       BrailleBackground.getInstance()
           .getTranslatorManager()
@@ -182,8 +182,8 @@
           .backTranslate(cells, resolve);
     }));
 
-BackgroundBridge.registerHandler(
-    /*target=*/ 'BrailleBackground', 'refreshBrailleTable',
+BridgeHelper.registerHandler(
+    BridgeTarget.BRAILLE_BACKGROUND, BridgeAction.REFRESH_BRAILLE_TABLE,
     (brailleTable) =>
         BrailleBackground.getInstance().getTranslatorManager().refresh(
             brailleTable));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
index 52f4cce..0e0b601 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/chromevox_state.js
@@ -30,7 +30,7 @@
 };
 
 /**
- * ChromeVox2 state object.
+ * ChromeVox state object.
  * @constructor
  */
 ChromeVoxState = function() {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
index 945605c..f332c87 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/classic_background.js
@@ -274,24 +274,6 @@
 
       switch (target) {
         case 'TTS':
-          if (msg['startCallbackId'] !== undefined) {
-            msg['properties']['startCallback'] = function(opt_cleanupOnly) {
-              port.postMessage({
-                'message': 'TTS_CALLBACK',
-                'cleanupOnly': opt_cleanupOnly,
-                'id': msg['startCallbackId']
-              });
-            };
-          }
-          if (msg['endCallbackId'] !== undefined) {
-            msg['properties']['endCallback'] = function(opt_cleanupOnly) {
-              port.postMessage({
-                'message': 'TTS_CALLBACK',
-                'cleanupOnly': opt_cleanupOnly,
-                'id': msg['endCallbackId']
-              });
-            };
-          }
           try {
             this.onTtsMessage(msg);
           } catch (err) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
index cfc91d3e..3dc9014 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/live_regions.js
@@ -14,7 +14,7 @@
 const TreeChangeType = chrome.automation.TreeChangeType;
 
 /**
- * ChromeVox2 live region handler.
+ * ChromeVox live region handler.
  */
 export class LiveRegions {
   /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index 6c74ec0..1bfb0e0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
@@ -11,11 +11,11 @@
 goog.require('AutomationTreeWalker');
 goog.require('AutomationUtil');
 goog.require('AutomationObjectConstructorInstaller');
-goog.require('BackgroundBridge');
 goog.require('BrailleDisplayState');
 goog.require('BrailleInterface');
 goog.require('BrailleKeyCommand');
 goog.require('BrailleKeyEvent');
+goog.require('BridgeHelper');
 goog.require('ChromeVox');
 goog.require('ChromeVoxState');
 goog.require('ChromeVoxStateObserver');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
index 5d1a4d9..fd6f747 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
@@ -199,12 +199,12 @@
 /** @type {!ChromeVoxPrefs} */
 ChromeVoxPrefs.instance = new ChromeVoxPrefs();
 
-const target = 'ChromeVoxPrefs';
-BackgroundBridge.registerHandler(
-    target, 'getPrefs', () => ChromeVoxPrefs.instance.getPrefs());
-BackgroundBridge.registerHandler(
-    target, 'setLoggingPrefs',
+BridgeHelper.registerHandler(
+    BridgeTarget.CHROMEVOX_PREFS, BridgeAction.GET_PREFS,
+    () => ChromeVoxPrefs.instance.getPrefs());
+BridgeHelper.registerHandler(
+    BridgeTarget.CHROMEVOX_PREFS, BridgeAction.SET_LOGGING_PREFS,
     ({key, value}) => ChromeVoxPrefs.instance.setLoggingPrefs(key, value));
-BackgroundBridge.registerHandler(
-    target, 'setPref',
+BridgeHelper.registerHandler(
+    BridgeTarget.CHROMEVOX_PREFS, BridgeAction.SET_PREF,
     ({key, value}) => ChromeVoxPrefs.instance.setPref(key, value));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
index 773e279..961c842 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
@@ -9,6 +9,10 @@
 
 goog.provide('BackgroundBridge');
 
+goog.require('BridgeAction');
+goog.require('BridgeHelper');
+goog.require('BridgeTarget');
+
 BackgroundBridge.BrailleBackground = {
   /**
    * Translate braille cells into text.
@@ -16,14 +20,15 @@
    * @return {!Promise<?string>}
    */
   async backTranslate(cells) {
-    return BackgroundBridge.sendMessage_(
-        'BrailleBackground', 'backTranslate', cells);
+    return BridgeHelper.sendMessage(
+        BridgeTarget.BRAILLE_BACKGROUND, BridgeAction.BACK_TRANSLATE, cells);
   },
 
   /** @param {string} brailleTable The table for this translator to use. */
   async refreshBrailleTable(brailleTable) {
-    return BackgroundBridge.sendMessage_(
-        'BrailleBackground', 'refreshBrailleTable', brailleTable);
+    return BridgeHelper.sendMessage(
+        BridgeTarget.BRAILLE_BACKGROUND, BridgeAction.REFRESH_BRAILLE_TABLE,
+        brailleTable);
   },
 };
 
@@ -34,7 +39,8 @@
    *     localStorage.
    */
   async getPrefs() {
-    return BackgroundBridge.sendMessage_('ChromeVoxPrefs', 'getPrefs');
+    return BridgeHelper.sendMessage(
+        BridgeTarget.CHROMEVOX_PREFS, BridgeAction.GET_PREFS);
   },
 
   /**
@@ -43,8 +49,9 @@
    * @param {boolean} value The new value of the pref.
    */
   async setLoggingPrefs(key, value) {
-    return BackgroundBridge.sendMessage_(
-        'ChromeVoxPrefs', 'setLoggingPrefs', {key, value});
+    return BridgeHelper.sendMessage(
+        BridgeTarget.CHROMEVOX_PREFS, BridgeAction.SET_LOGGING_PREFS,
+        {key, value});
   },
 
   /**
@@ -53,68 +60,7 @@
    * @param {Object|string|boolean} value The new value of the pref.
    */
   async setPref(key, value) {
-    return BackgroundBridge.sendMessage_(
-        'ChromeVoxPrefs', 'setPref', {key, value});
+    return BridgeHelper.sendMessage(
+        BridgeTarget.CHROMEVOX_PREFS, BridgeAction.SET_PREF, {key, value});
   },
 };
-
-// Helper functions:
-
-/** @private {!Object<string, Object<string, Function>>} */
-BackgroundBridge.handlers = {};
-
-/**
- * @param {string} target The name of the class that is registering the handler.
- * @param {string} action The name of the intended function or, if not a direct
- *     method of the class, a pseudo-function name.
- * @param {Function} handler A function that performs the indicated action. It
- *     may optionally take a single parameter, and may have an optional return
- *         value that will be forwarded to the requestor.
- *     If the method takes multiple parameters, they are passed as named members
- *         of an object literal.
- */
-BackgroundBridge.registerHandler = (target, action, handler) => {
-  if (!BackgroundBridge.handlers[target]) {
-    BackgroundBridge.handlers[target] = {};
-  }
-  BackgroundBridge.handlers[target][action] = handler;
-};
-
-BackgroundBridge.castTo = (type) => {
-  return (obj) => {
-    if (obj === null || obj === undefined) {
-      return obj;
-    }
-    Object.setPrototypeOf(obj, type.prototype);
-    return obj;
-  };
-};
-
-
-/**
- * @param {string} target The name of the class that will handle this request.
- * @param {string} action The name of the intended function or, if not a direct
- *     method of the class, a pseudo-function name.
- * @param {*=} value An optional single parameter to include with the message.
- *     If the method takes multiple parameters, they are passed as named members
- *     of an object literal.
- *
- * @return {!Promise} A promise, that resolves when the handler function has
- *     finished and contains any value returned by the handler.
- * @private
- */
-BackgroundBridge.sendMessage_ = (target, action, value) => {
-  return new Promise(
-      resolve => chrome.runtime.sendMessage({target, action, value}, resolve));
-};
-
-chrome.runtime.onMessage.addListener((message, sender, respond) => {
-  const targetHandlers = BackgroundBridge.handlers[message.target];
-  if (!targetHandlers || !targetHandlers[message.action]) {
-    return;
-  }
-
-  const handler = targetHandlers[message.action];
-  Promise.resolve(handler(message.value)).then(respond);
-  return true; /** Wait for asynchronous response. */
-});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js
new file mode 100644
index 0000000..47203d3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_constants.js
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Enums for BridgeHelper functions.
+ */
+goog.provide('BridgeAction');
+goog.provide('BridgeTarget');
+
+/**
+ * The class that a message is being sent to.
+ * @enum {string}
+ */
+BridgeTarget = {
+  BRAILLE_BACKGROUND: 'BrailleBackground',
+  CHROMEVOX_PREFS: 'ChromeVoxPrefs',
+};
+
+/**
+ * The action that the message is requesting be performed.
+ * @enum {string}
+ */
+BridgeAction = {
+  BACK_TRANSLATE: 'backTranslate',
+  GET_PREFS: 'getPrefs',
+  REFRESH_BRAILLE_TABLE: 'refreshBrailleTable',
+  SET_LOGGING_PREFS: 'setLoggingPrefs',
+  SET_PREF: 'setPref',
+};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_helper.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_helper.js
new file mode 100644
index 0000000..a5f9c769f3
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_helper.js
@@ -0,0 +1,73 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A collection of functions and behaviors helpful for message
+ * passing between renderers.
+ */
+goog.provide('BridgeHelper');
+
+goog.require('BridgeAction');
+goog.require('BridgeTarget');
+
+/** @typedef {!Object<BridgeAction, Function>} */
+let TargetHandlers;
+
+/** @private {!Object<BridgeTarget, TargetHandlers>} */
+BridgeHelper.handlers_ = {};
+
+/**
+ * This function should only be used by Bridges (e.g. BackgroundBridge,
+ * PanelBridge) and not called directly by other classes.
+ *
+ * @param {BridgeTarget} target The name of the class that will handle this
+ *     request.
+ * @param {BridgeAction} action The name of the intended function or, if not a
+ *     direct method of the class, a pseudo-function name.
+ * @param {*=} value An optional single parameter to include with the message.
+ *     If the method takes multiple parameters, they are passed as named members
+ *     of an object literal.
+ *
+ * @return {!Promise} A promise, that resolves when the handler function has
+ *     finished and contains any value returned by the handler.
+ */
+BridgeHelper.sendMessage = (target, action, value) => {
+  return new Promise(
+      resolve => chrome.runtime.sendMessage({target, action, value}, resolve));
+};
+
+/**
+ * @param {BridgeTarget} target The name of the class that is registering the
+ *     handler.
+ * @param {BridgeAction} action The name of the intended function or, if not a
+ *     direct method of the class, a pseudo-function name.
+ * @param {Function} handler A function that performs the indicated action. It
+ *     may optionally take a single parameter, and may have an optional return
+ *         value that will be forwarded to the requestor.
+ *     If the method takes multiple parameters, they are passed as named members
+ *         of an object literal.
+ */
+BridgeHelper.registerHandler = (target, action, handler) => {
+  if (!BridgeHelper.handlers_[target]) {
+    BridgeHelper.handlers_[target] = {};
+  }
+
+  if (BridgeHelper.handlers_[target][action]) {
+    throw 'Error: Re-assigning handlers for a specific target/action ' +
+        'is not permitted';
+  }
+
+  BridgeHelper.handlers_[target][action] = handler;
+};
+
+chrome.runtime.onMessage.addListener((message, sender, respond) => {
+  const targetHandlers = BridgeHelper.handlers_[message.target];
+  if (!targetHandlers || !targetHandlers[message.action]) {
+    return;
+  }
+
+  const handler = targetHandlers[message.action];
+  Promise.resolve(handler(message.value)).then(respond);
+  return true; /** Wait for asynchronous response. */
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
index f5e2943..4b71bfd 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
@@ -6,9 +6,7 @@
  * @fileoverview ChromeVox options page.
  */
 import {BrailleTable} from '../background/braille/braille_table.js';
-import {ConsoleTts} from '../background/console_tts.js';
 import {TtsBackground} from '../background/tts_background.js';
-
 import {AbstractTts} from '../common/abstract_tts.js';
 
 import {BluetoothBrailleDisplayUI} from './bluetooth_braille_display_ui.js';
@@ -503,12 +501,6 @@
 }
 
 /**
- * The ConsoleTts object.
- * @type {ConsoleTts}
- */
-OptionsPage.consoleTts;
-
-/**
  * The TtsBackground object.
  * @type {TtsBackground}
  */
@@ -545,11 +537,6 @@
   }, true);
 };
 
-
-chrome.runtime.sendMessage(
-    {target: 'ConsoleTts', action: 'getInstance'},
-    (consoleTts) => OptionsPage.consoleTts = consoleTts);
-
 document.addEventListener('DOMContentLoaded', async function() {
   await OptionsPage.init();
 }, false);
diff --git a/chrome/browser/resources/connectors_internals/app.ts b/chrome/browser/resources/connectors_internals/app.ts
index fb1b0a0..870d7f9 100644
--- a/chrome/browser/resources/connectors_internals/app.ts
+++ b/chrome/browser/resources/connectors_internals/app.ts
@@ -7,6 +7,7 @@
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {ConnectorsTabsElement} from './connectors_tabs.js';
 
@@ -16,7 +17,7 @@
   }
 
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   constructor() {
diff --git a/chrome/browser/resources/connectors_internals/connectors_tabs.ts b/chrome/browser/resources/connectors_internals/connectors_tabs.ts
index cdba7d0..78a1506 100644
--- a/chrome/browser/resources/connectors_internals/connectors_tabs.ts
+++ b/chrome/browser/resources/connectors_internals/connectors_tabs.ts
@@ -6,6 +6,7 @@
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {ZeroTrustConnectorElement} from './zero_trust_connector.js';
 
@@ -36,7 +37,7 @@
   }
 
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private get tabHeaders(): NodeList {
diff --git a/chrome/browser/resources/connectors_internals/tsconfig_base.json b/chrome/browser/resources/connectors_internals/tsconfig_base.json
index 99a81eca..4ca1e52 100644
--- a/chrome/browser/resources/connectors_internals/tsconfig_base.json
+++ b/chrome/browser/resources/connectors_internals/tsconfig_base.json
@@ -1,6 +1,12 @@
 {
   "extends": "../../../../tools/typescript/tsconfig_base.json",
   "compilerOptions": {
-    "allowJs": true
+    "allowJs": true,
+    "typeRoots": [
+      "../../../../third_party/node/node_modules/@types"
+    ],
+    "types": [
+      "trusted-types"
+    ]
   }
 }
diff --git a/chrome/browser/resources/connectors_internals/zero_trust_connector.ts b/chrome/browser/resources/connectors_internals/zero_trust_connector.ts
index ea829bc..5c83a7aa 100644
--- a/chrome/browser/resources/connectors_internals/zero_trust_connector.ts
+++ b/chrome/browser/resources/connectors_internals/zero_trust_connector.ts
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {KeyInfo, KeyManagerInitializedValue, KeyTrustLevel, KeyType, PageHandler, PageHandlerInterface, ZeroTrustState} from './connectors_internals.mojom-webui.js';
 
@@ -24,7 +25,7 @@
   }
 
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   public set enabledString(str: string) {
diff --git a/chrome/browser/resources/download_shelf/app.ts b/chrome/browser/resources/download_shelf/app.ts
index cd72d8d..6914a66 100644
--- a/chrome/browser/resources/download_shelf/app.ts
+++ b/chrome/browser/resources/download_shelf/app.ts
@@ -12,11 +12,13 @@
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
+
 import {DownloadShelfApiProxy, DownloadShelfApiProxyImpl} from './download_shelf_api_proxy.js';
 
 export class DownloadShelfAppElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private apiProxy_: DownloadShelfApiProxy;
diff --git a/chrome/browser/resources/download_shelf/download_item.ts b/chrome/browser/resources/download_shelf/download_item.ts
index 6c39985..676c646 100644
--- a/chrome/browser/resources/download_shelf/download_item.ts
+++ b/chrome/browser/resources/download_shelf/download_item.ts
@@ -13,6 +13,7 @@
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {DownloadItem, DownloadMode, DownloadState} from './download_shelf.mojom-webui.js';
 import {DownloadShelfApiProxy, DownloadShelfApiProxyImpl} from './download_shelf_api_proxy.js';
@@ -28,7 +29,7 @@
 
 export class DownloadItemElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private item_: DownloadItem;
diff --git a/chrome/browser/resources/download_shelf/download_list.ts b/chrome/browser/resources/download_shelf/download_list.ts
index fc85e06..2217ce2 100644
--- a/chrome/browser/resources/download_shelf/download_list.ts
+++ b/chrome/browser/resources/download_shelf/download_list.ts
@@ -9,6 +9,7 @@
 import './download_item.js';
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {listenOnce} from 'chrome://resources/js/util.m.js';
 
 import {DownloadItemElement} from './download_item.js';
@@ -17,7 +18,7 @@
 
 export class DownloadListElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private elements_: Array<DownloadItemElement> = [];
diff --git a/chrome/browser/resources/download_shelf/tsconfig_base.json b/chrome/browser/resources/download_shelf/tsconfig_base.json
index 3e71f763..b1e3bac 100644
--- a/chrome/browser/resources/download_shelf/tsconfig_base.json
+++ b/chrome/browser/resources/download_shelf/tsconfig_base.json
@@ -4,6 +4,12 @@
     "allowJs": true,
     "noUncheckedIndexedAccess": false,
     "noUnusedLocals": false,
-    "strictPropertyInitialization": false
+    "strictPropertyInitialization": false,
+    "typeRoots": [
+      "../../../../third_party/node/node_modules/@types"
+    ],
+    "types": [
+      "trusted-types"
+    ]
   }
 }
diff --git a/chrome/browser/resources/tab_strip/alert_indicator.ts b/chrome/browser/resources/tab_strip/alert_indicator.ts
index 50996cad..aaf536a7 100644
--- a/chrome/browser/resources/tab_strip/alert_indicator.ts
+++ b/chrome/browser/resources/tab_strip/alert_indicator.ts
@@ -6,6 +6,7 @@
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {TabAlertState} from './tabs.mojom-webui.js';
 
@@ -69,7 +70,7 @@
 
 export class AlertIndicatorElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private alertState_: TabAlertState;
diff --git a/chrome/browser/resources/tab_strip/alert_indicators.ts b/chrome/browser/resources/tab_strip/alert_indicators.ts
index 58f2b424..397f8247 100644
--- a/chrome/browser/resources/tab_strip/alert_indicators.ts
+++ b/chrome/browser/resources/tab_strip/alert_indicators.ts
@@ -5,13 +5,14 @@
 import './alert_indicator.js';
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {AlertIndicatorElement} from './alert_indicator.js';
 import {TabAlertState} from './tabs.mojom-webui.js';
 
 export class AlertIndicatorsElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private containerEl_: HTMLElement;
diff --git a/chrome/browser/resources/tab_strip/tab.ts b/chrome/browser/resources/tab_strip/tab.ts
index c027bbf..a529c6c 100644
--- a/chrome/browser/resources/tab_strip/tab.ts
+++ b/chrome/browser/resources/tab_strip/tab.ts
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 import './strings.m.js';
-
 import './alert_indicators.js';
 
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {getFavicon} from 'chrome://resources/js/icon.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {isRTL} from 'chrome://resources/js/util.m.js';
 
 import {AlertIndicatorsElement} from './alert_indicators.js';
@@ -40,7 +40,7 @@
 
 export class TabElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private alertIndicatorsEl_: AlertIndicatorsElement;
diff --git a/chrome/browser/resources/tab_strip/tab_group.ts b/chrome/browser/resources/tab_strip/tab_group.ts
index 5eacf5b..219754da 100644
--- a/chrome/browser/resources/tab_strip/tab_group.ts
+++ b/chrome/browser/resources/tab_strip/tab_group.ts
@@ -4,13 +4,14 @@
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 
 import {TabGroupVisualData} from './tab_strip.mojom-webui.js';
 import {TabsApiProxy, TabsApiProxyImpl} from './tabs_api_proxy.js';
 
 export class TabGroupElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private tabsApi_: TabsApiProxy;
diff --git a/chrome/browser/resources/tab_strip/tab_list.ts b/chrome/browser/resources/tab_strip/tab_list.ts
index 9beb022aa..24241622 100644
--- a/chrome/browser/resources/tab_strip/tab_list.ts
+++ b/chrome/browser/resources/tab_strip/tab_list.ts
@@ -11,6 +11,7 @@
 import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.m.js';
+import {getTrustedHTML} from 'chrome://resources/js/static_types.js';
 import {isRTL} from 'chrome://resources/js/util.m.js';
 
 import {DragManager, DragManagerDelegate} from './drag_manager.js';
@@ -148,7 +149,7 @@
   private scrollListener_: (e: Event) => void;
 
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   constructor() {
diff --git a/chrome/browser/resources/tab_strip/tsconfig_base.json b/chrome/browser/resources/tab_strip/tsconfig_base.json
index e4b4ff4..3c08a99 100644
--- a/chrome/browser/resources/tab_strip/tsconfig_base.json
+++ b/chrome/browser/resources/tab_strip/tsconfig_base.json
@@ -4,6 +4,12 @@
     "allowJs": true,
     "noPropertyAccessFromIndexSignature": false,
     "noUnusedLocals": false,
-    "strictPropertyInitialization": false
+    "strictPropertyInitialization": false,
+    "typeRoots": [
+      "../../../../third_party/node/node_modules/@types"
+    ],
+    "types": [
+      "trusted-types"
+    ]
   }
 }
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
index 13da9a7..897dfc3 100644
--- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
@@ -154,7 +154,7 @@
   if (download_core_service) {
     ChromeDownloadManagerDelegate* delegate =
         download_core_service->GetDownloadManagerDelegate();
-    if (delegate && delegate->ShouldBlockFile(danger_type, item)) {
+    if (delegate && delegate->ShouldBlockFile(danger_type)) {
       return EventResult::BLOCKED;
     }
   }
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
index f8f16b99..92363ee 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer.cc
@@ -154,6 +154,8 @@
 
 void FileAnalyzer::OnZipAnalysisFinished(
     const ArchiveAnalyzerResults& archive_results) {
+  base::UmaHistogramEnumeration("SBClientDownload.ZipArchiveNotValidReason",
+                                archive_results.analysis_result);
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // Even if !results.success, some of the zip may have been parsed.
@@ -200,6 +202,8 @@
 void FileAnalyzer::OnRarAnalysisFinished(
     const ArchiveAnalyzerResults& archive_results) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  base::UmaHistogramEnumeration("SBClientDownload.RarArchiveNotValidReason",
+                                archive_results.analysis_result);
 
   results_.archive_is_valid =
       (archive_results.success ? ArchiveValid::VALID : ArchiveValid::INVALID);
@@ -256,6 +260,8 @@
 void FileAnalyzer::OnDmgAnalysisFinished(
     const safe_browsing::ArchiveAnalyzerResults& archive_results) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  base::UmaHistogramEnumeration("SBClientDownload.DmgArchiveNotValidReason",
+                                archive_results.analysis_result);
 
   if (archive_results.signature_blob.size() > 0) {
     results_.disk_image_signature =
diff --git a/chrome/browser/search_engines/template_url_scraper_browsertest.cc b/chrome/browser/search_engines/template_url_scraper_browsertest.cc
index 72e027b..22526ea 100644
--- a/chrome/browser/search_engines/template_url_scraper_browsertest.cc
+++ b/chrome/browser/search_engines/template_url_scraper_browsertest.cc
@@ -19,8 +19,10 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/search_engines/template_url_data.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/search_engines/template_url_starter_pack_data.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -94,8 +96,11 @@
   std::vector<std::unique_ptr<TemplateURLData>> prepopulate_urls =
       TemplateURLPrepopulateData::GetPrepopulatedEngines(
           browser()->profile()->GetPrefs(), nullptr);
+  std::vector<std::unique_ptr<TemplateURLData>> starter_pack_urls =
+      TemplateURLStarterPackData::GetStarterPackEngines();
 
-  EXPECT_EQ(prepopulate_urls.size(), all_urls.size());
+  EXPECT_EQ(prepopulate_urls.size() + starter_pack_urls.size(),
+            all_urls.size());
 
   std::string port(base::NumberToString(embedded_test_server()->port()));
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
@@ -112,5 +117,6 @@
   observer.Wait();
 
   all_urls = template_urls->GetTemplateURLs();
-  EXPECT_EQ(prepopulate_urls.size() + 1, all_urls.size());
+  EXPECT_EQ(prepopulate_urls.size() + starter_pack_urls.size() + 1,
+            all_urls.size());
 }
diff --git a/chrome/browser/search_engines/template_url_service_sync_unittest.cc b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
index 9874693d..45ccede 100644
--- a/chrome/browser/search_engines/template_url_service_sync_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/search_engines/template_url_service_client.h"
+#include "components/search_engines/template_url_starter_pack_data.h"
 #include "components/sync/model/sync_error_factory.h"
 #include "components/sync/protocol/entity_specifics.pb.h"
 #include "components/sync/protocol/search_engine_specifics.pb.h"
@@ -1768,6 +1769,9 @@
       TemplateURLPrepopulateData::GetPrepopulatedEngines(
           profile_a()->GetTestingPrefService(), nullptr);
 
+  std::vector<std::unique_ptr<TemplateURLData>> starter_pack_turls =
+      TemplateURLStarterPackData::GetStarterPackEngines();
+
   // We have to prematurely exit this test if for some reason this machine does
   // not have any prepopulate TemplateURLs.
   ASSERT_FALSE(prepop_turls.empty());
@@ -1806,12 +1810,12 @@
   initial_data.push_back(
       TemplateURLService::CreateSyncDataFromTemplateURL(*sync_turl));
 
-  ASSERT_EQ(prepop_turls.size(),
+  ASSERT_EQ(prepop_turls.size() + starter_pack_turls.size(),
             model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
   model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
                                     PassProcessor(),
                                     CreateAndPassSyncErrorFactory());
-  EXPECT_EQ(prepop_turls.size(),
+  EXPECT_EQ(prepop_turls.size() + starter_pack_turls.size(),
             model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
 
   ASSERT_EQ(added_turl, model()->GetTemplateURLForKeyword(kNewKeyword16));
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
index 4aed13e..649aa59 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl.cc
@@ -12,7 +12,6 @@
 namespace segmentation_platform {
 namespace {
 
-#if !BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 class DummyModelProvider : public ModelProvider {
  public:
   DummyModelProvider()
@@ -20,11 +19,14 @@
                           OPTIMIZATION_TARGET_UNKNOWN) {}
   void InitAndFetchModel(
       const ModelUpdatedCallback& model_updated_callback) override {}
+
   void ExecuteModelWithInput(const std::vector<float>& inputs,
-                             ExecutionCallback callback) override {}
+                             ExecutionCallback callback) override {
+    std::move(callback).Run(absl::nullopt);
+  }
+
   bool ModelAvailable() override { return false; }
 };
-#endif
 
 }  // namespace
 
@@ -40,6 +42,10 @@
 std::unique_ptr<ModelProvider> ModelProviderFactoryImpl::CreateProvider(
     optimization_guide::proto::OptimizationTarget optimization_target) {
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
+  if (!optimization_guide_provider_) {
+    // Optimization guide may not be available in some tests,
+    return std::make_unique<DummyModelProvider>();
+  }
   return std::make_unique<OptimizationGuideSegmentationModelProvider>(
       optimization_guide_provider_, background_task_runner_,
       optimization_target);
diff --git a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
index 6d0f8d20..e785850 100644
--- a/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
+++ b/chrome/browser/segmentation_platform/model_provider_factory_impl_unittest.cc
@@ -48,4 +48,41 @@
           OPTIMIZATION_TARGET_SEGMENTATION_SHARE));
 }
 
+class DummyModelProviderFactoryImplTest : public ModelProviderFactoryImplTest {
+ public:
+  void SetUp() override {
+    task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+    provider_factory_ =
+        std::make_unique<ModelProviderFactoryImpl>(nullptr, task_runner_);
+  }
+};
+
+TEST_F(DummyModelProviderFactoryImplTest, ProviderCreated) {
+  EXPECT_TRUE(provider_factory_->CreateProvider(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_VOICE));
+
+  auto provider = provider_factory_->CreateProvider(
+      optimization_guide::proto::OptimizationTarget::
+          OPTIMIZATION_TARGET_SEGMENTATION_SHARE);
+  ASSERT_TRUE(provider);
+
+  EXPECT_FALSE(provider->ModelAvailable());
+
+  // This callback should never be invoked. Send a null callback and chrome
+  // should not crash by invoking it.
+  provider->InitAndFetchModel(ModelProvider::ModelUpdatedCallback());
+
+  base::RunLoop wait;
+  provider->ExecuteModelWithInput(
+      {1, 2.5},
+      base::BindOnce(
+          [](base::OnceClosure quit, const absl::optional<float>& output) {
+            EXPECT_FALSE(output);
+            std::move(quit).Run();
+          },
+          wait.QuitClosure()));
+  wait.Run();
+}
+
 }  // namespace segmentation_platform
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
index d07df0dc..2cc5b41c 100644
--- a/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
+++ b/chrome/browser/segmentation_platform/segmentation_platform_service_factory.cc
@@ -70,10 +70,6 @@
   OptimizationGuideKeyedService* optimization_guide =
       OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
 
-  // If optimization guide feature is disabled, then disable segmentation.
-  if (!optimization_guide)
-    return new DummySegmentationPlatformService();
-
   auto params = std::make_unique<SegmentationPlatformServiceImpl::InitParams>();
 
   params->history_service = HistoryServiceFactory::GetForProfile(
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index 03f05a1..268673b 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -140,20 +140,10 @@
   }
 }
 
-void SecurityStateTabHelper::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // Ignore non-primary FrameTree navigations, subframe navigations,
-  // same-document navigations, and navigations that did not commit (e.g.
-  // HTTP/204 or file downloads).
-  if (!navigation_handle->IsInPrimaryMainFrame() ||
-      navigation_handle->IsSameDocument() ||
-      !navigation_handle->HasCommitted()) {
-    return;
-  }
-
+void SecurityStateTabHelper::PrimaryPageChanged(content::Page& page) {
   net::CertStatus cert_status = GetVisibleSecurityState()->cert_status;
   if (net::IsCertStatusError(cert_status) &&
-      !navigation_handle->IsErrorPage()) {
+      !page.GetMainDocument().IsErrorDocument()) {
     // Record each time a user visits a site after having clicked through a
     // certificate warning interstitial. This is used as a baseline for
     // interstitial.ssl.did_user_revoke_decision2 in order to determine how
diff --git a/chrome/browser/ssl/security_state_tab_helper.h b/chrome/browser/ssl/security_state_tab_helper.h
index c52c033..200cdb0 100644
--- a/chrome/browser/ssl/security_state_tab_helper.h
+++ b/chrome/browser/ssl/security_state_tab_helper.h
@@ -37,8 +37,7 @@
   // content::WebContentsObserver:
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
+  void PrimaryPageChanged(content::Page& page) override;
 
   // Used by tests to specify a callback to be called when
   // GetVisibleSecurityState() is called.
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
index 394018b..1a39a5c 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
@@ -1886,30 +1886,27 @@
 
 // Tests that browser session restore isn't triggered when we launch a template
 // that contains a browser window.
-#if BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_SystemUIPreventBrowserSessionRestoreTest \
-  DISABLED_SystemUIPreventBrowserSessionRestoreTest
-#else
-#define MAYBE_SystemUIPreventBrowserSessionRestoreTest \
-  SystemUIPreventBrowserSessionRestoreTest
-#endif
 
 IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest,
-                       MAYBE_SystemUIPreventBrowserSessionRestoreTest) {
+                       SystemUIPreventBrowserSessionRestoreTest) {
   // Do not exit from test or delete the Profile* when last browser is closed.
   ScopedKeepAlive keep_alive(KeepAliveOrigin::BROWSER,
                              KeepAliveRestartOption::DISABLED);
+  Profile* profile = browser()->profile();
   ScopedProfileKeepAlive profile_keep_alive(
-      browser()->profile(), ProfileKeepAliveOrigin::kBrowserWindow);
+      profile, ProfileKeepAliveOrigin::kBrowserWindow);
 
   // Enable session service.
   SessionStartupPref pref(SessionStartupPref::LAST);
-  Profile* profile = browser()->profile();
   SessionStartupPref::SetStartupPref(profile, pref);
 
   const int expected_tab_count = 2;
-  chrome::AddTabAt(browser(), GURL(kExampleUrl2), /*index=*/-1,
+  GURL example2 = GURL(kExampleUrl2);
+  content::TestNavigationObserver navigation_observer(example2);
+  navigation_observer.StartWatchingNewWebContents();
+  chrome::AddTabAt(browser(), example2, /*index=*/-1,
                    /*foreground=*/true);
+  navigation_observer.Wait();
   EXPECT_EQ(expected_tab_count, browser()->tab_strip_model()->count());
 
   // Enter overview and save the current desk as a template.
@@ -1931,13 +1928,14 @@
   ash::WaitForDesksTemplatesUI();
   ClickZeroStateTemplatesButton();
   ClickFirstTemplateItem();
+  content::RunAllTasksUntilIdle();
 
   // Verify that the browser was launched with the correct number of tabs, and
   // that browser session restore did not restore any windows/tabs.
-  Browser* new_browser =
-      FindLaunchedBrowserByURLs({GURL(kAboutBlankUrl), GURL(kExampleUrl2)});
-  ASSERT_TRUE(new_browser);
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+  Browser* new_browser = BrowserList::GetInstance()->get(0);
+  ASSERT_TRUE(new_browser);
+  EXPECT_EQ(expected_tab_count, new_browser->tab_strip_model()->count());
 }
 
 // Tests that launching the same desk template multiple times creates desks with
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
index b2f6776..b454c69 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
@@ -469,9 +469,7 @@
 
 // CloseBubble is called from a ShareAction or after an app launches.
 void SharesheetBubbleView::CloseBubble(views::Widget::ClosedReason reason) {
-  if (!is_bubble_closing_) {
-    CloseWidgetWithAnimateFadeOut(reason);
-  }
+  CloseWidgetWithAnimateFadeOut(reason);
 }
 
 bool SharesheetBubbleView::AcceleratorPressed(
@@ -484,6 +482,13 @@
       delegator_->OnAcceleratorPressed(accelerator, active_target_)) {
     return true;
   }
+
+  // If the bubble is already in the process of closing, return early without
+  // doing anything.
+  if (is_bubble_closing_) {
+    return true;
+  }
+
   // If delivered_callback_ is not null at this point, then the sharesheet was
   // closed before a target was selected.
   if (delivered_callback_) {
@@ -493,7 +498,6 @@
   ::sharesheet::SharesheetMetrics::RecordSharesheetActionMetrics(
       ::sharesheet::SharesheetMetrics::UserAction::kCancelledThroughEscPress);
   CloseWidgetWithAnimateFadeOut(views::Widget::ClosedReason::kEscKeyPressed);
-
   return true;
 }
 
@@ -731,7 +735,9 @@
 
 void SharesheetBubbleView::CloseWidgetWithAnimateFadeOut(
     views::Widget::ClosedReason closed_reason) {
-  constexpr auto kSharesheetOpacityFadeOutTime = base::Milliseconds(80);
+  if (is_bubble_closing_) {
+    return;
+  }
 
   // Don't attempt to react to tablet mode changes while the sharesheet is
   // closing.
@@ -739,6 +745,7 @@
   is_bubble_closing_ = true;
   ui::Layer* layer = View::GetWidget()->GetLayer();
 
+  constexpr auto kSharesheetOpacityFadeOutTime = base::Milliseconds(80);
   auto scoped_settings =
       std::make_unique<ui::ScopedLayerAnimationSettings>(layer->GetAnimator());
   scoped_settings->SetTweenType(gfx::Tween::Type::LINEAR);
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view_unittest.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view_unittest.cc
index e08dfa4..1dcc8e8 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view_unittest.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view_unittest.cc
@@ -132,6 +132,15 @@
     ASSERT_FALSE(IsSharesheetVisible());
   }
 
+  void CloseBubbleWithEscKey() {
+    GetEventGenerator()->PressAndReleaseKey(ui::VKEY_ESCAPE);
+    // |bubble_delegate_| and |sharesheet_bubble_view_| destruct on close.
+    bubble_delegate_ = nullptr;
+    sharesheet_bubble_view_ = nullptr;
+
+    ASSERT_FALSE(IsSharesheetVisible());
+  }
+
   bool IsSharesheetVisible() { return sharesheet_widget_->IsVisible(); }
 
   SharesheetBubbleView* sharesheet_bubble_view() {
@@ -446,5 +455,26 @@
   CloseBubble();
 }
 
+TEST_F(SharesheetBubbleViewTest, CloseWithEscKey) {
+  ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(),
+                      ::sharesheet::LaunchSource::kUnknown);
+  CloseBubbleWithEscKey();
+}
+
+TEST_F(SharesheetBubbleViewTest, CloseMultipleTimes) {
+  ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(),
+                      ::sharesheet::LaunchSource::kUnknown);
+  CloseBubbleWithEscKey();
+  CloseBubbleWithEscKey();
+}
+
+TEST_F(SharesheetBubbleViewTest, HoldEscapeKey) {
+  GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EventFlags::EF_NONE);
+  ShowAndVerifyBubble(::sharesheet::CreateValidTextIntent(),
+                      ::sharesheet::LaunchSource::kUnknown);
+  GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, ui::EventFlags::EF_NONE);
+  CloseBubbleWithEscKey();
+}
+
 }  // namespace sharesheet
 }  // namespace ash
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
index 10691c1..dd602c4 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -254,8 +254,10 @@
   virtual std::unique_ptr<NiceMock<MockAutofillExternalDelegate>>
   CreateExternalDelegate() {
     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
-        web_contents(), autofill_client_.get(), "en-US",
-        BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+        web_contents(), autofill_client_.get(),
+        base::BindRepeating(&autofill::BrowserDriverInitHook,
+                            autofill_client_.get(), "en-US"));
+
     // Make sure RenderFrame is created.
     NavigateAndCommit(GURL("about:blank"));
     ContentAutofillDriverFactory* factory =
diff --git a/chrome/browser/ui/search_engines/template_url_table_model.cc b/chrome/browser/ui/search_engines/template_url_table_model.cc
index 138ca39..2485da2 100644
--- a/chrome/browser/ui/search_engines/template_url_table_model.cc
+++ b/chrome/browser/ui/search_engines/template_url_table_model.cc
@@ -37,6 +37,13 @@
       extension_entries;
   // Keywords that can be made the default first.
   for (auto* template_url : urls) {
+    // Don't include starter pack keywords if the starter pack feature flag is
+    // not enabled.
+    if (!OmniboxFieldTrial::IsSiteSearchStarterPackEnabled() &&
+        template_url->starter_pack_id() != 0) {
+      continue;
+    }
+
     if (template_url_service_->ShowInDefaultList(template_url)) {
       default_entries.push_back(template_url);
     } else if (template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION) {
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index d26e738..32f456d 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -306,8 +306,10 @@
   autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       web_contents,
       autofill::ChromeAutofillClient::FromWebContents(web_contents),
-      g_browser_process->GetApplicationLocale(),
-      autofill::BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      base::BindRepeating(
+          &autofill::BrowserDriverInitHook,
+          autofill::ChromeAutofillClient::FromWebContents(web_contents),
+          g_browser_process->GetApplicationLocale()));
   if (breadcrumbs::IsEnabled())
     BreadcrumbManagerTabHelper::CreateForWebContents(web_contents);
   chrome_browser_net::NetErrorTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc b/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc
index f658db6..525d6611 100644
--- a/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc
+++ b/chrome/browser/ui/views/media_router/presentation_receiver_window_view.cc
@@ -173,8 +173,10 @@
   autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       web_contents,
       autofill::ChromeAutofillClient::FromWebContents(web_contents),
-      g_browser_process->GetApplicationLocale(),
-      autofill::BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      base::BindRepeating(
+          &autofill::BrowserDriverInitHook,
+          autofill::ChromeAutofillClient::FromWebContents(web_contents),
+          g_browser_process->GetApplicationLocale()));
   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
       web_contents,
       autofill::ChromeAutofillClient::FromWebContents(web_contents));
diff --git a/chrome/browser/ui/webui/connectors_internals/connectors_internals_ui.cc b/chrome/browser/ui/webui/connectors_internals/connectors_internals_ui.cc
index 64210a45..2a2de91 100644
--- a/chrome/browser/ui/webui/connectors_internals/connectors_internals_ui.cc
+++ b/chrome/browser/ui/webui/connectors_internals/connectors_internals_ui.cc
@@ -17,6 +17,7 @@
 #include "chrome/grit/connectors_internals_resources_map.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
 
 namespace enterprise_connectors {
 
@@ -36,6 +37,9 @@
       base::make_span(kConnectorsInternalsResources,
                       kConnectorsInternalsResourcesSize),
       IDR_CONNECTORS_INTERNALS_INDEX_HTML);
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::TrustedTypes,
+      "trusted-types static-types;");
 
   content::WebUIDataSource::Add(profile, source);
 }
diff --git a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
index 5d9172ca..9ee58a3 100644
--- a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
+++ b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
@@ -20,6 +20,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
 
 namespace {
 
@@ -53,6 +54,9 @@
       source,
       base::make_span(kDownloadShelfResources, kDownloadShelfResourcesSize),
       IDR_DOWNLOAD_SHELF_DOWNLOAD_SHELF_HTML);
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::TrustedTypes,
+      "trusted-types static-types;");
   content::WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
                                 source);
 }
diff --git a/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc b/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
index 57b64d6a..cffd4f5 100644
--- a/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
+++ b/chrome/browser/ui/webui/ntp/app_launcher_handler_unittest.cc
@@ -12,7 +12,6 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
@@ -92,8 +91,6 @@
 
     extension_service_ = CreateTestExtensionService();
 
-    auto* const provider = web_app::FakeWebAppProvider::Get(profile());
-    provider->SkipAwaitingExtensionSystem();
     web_app::test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
diff --git a/chrome/browser/ui/webui/predictors/predictors_ui.cc b/chrome/browser/ui/webui/predictors/predictors_ui.cc
index 369f4fda..4099a9a4 100644
--- a/chrome/browser/ui/webui/predictors/predictors_ui.cc
+++ b/chrome/browser/ui/webui/predictors/predictors_ui.cc
@@ -13,6 +13,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
 
 namespace {
 
@@ -24,10 +25,13 @@
   source->AddResourcePath("predictors.js", IDR_PREDICTORS_JS);
   source->AddResourcePath("resource_prefetch_predictor.js",
                           IDR_PREDICTORS_RESOURCE_PREFETCH_PREDICTOR_JS);
-  // TODO (https://crbug.com/1317384): This is needed because custom_element.ts,
-  // which is imported by cr_tab_box.ts, directly assigns innerHTML.
-  source->DisableTrustedTypesCSP();
   source->SetDefaultResource(IDR_PREDICTORS_HTML);
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::RequireTrustedTypesFor,
+      "require-trusted-types-for 'script';");
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::TrustedTypes,
+      "trusted-types static-types;");
   return source;
 }
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index fee0fb9..90bd98f 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -28,6 +28,7 @@
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
 #include "content/public/common/url_constants.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "ui/base/theme_provider.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/gfx/color_utils.h"
@@ -50,6 +51,9 @@
   webui::SetupWebUIDataSource(
       html_source, base::make_span(kTabStripResources, kTabStripResourcesSize),
       IDR_TAB_STRIP_TAB_STRIP_HTML);
+  html_source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::TrustedTypes,
+      "trusted-types static-types;");
 
   html_source->AddString("tabIdDataType", kWebUITabIdDataType);
   html_source->AddString("tabGroupIdDataType", kWebUITabGroupIdDataType);
diff --git a/chrome/browser/web_applications/commands/clear_browsing_data_command_unittest.cc b/chrome/browser/web_applications/commands/clear_browsing_data_command_unittest.cc
index 17a5145d..209daae 100644
--- a/chrome/browser/web_applications/commands/clear_browsing_data_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/clear_browsing_data_command_unittest.cc
@@ -17,7 +17,6 @@
   void SetUp() override {
     WebAppTest::SetUp();
     web_app_provider_ = web_app::FakeWebAppProvider::Get(profile());
-    web_app_provider_->SkipAwaitingExtensionSystem();
     web_app_provider_->StartWithSubsystems();
   }
 
diff --git a/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc b/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc
index e9bdaea..6b1185b 100644
--- a/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc
+++ b/chrome/browser/web_applications/commands/run_on_os_login_command_unittest.cc
@@ -94,7 +94,6 @@
     os_integration_manager_ = os_integration_manager.get();
     provider_ = provider;
     provider->SetOsIntegrationManager(std::move(os_integration_manager));
-    provider->SkipAwaitingExtensionSystem();
     test::AwaitStartWebAppProviderAndSubsystems(profile());
   }
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_provider.cc b/chrome/browser/web_applications/extensions/bookmark_app_provider.cc
index 2858fff4..ad5cc88a 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_provider.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_provider.cc
@@ -13,12 +13,7 @@
 
 namespace web_app {
 
-void WebAppProvider::WaitForExtensionSystemReady() {
-  extensions::ExtensionSystem::Get(profile_)->ready().Post(
-      FROM_HERE, base::BindOnce(&WebAppProvider::OnExtensionSystemReady,
-                                weak_ptr_factory_.GetWeakPtr()));
-}
-
+// TODO(crbug.com/1201878): Delete bookmark_app_provider.cc.
 void WebAppProviderFactory::DependsOnExtensionsSystem() {
   DependsOn(
       extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
diff --git a/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc b/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
index 101a92d..43f707f 100644
--- a/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
+++ b/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
@@ -39,7 +39,6 @@
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
     web_app_provider_ = web_app::FakeWebAppProvider::Get(profile());
-    web_app_provider_->SkipAwaitingExtensionSystem();
     web_app_provider_->StartWithSubsystems();
     // TODO(https://crbug.com/891172): Use an extension agnostic test registry.
     extensions::TestExtensionSystem* test_system =
diff --git a/chrome/browser/web_applications/test/fake_web_app_provider.cc b/chrome/browser/web_applications/test/fake_web_app_provider.cc
index 3b977ac..0e022c7 100644
--- a/chrome/browser/web_applications/test/fake_web_app_provider.cc
+++ b/chrome/browser/web_applications/test/fake_web_app_provider.cc
@@ -178,11 +178,6 @@
   return *database_factory_;
 }
 
-void FakeWebAppProvider::SkipAwaitingExtensionSystem() {
-  CheckNotStarted();
-  skip_awaiting_extension_system_ = true;
-}
-
 void FakeWebAppProvider::StartWithSubsystems() {
   CheckNotStarted();
   SetRunSubsystemStartupTasks(true);
@@ -201,10 +196,6 @@
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kDisableDefaultApps);
 
-  // Default to not wait for a test extension system, that is usually never
-  // started in web app tests.
-  SkipAwaitingExtensionSystem();
-
   SetRegistrar(std::make_unique<WebAppRegistrarMutable>(profile_));
   SetDatabaseFactory(std::make_unique<FakeWebAppDatabaseFactory>());
 
diff --git a/chrome/browser/web_applications/test/fake_web_app_provider.h b/chrome/browser/web_applications/test/fake_web_app_provider.h
index fac44d9..d57ad74 100644
--- a/chrome/browser/web_applications/test/fake_web_app_provider.h
+++ b/chrome/browser/web_applications/test/fake_web_app_provider.h
@@ -95,7 +95,6 @@
   WebAppIconManager& GetIconManager() const;
   AbstractWebAppDatabaseFactory& GetDatabaseFactory() const;
 
-  void SkipAwaitingExtensionSystem();
   // Starts this WebAppProvider and its subsystems. It does not wait for systems
   // to be ready.
   void StartWithSubsystems();
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 7c2eecf..292c1074 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -231,18 +231,6 @@
 }
 
 void WebAppProvider::StartImpl() {
-  if (!skip_awaiting_extension_system_) {
-    // Basically the WebAppUiManagerImpl is dependent on ExtensionSystem
-    // initialization.
-    // TODO(crbug.com/1201878): Make WebAppUiManagerImpl lazily check
-    // ExtensionSystem readiness.
-    WaitForExtensionSystemReady();
-  } else {
-    OnExtensionSystemReady();
-  }
-}
-
-void WebAppProvider::OnExtensionSystemReady() {
   StartSyncBridge();
 }
 
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index 966aefb7..727f3787 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -159,8 +159,6 @@
 
  protected:
   virtual void StartImpl();
-  void WaitForExtensionSystemReady();
-  void OnExtensionSystemReady();
 
   void CreateSubsystems(Profile* profile);
 
@@ -201,8 +199,6 @@
   bool connected_ = false;
   bool is_registry_ready_ = false;
 
-  bool skip_awaiting_extension_system_ = false;
-
   base::WeakPtrFactory<WebAppProvider> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 67d1942..dc826e6 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1651060496-5443548dc9d78389ce41ca6dbd43c73bb37847b6.profdata
+chrome-linux-main-1651103926-6843ec94dfd409e12ca71ebf4090bd9f0ae8f5ab.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 7576f6f..98d8d35 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1651082056-ee84e0d08937274b8fcf6d2e3f1e5a736a21e20a.profdata
+chrome-mac-arm-main-1651103926-01bdc2fab7b3b4a75499e9564e830d047303e38d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 993d0370..cf85d16 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1651082056-9525af8a99e86002e967da38f208f8a9ee9ca3cd.profdata
+chrome-mac-main-1651103926-731446711d5a7d5f80f72f294771e3a454b8d37d.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 37fa4ee5..7d63ac7 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1651082056-f661d0c51d9c8f26a24b66c9cdaca610b95032d0.profdata
+chrome-win32-main-1651093021-e818e9bd1552da5d058ce05432b9767ad77cbe4f.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4d49ef7..b4c7a8a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1651082056-1448b7ec8f1feed2356cf67d96f3b30a375b00c0.profdata
+chrome-win64-main-1651093021-43a066eeafe6ff1da1300d8747790a8dfae68179.profdata
diff --git a/chrome/common/safe_browsing/archive_analyzer_results.cc b/chrome/common/safe_browsing/archive_analyzer_results.cc
index e32115a..118aa6a 100644
--- a/chrome/common/safe_browsing/archive_analyzer_results.cc
+++ b/chrome/common/safe_browsing/archive_analyzer_results.cc
@@ -87,13 +87,7 @@
 
 }  // namespace
 
-ArchiveAnalyzerResults::ArchiveAnalyzerResults()
-    : success(false),
-      has_executable(false),
-      has_archive(false),
-      file_count(0),
-      directory_count(0) {}
-
+ArchiveAnalyzerResults::ArchiveAnalyzerResults() = default;
 ArchiveAnalyzerResults::ArchiveAnalyzerResults(
     const ArchiveAnalyzerResults& other) = default;
 
diff --git a/chrome/common/safe_browsing/archive_analyzer_results.h b/chrome/common/safe_browsing/archive_analyzer_results.h
index 6fa66b81..41f9ac90 100644
--- a/chrome/common/safe_browsing/archive_analyzer_results.h
+++ b/chrome/common/safe_browsing/archive_analyzer_results.h
@@ -20,10 +20,27 @@
 
 namespace safe_browsing {
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ArchiveAnalysisResult {
+  kUnknown = 0,  // kUnknown indicates a case where we don't have a specific
+                 // reason, but parsing failed. This bucket will be broken into
+                 // more buckets in the future, as we identify more reasons for
+                 // analysis failure.
+  kUnspecified = 1,  // kUnspecified indicates that the analysis code provided
+                     // no reason at all. Logging this value indicates a bug.
+  kValid = 2,
+  kTooLarge = 3,
+  kTimeout = 4,
+  kFailedToOpen = 5,
+  kFailedToOpenTempFile = 6,
+  kMaxValue = kFailedToOpenTempFile
+};
+
 struct ArchiveAnalyzerResults {
-  bool success;
-  bool has_executable;
-  bool has_archive;
+  bool success = false;
+  bool has_executable = false;
+  bool has_archive = false;
   google::protobuf::RepeatedPtrField<ClientDownloadRequest_ArchivedBinary>
       archived_binary;
   std::vector<base::FilePath> archived_archive_filenames;
@@ -33,8 +50,10 @@
       ClientDownloadRequest_DetachedCodeSignature>
       detached_code_signatures;
 #endif  // BUILDFLAG(IS_MAC)
-  int file_count;
-  int directory_count;
+  int file_count = 0;
+  int directory_count = 0;
+  ArchiveAnalysisResult analysis_result = ArchiveAnalysisResult::kUnspecified;
+
   ArchiveAnalyzerResults();
   ArchiveAnalyzerResults(const ArchiveAnalyzerResults& other);
   ~ArchiveAnalyzerResults();
diff --git a/chrome/common/safe_browsing/rar_analyzer.cc b/chrome/common/safe_browsing/rar_analyzer.cc
index 9932c61..1cb98a97e 100644
--- a/chrome/common/safe_browsing/rar_analyzer.cc
+++ b/chrome/common/safe_browsing/rar_analyzer.cc
@@ -43,12 +43,16 @@
   bool too_big_to_unpack =
       base::checked_cast<uint64_t>(rar_file.GetLength()) >
       FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze("rar");
-  if (too_big_to_unpack)
+  if (too_big_to_unpack) {
+    results->analysis_result = ArchiveAnalysisResult::kTooLarge;
     return;
+  }
 
   third_party_unrar::RarReader reader;
-  if (!reader.Open(std::move(rar_file), temp_file.Duplicate()))
+  if (!reader.Open(std::move(rar_file), temp_file.Duplicate())) {
+    results->analysis_result = ArchiveAnalysisResult::kUnknown;
     return;
+  }
 
   bool timeout = false;
   while (reader.ExtractNextEntry()) {
@@ -68,6 +72,8 @@
       results->file_count++;
   }
 
+  results->analysis_result =
+      timeout ? ArchiveAnalysisResult::kTimeout : ArchiveAnalysisResult::kValid;
   results->success = !timeout;
 }
 
diff --git a/chrome/common/safe_browsing/zip_analyzer.cc b/chrome/common/safe_browsing/zip_analyzer.cc
index 708e1ba..7127f413 100644
--- a/chrome/common/safe_browsing/zip_analyzer.cc
+++ b/chrome/common/safe_browsing/zip_analyzer.cc
@@ -39,6 +39,7 @@
   zip::ZipReader reader;
   if (!reader.OpenFromPlatformFile(zip_file.GetPlatformFile())) {
     DVLOG(1) << "Failed to open zip file";
+    results->analysis_result = ArchiveAnalysisResult::kUnknown;
     return;
   }
 
@@ -47,6 +48,7 @@
       FileTypePolicies::GetInstance()->GetMaxFileSizeToAnalyze("zip");
   if (too_big_to_unpack) {
     results->success = false;
+    results->analysis_result = ArchiveAnalysisResult::kTooLarge;
     return;
   }
 
@@ -82,6 +84,14 @@
       results->file_count++;
   }
 
+  if (timeout) {
+    results->analysis_result = ArchiveAnalysisResult::kTimeout;
+  } else if (reader.ok()) {
+    results->analysis_result = ArchiveAnalysisResult::kValid;
+  } else {
+    results->analysis_result = ArchiveAnalysisResult::kUnknown;
+  }
+
   results->success = reader.ok() && !timeout;
 }
 
diff --git a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc
index 9f0edc6..18d6902 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.cc
@@ -50,7 +50,7 @@
 
   if (!file.IsValid()) {
     DLOG(ERROR) << "Could not open file: " << file_path_.value();
-    ReportFileFailure();
+    ReportFileFailure(safe_browsing::ArchiveAnalysisResult::kFailedToOpen);
     return;
   }
 
@@ -59,7 +59,7 @@
   bool too_big_to_unpack = base::checked_cast<uint64_t>(size) > max_size_;
   if (too_big_to_unpack) {
     DLOG(ERROR) << "File is too big: " << file_path_.value();
-    ReportFileFailure();
+    ReportFileFailure(safe_browsing::ArchiveAnalysisResult::kTooLarge);
     return;
   }
 
@@ -68,13 +68,16 @@
                                 std::move(file)));
 }
 
-void SandboxedDMGAnalyzer::ReportFileFailure() {
+void SandboxedDMGAnalyzer::ReportFileFailure(
+    safe_browsing::ArchiveAnalysisResult reason) {
   DCHECK(!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
 
   if (callback_) {
+    safe_browsing::ArchiveAnalyzerResults results;
+    results.analysis_result = reason;
+
     content::GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback_),
-                                  safe_browsing::ArchiveAnalyzerResults()));
+        FROM_HERE, base::BindOnce(std::move(callback_), results));
   }
 }
 
diff --git a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h
index 60c14da8..5f5d0c1 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h
+++ b/chrome/services/file_util/public/cpp/sandboxed_dmg_analyzer_mac.h
@@ -15,6 +15,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace safe_browsing {
+enum class ArchiveAnalysisResult;
 struct ArchiveAnalyzerResults;
 }
 
@@ -48,7 +49,7 @@
   void PrepareFileToAnalyze();
 
   // If file preparation failed, analysis has failed: report failure.
-  void ReportFileFailure();
+  void ReportFileFailure(safe_browsing::ArchiveAnalysisResult reason);
 
   // Starts the utility process and sends it a file analyze request.
   void AnalyzeFile(base::File file);
diff --git a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc
index de512b0..d49a22f0 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.cc
@@ -71,7 +71,7 @@
   if (file_path_.value().empty()) {
     // TODO(vakh): Add UMA metrics here to check how often this happens.
     DLOG(ERROR) << "file_path_ empty!";
-    ReportFileFailure();
+    ReportFileFailure(safe_browsing::ArchiveAnalysisResult::kFailedToOpen);
     return;
   }
 
@@ -79,7 +79,7 @@
   if (!file.IsValid()) {
     // TODO(vakh): Add UMA metrics here to check how often this happens.
     DLOG(ERROR) << "Could not open file: " << file_path_.value();
-    ReportFileFailure();
+    ReportFileFailure(safe_browsing::ArchiveAnalysisResult::kFailedToOpen);
     return;
   }
 
@@ -94,7 +94,8 @@
 
   if (!temp_file.IsValid()) {
     DLOG(ERROR) << "Could not open temp file: " << temp_path.value();
-    ReportFileFailure();
+    ReportFileFailure(
+        safe_browsing::ArchiveAnalysisResult::kFailedToOpenTempFile);
     return;
   }
 
@@ -103,11 +104,14 @@
                                 std::move(file), std::move(temp_file)));
 }
 
-void SandboxedRarAnalyzer::ReportFileFailure() {
+void SandboxedRarAnalyzer::ReportFileFailure(
+    safe_browsing::ArchiveAnalysisResult reason) {
   if (callback_) {
+    safe_browsing::ArchiveAnalyzerResults results;
+    results.analysis_result = reason;
+
     content::GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback_),
-                                  safe_browsing::ArchiveAnalyzerResults()));
+        FROM_HERE, base::BindOnce(std::move(callback_), results));
   }
 }
 
diff --git a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h
index 273ce4f..1f48162 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h
+++ b/chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h
@@ -14,6 +14,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace safe_browsing {
+enum class ArchiveAnalysisResult;
 struct ArchiveAnalyzerResults;
 }
 
@@ -50,7 +51,7 @@
   void PrepareFileToAnalyze();
 
   // If file preparation failed, analysis has failed: report failure.
-  void ReportFileFailure();
+  void ReportFileFailure(safe_browsing::ArchiveAnalysisResult reason);
 
   // Starts the utility process and sends it a request to analyze the file
   // |file|, given a handle for |temp_file|, where it can extract files.
diff --git a/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.cc b/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.cc
index a91fc35..23f050b 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.cc
@@ -48,7 +48,7 @@
 
   if (!file.IsValid()) {
     DLOG(ERROR) << "Could not open file: " << file_path_.value();
-    ReportFileFailure();
+    ReportFileFailure(safe_browsing::ArchiveAnalysisResult::kFailedToOpen);
     return;
   }
 
@@ -63,7 +63,8 @@
 
   if (!temp_file.IsValid()) {
     DLOG(ERROR) << "Could not open temp file: " << temp_path.value();
-    ReportFileFailure();
+    ReportFileFailure(
+        safe_browsing::ArchiveAnalysisResult::kFailedToOpenTempFile);
     return;
   }
 
@@ -72,11 +73,14 @@
                                 std::move(file), std::move(temp_file)));
 }
 
-void SandboxedZipAnalyzer::ReportFileFailure() {
+void SandboxedZipAnalyzer::ReportFileFailure(
+    safe_browsing::ArchiveAnalysisResult reason) {
   if (callback_) {
+    safe_browsing::ArchiveAnalyzerResults results;
+    results.analysis_result = reason;
+
     content::GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback_),
-                                  safe_browsing::ArchiveAnalyzerResults()));
+        FROM_HERE, base::BindOnce(std::move(callback_), results));
   }
 }
 
diff --git a/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h b/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h
index ae7d41f6..3c11b78 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h
+++ b/chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h
@@ -15,6 +15,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace safe_browsing {
+enum class ArchiveAnalysisResult;
 struct ArchiveAnalyzerResults;
 }
 
@@ -48,7 +49,7 @@
   void PrepareFileToAnalyze();
 
   // If file preparation failed, analysis has failed: report failure.
-  void ReportFileFailure();
+  void ReportFileFailure(safe_browsing::ArchiveAnalysisResult reason);
 
   // Starts the utility process and sends it a file analyze request.
   void AnalyzeFile(base::File file, base::File temp);
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 9c0eb8f..0380c03 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5338,25 +5338,6 @@
     ]
   }
 
-  if (is_chromeos) {
-    sources += [
-      "../browser/apps/intent_helper/metrics/intent_handling_metrics_unittest.cc",
-      "../browser/download/notification/download_item_notification_unittest.cc",
-      "../browser/policy/system_features_disable_list_policy_handler_unittest.cc",
-      "chromeos/printing/fake_local_printer_chromeos.cc",
-      "chromeos/printing/fake_local_printer_chromeos.h",
-    ]
-
-    if (use_cups) {
-      sources += [
-        "../browser/chromeos/printing/test_cups_wrapper.cc",
-        "../browser/chromeos/printing/test_cups_wrapper.h",
-        "../browser/extensions/api/printing/fake_print_job_controller.cc",
-        "../browser/extensions/api/printing/fake_print_job_controller.h",
-      ]
-    }
-  }
-
   if (is_chromeos_ash) {
     sources += [
       "../browser/ash/attestation/attestation_policy_unittest.cc",
@@ -5408,10 +5389,6 @@
     sources += [ "../common/net/x509_certificate_model_unittest.cc" ]
   }
 
-  if (is_chromeos) {
-    sources += [ "../browser/media/platform_verification_chromeos_unittest.cc" ]
-  }
-
   if (is_chromeos_ash) {
     sources += [
       "../browser/device_identity/chromeos/device_oauth2_token_store_chromeos_unittest.cc",
@@ -6879,9 +6856,6 @@
       "//ui/native_theme:test_support",
       "//ui/webui/resources/js/browser_command:mojo_bindings",
     ]
-    if (is_chromeos) {
-      deps += [ "//ui/chromeos" ]
-    }
     if (is_chromeos_ash) {
       deps += [
         "//ash/assistant/model",
@@ -6961,6 +6935,36 @@
     }
   }
 
+  if (is_chromeos) {
+    sources += [
+      "../browser/apps/intent_helper/metrics/intent_handling_metrics_unittest.cc",
+      "../browser/apps/intent_helper/supported_links_infobar_prefs_service_unittest.cc",
+      "../browser/chromeos/arc/arc_external_protocol_dialog_unittest.cc",
+      "../browser/chromeos/arc/open_with_menu_unittest.cc",
+      "../browser/download/notification/download_item_notification_unittest.cc",
+      "../browser/media/platform_verification_chromeos_unittest.cc",
+      "../browser/policy/system_features_disable_list_policy_handler_unittest.cc",
+      "chromeos/printing/fake_local_printer_chromeos.cc",
+      "chromeos/printing/fake_local_printer_chromeos.h",
+    ]
+
+    deps += [
+      "//components/arc/common",
+      "//components/arc/common:arc_intent_helper_constants",
+      "//components/arc/common:arc_test_support",
+      "//ui/chromeos",
+    ]
+
+    if (use_cups) {
+      sources += [
+        "../browser/chromeos/printing/test_cups_wrapper.cc",
+        "../browser/chromeos/printing/test_cups_wrapper.h",
+        "../browser/extensions/api/printing/fake_print_job_controller.cc",
+        "../browser/extensions/api/printing/fake_print_job_controller.h",
+      ]
+    }
+  }
+
   if (is_chromeos_lacros) {
     sources += [
       "../browser/apps/app_service/app_service_proxy_lacros_unittest.cc",
@@ -6990,19 +6994,6 @@
     }
   }
 
-  if (is_chromeos) {
-    sources += [
-      "../browser/apps/intent_helper/supported_links_infobar_prefs_service_unittest.cc",
-      "../browser/chromeos/arc/arc_external_protocol_dialog_unittest.cc",
-      "../browser/chromeos/arc/open_with_menu_unittest.cc",
-    ]
-    deps += [
-      "//components/arc/common",
-      "//components/arc/common:arc_intent_helper_constants",
-      "//components/arc/common:arc_test_support",
-    ]
-  }
-
   if (is_chromeos_lacros) {
     assert(enable_system_notifications)
     sources += [
diff --git a/chrome/test/data/extensions/api_test/keybinding/dont_overwrite_system/manifest.json b/chrome/test/data/extensions/api_test/keybinding/dont_overwrite_system/manifest.json
index 2cd7b98..d697dd1 100644
--- a/chrome/test/data/extensions/api_test/keybinding/dont_overwrite_system/manifest.json
+++ b/chrome/test/data/extensions/api_test/keybinding/dont_overwrite_system/manifest.json
@@ -17,7 +17,7 @@
     },
     "alt_shift_f": {
       "suggested_key": "Alt+Shift+F",
-      "description": "Send a ctrl shift f test message"
+      "description": "Send an alt shift f test message"
     }
   }
 }
diff --git a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
index 4547eb0..416059e 100644
--- a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
@@ -5,7 +5,8 @@
 import {SelectorItem} from 'chrome://resources/ash/common/navigation_selector.js';
 import {NavigationViewPanelElement} from 'chrome://resources/ash/common/navigation_view_panel.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+
+import {assertEquals, assertFalse, assertThrows, assertTrue} from '../../chai_assert.js';
 import {eventToPromise, flushTasks} from '../../test_util.js';
 
 export function navigationViewPanelTestSuite() {
@@ -253,4 +254,28 @@
     assertFalse(drawer.open);
     assertTrue(drawer.wasCanceled());
   });
+
+  test('removeSelectedPage', async () => {
+    await addNavigationSections([
+      viewElement.createSelectorItem(
+          'Page 1', 'dummy-page1', /*icon=*/ '', 'dummy1'),
+      viewElement.createSelectorItem(
+          'Page 2', 'dummy-page2', /*icon=*/ '', 'dummy2'),
+    ]);
+
+    const navElements = getNavElements();
+    navElements[1].click();
+    await flushTasks();
+
+    viewElement.removeSelectorById('dummy2');
+    assertEquals('dummy1', viewElement.selectedItem.id);
+  });
+
+  test('removeLastPage', async () => {
+    await addNavigationSection('dummyPage1', 'dummy-page1', '', 'dummy1');
+    await flushTasks();
+    assertThrows(
+        () => viewElement.removeSelectorById('dummy1'),
+        'Removing the last selector is not supported.');
+  });
 }
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
index 99328c1..4850df60 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
@@ -216,10 +216,27 @@
         assertFalse(!!multideviceSubpage.$$('#phoneHubCameraRollItem'));
       });
 
-  test('clicking SmartLock item routes to SmartLock subpage', function() {
-    multideviceSubpage.$$('#smartLockItem').$$('.link-wrapper').click();
-    assertEquals(Router.getInstance().getCurrentRoute(), routes.SMART_LOCK);
-  });
+  test(
+      'SmartLock item routes to subpage with isSmartLockSignInRemoved disabled',
+      function() {
+        multideviceSubpage.remove();
+        loadTimeData.overrideValues({'isSmartLockSignInRemoved': false});
+        browserProxy = new TestMultideviceBrowserProxy();
+        MultiDeviceBrowserProxyImpl.instance_ = browserProxy;
+
+        PolymerTest.clearBody();
+        multideviceSubpage =
+            document.createElement('settings-multidevice-subpage');
+        multideviceSubpage.pageContentData = {hostDeviceName: 'Pixel XL'};
+        setMode(MultiDeviceSettingsMode.HOST_SET_VERIFIED);
+        setSupportedFeatures(Object.values(MultiDeviceFeature));
+
+        document.body.appendChild(multideviceSubpage);
+        flush();
+
+        multideviceSubpage.$$('#smartLockItem').$$('.link-wrapper').click();
+        assertEquals(Router.getInstance().getCurrentRoute(), routes.SMART_LOCK);
+      });
 
   test(
       'setting isSmartLockSignInRemoved flag removes SmartLock subpage route',
diff --git a/chrome/test/data/webui/tab_strip/tsconfig_base.json b/chrome/test/data/webui/tab_strip/tsconfig_base.json
index ca011a1..96502c6d 100644
--- a/chrome/test/data/webui/tab_strip/tsconfig_base.json
+++ b/chrome/test/data/webui/tab_strip/tsconfig_base.json
@@ -4,6 +4,9 @@
     "typeRoots": [
        "./../../../../../third_party/node/node_modules/@types"
     ],
-    "types": ["mocha"]
+    "types": [
+      "mocha",
+      "trusted-types"
+    ]
   }
 }
diff --git a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
index 1904d04..e66ebc7 100644
--- a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
+++ b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc
@@ -145,6 +145,7 @@
   base::UmaHistogramBoolean("SBClientDownload.DmgIterationSuccess",
                             opened_iterator);
   if (!opened_iterator) {
+    results->analysis_result = safe_browsing::ArchiveAnalysisResult::kUnknown;
     return;
   }
 
@@ -204,10 +205,12 @@
     }
   }
 
-  base::UmaHistogramBoolean("SBClientDownload.DmgAnalysisTimedOut", timeout);
-
-  if (!timeout)
+  if (timeout) {
+    results->analysis_result = safe_browsing::ArchiveAnalysisResult::kTimeout;
+  } else {
+    results->analysis_result = safe_browsing::ArchiveAnalysisResult::kValid;
     results->success = true;
+  }
 }
 
 }  // namespace dmg
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 23f9a9e3..26e9164 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14746.0.0
+14748.0.0
\ No newline at end of file
diff --git a/chromeos/components/quick_answers/understanding/intent_generator.cc b/chromeos/components/quick_answers/understanding/intent_generator.cc
index 9f6bbec0..a74fc8ad 100644
--- a/chromeos/components/quick_answers/understanding/intent_generator.cc
+++ b/chromeos/components/quick_answers/understanding/intent_generator.cc
@@ -303,20 +303,22 @@
 
 void IntentGenerator::LanguageDetectorCallback(
     const QuickAnswersRequest& request,
-    absl::optional<std::string> detected_language) {
+    absl::optional<std::string> detected_locale) {
   language_detector_.reset();
 
   auto device_language =
       l10n_util::GetLanguage(QuickAnswersState::Get()->application_locale());
+  auto detected_language = detected_locale.has_value()
+                               ? l10n_util::GetLanguage(detected_locale.value())
+                               : std::string();
 
   // Generate translation intent if the detected language is different to the
   // system language and is not one of the preferred languages.
-  if (detected_language.has_value() &&
-      detected_language.value() != device_language &&
-      !IsPreferredLanguage(detected_language.value())) {
+  if (!detected_language.empty() && detected_language != device_language &&
+      !IsPreferredLanguage(detected_language)) {
     std::move(complete_callback_)
         .Run(IntentInfo(request.selected_text, IntentType::kTranslation,
-                        device_language, detected_language.value()));
+                        device_language, detected_language));
     return;
   }
 
diff --git a/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc b/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc
index d301adaf..0d8c346 100644
--- a/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc
+++ b/chromeos/components/quick_answers/understanding/intent_generator_unittest.cc
@@ -145,6 +145,27 @@
   EXPECT_EQ("en", intent_info_.source_language);
 }
 
+TEST_F(IntentGeneratorTest, TranslationIntentWithSubtag) {
+  std::vector<TextLanguagePtr> languages;
+  languages.push_back(TextLanguage::New("en-US", /* confidence */ 1));
+  UseFakeServiceConnection({}, languages);
+
+  QuickAnswersRequest request;
+  request.selected_text = "quick answers";
+  fake_quick_answers_state()->set_application_locale("es");
+  fake_quick_answers_state()->set_preferred_languages("es");
+  intent_generator_->GenerateIntent(request);
+
+  FlushForTesting();
+
+  // Should generate translation intent.
+  EXPECT_EQ(IntentType::kTranslation, intent_info_.intent_type);
+  EXPECT_EQ("quick answers", intent_info_.intent_text);
+  EXPECT_EQ("es", intent_info_.device_language);
+  // Should drop substag for source language.
+  EXPECT_EQ("en", intent_info_.source_language);
+}
+
 TEST_F(IntentGeneratorTest, TranslationIntentSameLanguage) {
   std::vector<TextLanguagePtr> languages;
   languages.push_back(DefaultLanguage());
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 86109bb..d53b9a1 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -10,6 +10,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
+#include "ash/public/cpp/assistant/assistant_state.h"
 #include "ash/public/cpp/assistant/assistant_state_base.h"
 #include "ash/public/cpp/assistant/controller/assistant_notification_controller.h"
 #include "base/barrier_closure.h"
@@ -54,6 +55,7 @@
 namespace assistant {
 namespace {
 
+static base::OnceCallback<void()> initialized_internal_callback_for_testing;
 static bool is_first_init = true;
 
 constexpr char kAndroidSettingsAppPackage[] = "com.android.settings";
@@ -145,6 +147,18 @@
 };
 
 // static
+void AssistantManagerServiceImpl::SetInitializedInternalCallbackForTesting(
+    base::OnceCallback<void()> callback) {
+  CHECK(initialized_internal_callback_for_testing.is_null());
+  // We expect that the callback is set when AssistantStatus is NOT_READY to
+  // confirm that AssistantStatus has changed from NOT_READY to READY. See more
+  // details at a comment in AssistantManagerServiceImpl::OnDeviceAppsEnabled.
+  CHECK(ash::AssistantState::Get()->assistant_status() ==
+        chromeos::assistant::AssistantStatus::NOT_READY);
+  initialized_internal_callback_for_testing = std::move(callback);
+}
+
+// static
 void AssistantManagerServiceImpl::ResetIsFirstInitFlagForTesting() {
   is_first_init = true;
 }
@@ -555,6 +569,25 @@
     return;
 
   display_controller().SetDeviceAppsEnabled(enabled);
+
+  // You can set initialized_internal callback only when AssistantStatus is
+  // NOT_READY. Also this line reaches only after GetState() becomes RUNNING
+  // (i.e. READY). From that reason, test code can assume that status has
+  // changed from NOT_READY to READY between those two points.
+  //
+  // Test code expects those things when Assistant gets initialized:
+  //
+  // - Status becomes READY.
+  // - All necessary settings are passed to LibAssistant.
+  //
+  // We update necessary settings after status becomes READY. For now,
+  // DeviceAppsEnabled is the only settings update which involves async call.
+  // As other settings are sync, if this async call gets completed, we can also
+  // assume that all necessary settings are passed to LibAssistant, i.e.
+  // initialized.
+  if (!initialized_internal_callback_for_testing.is_null()) {
+    std::move(initialized_internal_callback_for_testing).Run();
+  }
 }
 
 void AssistantManagerServiceImpl::AddTimeToTimer(const std::string& id,
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index bd08fae5..1be0c1f 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -95,6 +95,10 @@
       private chromeos::libassistant::mojom::StateObserver,
       public ConversationObserver {
  public:
+  // |callback| is called when AssistantManagerServiceImpl got initialized
+  // internally. This waits DeviceApps config value sync.
+  static void SetInitializedInternalCallbackForTesting(
+      base::OnceCallback<void()> callback);
   static void ResetIsFirstInitFlagForTesting();
 
   // |service| owns this class and must outlive this class.
diff --git a/components/android_autofill/browser/android_autofill_manager.cc b/components/android_autofill/browser/android_autofill_manager.cc
index a18f50b..f081cb4 100644
--- a/components/android_autofill/browser/android_autofill_manager.cc
+++ b/components/android_autofill/browser/android_autofill_manager.cc
@@ -14,24 +14,26 @@
 
 using base::TimeTicks;
 
-// static
-std::unique_ptr<AutofillManager> AndroidAutofillManager::Create(
-    AutofillDriver* driver,
+void AndroidDriverInitHook(
     AutofillClient* client,
-    const std::string& /*app_locale*/,
-    AutofillManager::AutofillDownloadManagerState enable_download_manager) {
-  return base::WrapUnique(
-      new AndroidAutofillManager(driver, client, enable_download_manager));
+    AutofillManager::EnableDownloadManager enable_download_manager,
+    ContentAutofillDriver* driver) {
+  driver->set_autofill_manager(base::WrapUnique(
+      new AndroidAutofillManager(driver, client, enable_download_manager)));
+  driver->GetAutofillAgent()->SetUserGestureRequired(false);
+  driver->GetAutofillAgent()->SetSecureContextRequired(true);
+  driver->GetAutofillAgent()->SetFocusRequiresScroll(false);
+  driver->GetAutofillAgent()->SetQueryPasswordSuggestion(true);
 }
 
 AndroidAutofillManager::AndroidAutofillManager(
     AutofillDriver* driver,
     AutofillClient* client,
-    AutofillManager::AutofillDownloadManagerState enable_download_manager)
+    EnableDownloadManager enable_download_manager)
     : AutofillManager(driver,
                       client,
-                      enable_download_manager,
-                      version_info::Channel::UNKNOWN) {}
+                      version_info::Channel::UNKNOWN,
+                      enable_download_manager) {}
 
 AndroidAutofillManager::~AndroidAutofillManager() = default;
 
diff --git a/components/android_autofill/browser/android_autofill_manager.h b/components/android_autofill/browser/android_autofill_manager.h
index 25fe74e..b3bbf22 100644
--- a/components/android_autofill/browser/android_autofill_manager.h
+++ b/components/android_autofill/browser/android_autofill_manager.h
@@ -13,16 +13,24 @@
 namespace autofill {
 
 class AutofillProvider;
+class ContentAutofillDriver;
+
+// Creates an AndroidAutofillManager and attaches it to the `driver`.
+//
+// This hook is to be passed to CreateForWebContentsAndDelegate().
+// It is the glue between ContentAutofillDriver[Factory] and
+// AndroidAutofillManager.
+//
+// Other embedders (which don't want to use AndroidAutofillManager) shall use
+// other implementations.
+void AndroidDriverInitHook(
+    AutofillClient* client,
+    AutofillManager::EnableDownloadManager enable_download_manager,
+    ContentAutofillDriver* driver);
 
 // This class forwards AutofillManager calls to AutofillProvider.
 class AndroidAutofillManager : public AutofillManager {
  public:
-  static std::unique_ptr<AutofillManager> Create(
-      AutofillDriver* driver,
-      AutofillClient* client,
-      const std::string& app_locale,
-      AutofillManager::AutofillDownloadManagerState enable_download_manager);
-
   AndroidAutofillManager(const AndroidAutofillManager&) = delete;
   AndroidAutofillManager& operator=(const AndroidAutofillManager&) = delete;
 
@@ -68,10 +76,15 @@
                          const FormData& form);
 
  protected:
+  friend void AndroidDriverInitHook(
+      AutofillClient* client,
+      AutofillManager::EnableDownloadManager enable_download_manager,
+      ContentAutofillDriver* driver);
+
   AndroidAutofillManager(
       AutofillDriver* driver,
       AutofillClient* client,
-      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+      AutofillManager::EnableDownloadManager enable_download_manager);
 
   void OnFormSubmittedImpl(const FormData& form,
                            bool known_success,
diff --git a/components/android_autofill/browser/autofill_provider_unittest.cc b/components/android_autofill/browser/autofill_provider_unittest.cc
index 0568558..572a8b60 100644
--- a/components/android_autofill/browser/autofill_provider_unittest.cc
+++ b/components/android_autofill/browser/autofill_provider_unittest.cc
@@ -15,9 +15,7 @@
 class AndroidAutofillManagerTestHelper : public AndroidAutofillManager {
  public:
   explicit AndroidAutofillManagerTestHelper(AutofillProvider* autofill_provider)
-      : AndroidAutofillManager(nullptr,
-                               nullptr,
-                               DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {
+      : AndroidAutofillManager(nullptr, nullptr, EnableDownloadManager(false)) {
     set_autofill_provider_for_testing(autofill_provider);
   }
 
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.cc b/components/autofill/content/browser/content_autofill_driver_factory.cc
index 31f5f6e..d46c26e 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory.cc
@@ -39,6 +39,16 @@
 
 }  // namespace
 
+void BrowserDriverInitHook(AutofillClient* client,
+                           const std::string& app_locale,
+                           ContentAutofillDriver* driver) {
+  driver->set_autofill_manager(std::make_unique<BrowserAutofillManager>(
+      driver, client, app_locale,
+      AutofillManager::EnableDownloadManager(true)));
+  if (client && ShouldEnableHeavyFormDataScraping(client->GetChannel()))
+    driver->GetAutofillAgent()->EnableHeavyFormDataScraping();
+}
+
 const char ContentAutofillDriverFactory::
     kContentAutofillDriverFactoryWebContentsUserDataKey[] =
         "web_contents_autofill_driver_factory";
@@ -47,19 +57,13 @@
 void ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
     content::WebContents* contents,
     AutofillClient* client,
-    const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback) {
+    DriverInitCallback driver_init_hook) {
   if (FromWebContents(contents))
     return;
 
-  contents->SetUserData(
-      kContentAutofillDriverFactoryWebContentsUserDataKey,
-      base::WrapUnique(new ContentAutofillDriverFactory(
-          contents, client, app_locale, enable_download_manager,
-          std::move(autofill_manager_factory_callback))));
+  contents->SetUserData(kContentAutofillDriverFactoryWebContentsUserDataKey,
+                        base::WrapUnique(new ContentAutofillDriverFactory(
+                            contents, client, std::move(driver_init_hook))));
 }
 
 // static
@@ -93,36 +97,17 @@
 ContentAutofillDriverFactory::ContentAutofillDriverFactory(
     content::WebContents* web_contents,
     AutofillClient* client,
-    const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback)
+    DriverInitCallback driver_init_hook)
     : content::WebContentsObserver(web_contents),
       client_(client),
-      app_locale_(app_locale),
-      enable_download_manager_(enable_download_manager),
-      autofill_manager_factory_callback_(
-          std::move(autofill_manager_factory_callback)) {}
+      driver_init_hook_(std::move(driver_init_hook)) {}
 
 ContentAutofillDriverFactory::~ContentAutofillDriverFactory() = default;
 
 std::unique_ptr<ContentAutofillDriver>
 ContentAutofillDriverFactory::CreateDriver(content::RenderFrameHost* rfh) {
   auto driver = std::make_unique<ContentAutofillDriver>(rfh, &router_);
-  if (autofill_manager_factory_callback_) {
-    driver->set_autofill_manager(autofill_manager_factory_callback_.Run(
-        driver.get(), client(), app_locale_, enable_download_manager_));
-    driver->GetAutofillAgent()->SetUserGestureRequired(false);
-    driver->GetAutofillAgent()->SetSecureContextRequired(true);
-    driver->GetAutofillAgent()->SetFocusRequiresScroll(false);
-    driver->GetAutofillAgent()->SetQueryPasswordSuggestion(true);
-  } else {
-    driver->set_autofill_manager(std::make_unique<BrowserAutofillManager>(
-        driver.get(), client(), app_locale_, enable_download_manager_));
-  }
-  if (client() && ShouldEnableHeavyFormDataScraping(client()->GetChannel()))
-    driver->GetAutofillAgent()->EnableHeavyFormDataScraping();
+  driver_init_hook_.Run(driver.get());
   return driver;
 }
 
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.h b/components/autofill/content/browser/content_autofill_driver_factory.h
index 998d607..33aa10f 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.h
+++ b/components/autofill/content/browser/content_autofill_driver_factory.h
@@ -24,29 +24,37 @@
 
 class ContentAutofillDriver;
 
+// Creates an BrowserAutofillManager and attaches it to the `driver`.
+//
+// This hook is to be passed to CreateForWebContentsAndDelegate().
+// It is the glue between ContentAutofillDriver[Factory] and
+// BrowserAutofillManager.
+//
+// Other embedders (which don't want to use BrowserAutofillManager) shall use
+// other implementations.
+void BrowserDriverInitHook(AutofillClient* client,
+                           const std::string& app_locale,
+                           ContentAutofillDriver* driver);
+
 // Manages lifetime of ContentAutofillDriver. One Factory per WebContents
 // creates one Driver per RenderFrame.
 class ContentAutofillDriverFactory : public content::WebContentsObserver,
                                      public base::SupportsUserData::Data {
  public:
+  using DriverInitCallback =
+      base::RepeatingCallback<void(ContentAutofillDriver*)>;
+
   static const char kContentAutofillDriverFactoryWebContentsUserDataKey[];
 
   // Creates a factory for a WebContents object.
   //
-  // The |autofill_manager_factory_callback| is eventually called by
-  // ContentAutofillDriver's constructor. Chrome passes a null callback and
-  // ContentAutofillDriver calls SetBrowserAutofillManager() in this case.
-  //
-  // TODO(crbug.com/1200511): Remove default parameter and pass proper callback
-  // and remove ContentAutofillDriver::SetBrowserAutofillManager().
+  // The `driver_init_hook` is called whenever a driver is constructed, so it
+  // may configure the driver. In particular, it may create and set the driver's
+  // AutofillManager.
   static void CreateForWebContentsAndDelegate(
       content::WebContents* contents,
       AutofillClient* client,
-      const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback
-          autofill_manager_factory_callback = {});
+      DriverInitCallback driver_init_hook);
 
   static ContentAutofillDriverFactory* FromWebContents(
       content::WebContents* contents);
@@ -78,23 +86,15 @@
  private:
   friend class ContentAutofillDriverFactoryTestApi;
 
-  ContentAutofillDriverFactory(
-      content::WebContents* web_contents,
-      AutofillClient* client,
-      const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback
-          autofill_manager_factory_callback);
+  ContentAutofillDriverFactory(content::WebContents* web_contents,
+                               AutofillClient* client,
+                               DriverInitCallback driver_init_hook);
 
   std::unique_ptr<ContentAutofillDriver> CreateDriver(
       content::RenderFrameHost* rfh);
 
   const raw_ptr<AutofillClient> client_;
-  std::string app_locale_;
-  BrowserAutofillManager::AutofillDownloadManagerState enable_download_manager_;
-  AutofillManager::AutofillManagerFactoryCallback
-      autofill_manager_factory_callback_;
+  DriverInitCallback driver_init_hook_;
 
   // Routes events between different drivers.
   // Must be destroyed after |driver_map_|'s elements.
diff --git a/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc b/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc
index fe8583e..5808fd8 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory_test_api.cc
@@ -11,14 +11,9 @@
 ContentAutofillDriverFactoryTestApi::Create(
     content::WebContents* web_contents,
     AutofillClient* client,
-    const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback) {
-  return base::WrapUnique(new ContentAutofillDriverFactory(
-      web_contents, client, app_locale, enable_download_manager,
-      autofill_manager_factory_callback));
+    ContentAutofillDriverFactory::DriverInitCallback driver_init_hook) {
+  return base::WrapUnique(
+      new ContentAutofillDriverFactory(web_contents, client, driver_init_hook));
 }
 
 ContentAutofillDriverFactoryTestApi::ContentAutofillDriverFactoryTestApi(
diff --git a/components/autofill/content/browser/content_autofill_driver_factory_test_api.h b/components/autofill/content/browser/content_autofill_driver_factory_test_api.h
index 8c1c4fcd..416e0e5 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory_test_api.h
+++ b/components/autofill/content/browser/content_autofill_driver_factory_test_api.h
@@ -18,11 +18,7 @@
   static std::unique_ptr<ContentAutofillDriverFactory> Create(
       content::WebContents* web_contents,
       AutofillClient* client,
-      const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback
-          autofill_manager_factory_callback);
+      ContentAutofillDriverFactory::DriverInitCallback driver_init_hook);
 
   explicit ContentAutofillDriverFactoryTestApi(
       ContentAutofillDriverFactory* factory);
diff --git a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
index 8cb0b4ed..75dab6a4 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory_unittest.cc
@@ -133,10 +133,9 @@
                             base::Unretained(agent_.get())));
 
     factory_ = ContentAutofillDriverFactoryTestApi::Create(
-        web_contents(), client_.get(), "en_US",
-        BrowserAutofillManager::AutofillDownloadManagerState::
-            ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
-        AutofillManager::AutofillManagerFactoryCallback());
+        web_contents(), client_.get(),
+        base::BindRepeating(&autofill::BrowserDriverInitHook, client_.get(),
+                            "en-US"));
   }
 
   void TearDown() override {
diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc
index 3942cb9..3bee7721 100644
--- a/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -55,8 +55,6 @@
 namespace {
 
 const char kAppLocale[] = "en-US";
-const BrowserAutofillManager::AutofillDownloadManagerState kDownloadState =
-    BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER;
 
 class FakeAutofillAgent : public mojom::AutofillAgent {
  public:
@@ -286,8 +284,11 @@
 class MockBrowserAutofillManager : public BrowserAutofillManager {
  public:
   MockBrowserAutofillManager(AutofillDriver* driver, AutofillClient* client)
-      : BrowserAutofillManager(driver, client, kAppLocale, kDownloadState) {}
-  ~MockBrowserAutofillManager() override {}
+      : BrowserAutofillManager(driver,
+                               client,
+                               kAppLocale,
+                               EnableDownloadManager(false)) {}
+  ~MockBrowserAutofillManager() override = default;
 
   MOCK_METHOD(void, Reset, (), (override));
   MOCK_METHOD(bool, ShouldParseForms, (const std::vector<FormData>&), ());
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index 5f8fc52f..ee211e4d 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -102,28 +102,15 @@
          channel == version_info::Channel::DEV;
 }
 
-AutofillManager::AutofillManager(
-    AutofillDriver* driver,
-    AutofillClient* client,
-    AutofillDownloadManagerState enable_download_manager)
-    : AutofillManager(driver,
-                      client,
-                      enable_download_manager,
-                      client->GetChannel()) {
-  DCHECK(driver);
-  DCHECK(client);
-}
-
-AutofillManager::AutofillManager(
-    AutofillDriver* driver,
-    AutofillClient* client,
-    AutofillDownloadManagerState enable_download_manager,
-    version_info::Channel channel)
+AutofillManager::AutofillManager(AutofillDriver* driver,
+                                 AutofillClient* client,
+                                 version_info::Channel channel,
+                                 EnableDownloadManager enable_download_manager)
     : driver_(driver),
       client_(client),
       log_manager_(client ? client->GetLogManager() : nullptr),
       form_interactions_ukm_logger_(CreateFormInteractionsUkmLogger()) {
-  if (enable_download_manager == ENABLE_AUTOFILL_DOWNLOAD_MANAGER) {
+  if (enable_download_manager) {
     download_manager_ = std::make_unique<AutofillDownloadManager>(
         driver, this, GetAPIKeyForUrl(channel),
         AutofillDownloadManager::IsRawMetadataUploadingEnabled(
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 858170a0..02d9b255 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -14,6 +14,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
+#include "base/types/strong_alias.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_download_manager.h"
 #include "components/autofill/core/browser/autofill_driver.h"
@@ -47,11 +48,6 @@
     : public AutofillDownloadManager::Observer,
       public translate::TranslateDriver::LanguageDetectionObserver {
  public:
-  enum AutofillDownloadManagerState {
-    ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
-    DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
-  };
-
   // An observer class used by browsertests that gets notified whenever
   // particular actions occur.
   class ObserverForTest {
@@ -59,14 +55,8 @@
     virtual void OnFormParsed() = 0;
   };
 
-  // The factory method for the embedder to create the subclass of
-  // AutofillManager in ContentAutofillDriver.
-  using AutofillManagerFactoryCallback =
-      base::RepeatingCallback<std::unique_ptr<AutofillManager>(
-          AutofillDriver*,
-          AutofillClient*,
-          const std::string& app_locale,
-          AutofillManager::AutofillDownloadManagerState)>;
+  using EnableDownloadManager =
+      base::StrongAlias<struct EnableDownloadManagerTag, bool>;
 
   // Raw metadata uploading enabled iff this Chrome instance is on Canary or Dev
   // channel.
@@ -273,11 +263,8 @@
  protected:
   AutofillManager(AutofillDriver* driver,
                   AutofillClient* client,
-                  AutofillDownloadManagerState enable_download_manager);
-  AutofillManager(AutofillDriver* driver,
-                  AutofillClient* client,
-                  AutofillDownloadManagerState enable_download_manager,
-                  version_info::Channel channel);
+                  version_info::Channel channel,
+                  EnableDownloadManager enable_download_manager);
 
   LogManager* log_manager() { return log_manager_; }
 
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc
index eb0fa66..6b025a6 100644
--- a/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -50,7 +50,10 @@
 class MockAutofillManager : public AutofillManager {
  public:
   MockAutofillManager(AutofillDriver* driver, AutofillClient* client)
-      : AutofillManager(driver, client, DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {}
+      : AutofillManager(driver,
+                        client,
+                        client->GetChannel(),
+                        EnableDownloadManager(false)) {}
   MOCK_METHOD(bool, ShouldClearPreviewedForm, (), (override));
   MOCK_METHOD(AutofillOfferManager*, GetOfferManager, (), (override));
   MOCK_METHOD(CreditCardAccessManager*,
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 87c6eff..3a09b76 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -412,7 +412,7 @@
     AutofillDriver* driver,
     AutofillClient* client,
     const std::string& app_locale,
-    AutofillDownloadManagerState enable_download_manager)
+    EnableDownloadManager enable_download_manager)
     : BrowserAutofillManager(driver,
                              client,
                              client->GetPersonalDataManager(),
@@ -424,8 +424,11 @@
     AutofillClient* client,
     PersonalDataManager* personal_data,
     const std::string app_locale,
-    AutofillDownloadManagerState enable_download_manager)
-    : AutofillManager(driver, client, enable_download_manager),
+    EnableDownloadManager enable_download_manager)
+    : AutofillManager(driver,
+                      client,
+                      client->GetChannel(),
+                      enable_download_manager),
       external_delegate_(
           std::make_unique<AutofillExternalDelegate>(this, driver)),
       app_locale_(app_locale),
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 7bd14a0..42eb691e 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -105,7 +105,7 @@
   BrowserAutofillManager(AutofillDriver* driver,
                          AutofillClient* client,
                          const std::string& app_locale,
-                         AutofillDownloadManagerState enable_download_manager);
+                         EnableDownloadManager enable_download_manager);
 
   BrowserAutofillManager(const BrowserAutofillManager&) = delete;
   BrowserAutofillManager& operator=(const BrowserAutofillManager&) = delete;
@@ -341,8 +341,8 @@
                          AutofillClient* client,
                          PersonalDataManager* personal_data,
                          const std::string app_locale = "en-US",
-                         AutofillDownloadManagerState enable_download_manager =
-                             DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+                         EnableDownloadManager enable_download_manager =
+                             EnableDownloadManager(false));
 
   // Uploads the form data to the Autofill server. |observed_submission|
   // indicates that upload is the result of a submission event.
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index c0f1e85..f68a4ba 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -81,12 +81,12 @@
 
 using ::autofill::metrics::kTestGuid;
 using base::ASCIIToUTF16;
-using base::Bucket;
 using base::TimeTicks;
 using ::testing::ElementsAre;
 using ::testing::HasSubstr;
 using ::testing::Matcher;
 using ::testing::NiceMock;
+using ::testing::UnorderedElementsAre;
 using ::testing::UnorderedPointwise;
 
 namespace autofill {
@@ -325,28 +325,15 @@
   return response_string;
 }
 
-template <typename T>
-struct HistogramBucketExpectation {
-  T bucket;
-  size_t count;
+template <typename MetricEnum>
+struct Bucket : public base::Bucket {
+  Bucket(MetricEnum bucket, base::HistogramBase::Count count)
+      : base::Bucket(static_cast<base::HistogramBase::Sample>(bucket), count) {}
 };
 
-// Checks that the given buckets have the given counts.
-// Additionally checks that the overall count is `total_count`, which defaults
-// to the sum of `expectations` counts.
-template <typename T>
-void ExpectBuckets(const base::HistogramTester& histogram_tester,
-                   base::StringPiece metric,
-                   std::vector<HistogramBucketExpectation<T>> expectations,
-                   absl::optional<size_t> total_size = absl::nullopt) {
-  if (!total_size) {
-    total_size = 0;
-    for (const auto& e : expectations)
-      *total_size += e.count;
-  }
-  histogram_tester.ExpectTotalCount(metric, *total_size);
-  for (const auto& e : expectations)
-    histogram_tester.ExpectBucketCount(metric, e.bucket, e.count);
+template <typename... MetricEnum>
+auto AreBuckets(Bucket<MetricEnum>... buckets) {
+  return ::testing::UnorderedElementsAre(buckets...);
 }
 
 }  // namespace
@@ -12648,6 +12635,10 @@
   };
 
   base::HistogramTester histogram_tester;
+  auto SamplesOf = [&histogram_tester](base::StringPiece metric) {
+    return histogram_tester.GetAllSamples(metric);
+  };
+
   SeeForm();
 
   fill_data().cvc = u"";
@@ -12676,52 +12667,47 @@
   SubmitForm();
   ResetDriverToCommitMetrics();
 
-  ExpectBuckets<Metric>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFillable.AtFillTimeBeforeSecurityPolicy",
-      {{Metric::kFullFill, 2}});
-  ExpectBuckets<int>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFillable.AtFillTimeBeforeSecurityPolicy."
-      "Bitmask",
-      {{kName | kNumber | kExp | kCvc, 2}});
+  EXPECT_THAT(SamplesOf("Autofill.CreditCard.SeamlessFillable."
+                        "AtFillTimeBeforeSecurityPolicy"),
+              AreBuckets(Bucket(Metric::kFullFill, 2)));
+  EXPECT_THAT(SamplesOf("Autofill.CreditCard.SeamlessFillable."
+                        "AtFillTimeBeforeSecurityPolicy"),
+              AreBuckets(Bucket(Metric::kFullFill, 2)));
+  EXPECT_THAT(SamplesOf("Autofill.CreditCard.SeamlessFillable."
+                        "AtFillTimeBeforeSecurityPolicy.Bitmask"),
+              AreBuckets(Bucket(kName | kNumber | kExp | kCvc, 2)));
 
-  ExpectBuckets<Metric>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy",
-      {{Metric::kPartialFill, 2}});
-  ExpectBuckets<int>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy."
-      "Bitmask",
-      {{kName | kExp, 1}, {kNumber | kCvc, 1}});
+  EXPECT_THAT(
+      SamplesOf(
+          "Autofill.CreditCard.SeamlessFillable.AtFillTimeAfterSecurityPolicy"),
+      AreBuckets(Bucket(Metric::kPartialFill, 2)));
+  EXPECT_THAT(SamplesOf("Autofill.CreditCard.SeamlessFillable."
+                        "AtFillTimeAfterSecurityPolicy.Bitmask"),
+              AreBuckets(Bucket(kName | kExp, 1), Bucket(kNumber | kCvc, 1)));
 
-  ExpectBuckets<Metric>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy",
-      {{Metric::kOptionalCvcMissing, 1}, {Metric::kPartialFill, 1}});
-  ExpectBuckets<int>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy."
-      "Bitmask",
-      {{kName | kNumber | kExp, 1}, {kNumber, 1}});
+  EXPECT_THAT(
+      SamplesOf(
+          "Autofill.CreditCard.SeamlessFills.AtFillTimeBeforeSecurityPolicy"),
+      AreBuckets(Bucket(Metric::kOptionalCvcMissing, 1),
+                 Bucket(Metric::kPartialFill, 1)));
+  EXPECT_THAT(
+      SamplesOf("Autofill.CreditCard.SeamlessFills."
+                "AtFillTimeBeforeSecurityPolicy.Bitmask"),
+      AreBuckets(Bucket(kName | kNumber | kExp, 1), Bucket(kNumber, 1)));
 
-  ExpectBuckets<Metric>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy",
-      {{Metric::kPartialFill, 2}});
-  ExpectBuckets<int>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy.Bitmask",
-      {{kName | kExp, 1}, {kNumber, 1}});
+  EXPECT_THAT(
+      SamplesOf(
+          "Autofill.CreditCard.SeamlessFills.AtFillTimeAfterSecurityPolicy"),
+      AreBuckets(Bucket(Metric::kPartialFill, 2)));
+  EXPECT_THAT(SamplesOf("Autofill.CreditCard.SeamlessFills."
+                        "AtFillTimeAfterSecurityPolicy.Bitmask"),
+              AreBuckets(Bucket(kName | kExp, 1), Bucket(kNumber, 1)));
 
-  ExpectBuckets<Metric>(histogram_tester,
-                        "Autofill.CreditCard.SeamlessFills.AtSubmissionTime",
-                        {{Metric::kOptionalCvcMissing, 1}});
-  ExpectBuckets<int>(
-      histogram_tester,
-      "Autofill.CreditCard.SeamlessFills.AtSubmissionTime.Bitmask",
-      {{kName | kNumber | kExp, 1}});
+  EXPECT_THAT(SamplesOf("Autofill.CreditCard.SeamlessFills.AtSubmissionTime"),
+              AreBuckets(Bucket(Metric::kOptionalCvcMissing, 1)));
+  EXPECT_THAT(
+      SamplesOf("Autofill.CreditCard.SeamlessFills.AtSubmissionTime.Bitmask"),
+      AreBuckets(Bucket(kName | kNumber | kExp, 1)));
 
   VerifyUkm(
       test_ukm_recorder_, form_, UkmBuilder::kEntryName,
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm
index eb64f04b..29aba95b 100644
--- a/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -129,7 +129,7 @@
   std::string locale("en");
   autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
       &fake_web_state_, &client_, nil, locale,
-      autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      autofill::AutofillManager::EnableDownloadManager(false));
 
   autofill::FormData form;
   form.url = GURL("https://myform.com");
@@ -369,7 +369,7 @@
   std::string locale("en");
   autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
       &fake_web_state_, &client_, nil, locale,
-      autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      autofill::AutofillManager::EnableDownloadManager(false));
 
   // Remove the current main frame.
   RemoveWebFrame(fake_main_frame_->GetFrameId());
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index 11c752c..90fb7812 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -34,8 +34,7 @@
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager);
+      AutofillManager::EnableDownloadManager enable_download_manager);
 
   static AutofillDriverIOS* FromWebStateAndWebFrame(web::WebState* web_state,
                                                     web::WebFrame* web_frame);
@@ -87,13 +86,13 @@
   void set_processed(bool processed) { processed_ = processed; }
 
  protected:
-  AutofillDriverIOS(web::WebState* web_state,
-                    web::WebFrame* web_frame,
-                    AutofillClient* client,
-                    id<AutofillDriverIOSBridge> bridge,
-                    const std::string& app_locale,
-                    BrowserAutofillManager::AutofillDownloadManagerState
-                        enable_download_manager);
+  AutofillDriverIOS(
+      web::WebState* web_state,
+      web::WebFrame* web_frame,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::EnableDownloadManager enable_download_manager);
 
  private:
   // The WebState with which this object is associated.
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 79b81eb..0a7ed36 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -28,8 +28,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager) {
+    AutofillManager::EnableDownloadManager enable_download_manager) {
   // By the time this method is called, no web_frame is available. This method
   // only prepares the factory and the AutofillDriverIOS will be created in the
   // first call to FromWebStateAndWebFrame.
@@ -52,8 +51,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager)
+    AutofillManager::EnableDownloadManager enable_download_manager)
     : web_state_(web_state),
       bridge_(bridge),
       browser_autofill_manager_(this,
@@ -63,7 +61,7 @@
   web_frame_id_ = web::GetWebFrameId(web_frame);
 }
 
-AutofillDriverIOS::~AutofillDriverIOS() {}
+AutofillDriverIOS::~AutofillDriverIOS() = default;
 
 bool AutofillDriverIOS::IsIncognito() const {
   return web_state_->GetBrowserState()->IsOffTheRecord();
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webframe.h b/components/autofill/ios/browser/autofill_driver_ios_webframe.h
index ad55589..3143dfa 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_webframe.h
+++ b/components/autofill/ios/browser/autofill_driver_ios_webframe.h
@@ -31,8 +31,7 @@
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager);
+      AutofillManager::EnableDownloadManager enable_download_manager);
   ~AutofillDriverIOSWebFrameFactory() override;
 
   AutofillDriverIOSWebFrameFactory(
@@ -40,8 +39,7 @@
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager);
+      AutofillManager::EnableDownloadManager enable_download_manager);
 
   // Returns a AutofillDriverIOSFromWebFrame for |web_frame|, creating it if
   // needed.
@@ -55,7 +53,7 @@
   AutofillClient* client_ = nullptr;
   id<AutofillDriverIOSBridge> bridge_ = nil;
   std::string app_locale_;
-  BrowserAutofillManager::AutofillDownloadManagerState enable_download_manager_;
+  AutofillManager::EnableDownloadManager enable_download_manager_;
   WEB_STATE_USER_DATA_KEY_DECL();
 };
 
@@ -76,8 +74,7 @@
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager);
+      AutofillManager::EnableDownloadManager enable_download_manager);
 
  private:
   friend class base::RefCountedThreadSafe<AutofillDriverIOSRefCountable>;
@@ -96,21 +93,20 @@
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
-      BrowserAutofillManager::AutofillDownloadManagerState
-          enable_download_manager);
+      AutofillManager::EnableDownloadManager enable_download_manager);
 
   ~AutofillDriverIOSWebFrame() override;
 
   AutofillDriverIOS* driver() { return driver_.get(); }
   scoped_refptr<AutofillDriverIOSRefCountable> GetRetainableDriver();
 
-  AutofillDriverIOSWebFrame(web::WebState* web_state,
-                            web::WebFrame* web_frame,
-                            AutofillClient* client,
-                            id<AutofillDriverIOSBridge> bridge,
-                            const std::string& app_locale,
-                            BrowserAutofillManager::AutofillDownloadManagerState
-                                enable_download_manager);
+  AutofillDriverIOSWebFrame(
+      web::WebState* web_state,
+      web::WebFrame* web_frame,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::EnableDownloadManager enable_download_manager);
   scoped_refptr<AutofillDriverIOSRefCountable> driver_;
 };
 }  // namespace autofill
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webframe.mm b/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
index 67b30cf6..95f8fd85 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
@@ -12,8 +12,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager) {
+    AutofillManager::EnableDownloadManager enable_download_manager) {
   if (FromWebState(web_state))
     return;
 
@@ -28,8 +27,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager)
+    AutofillManager::EnableDownloadManager enable_download_manager)
     : web_state_(web_state),
       client_(client),
       bridge_(bridge),
@@ -54,8 +52,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager) {
+    AutofillManager::EnableDownloadManager enable_download_manager) {
   if (FromWebFrame(web_frame))
     return;
 
@@ -71,9 +68,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager)
-
+    AutofillManager::EnableDownloadManager enable_download_manager)
     : AutofillDriverIOS(web_state,
                         web_frame,
                         client,
@@ -87,8 +82,7 @@
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
-    BrowserAutofillManager::AutofillDownloadManagerState
-        enable_download_manager)
+    AutofillManager::EnableDownloadManager enable_download_manager)
     : driver_(base::MakeRefCounted<AutofillDriverIOSRefCountable>(
           web_state,
           web_frame,
diff --git a/components/autofill_assistant/browser/full_card_requester_unittest.cc b/components/autofill_assistant/browser/full_card_requester_unittest.cc
index 7844e44..4012f736 100644
--- a/components/autofill_assistant/browser/full_card_requester_unittest.cc
+++ b/components/autofill_assistant/browser/full_card_requester_unittest.cc
@@ -50,8 +50,9 @@
         &browser_context_, nullptr);
     autofill_client_.SetPrefs(autofill::test::PrefServiceForTesting());
     autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
-        web_contents_.get(), &autofill_client_, "en-US",
-        autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+        web_contents_.get(), &autofill_client_,
+        base::BindRepeating(&autofill::BrowserDriverInitHook, &autofill_client_,
+                            "en-US"));
     autofill_client_.set_test_payments_client(
         std::make_unique<autofill::payments::TestPaymentsClient>(
             test_url_loader_factory_.GetSafeWeakWrapper(),
diff --git a/components/global_media_controls/public/media_session_item_producer.cc b/components/global_media_controls/public/media_session_item_producer.cc
index bb142c7..358ee9d 100644
--- a/components/global_media_controls/public/media_session_item_producer.cc
+++ b/components/global_media_controls/public/media_session_item_producer.cc
@@ -345,6 +345,7 @@
 
   session->set_dismiss_reason(
       GlobalMediaControlsDismissReason::kUserDismissedNotification);
+  session->item()->Stop();
   session->item()->Dismiss();
 }
 
diff --git a/components/global_media_controls/public/media_session_notification_item.cc b/components/global_media_controls/public/media_session_notification_item.cc
index f01ff8e1..5f4b739c 100644
--- a/components/global_media_controls/public/media_session_notification_item.cc
+++ b/components/global_media_controls/public/media_session_notification_item.cc
@@ -176,8 +176,6 @@
 }
 
 void MediaSessionNotificationItem::Dismiss() {
-  if (media_controller_remote_.is_bound())
-    media_controller_remote_->Stop();
   delegate_->RemoveItem(request_id_);
 }
 
@@ -185,6 +183,11 @@
   return media_message_center::SourceType::kLocalMediaSession;
 }
 
+void MediaSessionNotificationItem::Stop() {
+  if (media_controller_remote_.is_bound())
+    media_controller_remote_->Stop();
+}
+
 void MediaSessionNotificationItem::Raise() {
   if (!media_controller_remote_.is_bound())
     return;
diff --git a/components/global_media_controls/public/media_session_notification_item.h b/components/global_media_controls/public/media_session_notification_item.h
index 5e8607a..38f8b9db 100644
--- a/components/global_media_controls/public/media_session_notification_item.h
+++ b/components/global_media_controls/public/media_session_notification_item.h
@@ -96,6 +96,9 @@
   void SetVolume(float volume) override {}
   void SetMute(bool mute) override;
 
+  // Stops the media session.
+  void Stop();
+
   // Calls |Raise()| on the underlying MediaSession, which will focus the
   // WebContents if the MediaSession is associated with one.
   void Raise();
diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc
index b2b7711..476d5c3 100644
--- a/components/guest_view/browser/guest_view_base.cc
+++ b/components/guest_view/browser/guest_view_base.cc
@@ -581,9 +581,9 @@
 
 void GuestViewBase::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
+  // TODO(crbug.com/1261928): Due to the use of inner WebContents, a
+  // GuestViewBase's main frame is considered primary. This will no
+  // longer be the case once we migrate guest views to MPArch.
   if (!navigation_handle->IsInPrimaryMainFrame() ||
       !navigation_handle->HasCommitted())
     return;
diff --git a/components/history_clusters/core/config.cc b/components/history_clusters/core/config.cc
index f2911dd2..455f6f18 100644
--- a/components/history_clusters/core/config.cc
+++ b/components/history_clusters/core/config.cc
@@ -209,17 +209,15 @@
       features::kOnDeviceClustering, "split_clusters_at_search_visits",
       split_clusters_at_search_visits);
 
-  should_label_clusters = GetFieldTrialParamByFeatureAsBool(
-      features::kOnDeviceClustering, "should_label_clusters",
-      should_label_clusters);
+  should_label_clusters =
+      base::FeatureList::IsEnabled(internal::kJourneysLabels);
 
   labels_from_hostnames = GetFieldTrialParamByFeatureAsBool(
-      features::kOnDeviceClustering, "labels_from_hostnames",
+      internal::kJourneysLabels, "labels_from_hostnames",
       labels_from_hostnames);
 
   labels_from_entities = GetFieldTrialParamByFeatureAsBool(
-      features::kOnDeviceClustering, "labels_from_entities",
-      labels_from_entities);
+      internal::kJourneysLabels, "labels_from_entities", labels_from_entities);
 
   should_check_hosts_to_skip_clustering_for =
       base::FeatureList::IsEnabled(features::kOnDeviceClusteringBlocklists);
diff --git a/components/history_clusters/core/features.cc b/components/history_clusters/core/features.cc
index cd19bda..ef5e6c2 100644
--- a/components/history_clusters/core/features.cc
+++ b/components/history_clusters/core/features.cc
@@ -29,6 +29,9 @@
 
 const base::Feature kJourneys{"Journeys", enabled_by_default_desktop_only};
 
+const base::Feature kJourneysLabels{"JourneysLabel",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kOmniboxAction{"JourneysOmniboxAction",
                                    enabled_by_default_desktop_only};
 
diff --git a/components/history_clusters/core/features.h b/components/history_clusters/core/features.h
index c84cf422c..3a97b119 100644
--- a/components/history_clusters/core/features.h
+++ b/components/history_clusters/core/features.h
@@ -19,6 +19,9 @@
 // directly. Instead use `IsJourneysEnabled()` for the system language filter.
 extern const base::Feature kJourneys;
 
+// Enables labelling of Journeys in UI.
+extern const base::Feature kJourneysLabels;
+
 // Enables the Journeys Omnibox Action chip. `kJourneys` must also be enabled
 // for this to take effect.
 extern const base::Feature kOmniboxAction;
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index b939925..f080e71 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -767,7 +767,7 @@
        i != providers_.end(); ++i)
     result_.AppendMatches(input_, (*i)->matches());
 
-  bool perform_tab_match = OmniboxFieldTrial::IsTabSwitchSuggestionsEnabled();
+  bool perform_tab_match = true;
 #if BUILDFLAG(IS_ANDROID)
   // Do not look for matching tabs on Android unless we collected all the
   // suggestions. Tab matching is an expensive process with multiple JNI calls
diff --git a/components/omnibox/browser/keyword_provider.cc b/components/omnibox/browser/keyword_provider.cc
index 7569cf1..a08971d1 100644
--- a/components/omnibox/browser/keyword_provider.cc
+++ b/components/omnibox/browser/keyword_provider.cc
@@ -217,6 +217,13 @@
     return std::u16string();
   }
 
+  // Don't provide a keyword if it's a starter pack engine and the starter pack
+  // feature flag is not enabled.
+  if (!OmniboxFieldTrial::IsSiteSearchStarterPackEnabled() &&
+      template_url->starter_pack_id() != 0) {
+    return std::u16string();
+  }
+
   return keyword;
 }
 
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index a6b72b45..d86cc4b 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -602,10 +602,6 @@
   return value;
 }
 
-bool OmniboxFieldTrial::IsTabSwitchSuggestionsEnabled() {
-  return base::FeatureList::IsEnabled(omnibox::kOmniboxTabSwitchSuggestions);
-}
-
 bool OmniboxFieldTrial::IsFuzzyUrlSuggestionsEnabled() {
   return base::FeatureList::IsEnabled(omnibox::kOmniboxFuzzyUrlSuggestions);
 }
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index b437a6e..61f87a2 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -353,9 +353,6 @@
 // ---------------------------------------------------------
 // For UI experiments.
 
-// Returns true if the tab switch suggestions flag is enabled.
-bool IsTabSwitchSuggestionsEnabled();
-
 // Returns true if the fuzzy URL suggestions feature is enabled.
 bool IsFuzzyUrlSuggestionsEnabled();
 
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 2ab31d7a..3098204 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -37,12 +37,6 @@
 //     base::FEATURE_DISABLED_BY_DEFAULT;
 // #endif
 
-// Feature that enables the tab-switch suggestions corresponding to an open
-// tab, for a button or dedicated suggestion. Enabled by default on Desktop, iOS
-// and Android.
-const base::Feature kOmniboxTabSwitchSuggestions{
-    "OmniboxTabSwitchSuggestions", base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Feature used to enable various experiments on keyword mode, UI and
 // suggestions.
 const base::Feature kExperimentalKeywordMode{"OmniboxExperimentalKeywordMode",
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 44466ac1..d2ce023 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -13,7 +13,6 @@
 // Please do not add more features to this "big blob" list.
 // Instead, use the categorized and alphabetized lists below this "big blob".
 // You can create a new category if none of the existing ones fit.
-extern const base::Feature kOmniboxTabSwitchSuggestions;
 extern const base::Feature kExperimentalKeywordMode;
 extern const base::Feature kImageSearchSuggestionThumbnail;
 extern const base::Feature kDisplayTitleForCurrentUrl;
diff --git a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
index b0a4a0e..7e83431f 100644
--- a/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
+++ b/components/remote_cocoa/app_shim/native_widget_ns_window_bridge.mm
@@ -539,9 +539,9 @@
             display:YES
             animate:NO];
 
-  // If the window has focus but is not on the
-  // active space and the window was moved to a different display, re-activate
-  // it to switch the space to the active window. (crbug.com/1316543)
+  // If the window has focus but is not on the active space and the window was
+  // moved to a different display, re-activate it to switch the space to the
+  // active window. (crbug.com/1316543)
   if ([window_ isKeyWindow] && ![window_ isOnActiveSpace] &&
       [window_ screen] != previous_screen) {
     SetVisibilityState(WindowVisibilityState::kShowAndActivateWindow);
diff --git a/components/reporting/util/statusor.h b/components/reporting/util/statusor.h
index 0d68bd8..81b2f5e 100644
--- a/components/reporting/util/statusor.h
+++ b/components/reporting/util/statusor.h
@@ -62,7 +62,6 @@
 
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
-#include "base/template_util.h"
 #include "components/reporting/util/status.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -90,8 +89,8 @@
   // is statically set to true, otherwise it is statically set to false.
   template <class U, typename V>
   struct is_implicitly_constructible
-      : base::conjunction<std::is_constructible<U, V>,
-                          std::is_convertible<V, U>> {};
+      : std::conjunction<std::is_constructible<U, V>,
+                         std::is_convertible<V, U>> {};
 
  public:
   // Constructs a new StatusOr with UNINITIALIZED status and no value.
diff --git a/components/search_engines/keyword_web_data_service.cc b/components/search_engines/keyword_web_data_service.cc
index 6ccf3e4d..64f9f210 100644
--- a/components/search_engines/keyword_web_data_service.cc
+++ b/components/search_engines/keyword_web_data_service.cc
@@ -32,6 +32,7 @@
   result.default_search_provider_id =
       keyword_table->GetDefaultSearchProviderID();
   result.builtin_keyword_version = keyword_table->GetBuiltinKeywordVersion();
+  result.starter_pack_version = keyword_table->GetStarterPackKeywordVersion();
   return std::make_unique<WDResult<WDKeywordsResult>>(KEYWORDS_RESULT, result);
 }
 
diff --git a/components/search_engines/keyword_web_data_service.h b/components/search_engines/keyword_web_data_service.h
index cb418bf..9066466 100644
--- a/components/search_engines/keyword_web_data_service.h
+++ b/components/search_engines/keyword_web_data_service.h
@@ -32,8 +32,10 @@
   // Identifies the ID of the TemplateURL that is the default search. A value of
   // 0 indicates there is no default search provider.
   int64_t default_search_provider_id = 0;
-  // Version of the built-in keywords. A value of 0 indicates a first run.
+  // Version of the built-in keywords and starter pack engines. A value of 0
+  // indicates a first run.
   int builtin_keyword_version = 0;
+  int starter_pack_version = 0;
 };
 
 class WebDataServiceConsumer;
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 1c5a390..99aa92bb 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -930,13 +930,15 @@
   std::unique_ptr<OwnedTemplateURLVector> template_urls =
       std::make_unique<OwnedTemplateURLVector>();
   int new_resource_keyword_version = 0;
+  int new_resource_starter_pack_version = 0;
   {
     GetSearchProvidersUsingKeywordResult(
         *result, web_data_service_.get(), prefs_, template_urls.get(),
         (default_search_provider_source_ == DefaultSearchManager::FROM_USER)
             ? initial_default_search_provider_.get()
             : nullptr,
-        search_terms_data(), &new_resource_keyword_version, &pre_sync_deletes_);
+        search_terms_data(), &new_resource_keyword_version,
+        &new_resource_starter_pack_version, &pre_sync_deletes_);
   }
 
   Scoper scoper(this);
@@ -957,6 +959,10 @@
 
     if (new_resource_keyword_version)
       web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
+
+    if (new_resource_starter_pack_version)
+      web_data_service_->SetStarterPackKeywordVersion(
+          new_resource_starter_pack_version);
   }
 
   if (default_search_provider_) {
diff --git a/components/search_engines/util.cc b/components/search_engines/util.cc
index dd82928..4731ee8 100644
--- a/components/search_engines/util.cc
+++ b/components/search_engines/util.cc
@@ -21,6 +21,7 @@
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_prepopulate_data.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/search_engines/template_url_starter_pack_data.h"
 
 std::u16string GetDefaultSearchEngineName(TemplateURLService* service) {
   DCHECK(service);
@@ -184,6 +185,8 @@
                          TemplateURLData* url_to_update) {
   DCHECK(original_turl->prepopulate_id() == 0 ||
          original_turl->prepopulate_id() == url_to_update->prepopulate_id);
+  DCHECK(original_turl->starter_pack_id() == 0 ||
+         original_turl->starter_pack_id() == url_to_update->starter_pack_id);
   // When the user modified search engine's properties or search engine is
   // imported from Play API data we need to preserve certain search engine
   // properties from overriding with prepopulated data.
@@ -309,6 +312,78 @@
   return actions;
 }
 
+void MergeEnginesFromStarterPackData(
+    KeywordWebDataService* service,
+    TemplateURLService::OwnedTemplateURLVector* template_urls,
+    TemplateURL* default_search_provider,
+    std::set<std::string>* removed_keyword_guids) {
+  DCHECK(template_urls);
+
+  std::vector<std::unique_ptr<TemplateURLData>> starter_pack_urls =
+      TemplateURLStarterPackData::GetStarterPackEngines();
+
+  ActionsFromCurrentData actions(CreateActionsFromCurrentStarterPackData(
+      &starter_pack_urls, *template_urls));
+
+  ApplyActionsFromCurrentData(actions, service, template_urls,
+                              default_search_provider, removed_keyword_guids);
+}
+
+ActionsFromCurrentData CreateActionsFromCurrentStarterPackData(
+    std::vector<std::unique_ptr<TemplateURLData>>* starter_pack_urls,
+    const TemplateURLService::OwnedTemplateURLVector& existing_urls) {
+  // Create a map to hold all provided |template_urls| that originally came from
+  // starter_pack data (i.e. have a non-zero starter_pack_id()).
+  std::map<int, TemplateURL*> id_to_turl;
+  for (auto& turl : existing_urls) {
+    int starter_pack_id = turl->starter_pack_id();
+    if (starter_pack_id > 0)
+      id_to_turl[starter_pack_id] = turl.get();
+  }
+
+  // For each current starter pack URL, check whether |template_urls| contained
+  // a matching starter pack URL.  If so, update the passed-in URL to match the
+  // current data.  (If the passed-in URL was user-edited, we persist the user's
+  // name and keyword.)  If not, add the prepopulated URL.
+  ActionsFromCurrentData actions;
+  for (auto& url : *starter_pack_urls) {
+    const int starter_pack_id = url->starter_pack_id;
+    DCHECK_NE(0, starter_pack_id);
+
+    auto existing_url_iter = id_to_turl.find(starter_pack_id);
+    TemplateURL* existing_url = nullptr;
+    if (existing_url_iter != id_to_turl.end()) {
+      existing_url = existing_url_iter->second;
+      id_to_turl.erase(existing_url_iter);
+    }
+
+    if (existing_url != nullptr) {
+      // Update the data store with the new prepopulated data. Preserve user
+      // edits to the name and keyword.
+      MergeIntoEngineData(existing_url, url.get());
+      // Update last_modified to ensure that if this entry is later merged with
+      // entries from Sync, the conflict resolution logic knows that this was
+      // updated and propagates the new values to the server.
+      url->last_modified = base::Time::Now();
+      actions.edited_engines.push_back({existing_url, *url});
+    } else {
+      actions.added_engines.push_back(*url);
+    }
+  }
+
+  // The block above removed all the URLs from the |id_to_turl| map that were
+  // found in the prepopulate data.  Any remaining URLs that haven't been
+  // user-edited can be removed from the data store.
+  for (auto& i : id_to_turl) {
+    TemplateURL* template_url = i.second;
+    if (template_url->safe_for_autoreplace()) {
+      actions.removed_engines.push_back(template_url);
+    }
+  }
+
+  return actions;
+}
+
 void ApplyActionsFromCurrentData(
     ActionsFromCurrentData actions,
     KeywordWebDataService* service,
@@ -356,6 +431,7 @@
     TemplateURL* default_search_provider,
     const SearchTermsData& search_terms_data,
     int* new_resource_keyword_version,
+    int* new_resource_starter_pack_version,
     std::set<std::string>* removed_keyword_guids) {
   DCHECK(template_urls);
   DCHECK(template_urls->empty());
@@ -381,11 +457,11 @@
   }
 
   *new_resource_keyword_version = keyword_result.builtin_keyword_version;
-  GetSearchProvidersUsingLoadedEngines(service, prefs, template_urls,
-                                       default_search_provider,
-                                       search_terms_data,
-                                       new_resource_keyword_version,
-                                       removed_keyword_guids);
+  *new_resource_starter_pack_version = keyword_result.starter_pack_version;
+  GetSearchProvidersUsingLoadedEngines(
+      service, prefs, template_urls, default_search_provider, search_terms_data,
+      new_resource_keyword_version, new_resource_starter_pack_version,
+      removed_keyword_guids);
 }
 
 void GetSearchProvidersUsingLoadedEngines(
@@ -395,6 +471,7 @@
     TemplateURL* default_search_provider,
     const SearchTermsData& search_terms_data,
     int* resource_keyword_version,
+    int* resource_starter_pack_version,
     std::set<std::string>* removed_keyword_guids) {
   DCHECK(template_urls);
   DCHECK(resource_keyword_version);
@@ -414,6 +491,16 @@
   } else {
     *resource_keyword_version = 0;
   }
+
+  const int starter_pack_data_version =
+      TemplateURLStarterPackData::GetDataVersion();
+  if (*resource_starter_pack_version < starter_pack_data_version) {
+    MergeEnginesFromStarterPackData(
+        service, template_urls, default_search_provider, removed_keyword_guids);
+    *resource_starter_pack_version = starter_pack_data_version;
+  } else {
+    *resource_starter_pack_version = 0;
+  }
 }
 
 bool DeDupeEncodings(std::vector<std::string>* encodings) {
diff --git a/components/search_engines/util.h b/components/search_engines/util.h
index 0ab6c12..5f7f0cbc 100644
--- a/components/search_engines/util.h
+++ b/components/search_engines/util.h
@@ -38,19 +38,20 @@
 void MergeIntoEngineData(const TemplateURL* original_turl,
                          TemplateURLData* url_to_update);
 
-// CreateActionsFromCurrentPrepopulateData() (see below) takes in the current
-// prepopulated URLs as well as the user's current URLs, and returns an instance
-// of the following struct representing the changes necessary to bring the
-// user's URLs in line with the prepopulated URLs.
+// CreateActionsFromCurrentPrepopulateData() and
+// CreateActionsFromStarterPackData() (see below) takes in the current built-in
+// (prepopulated or starter pack) URLs as well as the user's current URLs, and
+// returns an instance of the following struct representing the changes
+// necessary to bring the user's URLs in line with the built-in URLs.
 //
 // There are three types of changes:
-// (1) Previous prepopulated engines that no longer exist in the current set of
-//     prepopulated engines and thus should be removed from the user's current
+// (1) Previous built-in engines that no longer exist in the current set of
+//     built-in engines and thus should be removed from the user's current
 //     URLs.
-// (2) Previous prepopulated engines whose data has changed.  The existing
+// (2) Previous built-in engines whose data has changed.  The existing
 //     entries for these engines should be updated to reflect the new data,
 //     except for any user-set names and keywords, which can be preserved.
-// (3) New prepopulated engines not in the user's engine list, which should be
+// (3) New built-in engines not in the user's engine list, which should be
 //     added.
 
 // The pair of current search engine and its new value.
@@ -94,9 +95,30 @@
     const TemplateURLService::OwnedTemplateURLVector& existing_urls,
     const TemplateURL* default_search_provider);
 
+// MergeEnginesFromStarterPackData merges search engines from the built-in
+// TemplateURLStarterPackData class into |template_urls|. Calls
+// CreateActionsFromCurrentStarterPackData() to collect actions and then applies
+// them on |template_urls|. MergeEgninesFromStarterPackData is invoked when the
+// version of the starter pack data changes. If |removed_keyword_guids| is not
+// nullptr, the Sync GUID of each item removed from the DB will be added to it.
+void MergeEnginesFromStarterPackData(
+    KeywordWebDataService* service,
+    TemplateURLService::OwnedTemplateURLVector* template_urls,
+    TemplateURL* default_search_provider,
+    std::set<std::string>* removed_keyword_guids);
+
+// Given the user's current URLs and the current set of Starter Pack URLs,
+// produces the set of actions (see above) required to make the user's URLs
+// reflect the starter pack data.
+//
+// NOTE: Takes ownership of, and clears, |starter_pack_urls|.
+ActionsFromCurrentData CreateActionsFromCurrentStarterPackData(
+    std::vector<std::unique_ptr<TemplateURLData>>* starter_pack_urls,
+    const TemplateURLService::OwnedTemplateURLVector& existing_urls);
+
 // Takes in an ActionsFromCurrentData (see above) and applies the actions (add,
 // edit, or remove) to the user's current URLs.  This is called by
-// MergeEnginesFromPrepopulateData().
+// MergeEnginesFromPrepopulateData() and MergeEnginesFromStarterPackData().
 void ApplyActionsFromCurrentData(
     ActionsFromCurrentData actions,
     KeywordWebDataService* service,
@@ -124,6 +146,7 @@
     TemplateURL* default_search_provider,
     const SearchTermsData& search_terms_data,
     int* new_resource_keyword_version,
+    int* new_resource_starter_pack_version,
     std::set<std::string>* removed_keyword_guids);
 
 // Like GetSearchProvidersUsingKeywordResult(), but allows the caller to pass in
@@ -140,6 +163,7 @@
     TemplateURL* default_search_provider,
     const SearchTermsData& search_terms_data,
     int* resource_keyword_version,
+    int* resource_starter_pack_version,
     std::set<std::string>* removed_keyword_guids);
 
 // Due to a bug, the |input_encodings| field of TemplateURLData could have
diff --git a/components/services/storage/public/cpp/buckets/bucket_locator.h b/components/services/storage/public/cpp/buckets/bucket_locator.h
index a391010e..9f0b587 100644
--- a/components/services/storage/public/cpp/buckets/bucket_locator.h
+++ b/components/services/storage/public/cpp/buckets/bucket_locator.h
@@ -39,8 +39,8 @@
   COMPONENT_EXPORT(STORAGE_SERVICE_BUCKETS_SUPPORT)
   friend bool operator<(const BucketLocator& lhs, const BucketLocator& rhs);
 
-  BucketId id;
-  blink::StorageKey storage_key;
+  BucketId id = BucketId::FromUnsafeValue(0);
+  blink::StorageKey storage_key = blink::StorageKey();
   blink::mojom::StorageType type = blink::mojom::StorageType::kUnknown;
   bool is_default = false;
 };
diff --git a/components/services/storage/public/mojom/indexed_db_control_test.mojom b/components/services/storage/public/mojom/indexed_db_control_test.mojom
index 6637a74..4efbc952 100644
--- a/components/services/storage/public/mojom/indexed_db_control_test.mojom
+++ b/components/services/storage/public/mojom/indexed_db_control_test.mojom
@@ -4,6 +4,7 @@
 
 module storage.mojom;
 
+import "components/services/storage/public/mojom/buckets/bucket_locator.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
 import "mojo/public/mojom/base/values.mojom";
 import "mojo/public/mojom/base/time.mojom";
@@ -52,27 +53,27 @@
   ResetCachesForTesting() => ();
 
   // Downgrades databases to V2.
-  ForceSchemaDowngradeForTesting(blink.mojom.StorageKey storage_key) =>
+  ForceSchemaDowngradeForTesting(storage.mojom.BucketLocator bucket_locator) =>
     (bool downgraded);
 
   // Returns the corruption status for a given database.
-  HasV2SchemaCorruptionForTesting(blink.mojom.StorageKey storage_key) =>
+  HasV2SchemaCorruptionForTesting(storage.mojom.BucketLocator bucket_locator) =>
       (V2SchemaCorruptionStatus status);
 
   // Does a direct write to a database with a given key/value pair.
-  WriteToIndexedDBForTesting(blink.mojom.StorageKey storage_key, string key,
-                             string value) => ();
+  WriteToIndexedDBForTesting(storage.mojom.BucketLocator bucket_locator,
+                             string key, string value) => ();
 
   // Returns the number of blobs for a given `storage_key`.
   GetBlobCountForTesting(blink.mojom.StorageKey storage_key) =>
     (int64 num_blobs);
 
   // Returns the next blob id for a given `storage_key`.
-  GetNextBlobNumberForTesting(blink.mojom.StorageKey storage_key,
+  GetNextBlobNumberForTesting(storage.mojom.BucketLocator bucket_locator,
                               int64 database_id) => (int64 next_blob_number);
 
   // Returns the path for a given key/database/blob.
-  GetPathForBlobForTesting(blink.mojom.StorageKey storage_key,
+  GetPathForBlobForTesting(storage.mojom.BucketLocator bucket_locator,
                            int64 database_id, int64 blob_number) =>
       (mojo_base.mojom.FilePath path);
 
diff --git a/components/sync/engine/loopback_server/loopback_connection_manager.cc b/components/sync/engine/loopback_server/loopback_connection_manager.cc
index 9a66e7f..6f05f417 100644
--- a/components/sync/engine/loopback_server/loopback_connection_manager.cc
+++ b/components/sync/engine/loopback_server/loopback_connection_manager.cc
@@ -18,6 +18,7 @@
 HttpResponse LoopbackConnectionManager::PostBuffer(
     const std::string& buffer_in,
     const std::string& access_token,
+    bool allow_batching,
     std::string* buffer_out) {
   buffer_out->clear();
 
diff --git a/components/sync/engine/loopback_server/loopback_connection_manager.h b/components/sync/engine/loopback_server/loopback_connection_manager.h
index 795d26d..8991087 100644
--- a/components/sync/engine/loopback_server/loopback_connection_manager.h
+++ b/components/sync/engine/loopback_server/loopback_connection_manager.h
@@ -30,6 +30,7 @@
   // Overridden ServerConnectionManager functions.
   HttpResponse PostBuffer(const std::string& buffer_in,
                           const std::string& access_token,
+                          bool allow_batching,
                           std::string* buffer_out) override;
 
   // The loopback server that will handle the requests locally.
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc
index c22d5be5..600351e 100644
--- a/components/sync/engine/net/http_bridge.cc
+++ b/components/sync/engine/net/http_bridge.cc
@@ -100,6 +100,11 @@
   extra_headers_.assign(headers);
 }
 
+void HttpBridge::SetAllowBatching(bool allow_batching) {
+  DCHECK(!fetch_state_.url_loader);
+  allow_batching_ = allow_batching;
+}
+
 void HttpBridge::SetURL(const GURL& url) {
 #if DCHECK_IS_ON()
   DCHECK(thread_checker_.CalledOnValidThread());
@@ -245,8 +250,10 @@
       std::move(resource_request), traffic_annotation);
   network::SimpleURLLoader* url_loader = fetch_state_.url_loader.get();
 
-  if (network::SimpleURLLoaderThrottle::IsBatchingEnabled(traffic_annotation))
+  if (allow_batching_ &&
+      network::SimpleURLLoaderThrottle::IsBatchingEnabled(traffic_annotation)) {
     url_loader->SetAllowBatching();
+  }
 
   std::string request_to_send;
   compression::GzipCompress(request_content_, &request_to_send);
diff --git a/components/sync/engine/net/http_bridge.h b/components/sync/engine/net/http_bridge.h
index 9686508..73d9f18e 100644
--- a/components/sync/engine/net/http_bridge.h
+++ b/components/sync/engine/net/http_bridge.h
@@ -54,6 +54,7 @@
   void SetPostPayload(const char* content_type,
                       int content_length,
                       const char* content) override;
+  void SetAllowBatching(bool allow_batching) override;
   bool MakeSynchronousPost(int* net_error_code, int* http_status_code) override;
   void Abort() override;
 
@@ -132,6 +133,11 @@
   std::string request_content_;
   std::string extra_headers_;
 
+  // When true `fetch_state_.url_loader` is configured so that it can be
+  // batched in the network layer. See the comment in
+  // network::SimpleURLLoader::SetAllowBatching().
+  bool allow_batching_ = false;
+
   // A waitable event we use to provide blocking semantics to
   // MakeSynchronousPost. We block created_on_loop_ while the IO loop fetches
   // network request.
diff --git a/components/sync/engine/net/http_post_provider.h b/components/sync/engine/net/http_post_provider.h
index 55314de..a0f46ab 100644
--- a/components/sync/engine/net/http_post_provider.h
+++ b/components/sync/engine/net/http_post_provider.h
@@ -36,6 +36,10 @@
                               int content_length,
                               const char* content) = 0;
 
+  // Set whether the POST message can be batched in the network stack of the
+  // embedding application.
+  virtual void SetAllowBatching(bool allow_batching) = 0;
+
   // Returns true if the URL request succeeded. If the request failed,
   // error() may be non-zero and hence contain more information.
   virtual bool MakeSynchronousPost(int* net_error_code,
diff --git a/components/sync/engine/net/server_connection_manager.cc b/components/sync/engine/net/server_connection_manager.cc
index 22cb64e..c7bacde 100644
--- a/components/sync/engine/net/server_connection_manager.cc
+++ b/components/sync/engine/net/server_connection_manager.cc
@@ -151,9 +151,11 @@
 
 HttpResponse ServerConnectionManager::PostBufferWithCachedAuth(
     const std::string& buffer_in,
+    bool allow_batching,
     std::string* buffer_out) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  HttpResponse http_response = PostBuffer(buffer_in, access_token_, buffer_out);
+  HttpResponse http_response =
+      PostBuffer(buffer_in, access_token_, allow_batching, buffer_out);
   SetServerResponse(http_response);
   return http_response;
 }
diff --git a/components/sync/engine/net/server_connection_manager.h b/components/sync/engine/net/server_connection_manager.h
index e8d0751..a2c2c63 100644
--- a/components/sync/engine/net/server_connection_manager.h
+++ b/components/sync/engine/net/server_connection_manager.h
@@ -98,8 +98,13 @@
   virtual ~ServerConnectionManager();
 
   // POSTs |buffer_in| and reads the body of the response into |buffer_out|.
-  // Uses the currently set access token in the headers.
+  // Uses the currently set access token in the headers. When |allow_batching|
+  // is true, the embedder's network stack may batch the post request depending
+  // on the network quality to streamline network access.
+  // TODO(https://crbug.com/1293657): Consider integrating batching logic into
+  // the sync code rather than relying on the embedder's network stack.
   HttpResponse PostBufferWithCachedAuth(const std::string& buffer_in,
+                                        bool allow_batching,
                                         std::string* buffer_out);
 
   void AddListener(ServerConnectionEventListener* listener);
@@ -136,6 +141,7 @@
   // implement.
   virtual HttpResponse PostBuffer(const std::string& buffer_in,
                                   const std::string& access_token,
+                                  bool allow_batching,
                                   std::string* buffer_out) = 0;
 
   void ClearAccessToken();
diff --git a/components/sync/engine/net/sync_server_connection_manager.cc b/components/sync/engine/net/sync_server_connection_manager.cc
index 45bdb0a22..c949b68 100644
--- a/components/sync/engine/net/sync_server_connection_manager.cc
+++ b/components/sync/engine/net/sync_server_connection_manager.cc
@@ -35,7 +35,8 @@
 
   HttpResponse Init(const GURL& connection_url,
                     const std::string& access_token,
-                    const std::string& payload);
+                    const std::string& payload,
+                    bool allow_batching);
   bool ReadBufferResponse(std::string* buffer_out, HttpResponse* response);
 
   // CancelationSignal::Observer overrides.
@@ -69,7 +70,8 @@
 
 HttpResponse Connection::Init(const GURL& sync_request_url,
                               const std::string& access_token,
-                              const std::string& payload) {
+                              const std::string& payload,
+                              bool allow_batching) {
   post_provider_->SetURL(sync_request_url);
 
   if (!access_token.empty()) {
@@ -82,6 +84,8 @@
   post_provider_->SetPostPayload("application/octet-stream", payload.length(),
                                  payload.data());
 
+  post_provider_->SetAllowBatching(allow_batching);
+
   // Issue the POST, blocking until it finishes.
   if (!cancelation_signal_->TryRegisterHandler(this)) {
     // Return early because cancelation signal was signaled.
@@ -157,6 +161,7 @@
 HttpResponse SyncServerConnectionManager::PostBuffer(
     const std::string& buffer_in,
     const std::string& access_token,
+    bool allow_batching,
     std::string* buffer_out) {
   if (access_token.empty()) {
     // Print a log to distinguish this "known failure" from others.
@@ -174,8 +179,8 @@
 
   // Note that the post may be aborted by now, which will just cause Init to
   // fail with CONNECTION_UNAVAILABLE.
-  HttpResponse http_response =
-      connection->Init(sync_request_url_, access_token, buffer_in);
+  HttpResponse http_response = connection->Init(sync_request_url_, access_token,
+                                                buffer_in, allow_batching);
 
   if (http_response.server_status == HttpResponse::SYNC_AUTH_ERROR) {
     ClearAccessToken();
diff --git a/components/sync/engine/net/sync_server_connection_manager.h b/components/sync/engine/net/sync_server_connection_manager.h
index b45623cb..f84e740 100644
--- a/components/sync/engine/net/sync_server_connection_manager.h
+++ b/components/sync/engine/net/sync_server_connection_manager.h
@@ -37,6 +37,7 @@
 
   HttpResponse PostBuffer(const std::string& buffer_in,
                           const std::string& access_token,
+                          bool allow_batching,
                           std::string* buffer_out) override;
 
  private:
diff --git a/components/sync/engine/net/sync_server_connection_manager_unittest.cc b/components/sync/engine/net/sync_server_connection_manager_unittest.cc
index 12948f1..d90f35a 100644
--- a/components/sync/engine/net/sync_server_connection_manager_unittest.cc
+++ b/components/sync/engine/net/sync_server_connection_manager_unittest.cc
@@ -31,6 +31,7 @@
   void SetPostPayload(const char* content_type,
                       int content_length,
                       const char* content) override {}
+  void SetAllowBatching(bool allow_batching) override {}
   bool MakeSynchronousPost(int* net_error_code,
                            int* http_status_code) override {
     wait_for_abort_.TimedWait(TestTimeouts::action_max_timeout());
@@ -71,7 +72,8 @@
       &signal);
 
   std::string buffer_out;
-  HttpResponse http_response = server.PostBuffer("", "testauth", &buffer_out);
+  HttpResponse http_response =
+      server.PostBuffer("", "testauth", /*allow_batching=*/false, &buffer_out);
 
   EXPECT_EQ(HttpResponse::CONNECTION_UNAVAILABLE, http_response.server_status);
 }
@@ -85,7 +87,8 @@
 
   signal.Signal();
   std::string buffer_out;
-  HttpResponse http_response = server.PostBuffer("", "testauth", &buffer_out);
+  HttpResponse http_response =
+      server.PostBuffer("", "testauth", /*allow_batching=*/false, &buffer_out);
 
   EXPECT_EQ(HttpResponse::CONNECTION_UNAVAILABLE, http_response.server_status);
 }
@@ -105,7 +108,8 @@
       TestTimeouts::tiny_timeout());
 
   std::string buffer_out;
-  HttpResponse http_response = server.PostBuffer("", "testauth", &buffer_out);
+  HttpResponse http_response =
+      server.PostBuffer("", "testauth", /*allow_batching=*/false, &buffer_out);
 
   EXPECT_EQ(HttpResponse::CONNECTION_UNAVAILABLE, http_response.server_status);
   abort_thread.Stop();
@@ -123,6 +127,7 @@
   void SetPostPayload(const char* content_type,
                       int content_length,
                       const char* content) override {}
+  void SetAllowBatching(bool allow_batching) override {}
   bool MakeSynchronousPost(int* net_error_code,
                            int* http_status_code) override {
     *net_error_code = net_error_code_;
@@ -168,7 +173,8 @@
       std::make_unique<FailingHttpPostFactory>(net::ERR_TIMED_OUT), &signal);
 
   std::string buffer_out;
-  HttpResponse http_response = server.PostBuffer("", "testauth", &buffer_out);
+  HttpResponse http_response =
+      server.PostBuffer("", "testauth", /*allow_batching=*/false, &buffer_out);
 
   EXPECT_EQ(HttpResponse::CONNECTION_UNAVAILABLE, http_response.server_status);
 }
diff --git a/components/sync/engine/sync_manager_impl_unittest.cc b/components/sync/engine/sync_manager_impl_unittest.cc
index 2f732ba..f3ca1805 100644
--- a/components/sync/engine/sync_manager_impl_unittest.cc
+++ b/components/sync/engine/sync_manager_impl_unittest.cc
@@ -61,6 +61,7 @@
   void SetPostPayload(const char* content_type,
                       int content_length,
                       const char* content) override {}
+  void SetAllowBatching(bool allow_batching) override {}
   bool MakeSynchronousPost(int* net_error_code,
                            int* http_status_code) override {
     return false;
diff --git a/components/sync/engine/syncer_proto_util.cc b/components/sync/engine/syncer_proto_util.cc
index 5492451..adf7054 100644
--- a/components/sync/engine/syncer_proto_util.cc
+++ b/components/sync/engine/syncer_proto_util.cc
@@ -348,10 +348,19 @@
 
   const base::Time start_time = base::Time::Now();
 
+  // User-initiated sync messages should not be batched. GET_UPDATES messages
+  // are mostly safe to consider non-user-initiated.
+  // TODO(https://crbug.com/1293657): Confirm that treating GET_UPDATES as
+  // non-user-initiated is reasonable. GET_UPDATES messages could be latency
+  // sensitive since these requests most commonly happen because of some
+  // user-initiated changes on a different device.
+  bool allow_batching =
+      msg.message_contents() == ClientToServerMessage::GET_UPDATES;
+
   // Fills in buffer_out.
   std::string buffer_out;
   HttpResponse http_response =
-      scm->PostBufferWithCachedAuth(buffer_in, &buffer_out);
+      scm->PostBufferWithCachedAuth(buffer_in, allow_batching, &buffer_out);
   if (http_response.server_status != HttpResponse::SERVER_CONNECTION_OK) {
     LOG(WARNING) << "Error posting from syncer:" << http_response;
     return false;
diff --git a/components/sync/engine/syncer_proto_util_unittest.cc b/components/sync/engine/syncer_proto_util_unittest.cc
index 786ffc7..8ae29a7c 100644
--- a/components/sync/engine/syncer_proto_util_unittest.cc
+++ b/components/sync/engine/syncer_proto_util_unittest.cc
@@ -165,6 +165,7 @@
 
   HttpResponse PostBuffer(const std::string& buffer_in,
                           const std::string& access_token,
+                          bool allow_batching,
                           std::string* buffer_out) override {
     if (send_error_) {
       return HttpResponse::ForIoError();
diff --git a/components/sync/test/engine/mock_connection_manager.cc b/components/sync/test/engine/mock_connection_manager.cc
index d5be79b..b00213a 100644
--- a/components/sync/test/engine/mock_connection_manager.cc
+++ b/components/sync/test/engine/mock_connection_manager.cc
@@ -68,6 +68,7 @@
 
 HttpResponse MockConnectionManager::PostBuffer(const std::string& buffer_in,
                                                const std::string& access_token,
+                                               bool allow_batching,
                                                std::string* buffer_out) {
   ClientToServerMessage post;
   if (!post.ParseFromString(buffer_in)) {
diff --git a/components/sync/test/engine/mock_connection_manager.h b/components/sync/test/engine/mock_connection_manager.h
index 08405bd..3210e5b8 100644
--- a/components/sync/test/engine/mock_connection_manager.h
+++ b/components/sync/test/engine/mock_connection_manager.h
@@ -58,6 +58,7 @@
   // Overridden ServerConnectionManager functions.
   HttpResponse PostBuffer(const std::string& buffer_in,
                           const std::string& access_token,
+                          bool allow_batching,
                           std::string* buffer_out) override;
 
   // Control of commit response.
diff --git a/components/sync/test/fake_server/fake_server_http_post_provider.cc b/components/sync/test/fake_server/fake_server_http_post_provider.cc
index a121f3c..01cc9d9b 100644
--- a/components/sync/test/fake_server/fake_server_http_post_provider.cc
+++ b/components/sync/test/fake_server/fake_server_http_post_provider.cc
@@ -66,6 +66,8 @@
   request_content_.assign(content, content_length);
 }
 
+void FakeServerHttpPostProvider::SetAllowBatching(bool allow_batching) {}
+
 bool FakeServerHttpPostProvider::MakeSynchronousPost(int* net_error_code,
                                                      int* http_status_code) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/sync/test/fake_server/fake_server_http_post_provider.h b/components/sync/test/fake_server/fake_server_http_post_provider.h
index feff275..fb12925 100644
--- a/components/sync/test/fake_server/fake_server_http_post_provider.h
+++ b/components/sync/test/fake_server/fake_server_http_post_provider.h
@@ -37,6 +37,7 @@
   void SetPostPayload(const char* content_type,
                       int content_length,
                       const char* content) override;
+  void SetAllowBatching(bool allow_batching) override;
   bool MakeSynchronousPost(int* net_error_code, int* http_status_code) override;
   void Abort() override;
   int GetResponseContentLength() const override;
diff --git a/content/browser/devtools/devtools_instrumentation.cc b/content/browser/devtools/devtools_instrumentation.cc
index 36b53d4..0f5d74a 100644
--- a/content/browser/devtools/devtools_instrumentation.cc
+++ b/content/browser/devtools/devtools_instrumentation.cc
@@ -1306,7 +1306,7 @@
     const GlobalRenderFrameHostId& requesting_frame_id,
     const ServiceWorkerContextWrapper* context_wrapper,
     int64_t version_id,
-    const network::ResourceRequest& request) {
+    network::ResourceRequest& request) {
   // Currently, `requesting_frame_id` is invalid when payment apps and
   // extensions register a service worker. See the callers of
   // ServiceWorkerContextWrapper::RegisterServiceWorker().
@@ -1327,7 +1327,7 @@
           ->GetDevToolsAgentHostForNewInstallingWorker(context_wrapper,
                                                        version_id);
   DCHECK(agent_host);
-  DCHECK(request.devtools_request_id.has_value());
+  request.devtools_request_id = agent_host->devtools_worker_token().ToString();
   for (auto* network_handler :
        protocol::NetworkHandler::ForAgentHost(agent_host)) {
     network_handler->RequestSent(
diff --git a/content/browser/devtools/devtools_instrumentation.h b/content/browser/devtools/devtools_instrumentation.h
index 3161f70..43f4d987 100644
--- a/content/browser/devtools/devtools_instrumentation.h
+++ b/content/browser/devtools/devtools_instrumentation.h
@@ -286,7 +286,7 @@
     const GlobalRenderFrameHostId& requesting_frame_id,
     const ServiceWorkerContextWrapper* context_wrapper,
     int64_t version_id,
-    const network::ResourceRequest& request);
+    network::ResourceRequest& request);
 
 // Fires `Network.onLoadingFailed` event for a dedicated worker main script.
 // Used for PlzDedicatedWorker.
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index 61fe4625..e5422cb 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -133,8 +133,10 @@
   return path;
 }
 
-std::string ComputeOriginIdentifier(const blink::StorageKey& storage_key) {
-  return storage::GetIdentifierFromOrigin(storage_key.origin()) + "@1";
+std::string ComputeOriginIdentifier(
+    const storage::BucketLocator& bucket_locator) {
+  return storage::GetIdentifierFromOrigin(bucket_locator.storage_key.origin()) +
+         "@1";
 }
 
 // TODO(ericu): Error recovery. If we persistently can't read the
@@ -626,7 +628,7 @@
 IndexedDBBackingStore::IndexedDBBackingStore(
     Mode backing_store_mode,
     TransactionalLevelDBFactory* transactional_leveldb_factory,
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const base::FilePath& blob_path,
     std::unique_ptr<TransactionalLevelDBDatabase> db,
     storage::mojom::BlobStorageContext* blob_storage_context,
@@ -637,13 +639,13 @@
     scoped_refptr<base::SequencedTaskRunner> idb_task_runner)
     : backing_store_mode_(backing_store_mode),
       transactional_leveldb_factory_(transactional_leveldb_factory),
-      storage_key_(storage_key),
+      bucket_locator_(bucket_locator),
       blob_path_(backing_store_mode == Mode::kInMemory ? base::FilePath()
                                                        : blob_path),
       blob_storage_context_(blob_storage_context),
       file_system_access_context_(file_system_access_context),
       filesystem_proxy_(std::move(filesystem_proxy)),
-      origin_identifier_(ComputeOriginIdentifier(storage_key)),
+      origin_identifier_(ComputeOriginIdentifier(bucket_locator)),
       idb_task_runner_(std::move(idb_task_runner)),
       db_(std::move(db)),
       blob_files_cleaned_(std::move(blob_files_cleaned)) {
@@ -709,7 +711,9 @@
     INTERNAL_READ_ERROR(SET_UP_METADATA);
     return s;
   }
-  indexed_db::ReportSchemaVersion(db_schema_version, storage_key_);
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+  indexed_db::ReportSchemaVersion(db_schema_version,
+                                  bucket_locator_.storage_key);
   if (!found) {
     // Initialize new backing store.
     db_schema_version = indexed_db::kLatestKnownSchemaVersion;
@@ -789,9 +793,10 @@
   s = db_->Write(write_batch.get());
   write_batch.reset();
   if (!s.ok()) {
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     indexed_db::ReportOpenStatus(
         indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_METADATA_SETUP,
-        storage_key_);
+        bucket_locator_.storage_key);
     INTERNAL_WRITE_ERROR(SET_UP_METADATA);
     return s;
   }
@@ -799,10 +804,11 @@
   if (clean_active_journal) {
     s = CleanUpBlobJournal(ActiveBlobJournalKey::Encode());
     if (!s.ok()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
       indexed_db::ReportOpenStatus(
           indexed_db::
               INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
-          storage_key_);
+          bucket_locator_.storage_key);
     }
   }
 #if DCHECK_IS_ON()
@@ -3188,7 +3194,7 @@
   const std::string schema_version_key = SchemaVersionKey::Encode();
   Status s;
 
-  if (storage_key_.origin().host() != "docs.google.com") {
+  if (bucket_locator_.storage_key.origin().host() != "docs.google.com") {
     s = ValidateBlobFiles(db_.get());
     if (!s.ok()) {
       INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
diff --git a/content/browser/indexed_db/indexed_db_backing_store.h b/content/browser/indexed_db/indexed_db_backing_store.h
index bc73164d..53563f18 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -27,6 +27,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/services/storage/indexed_db/locks/leveled_lock.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/cpp/filesystem/filesystem_proxy.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom-forward.h"
 #include "components/services/storage/public/mojom/file_system_access_context.mojom-forward.h"
@@ -38,18 +39,18 @@
 #include "storage/browser/blob/blob_data_handle.h"
 #include "storage/common/file_system/file_system_mount_option.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
 #include "url/gurl.h"
 
 namespace base {
 class SequencedTaskRunner;
-}
+}  // namespace base
 
 namespace blink {
 class IndexedDBKeyRange;
 struct IndexedDBDatabaseMetadata;
+class StorageKey;
 }  // namespace blink
 
 namespace content {
@@ -388,7 +389,7 @@
   IndexedDBBackingStore(
       Mode backing_store_mode,
       TransactionalLevelDBFactory* transactional_leveldb_factory,
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext* blob_storage_context,
@@ -407,7 +408,9 @@
   // operations or method calls on this object.
   leveldb::Status Initialize(bool clean_active_blob_journal);
 
-  const blink::StorageKey& storage_key() const { return storage_key_; }
+  const storage::BucketLocator& bucket_locator() const {
+    return bucket_locator_;
+  }
   base::SequencedTaskRunner* idb_task_runner() const {
     return idb_task_runner_.get();
   }
@@ -663,7 +666,7 @@
 
   const Mode backing_store_mode_;
   const raw_ptr<TransactionalLevelDBFactory> transactional_leveldb_factory_;
-  const blink::StorageKey storage_key_;
+  const storage::BucketLocator bucket_locator_;
   const base::FilePath blob_path_;
 
   // IndexedDB can store blobs and File System Access handles. These mojo
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index fea887f..ee07956 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -35,6 +35,7 @@
 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/leveldb_write_batch.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/mojom/indexed_db_control.mojom-test-utils.h"
 #include "content/browser/indexed_db/indexed_db_bucket_state.h"
 #include "content/browser/indexed_db/indexed_db_class_factory.h"
@@ -71,7 +72,7 @@
   TestableIndexedDBBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       TransactionalLevelDBFactory* leveldb_factory,
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext* blob_storage_context,
@@ -82,7 +83,7 @@
       scoped_refptr<base::SequencedTaskRunner> idb_task_runner)
       : IndexedDBBackingStore(backing_store_mode,
                               leveldb_factory,
-                              storage_key,
+                              bucket_locator,
                               blob_path,
                               std::move(db),
                               blob_storage_context,
@@ -140,7 +141,7 @@
   std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       TransactionalLevelDBFactory* leveldb_factory,
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext*,
@@ -154,7 +155,7 @@
     // than the versions that were passed in to this method. This way tests can
     // use a different context from what is stored in the IndexedDBContext.
     return std::make_unique<TestableIndexedDBBackingStore>(
-        backing_store_mode, leveldb_factory, storage_key, blob_path,
+        backing_store_mode, leveldb_factory, bucket_locator, blob_path,
         std::move(db), blob_storage_context_, file_system_access_context_,
         std::move(filesystem_proxy), std::move(blob_files_cleaned),
         std::move(report_outstanding_blobs), std::move(idb_task_runner));
@@ -336,6 +337,8 @@
   void CreateFactoryAndBackingStore() {
     const blink::StorageKey storage_key =
         blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+    auto bucket_locator = storage::BucketLocator();
+    bucket_locator.storage_key = storage_key;
     idb_factory_ = std::make_unique<TestIDBFactory>(
         idb_context_.get(), blob_context_.get(),
         file_system_access_context_.get());
@@ -343,7 +346,7 @@
     leveldb::Status s;
     std::tie(bucket_state_handle_, s, std::ignore, data_loss_info_,
              std::ignore) =
-        idb_factory_->GetOrOpenBucketFactory(storage_key,
+        idb_factory_->GetOrOpenBucketFactory(bucket_locator,
                                              idb_context_->data_path(),
                                              /*create_if_missing=*/true);
     if (!bucket_state_handle_.IsHeld()) {
@@ -379,15 +382,15 @@
     if (idb_context_ && !idb_context_->IsInMemoryContext()) {
       IndexedDBFactoryImpl* factory = idb_context_->GetIDBFactory();
 
-      // Loop through all open origins, and force close them, and request the
+      // Loop through all open buckets, and force close them, and request the
       // deletion of the leveldb state. Once the states are no longer around,
       // delete all of the databases on disk.
       auto open_factory_buckets = factory->GetOpenBuckets();
 
-      for (const auto& bucket : open_factory_buckets) {
+      for (const auto& bucket_locator : open_factory_buckets) {
         base::RunLoop loop;
         IndexedDBBucketState* per_bucket_factory =
-            factory->GetBucketFactory(bucket);
+            factory->GetBucketFactory(bucket_locator);
 
         auto* leveldb_state =
             per_bucket_factory->backing_store()->db()->leveldb_state();
@@ -402,7 +405,7 @@
             base::SequencedTaskRunnerHandle::Get());
 
         idb_context_->ForceCloseSync(
-            bucket,
+            bucket_locator.storage_key,
             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
         loop.Run();
         // There is a possible race in `leveldb_close_event` where the signaling
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index 67fce13..9e554623 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -28,6 +28,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/mojom/indexed_db_control.mojom-test-utils.h"
 #include "components/services/storage/public/mojom/indexed_db_control_test.mojom.h"
 #include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
@@ -243,12 +244,12 @@
     return count;
   }
 
-  bool RequestSchemaDowngrade(const blink::StorageKey& storage_key) {
+  bool RequestSchemaDowngrade(const storage::BucketLocator& bucket_locator) {
     base::RunLoop loop;
     bool downgraded;
     auto control_test = GetControlTest();
     control_test->ForceSchemaDowngradeForTesting(
-        storage_key,
+        bucket_locator,
         base::BindOnce(base::BindLambdaForTesting([&](bool was_downgraded) {
           downgraded = was_downgraded;
           loop.Quit();
@@ -258,28 +259,29 @@
   }
 
   storage::mojom::V2SchemaCorruptionStatus RequestHasV2SchemaCorruption(
-      const blink::StorageKey& storage_key) {
+      const storage::BucketLocator& bucket_locator) {
     base::RunLoop loop;
     storage::mojom::V2SchemaCorruptionStatus ret;
     auto control_test = GetControlTest();
     control_test->HasV2SchemaCorruptionForTesting(
-        storage_key, base::BindLambdaForTesting(
-                         [&](storage::mojom::V2SchemaCorruptionStatus status) {
-                           ret = status;
-                           loop.Quit();
-                         }));
+        bucket_locator,
+        base::BindLambdaForTesting(
+            [&](storage::mojom::V2SchemaCorruptionStatus status) {
+              ret = status;
+              loop.Quit();
+            }));
     loop.Run();
     return ret;
   }
 
   // Synchronously writes to the IndexedDB database at the given storage_key.
-  void WriteToIndexedDB(const blink::StorageKey& storage_key,
+  void WriteToIndexedDB(const storage::BucketLocator& bucket_locator,
                         std::string key,
                         std::string value) {
     auto control_test = GetControlTest();
     base::RunLoop loop;
     control_test->WriteToIndexedDBForTesting(
-        storage_key, std::move(key), std::move(value), loop.QuitClosure());
+        bucket_locator, std::move(key), std::move(value), loop.QuitClosure());
     loop.Run();
   }
 
@@ -376,13 +378,26 @@
 
 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NegativeDBSchemaVersion) {
   const GURL database_open_url = GetTestUrl("indexeddb", "database_test.html");
-  const blink::StorageKey kTestStorageKey =
-      blink::StorageKey(url::Origin::Create(database_open_url));
+
   // Create the database.
   SimpleTest(database_open_url);
   // -10, little endian.
   std::string value = "\xF6\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
 
+  // Find the bucket that was created.
+  const auto maybe_bucket_info =
+      shell()
+          ->web_contents()
+          ->GetBrowserContext()
+          ->GetDefaultStoragePartition()
+          ->GetQuotaManager()
+          ->proxy()
+          ->GetOrCreateBucketSync(
+              blink::StorageKey(url::Origin::Create(database_open_url)),
+              storage::kDefaultBucketName);
+  ASSERT_TRUE(maybe_bucket_info.ok());
+  const auto bucket_locator = maybe_bucket_info->ToBucketLocator();
+
   auto control_test = GetControlTest();
   base::RunLoop loop;
   std::string key;
@@ -394,7 +409,7 @@
       }));
   loop.Run();
 
-  WriteToIndexedDB(kTestStorageKey, key, value);
+  WriteToIndexedDB(bucket_locator, key, value);
   // Crash the tab to ensure no old navigations are picked up.
   CrashTab(shell()->web_contents());
   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
@@ -402,13 +417,26 @@
 
 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NegativeDBDataVersion) {
   const GURL database_open_url = GetTestUrl("indexeddb", "database_test.html");
-  const blink::StorageKey kTestStorageKey =
-      blink::StorageKey(url::Origin::Create(database_open_url));
+
   // Create the database.
   SimpleTest(database_open_url);
   // -10, little endian.
   std::string value = "\xF6\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
 
+  // Find the bucket that was created.
+  const auto maybe_bucket_info =
+      shell()
+          ->web_contents()
+          ->GetBrowserContext()
+          ->GetDefaultStoragePartition()
+          ->GetQuotaManager()
+          ->proxy()
+          ->GetOrCreateBucketSync(
+              blink::StorageKey(url::Origin::Create(database_open_url)),
+              storage::kDefaultBucketName);
+  ASSERT_TRUE(maybe_bucket_info.ok());
+  const auto bucket_locator = maybe_bucket_info->ToBucketLocator();
+
   auto control_test = GetControlTest();
   base::RunLoop loop;
   std::string key;
@@ -420,7 +448,7 @@
       }));
   loop.Run();
 
-  WriteToIndexedDB(kTestStorageKey, key, value);
+  WriteToIndexedDB(bucket_locator, key, value);
   // Crash the tab to ensure no old navigations are picked up.
   CrashTab(shell()->web_contents());
   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
@@ -1148,8 +1176,6 @@
 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestV2SchemaCorruption, LifecycleTest) {
   ASSERT_TRUE(embedded_test_server()->Started() ||
               embedded_test_server()->InitializeAndListen());
-  const blink::StorageKey kTestStorageKey = blink::StorageKey(
-      url::Origin::Create(embedded_test_server()->base_url()));
   embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
       &StaticFileRequestHandler, s_indexeddb_test_prefix, this));
   embedded_test_server()->StartAcceptingConnections();
@@ -1159,21 +1185,35 @@
       std::string(s_indexeddb_test_prefix) + "v2schemacorrupt_setup.html";
   SimpleTest(embedded_test_server()->GetURL(test_file));
 
+  // Find the bucket that was created.
+  const auto maybe_bucket_info =
+      shell()
+          ->web_contents()
+          ->GetBrowserContext()
+          ->GetDefaultStoragePartition()
+          ->GetQuotaManager()
+          ->proxy()
+          ->GetOrCreateBucketSync(blink::StorageKey(url::Origin::Create(
+                                      embedded_test_server()->base_url())),
+                                  storage::kDefaultBucketName);
+  ASSERT_TRUE(maybe_bucket_info.ok());
+  const auto bucket_locator = maybe_bucket_info->ToBucketLocator();
+
   // Verify the backing store does not have corruption.
   storage::mojom::V2SchemaCorruptionStatus has_corruption =
-      RequestHasV2SchemaCorruption(kTestStorageKey);
+      RequestHasV2SchemaCorruption(bucket_locator);
   ASSERT_EQ(has_corruption,
             storage::mojom::V2SchemaCorruptionStatus::CORRUPTION_NO);
 
   // Revert schema to v2.  This closes the targeted backing store.
-  bool schema_downgrade = RequestSchemaDowngrade(kTestStorageKey);
+  bool schema_downgrade = RequestSchemaDowngrade(bucket_locator);
   ASSERT_EQ(schema_downgrade, true);
 
   // Re-open the backing store and verify it has corruption.
   test_file =
       std::string(s_indexeddb_test_prefix) + "v2schemacorrupt_reopen.html";
   SimpleTest(embedded_test_server()->GetURL(test_file));
-  has_corruption = RequestHasV2SchemaCorruption(kTestStorageKey);
+  has_corruption = RequestHasV2SchemaCorruption(bucket_locator);
   ASSERT_EQ(has_corruption,
             storage::mojom::V2SchemaCorruptionStatus::CORRUPTION_YES);
 
@@ -1192,14 +1232,14 @@
 // This test is for https://crbug.com/1039446.
 class IndexedDBBrowserTestBlobKeyCorruption : public IndexedDBBrowserTest {
  public:
-  int64_t GetNextBlobNumber(const blink::StorageKey& storage_key,
+  int64_t GetNextBlobNumber(const storage::BucketLocator& bucket_locator,
                             int64_t database_id) {
     int64_t number;
 
     base::RunLoop loop;
     auto control_test = GetControlTest();
     control_test->GetNextBlobNumberForTesting(
-        storage_key, database_id,
+        bucket_locator, database_id,
         base::BindLambdaForTesting([&](int64_t next_blob_number) {
           number = next_blob_number;
           loop.Quit();
@@ -1208,14 +1248,14 @@
     return number;
   }
 
-  base::FilePath PathForBlob(const blink::StorageKey& storage_key,
+  base::FilePath PathForBlob(const storage::BucketLocator& bucket_locator,
                              int64_t database_id,
                              int64_t blob_number) {
     base::FilePath path;
     base::RunLoop loop;
     auto control_test = GetControlTest();
     control_test->GetPathForBlobForTesting(
-        storage_key, database_id, blob_number,
+        bucket_locator, database_id, blob_number,
         base::BindLambdaForTesting([&](const base::FilePath& blob_path) {
           path = blob_path;
           loop.Quit();
@@ -1232,8 +1272,6 @@
 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestBlobKeyCorruption, LifecycleTest) {
   ASSERT_TRUE(embedded_test_server()->Started() ||
               embedded_test_server()->InitializeAndListen());
-  const blink::StorageKey kTestStorageKey = blink::StorageKey(
-      url::Origin::Create(embedded_test_server()->base_url()));
   embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
       &StaticFileRequestHandler, s_indexeddb_test_prefix, this));
   embedded_test_server()->StartAcceptingConnections();
@@ -1242,12 +1280,26 @@
   std::string test_file =
       std::string(s_indexeddb_test_prefix) + "write_and_read_blob.html";
   SimpleTest(embedded_test_server()->GetURL(test_file));
-  int64_t next_blob_number = GetNextBlobNumber(kTestStorageKey, 1);
+
+  // Find the bucket that was created.
+  const auto maybe_bucket_info =
+      shell()
+          ->web_contents()
+          ->GetBrowserContext()
+          ->GetDefaultStoragePartition()
+          ->GetQuotaManager()
+          ->proxy()
+          ->GetOrCreateBucketSync(blink::StorageKey(url::Origin::Create(
+                                      embedded_test_server()->base_url())),
+                                  storage::kDefaultBucketName);
+  ASSERT_TRUE(maybe_bucket_info.ok());
+  const auto bucket_locator = maybe_bucket_info->ToBucketLocator();
+  int64_t next_blob_number = GetNextBlobNumber(bucket_locator, 1);
 
   base::FilePath first_blob =
-      PathForBlob(kTestStorageKey, 1, next_blob_number - 1);
+      PathForBlob(bucket_locator, 1, next_blob_number - 1);
   base::FilePath corrupt_blob =
-      PathForBlob(kTestStorageKey, 1, next_blob_number);
+      PathForBlob(bucket_locator, 1, next_blob_number);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
     EXPECT_TRUE(base::PathExists(first_blob));
diff --git a/content/browser/indexed_db/indexed_db_bucket_state.cc b/content/browser/indexed_db/indexed_db_bucket_state.cc
index 363623f..06732c3 100644
--- a/content/browser/indexed_db/indexed_db_bucket_state.cc
+++ b/content/browser/indexed_db/indexed_db_bucket_state.cc
@@ -119,7 +119,7 @@
     IndexedDBBucketState::kMaxEarliestBucketCompactionFromNow;
 
 IndexedDBBucketState::IndexedDBBucketState(
-    blink::StorageKey storage_key,
+    storage::BucketLocator bucket_locator,
     bool persist_for_incognito,
     base::Clock* clock,
     TransactionalLevelDBFactory* transactional_leveldb_factory,
@@ -129,7 +129,7 @@
     TasksAvailableCallback notify_tasks_callback,
     TearDownCallback tear_down_callback,
     std::unique_ptr<IndexedDBBackingStore> backing_store)
-    : storage_key_(std::move(storage_key)),
+    : bucket_locator_(std::move(bucket_locator)),
       persist_for_incognito_(persist_for_incognito),
       clock_(clock),
       transactional_leveldb_factory_(transactional_leveldb_factory),
diff --git a/content/browser/indexed_db/indexed_db_bucket_state.h b/content/browser/indexed_db/indexed_db_bucket_state.h
index 9dc4eb2..39b060c 100644
--- a/content/browser/indexed_db/indexed_db_bucket_state.h
+++ b/content/browser/indexed_db/indexed_db_bucket_state.h
@@ -19,10 +19,10 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "content/browser/indexed_db/indexed_db_bucket_state_handle.h"
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
 #include "content/common/content_export.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/leveldatabase/src/include/leveldb/status.h"
 
 namespace content {
@@ -94,8 +94,7 @@
   // `earliest_global_sweep_time` and `earliest_global_compaction_time` are
   // expected to outlive this object.
   IndexedDBBucketState(
-      // TODO(crbug.com/1218100): This needs a BucketLocator
-      blink::StorageKey storage_key,
+      storage::BucketLocator bucket_locator,
       bool persist_for_incognito,
       base::Clock* clock,
       TransactionalLevelDBFactory* transactional_leveldb_factory,
@@ -129,7 +128,7 @@
 
   void StopPersistingForIncognito();
 
-  const blink::StorageKey& storage_key() { return storage_key_; }
+  const storage::BucketLocator& bucket_locator() { return bucket_locator_; }
   IndexedDBBackingStore* backing_store() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     return backing_store_.get();
@@ -214,7 +213,7 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  blink::StorageKey storage_key_;
+  storage::BucketLocator bucket_locator_;
 
   // True if this factory should be remain alive due to the storage partition
   // being for incognito mode, and our backing store being in-memory. This is
diff --git a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
index 66fcf57..6836257 100644
--- a/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_cleanup_on_io_error_unittest.cc
@@ -16,6 +16,7 @@
 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "content/browser/indexed_db/indexed_db_backing_store.h"
 #include "content/browser/indexed_db/indexed_db_class_factory.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
@@ -36,6 +37,8 @@
   base::test::TaskEnvironment task_env;
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   base::ScopedTempDir temp_directory;
   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
   const base::FilePath path = temp_directory.GetPath();
@@ -45,7 +48,7 @@
   std::unique_ptr<IndexedDBBackingStore> backing_store = std::make_unique<
       IndexedDBBackingStore>(
       IndexedDBBackingStore::Mode::kInMemory, &transactional_leveldb_factory,
-      storage_key, path,
+      bucket_locator, path,
       transactional_leveldb_factory.CreateLevelDBDatabase(
           FakeLevelDBFactory::GetBrokenLevelDB(
               leveldb::Status::IOError("It's broken!"), path),
@@ -65,6 +68,8 @@
   base::test::TaskEnvironment task_env;
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   base::ScopedTempDir temp_directory;
   ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
   const base::FilePath path = temp_directory.GetPath();
@@ -84,7 +89,7 @@
     std::unique_ptr<IndexedDBBackingStore> backing_store = std::make_unique<
         IndexedDBBackingStore>(
         IndexedDBBackingStore::Mode::kInMemory, &transactional_leveldb_factory,
-        storage_key, path,
+        bucket_locator, path,
         transactional_leveldb_factory.CreateLevelDBDatabase(
             FakeLevelDBFactory::GetBrokenLevelDB(error_status, path), nullptr,
             task_runner.get(),
diff --git a/content/browser/indexed_db/indexed_db_context_impl.cc b/content/browser/indexed_db/indexed_db_context_impl.cc
index f0378553..0c8e146 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -30,6 +30,7 @@
 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/cpp/buckets/constants.h"
 #include "components/services/storage/public/cpp/quota_client_callback_wrapper.h"
 #include "components/services/storage/public/cpp/quota_error_or.h"
@@ -537,32 +538,35 @@
 }
 
 void IndexedDBContextImpl::ForceSchemaDowngradeForTesting(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     ForceSchemaDowngradeForTestingCallback callback) {
   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
 
-  if (is_incognito() || !HasBucket(storage_key)) {
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+  if (is_incognito() || !HasBucket(bucket_locator.storage_key)) {
     std::move(callback).Run(false);
     return;
   }
 
   if (indexeddb_factory_.get()) {
-    indexeddb_factory_->ForceSchemaDowngrade(storage_key);
+    indexeddb_factory_->ForceSchemaDowngrade(bucket_locator);
     std::move(callback).Run(true);
     return;
   }
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   ForceCloseSync(
-      storage_key,
+      bucket_locator.storage_key,
       storage::mojom::ForceCloseReason::FORCE_SCHEMA_DOWNGRADE_INTERNALS_PAGE);
   std::move(callback).Run(false);
 }
 
 void IndexedDBContextImpl::HasV2SchemaCorruptionForTesting(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     HasV2SchemaCorruptionForTestingCallback callback) {
   DCHECK(IDBTaskRunner()->RunsTasksInCurrentSequence());
 
-  if (is_incognito() || !HasBucket(storage_key)) {
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+  if (is_incognito() || !HasBucket(bucket_locator.storage_key)) {
     std::move(callback).Run(
         storage::mojom::V2SchemaCorruptionStatus::CORRUPTION_UNKNOWN);
     return;
@@ -571,7 +575,7 @@
   if (indexeddb_factory_.get()) {
     std::move(callback).Run(
         static_cast<storage::mojom::V2SchemaCorruptionStatus>(
-            indexeddb_factory_->HasV2SchemaCorruption(storage_key)));
+            indexeddb_factory_->HasV2SchemaCorruption(bucket_locator)));
     return;
   }
   return std::move(callback).Run(
@@ -579,14 +583,14 @@
 }
 
 void IndexedDBContextImpl::WriteToIndexedDBForTesting(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const std::string& key,
     const std::string& value,
     base::OnceClosure callback) {
   IndexedDBBucketStateHandle handle;
   leveldb::Status s;
   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
-      GetIDBFactory()->GetOrOpenBucketFactory(storage_key, data_path(),
+      GetIDBFactory()->GetOrOpenBucketFactory(bucket_locator, data_path(),
                                               /*create_if_missing=*/true);
   CHECK(s.ok()) << s.ToString();
   CHECK(handle.IsHeld());
@@ -597,7 +601,8 @@
   s = db->Put(key, &value_copy);
   CHECK(s.ok()) << s.ToString();
   handle.Release();
-  GetIDBFactory()->ForceClose(storage_key, true);
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+  GetIDBFactory()->ForceClose(bucket_locator.storage_key, true);
   std::move(callback).Run();
 }
 
@@ -608,13 +613,13 @@
 }
 
 void IndexedDBContextImpl::GetNextBlobNumberForTesting(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     int64_t database_id,
     GetNextBlobNumberForTestingCallback callback) {
   IndexedDBBucketStateHandle handle;
   leveldb::Status s;
   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
-      GetIDBFactory()->GetOrOpenBucketFactory(storage_key, data_path(),
+      GetIDBFactory()->GetOrOpenBucketFactory(bucket_locator, data_path(),
                                               /*create_if_missing=*/true);
   CHECK(s.ok()) << s.ToString();
   CHECK(handle.IsHeld());
@@ -638,14 +643,14 @@
 }
 
 void IndexedDBContextImpl::GetPathForBlobForTesting(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     int64_t database_id,
     int64_t blob_number,
     GetPathForBlobForTestingCallback callback) {
   IndexedDBBucketStateHandle handle;
   leveldb::Status s;
   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
-      GetIDBFactory()->GetOrOpenBucketFactory(storage_key, data_path(),
+      GetIDBFactory()->GetOrOpenBucketFactory(bucket_locator, data_path(),
                                               /*create_if_missing=*/true);
   CHECK(s.ok()) << s.ToString();
   CHECK(handle.IsHeld());
diff --git a/content/browser/indexed_db/indexed_db_context_impl.h b/content/browser/indexed_db/indexed_db_context_impl.h
index 6aff990..4f062c65 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -40,15 +40,16 @@
 class FilePath;
 class SequencedTaskRunner;
 class Value;
-}
+}  // namespace base
 
 namespace blink {
 class StorageKey;
-}
+}  // namespace blink
 
 namespace storage {
+struct BucketLocator;
 class QuotaClientCallbackWrapper;
-}
+}  // namespace storage
 
 namespace content {
 class IndexedDBConnection;
@@ -117,23 +118,23 @@
                              GetFilePathForTestingCallback callback) override;
   void ResetCachesForTesting(base::OnceClosure callback) override;
   void ForceSchemaDowngradeForTesting(
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       ForceSchemaDowngradeForTestingCallback callback) override;
   void HasV2SchemaCorruptionForTesting(
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       HasV2SchemaCorruptionForTestingCallback callback) override;
-  void WriteToIndexedDBForTesting(const blink::StorageKey& storage_key,
+  void WriteToIndexedDBForTesting(const storage::BucketLocator& bucket_locator,
                                   const std::string& key,
                                   const std::string& value,
                                   base::OnceClosure callback) override;
   void GetBlobCountForTesting(const blink::StorageKey& storage_key,
                               GetBlobCountForTestingCallback callback) override;
   void GetNextBlobNumberForTesting(
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       int64_t database_id,
       GetNextBlobNumberForTestingCallback callback) override;
   void GetPathForBlobForTesting(
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       int64_t database_id,
       int64_t blob_number,
       GetPathForBlobForTestingCallback callback) override;
diff --git a/content/browser/indexed_db/indexed_db_context_unittest.cc b/content/browser/indexed_db/indexed_db_context_unittest.cc
index eb606e18..d8bec47 100644
--- a/content/browser/indexed_db/indexed_db_context_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_context_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/threading/thread.h"
 #include "base/time/default_clock.h"
 #include "components/services/storage/public/cpp/buckets/bucket_info.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/cpp/buckets/constants.h"
 #include "components/services/storage/public/cpp/quota_error_or.h"
 #include "content/browser/indexed_db/indexed_db_context_impl.h"
@@ -91,10 +92,14 @@
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
       /*expect_connection=*/false);
   callbacks->CallOnInfoSuccess(base::BarrierClosure(2, loop.QuitClosure()));
+  auto example_bucket_locator = storage::BucketLocator();
+  example_bucket_locator.storage_key = example_storage_key_;
   indexed_db_context_->GetIDBFactory()->GetDatabaseInfo(
-      callbacks, example_storage_key_, indexed_db_context_->data_path());
+      callbacks, example_bucket_locator, indexed_db_context_->data_path());
+  auto google_bucket_locator = storage::BucketLocator();
+  google_bucket_locator.storage_key = google_storage_key_;
   indexed_db_context_->GetIDBFactory()->GetDatabaseInfo(
-      callbacks, google_storage_key_, indexed_db_context_->data_path());
+      callbacks, google_bucket_locator, indexed_db_context_->data_path());
   loop.Run();
 
   // Check default bucket exists for https://example.com.
diff --git a/content/browser/indexed_db/indexed_db_cursor.cc b/content/browser/indexed_db/indexed_db_cursor.cc
index aa395181..7af4a72 100644
--- a/content/browser/indexed_db/indexed_db_cursor.cc
+++ b/content/browser/indexed_db/indexed_db_cursor.cc
@@ -12,6 +12,7 @@
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/trace_event/base_tracing.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "content/browser/indexed_db/indexed_db_callback_helpers.h"
 #include "content/browser/indexed_db/indexed_db_callbacks.h"
 #include "content/browser/indexed_db/indexed_db_context_impl.h"
@@ -51,9 +52,9 @@
     indexed_db::CursorType cursor_type,
     blink::mojom::IDBTaskType task_type,
     base::WeakPtr<IndexedDBTransaction> transaction)
-    : storage_key_(transaction->BackingStoreTransaction()
-                       ->backing_store()
-                       ->storage_key()),
+    : bucket_locator_(transaction->BackingStoreTransaction()
+                          ->backing_store()
+                          ->bucket_locator()),
       task_type_(task_type),
       cursor_type_(cursor_type),
       transaction_(std::move(transaction)),
@@ -128,7 +129,7 @@
   if (value) {
     mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
     external_objects.swap(value->external_objects);
-    dispatcher_host->CreateAllExternalObjects(storage_key_, external_objects,
+    dispatcher_host->CreateAllExternalObjects(bucket_locator_, external_objects,
                                               &mojo_value->external_objects);
   } else {
     mojo_value = blink::mojom::IDBValue::New();
@@ -209,7 +210,7 @@
   if (value) {
     mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
     external_objects.swap(value->external_objects);
-    dispatcher_host->CreateAllExternalObjects(storage_key_, external_objects,
+    dispatcher_host->CreateAllExternalObjects(bucket_locator_, external_objects,
                                               &mojo_value->external_objects);
   } else {
     mojo_value = blink::mojom::IDBValue::New();
@@ -337,7 +338,7 @@
     mojo_values.push_back(
         IndexedDBValue::ConvertAndEraseValue(&found_values[i]));
     dispatcher_host->CreateAllExternalObjects(
-        storage_key_, found_values[i].external_objects,
+        bucket_locator_, found_values[i].external_objects,
         &mojo_values[i]->external_objects);
   }
 
diff --git a/content/browser/indexed_db/indexed_db_cursor.h b/content/browser/indexed_db/indexed_db_cursor.h
index 2ec5598..08d5f30 100644
--- a/content/browser/indexed_db/indexed_db_cursor.h
+++ b/content/browser/indexed_db/indexed_db_cursor.h
@@ -15,9 +15,12 @@
 #include "content/browser/indexed_db/indexed_db_database.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
+namespace storage {
+struct BucketLocator;
+}  // namespace storage
+
 namespace content {
 
 class IndexedDBCursor {
@@ -78,7 +81,7 @@
       IndexedDBTransaction* transaction);
 
  private:
-  const blink::StorageKey storage_key_;
+  const storage::BucketLocator bucket_locator_;
   blink::mojom::IDBTaskType task_type_;
   indexed_db::CursorType cursor_type_;
 
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 58c59f3..912be1d 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -70,14 +70,14 @@
 std::vector<blink::mojom::IDBReturnValuePtr> CreateMojoValues(
     std::vector<IndexedDBReturnValue>& found_values,
     IndexedDBDispatcherHost* dispatcher_host,
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   std::vector<blink::mojom::IDBReturnValuePtr> mojo_values;
   mojo_values.reserve(found_values.size());
   for (size_t i = 0; i < found_values.size(); ++i) {
     mojo_values.push_back(
         IndexedDBReturnValue::ConvertReturnValue(&found_values[i]));
     dispatcher_host->CreateAllExternalObjects(
-        storage_key, found_values[i].external_objects,
+        bucket_locator, found_values[i].external_objects,
         &mojo_values[i]->value->external_objects);
   }
   return mojo_values;
@@ -843,7 +843,7 @@
     blink::mojom::IDBReturnValuePtr mojo_value =
         IndexedDBReturnValue::ConvertReturnValue(&value);
     dispatcher_host->CreateAllExternalObjects(
-        storage_key(), value.external_objects,
+        bucket_locator(), value.external_objects,
         &mojo_value->value->external_objects);
     std::move(callback).Run(
         blink::mojom::IDBDatabaseGetResult::NewValue(std::move(mojo_value)));
@@ -901,7 +901,7 @@
   blink::mojom::IDBReturnValuePtr mojo_value =
       IndexedDBReturnValue::ConvertReturnValue(&value);
   dispatcher_host->CreateAllExternalObjects(
-      storage_key(), value.external_objects,
+      bucket_locator(), value.external_objects,
       &mojo_value->value->external_objects);
   std::move(callback).Run(
       blink::mojom::IDBDatabaseGetResult::NewValue(std::move(mojo_value)));
@@ -1063,7 +1063,7 @@
     } else {
       if (found_values.size() >= max_values_before_sending) {
         result_sink->ReceiveValues(CreateMojoValues(
-            found_values, dispatcher_host.get(), storage_key()));
+            found_values, dispatcher_host.get(), bucket_locator()));
         found_values.clear();
       }
     }
@@ -1075,8 +1075,8 @@
     }
   } else {
     if (!found_values.empty()) {
-      result_sink->ReceiveValues(
-          CreateMojoValues(found_values, dispatcher_host.get(), storage_key()));
+      result_sink->ReceiveValues(CreateMojoValues(
+          found_values, dispatcher_host.get(), bucket_locator()));
     }
   }
   return s;
@@ -1209,8 +1209,9 @@
     std::move(params->callback)
         .Run(blink::mojom::IDBTransactionPutResult::NewKey(*key));
   }
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   factory_->NotifyIndexedDBContentChanged(
-      storage_key(), metadata_.name,
+      bucket_locator().storage_key, metadata_.name,
       metadata_.object_stores[params->object_store_id].name);
   return s;
 }
@@ -1401,7 +1402,7 @@
       mojo_values.push_back(
           IndexedDBReturnValue::ConvertReturnValue(&found_values[j]));
       dispatcher_host->CreateAllExternalObjects(
-          storage_key(), found_values[j].external_objects,
+          bucket_locator(), found_values[j].external_objects,
           &mojo_values[j]->value->external_objects);
     }
     all_mojo_values.push_back(std::move(mojo_values));
@@ -1505,9 +1506,7 @@
   }
 
   if (mojo_value) {
-    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-    dispatcher_host->CreateAllExternalObjects(bucket_locator.storage_key,
-                                              external_objects,
+    dispatcher_host->CreateAllExternalObjects(bucket_locator, external_objects,
                                               &mojo_value->external_objects);
   }
 
@@ -1582,8 +1581,9 @@
   if (!s.ok())
     return s;
   callbacks->OnSuccess();
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   factory_->NotifyIndexedDBContentChanged(
-      storage_key(), metadata_.name,
+      bucket_locator().storage_key, metadata_.name,
       metadata_.object_stores[object_store_id].name);
   return s;
 }
@@ -1628,8 +1628,9 @@
     return s;
   callbacks->OnSuccess();
 
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   factory_->NotifyIndexedDBContentChanged(
-      storage_key(), metadata_.name,
+      bucket_locator().storage_key, metadata_.name,
       metadata_.object_stores[object_store_id].name);
   return s;
 }
diff --git a/content/browser/indexed_db/indexed_db_database.h b/content/browser/indexed_db/indexed_db_database.h
index 56e5687..00e81bf0 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -34,7 +34,6 @@
 #include "content/common/content_export.h"
 #include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 
 namespace blink {
@@ -57,8 +56,8 @@
 
 class CONTENT_EXPORT IndexedDBDatabase {
  public:
-  // Identifier is pair of (storage_key, database name).
-  using Identifier = std::pair<blink::StorageKey, std::u16string>;
+  // Identifier is pair of (bucket_locator, database name).
+  using Identifier = std::pair<storage::BucketLocator, std::u16string>;
   // Used to report irrecoverable backend errors. The second argument can be
   // null.
   using ErrorCallback =
@@ -77,7 +76,9 @@
 
   int64_t id() const { return metadata_.id; }
   const std::u16string& name() const { return metadata_.name; }
-  const blink::StorageKey& storage_key() const { return identifier_.first; }
+  const storage::BucketLocator& bucket_locator() const {
+    return identifier_.first;
+  }
   const blink::IndexedDBDatabaseMetadata& metadata() const { return metadata_; }
 
   LeveledLockManager* transaction_lock_manager() { return lock_manager_; }
diff --git a/content/browser/indexed_db/indexed_db_database_callbacks.cc b/content/browser/indexed_db/indexed_db_database_callbacks.cc
index 79df6b3..beaa2c8 100644
--- a/content/browser/indexed_db/indexed_db_database_callbacks.cc
+++ b/content/browser/indexed_db/indexed_db_database_callbacks.cc
@@ -79,8 +79,9 @@
   if (complete_)
     return;
 
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   indexed_db_context_->TransactionComplete(
-      transaction.database()->storage_key());
+      transaction.database()->bucket_locator().storage_key);
   if (callbacks_)
     callbacks_->Complete(transaction.id());
 }
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.cc b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
index d84539b7..264b0a5 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
@@ -291,9 +291,8 @@
       IDBTaskRunner());
 
   base::FilePath indexed_db_path = indexed_db_context_->data_path();
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   indexed_db_context_->GetIDBFactory()->GetDatabaseInfo(
-      std::move(callbacks), bucket_locator.storage_key, indexed_db_path);
+      std::move(callbacks), bucket_locator, indexed_db_path);
 }
 
 void IndexedDBDispatcherHost::Open(
@@ -337,9 +336,8 @@
 
   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
   // created) if this origin is already over quota.
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  indexed_db_context_->GetIDBFactory()->Open(
-      name, std::move(connection), bucket_locator.storage_key, indexed_db_path);
+  indexed_db_context_->GetIDBFactory()->Open(name, std::move(connection),
+                                             bucket_locator, indexed_db_path);
 }
 
 void IndexedDBDispatcherHost::DeleteDatabase(
@@ -365,10 +363,8 @@
       IDBTaskRunner());
 
   base::FilePath indexed_db_path = indexed_db_context_->data_path();
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   indexed_db_context_->GetIDBFactory()->DeleteDatabase(
-      name, std::move(callbacks), bucket_locator.storage_key, indexed_db_path,
-      force_close);
+      name, std::move(callbacks), bucket_locator, indexed_db_path, force_close);
 }
 
 void IndexedDBDispatcherHost::AbortTransactionsAndCompactDatabase(
@@ -385,9 +381,8 @@
   base::OnceCallback<void(leveldb::Status)> callback_on_io = base::BindOnce(
       &CallCompactionStatusCallbackOnIDBThread, std::move(mojo_callback));
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   indexed_db_context_->GetIDBFactory()->AbortTransactionsAndCompactDatabase(
-      std::move(callback_on_io), bucket_locator.storage_key);
+      std::move(callback_on_io), bucket_locator);
 }
 
 void IndexedDBDispatcherHost::AbortTransactionsForDatabase(
@@ -404,9 +399,8 @@
   base::OnceCallback<void(leveldb::Status)> callback_on_io = base::BindOnce(
       &CallAbortStatusCallbackOnIDBThread, std::move(mojo_callback));
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   indexed_db_context_->GetIDBFactory()->AbortTransactionsForDatabase(
-      std::move(callback_on_io), bucket_locator.storage_key);
+      std::move(callback_on_io), bucket_locator);
 }
 
 void IndexedDBDispatcherHost::CreateAndBindTransactionImpl(
@@ -448,7 +442,7 @@
 }
 
 void IndexedDBDispatcherHost::CreateAllExternalObjects(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const std::vector<IndexedDBExternalObject>& objects,
     std::vector<blink::mojom::IDBExternalObjectPtr>* mojo_objects) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -513,7 +507,7 @@
         } else {
           DCHECK(!blob_info.file_system_access_token().empty());
           file_system_access_context()->DeserializeHandle(
-              storage_key, blob_info.file_system_access_token(),
+              bucket_locator.storage_key, blob_info.file_system_access_token(),
               mojo_token.InitWithNewPipeAndPassReceiver());
         }
         mojo_object->get_file_system_access_token() = std::move(mojo_token);
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.h b/content/browser/indexed_db/indexed_db_dispatcher_host.h
index bec07d6..5a85a8c 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -28,11 +28,7 @@
 namespace base {
 class SequencedTaskRunner;
 class TaskRunner;
-}
-
-namespace blink {
-class StorageKey;
-}
+}  // namespace base
 
 namespace content {
 class IndexedDBContextImpl;
@@ -97,7 +93,7 @@
   // Create external objects from |objects| and store the results in
   // |mojo_objects|.  |mojo_objects| must be the same length as |objects|.
   void CreateAllExternalObjects(
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       const std::vector<IndexedDBExternalObject>& objects,
       std::vector<blink::mojom::IDBExternalObjectPtr>* mojo_objects);
 
diff --git a/content/browser/indexed_db/indexed_db_factory.h b/content/browser/indexed_db/indexed_db_factory.h
index c325be9..d0459db 100644
--- a/content/browser/indexed_db/indexed_db_factory.h
+++ b/content/browser/indexed_db/indexed_db_factory.h
@@ -24,6 +24,10 @@
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "url/gurl.h"
 
+namespace storage {
+struct BucketLocator;
+}  // namespace storage
+
 namespace content {
 
 class IndexedDBBackingStore;
@@ -38,25 +42,25 @@
   virtual ~IndexedDBFactory() = default;
 
   virtual void GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,
-                               const blink::StorageKey& storage_key,
+                               const storage::BucketLocator& bucket_locator,
                                const base::FilePath& data_directory) = 0;
   virtual void Open(const std::u16string& name,
                     std::unique_ptr<IndexedDBPendingConnection> connection,
-                    const blink::StorageKey& storage_key,
+                    const storage::BucketLocator& bucket_locator,
                     const base::FilePath& data_directory) = 0;
 
   virtual void DeleteDatabase(const std::u16string& name,
                               scoped_refptr<IndexedDBCallbacks> callbacks,
-                              const blink::StorageKey& storage_key,
+                              const storage::BucketLocator& bucket_locator,
                               const base::FilePath& data_directory,
                               bool force_close) = 0;
 
   virtual void AbortTransactionsAndCompactDatabase(
       base::OnceCallback<void(leveldb::Status)> callback,
-      const blink::StorageKey& storage_key) = 0;
+      const storage::BucketLocator& bucket_locator) = 0;
   virtual void AbortTransactionsForDatabase(
       base::OnceCallback<void(leveldb::Status)> callback,
-      const blink::StorageKey& storage_key) = 0;
+      const storage::BucketLocator& bucket_locator) = 0;
 
   virtual void HandleBackingStoreFailure(
       const blink::StorageKey& storage_key) = 0;
@@ -74,16 +78,18 @@
   virtual void ForceClose(const blink::StorageKey& storage_key,
                           bool delete_in_memory_store = false) = 0;
 
-  virtual void ForceSchemaDowngrade(const blink::StorageKey& storage_key) = 0;
+  virtual void ForceSchemaDowngrade(
+      const storage::BucketLocator& bucket_locator) = 0;
   virtual V2SchemaCorruptionStatus HasV2SchemaCorruption(
-      const blink::StorageKey& storage_key) = 0;
+      const storage::BucketLocator& bucket_locator) = 0;
 
   // Called by the IndexedDBContext destructor so the factory can do cleanup.
   virtual void ContextDestroyed() = 0;
 
   // Called by the IndexedDBActiveBlobRegistry.
-  virtual void ReportOutstandingBlobs(const blink::StorageKey& storage_key,
-                                      bool blobs_outstanding) = 0;
+  virtual void ReportOutstandingBlobs(
+      const storage::BucketLocator& bucket_locator,
+      bool blobs_outstanding) = 0;
 
   // Called by IndexedDBBackingStore when blob files have been cleaned.
   virtual void BlobFilesCleaned(const blink::StorageKey& storage_key) = 0;
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
index fcd6f8f..f04bfc9f5 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.cc
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -55,7 +55,6 @@
 #include "content/browser/indexed_db/indexed_db_reporting.h"
 #include "content/browser/indexed_db/indexed_db_task_helper.h"
 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
-#include "third_party/blink/public/common/storage_key/storage_key.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
 #include "third_party/leveldatabase/env_chromium.h"
 
@@ -188,7 +187,7 @@
 
 void IndexedDBFactoryImpl::GetDatabaseInfo(
     scoped_refptr<IndexedDBCallbacks> callbacks,
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const base::FilePath& data_directory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBFactoryImpl::GetDatabaseInfo");
@@ -199,7 +198,7 @@
   // Note: Any data loss information here is not piped up to the renderer, and
   // will be lost.
   std::tie(bucket_state_handle, s, error, std::ignore, std::ignore) =
-      GetOrOpenBucketFactory(storage_key, data_directory,
+      GetOrOpenBucketFactory(bucket_locator, data_directory,
                              /*create_if_missing=*/false);
   if (!bucket_state_handle.IsHeld() || !bucket_state_handle.bucket_state()) {
     if (s.IsNotFound()) {
@@ -207,8 +206,10 @@
     } else {
       callbacks->OnError(error);
     }
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
   IndexedDBBucketState* factory = bucket_state_handle.bucket_state();
@@ -222,8 +223,10 @@
                                    "Internal error opening backing store for "
                                    "indexedDB.databases().");
     callbacks->OnError(error);
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
   callbacks->OnSuccess(std::move(names_and_versions));
@@ -232,22 +235,24 @@
 void IndexedDBFactoryImpl::Open(
     const std::u16string& name,
     std::unique_ptr<IndexedDBPendingConnection> connection,
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const base::FilePath& data_directory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBFactoryImpl::Open");
-  IndexedDBDatabase::Identifier unique_identifier(storage_key, name);
+  IndexedDBDatabase::Identifier unique_identifier(bucket_locator, name);
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
   IndexedDBDatabaseError error;
   std::tie(bucket_state_handle, s, error, connection->data_loss_info,
            connection->was_cold_open) =
-      GetOrOpenBucketFactory(storage_key, data_directory,
+      GetOrOpenBucketFactory(bucket_locator, data_directory,
                              /*create_if_missing=*/true);
   if (!bucket_state_handle.IsHeld() || !bucket_state_handle.bucket_state()) {
     connection->callbacks->OnError(error);
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
   IndexedDBBucketState* factory = bucket_state_handle.bucket_state();
@@ -262,7 +267,7 @@
       name, factory->backing_store(), this,
       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForBucket,
                           bucket_state_destruction_weak_factory_.GetWeakPtr(),
-                          storage_key),
+                          bucket_locator),
       std::make_unique<IndexedDBMetadataCoding>(), std::move(unique_identifier),
       factory->lock_manager());
   if (!database.get()) {
@@ -270,8 +275,10 @@
         blink::mojom::IDBException::kUnknownError,
         u"Internal error creating database backend for indexedDB.open.");
     connection->callbacks->OnError(error);
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
 
@@ -286,24 +293,26 @@
 void IndexedDBFactoryImpl::DeleteDatabase(
     const std::u16string& name,
     scoped_refptr<IndexedDBCallbacks> callbacks,
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const base::FilePath& data_directory,
     bool force_close) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBFactoryImpl::DeleteDatabase");
-  IndexedDBDatabase::Identifier unique_identifier(storage_key, name);
+  IndexedDBDatabase::Identifier unique_identifier(bucket_locator, name);
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
   IndexedDBDatabaseError error;
   // Note: Any data loss information here is not piped up to the renderer, and
   // will be lost.
   std::tie(bucket_state_handle, s, error, std::ignore, std::ignore) =
-      GetOrOpenBucketFactory(storage_key, data_directory,
+      GetOrOpenBucketFactory(bucket_locator, data_directory,
                              /*create_if_missing=*/true);
   if (!bucket_state_handle.IsHeld() || !bucket_state_handle.bucket_state()) {
     callbacks->OnError(error);
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
   IndexedDBBucketState* factory = bucket_state_handle.bucket_state();
@@ -311,14 +320,18 @@
   auto it = factory->databases().find(name);
   if (it != factory->databases().end()) {
     base::WeakPtr<IndexedDBDatabase> database = it->second->AsWeakPtr();
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     database->ScheduleDeleteDatabase(
         std::move(bucket_state_handle), callbacks,
         base::BindOnce(&IndexedDBFactoryImpl::OnDatabaseDeleted,
-                       weak_factory_.GetWeakPtr(), storage_key));
+                       weak_factory_.GetWeakPtr(), bucket_locator.storage_key));
     if (force_close) {
       leveldb::Status status = database->ForceCloseAndRunTasks();
-      if (!status.ok())
-        OnDatabaseError(storage_key, status, "Error aborting transactions.");
+      if (!status.ok()) {
+        // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+        OnDatabaseError(bucket_locator.storage_key, status,
+                        "Error aborting transactions.");
+      }
     }
     return;
   }
@@ -335,8 +348,10 @@
                                    "Internal error opening backing store for "
                                    "indexedDB.deleteDatabase.");
     callbacks->OnError(error);
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
 
@@ -351,7 +366,7 @@
       name, factory->backing_store(), this,
       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForBucket,
                           bucket_state_destruction_weak_factory_.GetWeakPtr(),
-                          storage_key),
+                          bucket_locator),
       std::make_unique<IndexedDBMetadataCoding>(), unique_identifier,
       factory->lock_manager());
   if (!database.get()) {
@@ -359,31 +374,37 @@
                                    u"Internal error creating database backend "
                                    u"for indexedDB.deleteDatabase.");
     callbacks->OnError(error);
-    if (s.IsCorruption())
-      HandleBackingStoreCorruption(storage_key, error);
+    if (s.IsCorruption()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
+    }
     return;
   }
 
   base::WeakPtr<IndexedDBDatabase> database_ptr =
       factory->AddDatabase(name, std::move(database))->AsWeakPtr();
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   database_ptr->ScheduleDeleteDatabase(
       std::move(bucket_state_handle), std::move(callbacks),
       base::BindOnce(&IndexedDBFactoryImpl::OnDatabaseDeleted,
-                     weak_factory_.GetWeakPtr(), storage_key));
+                     weak_factory_.GetWeakPtr(), bucket_locator.storage_key));
   if (force_close) {
     leveldb::Status status = database_ptr->ForceCloseAndRunTasks();
-    if (!status.ok())
-      OnDatabaseError(storage_key, status, "Error aborting transactions.");
+    if (!status.ok()) {
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      OnDatabaseError(bucket_locator.storage_key, status,
+                      "Error aborting transactions.");
+    }
   }
 }
 
 void IndexedDBFactoryImpl::AbortTransactionsAndCompactDatabase(
     base::OnceCallback<void(leveldb::Status)> callback,
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB",
                "IndexedDBFactoryImpl::AbortTransactionsAndCompactDatabase");
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end()) {
     std::move(callback).Run(leveldb::Status::OK());
     return;
@@ -395,11 +416,11 @@
 
 void IndexedDBFactoryImpl::AbortTransactionsForDatabase(
     base::OnceCallback<void(leveldb::Status)> callback,
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB",
                "IndexedDBFactoryImpl::AbortTransactionsForDatabase");
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end()) {
     std::move(callback).Run(leveldb::Status::OK());
     return;
@@ -456,10 +477,13 @@
 std::vector<IndexedDBDatabase*> IndexedDBFactoryImpl::GetOpenDatabasesForBucket(
     const blink::StorageKey& storage_key) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
-  if (it == factories_per_bucket_.end()) {
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  auto bucket_it = storage_key_to_bucket_locator_.find(storage_key);
+  if (bucket_it == storage_key_to_bucket_locator_.end())
     return std::vector<IndexedDBDatabase*>();
-  }
+  auto it = factories_per_bucket_.find(bucket_it->second);
+  if (it == factories_per_bucket_.end())
+    return std::vector<IndexedDBDatabase*>();
   IndexedDBBucketState* factory = it->second.get();
   std::vector<IndexedDBDatabase*> out;
   out.reserve(factory->databases().size());
@@ -471,7 +495,11 @@
 void IndexedDBFactoryImpl::ForceClose(const blink::StorageKey& storage_key,
                                       bool delete_in_memory_store) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  auto bucket_it = storage_key_to_bucket_locator_.find(storage_key);
+  if (bucket_it == storage_key_to_bucket_locator_.end())
+    return;
+  auto it = factories_per_bucket_.find(bucket_it->second);
   if (it == factories_per_bucket_.end())
     return;
 
@@ -489,9 +517,9 @@
 }
 
 void IndexedDBFactoryImpl::ForceSchemaDowngrade(
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end())
     return;
 
@@ -501,9 +529,9 @@
 }
 
 V2SchemaCorruptionStatus IndexedDBFactoryImpl::HasV2SchemaCorruption(
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end())
     return V2SchemaCorruptionStatus::kUnknown;
 
@@ -524,16 +552,18 @@
   for (const auto& pair : factories_per_bucket_) {
     pair.second->ForceClose();
   }
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  storage_key_to_bucket_locator_.clear();
   factories_per_bucket_.clear();
 }
 
 void IndexedDBFactoryImpl::ReportOutstandingBlobs(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     bool blobs_outstanding) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!context_)
     return;
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   DCHECK(it != factories_per_bucket_.end());
 
   it->second->ReportOutstandingBlobs(blobs_outstanding);
@@ -551,7 +581,11 @@
 size_t IndexedDBFactoryImpl::GetConnectionCount(
     const blink::StorageKey& storage_key) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  auto bucket_it = storage_key_to_bucket_locator_.find(storage_key);
+  if (bucket_it == storage_key_to_bucket_locator_.end())
+    return 0;
+  auto it = factories_per_bucket_.find(bucket_it->second);
   if (it == factories_per_bucket_.end())
     return 0;
   size_t count = 0;
@@ -576,7 +610,11 @@
 int64_t IndexedDBFactoryImpl::GetInMemoryDBSize(
     const blink::StorageKey& storage_key) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  auto bucket_it = storage_key_to_bucket_locator_.find(storage_key);
+  if (bucket_it == storage_key_to_bucket_locator_.end())
+    return 0;
+  auto it = factories_per_bucket_.find(bucket_it->second);
   if (it == factories_per_bucket_.end())
     return 0;
   IndexedDBBackingStore* backing_store = it->second->backing_store();
@@ -592,16 +630,21 @@
 base::Time IndexedDBFactoryImpl::GetLastModified(
     const blink::StorageKey& storage_key) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  auto bucket_it = storage_key_to_bucket_locator_.find(storage_key);
+  if (bucket_it == storage_key_to_bucket_locator_.end())
+    return base::Time();
+  auto it = factories_per_bucket_.find(bucket_it->second);
   if (it == factories_per_bucket_.end())
     return base::Time();
   IndexedDBBackingStore* backing_store = it->second->backing_store();
   return backing_store->db()->LastModified();
 }
 
-std::vector<blink::StorageKey> IndexedDBFactoryImpl::GetOpenBuckets() const {
+std::vector<storage::BucketLocator> IndexedDBFactoryImpl::GetOpenBuckets()
+    const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::vector<blink::StorageKey> output;
+  std::vector<storage::BucketLocator> output;
   output.reserve(factories_per_bucket_.size());
   for (const auto& pair : factories_per_bucket_) {
     output.push_back(pair.first);
@@ -610,9 +653,9 @@
 }
 
 IndexedDBBucketState* IndexedDBFactoryImpl::GetBucketFactory(
-    const blink::StorageKey& storage_key) const {
+    const storage::BucketLocator& bucket_locator) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it != factories_per_bucket_.end())
     return it->second.get();
   return nullptr;
@@ -624,7 +667,7 @@
            IndexedDBDataLossInfo,
            /*is_cold_open=*/bool>
 IndexedDBFactoryImpl::GetOrOpenBucketFactory(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const base::FilePath& data_directory,
     bool create_if_missing) {
   TRACE_EVENT0("IndexedDB", "indexed_db::GetOrOpenBucketFactory");
@@ -634,7 +677,7 @@
   // where the flowchart should be seen as the 'master' logic template. Please
   // check the git history of both to make sure they are in sync.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it != factories_per_bucket_.end()) {
     return {it->second->CreateHandle(), leveldb::Status::OK(),
             IndexedDBDatabaseError(), IndexedDBDataLossInfo(),
@@ -651,8 +694,9 @@
   if (!is_incognito_and_in_memory) {
     // The database will be on-disk and not in-memory.
     auto filesystem_proxy = storage::CreateFilesystemProxy();
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     std::tie(database_path, blob_path, s) = CreateDatabaseDirectories(
-        filesystem_proxy.get(), data_directory, storage_key);
+        filesystem_proxy.get(), data_directory, bucket_locator.storage_key);
     if (!s.ok())
       return {IndexedDBBucketStateHandle(), s, CreateDefaultError(),
               IndexedDBDataLossInfo(), /*was_cold_open=*/true};
@@ -673,20 +717,21 @@
     scopes_options.lock_manager = lock_manager.get();
     scopes_options.metadata_key_prefix = ScopesPrefix::Encode();
     scopes_options.failure_callback = base::BindRepeating(
-        [](const blink::StorageKey& storage_key,
+        [](const storage::BucketLocator& bucket_locator,
            base::WeakPtr<IndexedDBFactoryImpl> factory, leveldb::Status s) {
           if (!factory)
             return;
-          factory->OnDatabaseError(storage_key, s, nullptr);
+          // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+          factory->OnDatabaseError(bucket_locator.storage_key, s, nullptr);
         },
-        storage_key, weak_factory_.GetWeakPtr());
+        bucket_locator, weak_factory_.GetWeakPtr());
     const bool is_first_attempt = i == 0;
     auto filesystem_proxy = !is_incognito_and_in_memory
                                 ? storage::CreateFilesystemProxy()
                                 : nullptr;
     std::tie(backing_store, s, data_loss_info, disk_full) =
         OpenAndVerifyIndexedDBBackingStore(
-            storage_key, data_directory, database_path, blob_path,
+            bucket_locator, data_directory, database_path, blob_path,
             std::move(scopes_options), &scopes_factory,
             std::move(filesystem_proxy), is_first_attempt, create_if_missing);
     if (LIKELY(is_first_attempt))
@@ -701,10 +746,13 @@
       std::string sanitized_message = leveldb_env::GetCorruptionMessage(s);
       base::ReplaceSubstringsAfterOffset(&sanitized_message, 0u,
                                          data_directory.AsUTF8Unsafe(), "...");
-      LOG(ERROR) << "Got corruption for " << storage_key.GetDebugString()
-                 << ", " << sanitized_message;
-      IndexedDBBackingStore::RecordCorruptionInfo(data_directory, storage_key,
-                                                  sanitized_message);
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      LOG(ERROR) << "Got corruption for "
+                 << bucket_locator.storage_key.GetDebugString() << ", "
+                 << sanitized_message;
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      IndexedDBBackingStore::RecordCorruptionInfo(
+          data_directory, bucket_locator.storage_key, sanitized_message);
     }
   }
 
@@ -720,11 +768,14 @@
   }
 
   if (UNLIKELY(!s.ok())) {
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
-                     storage_key);
+                     bucket_locator.storage_key);
 
     if (disk_full) {
-      context_->quota_manager_proxy()->NotifyWriteFailed(storage_key);
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      context_->quota_manager_proxy()->NotifyWriteFailed(
+          bucket_locator.storage_key);
       return {IndexedDBBucketStateHandle(), s,
               IndexedDBDatabaseError(blink::mojom::IDBException::kQuotaError,
                                      u"Encountered full disk while opening "
@@ -743,8 +794,9 @@
       LevelDBScopes::TaskRunnerMode::kNewCleanupAndRevertSequences);
 
   if (UNLIKELY(!s.ok())) {
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
-                     storage_key);
+                     bucket_locator.storage_key);
 
     return {IndexedDBBucketStateHandle(), s, CreateDefaultError(),
             data_loss_info, /*was_cold_open=*/true};
@@ -752,32 +804,37 @@
 
   if (!is_incognito_and_in_memory)
     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
-                     storage_key);
+                     bucket_locator.storage_key);
 
   auto run_tasks_callback = base::BindRepeating(
       &IndexedDBFactoryImpl::MaybeRunTasksForBucket,
-      bucket_state_destruction_weak_factory_.GetWeakPtr(), storage_key);
+      bucket_state_destruction_weak_factory_.GetWeakPtr(), bucket_locator);
 
   auto tear_down_callback = base::BindRepeating(
-      [](const blink::StorageKey& storage_key,
+      [](const storage::BucketLocator& bucket_locator,
          base::WeakPtr<IndexedDBFactoryImpl> factory, leveldb::Status s) {
         if (!factory)
           return;
-        factory->OnDatabaseError(storage_key, s, nullptr);
+        // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+        factory->OnDatabaseError(bucket_locator.storage_key, s, nullptr);
       },
-      storage_key, weak_factory_.GetWeakPtr());
+      bucket_locator, weak_factory_.GetWeakPtr());
 
   auto bucket_state = std::make_unique<IndexedDBBucketState>(
-      storage_key,
+      bucket_locator,
       /*persist_for_incognito=*/is_incognito_and_in_memory, clock_,
       &class_factory_->transactional_leveldb_factory(), &earliest_sweep_,
       &earliest_compaction_, std::move(lock_manager),
       std::move(run_tasks_callback), std::move(tear_down_callback),
       std::move(backing_store));
 
-  it =
-      factories_per_bucket_.emplace(storage_key, std::move(bucket_state)).first;
-  context_->FactoryOpened(storage_key);
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  storage_key_to_bucket_locator_.emplace(bucket_locator.storage_key,
+                                         bucket_locator);
+  it = factories_per_bucket_.emplace(bucket_locator, std::move(bucket_state))
+           .first;
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+  context_->FactoryOpened(bucket_locator.storage_key);
   return {it->second->CreateHandle(), s, IndexedDBDatabaseError(),
           data_loss_info, /*was_cold_open=*/true};
 }
@@ -785,7 +842,7 @@
 std::unique_ptr<IndexedDBBackingStore> IndexedDBFactoryImpl::CreateBackingStore(
     IndexedDBBackingStore::Mode backing_store_mode,
     TransactionalLevelDBFactory* transactional_leveldb_factory,
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     const base::FilePath& blob_path,
     std::unique_ptr<TransactionalLevelDBDatabase> db,
     storage::mojom::BlobStorageContext* blob_storage_context,
@@ -796,17 +853,18 @@
         report_outstanding_blobs,
     scoped_refptr<base::SequencedTaskRunner> idb_task_runner) {
   return std::make_unique<IndexedDBBackingStore>(
-      backing_store_mode, transactional_leveldb_factory, storage_key, blob_path,
-      std::move(db), blob_storage_context, file_system_access_context,
-      std::move(filesystem_proxy), std::move(blob_files_cleaned),
-      std::move(report_outstanding_blobs), std::move(idb_task_runner));
+      backing_store_mode, transactional_leveldb_factory, bucket_locator,
+      blob_path, std::move(db), blob_storage_context,
+      file_system_access_context, std::move(filesystem_proxy),
+      std::move(blob_files_cleaned), std::move(report_outstanding_blobs),
+      std::move(idb_task_runner));
 }
 std::tuple<std::unique_ptr<IndexedDBBackingStore>,
            leveldb::Status,
            IndexedDBDataLossInfo,
            bool /* is_disk_full */>
 IndexedDBFactoryImpl::OpenAndVerifyIndexedDBBackingStore(
-    const blink::StorageKey& storage_key,
+    const storage::BucketLocator& bucket_locator,
     base::FilePath data_directory,
     base::FilePath database_path,
     base::FilePath blob_path,
@@ -831,15 +889,17 @@
   if (!is_incognito_and_in_memory) {
     // Check for previous corruption, and if found then try to delete the
     // database.
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     std::string corruption_message = indexed_db::ReadCorruptionInfo(
-        filesystem_proxy.get(), data_directory, storage_key);
+        filesystem_proxy.get(), data_directory, bucket_locator.storage_key);
     if (UNLIKELY(!corruption_message.empty())) {
       LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
                     "database.";
       if (is_first_attempt) {
+        // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
         ReportOpenStatus(
             indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
-            storage_key);
+            bucket_locator.storage_key);
       }
       data_loss_info.status = blink::mojom::IDBDataLoss::Total;
       data_loss_info.message = base::StrCat(
@@ -905,35 +965,39 @@
     LOG(ERROR) << "IndexedDB had an error checking schema, treating it as "
                   "failure to open: "
                << status.ToString();
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     ReportOpenStatus(
         indexed_db::
             INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
-        storage_key);
+        bucket_locator.storage_key);
     return {nullptr, status, std::move(data_loss_info), /*is_disk_full=*/false};
   } else if (UNLIKELY(!are_schemas_known)) {
     LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it as "
                   "failure to open.";
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     ReportOpenStatus(
         indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
-        storage_key);
+        bucket_locator.storage_key);
     return {nullptr, leveldb::Status::Corruption("Unknown IndexedDB schema"),
             std::move(data_loss_info), /*is_disk_full=*/false};
   }
 
   bool first_open_since_startup =
-      backends_opened_since_startup_.insert(storage_key).second;
+      backends_opened_since_startup_.insert(bucket_locator).second;
   IndexedDBBackingStore::Mode backing_store_mode =
       is_incognito_and_in_memory ? IndexedDBBackingStore::Mode::kInMemory
                                  : IndexedDBBackingStore::Mode::kOnDisk;
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   std::unique_ptr<IndexedDBBackingStore> backing_store = CreateBackingStore(
       backing_store_mode, &class_factory_->transactional_leveldb_factory(),
-      storage_key, blob_path, std::move(database),
+      bucket_locator, blob_path, std::move(database),
       context_->blob_storage_context(), context_->file_system_access_context(),
       std::move(filesystem_proxy),
       base::BindRepeating(&IndexedDBFactoryImpl::BlobFilesCleaned,
-                          weak_factory_.GetWeakPtr(), storage_key),
+                          weak_factory_.GetWeakPtr(),
+                          bucket_locator.storage_key),
       base::BindRepeating(&IndexedDBFactoryImpl::ReportOutstandingBlobs,
-                          weak_factory_.GetWeakPtr(), storage_key),
+                          weak_factory_.GetWeakPtr(), bucket_locator),
       context_->IDBTaskRunner());
   status = backing_store->Initialize(
       /*clean_active_blob_journal=*/(!is_incognito_and_in_memory &&
@@ -947,8 +1011,10 @@
 }
 
 void IndexedDBFactoryImpl::RemoveBucketState(
-    const blink::StorageKey& storage_key) {
-  factories_per_bucket_.erase(storage_key);
+    const storage::BucketLocator& bucket_locator) {
+  // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+  storage_key_to_bucket_locator_.erase(bucket_locator.storage_key);
+  factories_per_bucket_.erase(bucket_locator);
 }
 
 void IndexedDBFactoryImpl::OnDatabaseError(const blink::StorageKey& storage_key,
@@ -984,10 +1050,10 @@
 }
 
 void IndexedDBFactoryImpl::MaybeRunTasksForBucket(
-    const blink::StorageKey& storage_key) {
+    const storage::BucketLocator& bucket_locator) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end())
     return;
 
@@ -1014,33 +1080,39 @@
     case IndexedDBBucketState::RunTasksResult::kDone:
       return;
     case IndexedDBBucketState::RunTasksResult::kError:
-      OnDatabaseError(bucket_state->storage_key(), status, nullptr);
+      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+      OnDatabaseError(bucket_state->bucket_locator().storage_key, status,
+                      nullptr);
       return;
     case IndexedDBBucketState::RunTasksResult::kCanBeDestroyed:
-      factories_per_bucket_.erase(bucket_state->storage_key());
+      // TODO(crbug.com/1218100): Remove once IndexedDBContextImpl is migrated.
+      storage_key_to_bucket_locator_.erase(
+          bucket_state->bucket_locator().storage_key);
+      factories_per_bucket_.erase(bucket_state->bucket_locator());
       return;
   }
 }
 
-bool IndexedDBFactoryImpl::IsDatabaseOpen(const blink::StorageKey& storage_key,
-                                          const std::u16string& name) const {
+bool IndexedDBFactoryImpl::IsDatabaseOpen(
+    const storage::BucketLocator& bucket_locator,
+    const std::u16string& name) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end())
     return false;
   return base::Contains(it->second->databases(), name);
 }
 
 bool IndexedDBFactoryImpl::IsBackingStoreOpen(
-    const blink::StorageKey& storage_key) const {
+    const storage::BucketLocator& bucket_locator) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return base::Contains(factories_per_bucket_, storage_key);
+  return base::Contains(factories_per_bucket_, bucket_locator);
 }
 
 bool IndexedDBFactoryImpl::IsBackingStorePendingClose(
-    const blink::StorageKey& storage_key) const {
+    const storage::BucketLocator& bucket_locator) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = factories_per_bucket_.find(storage_key);
+  auto it = factories_per_bucket_.find(bucket_locator);
   if (it == factories_per_bucket_.end())
     return false;
   return it->second->IsClosing();
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.h b/content/browser/indexed_db/indexed_db_factory_impl.h
index 72d6f91..3934b660 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.h
+++ b/content/browser/indexed_db/indexed_db_factory_impl.h
@@ -24,6 +24,7 @@
 #include "base/time/time.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "components/services/storage/indexed_db/scopes/leveldb_scopes_factory.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom-forward.h"
 #include "components/services/storage/public/mojom/file_system_access_context.mojom-forward.h"
 #include "content/browser/indexed_db/indexed_db_backing_store.h"
@@ -38,7 +39,7 @@
 namespace base {
 class FilePath;
 class SequencedTaskRunner;
-}
+}  // namespace base
 
 namespace content {
 class IndexedDBBucketState;
@@ -64,25 +65,25 @@
 
   // content::IndexedDBFactory overrides:
   void GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,
-                       const blink::StorageKey& storage_key,
+                       const storage::BucketLocator& bucket_locator,
                        const base::FilePath& data_directory) override;
   void Open(const std::u16string& name,
             std::unique_ptr<IndexedDBPendingConnection> connection,
-            const blink::StorageKey& storage_key,
+            const storage::BucketLocator& bucket_locator,
             const base::FilePath& data_directory) override;
 
   void DeleteDatabase(const std::u16string& name,
                       scoped_refptr<IndexedDBCallbacks> callbacks,
-                      const blink::StorageKey& storage_key,
+                      const storage::BucketLocator& bucket_locator,
                       const base::FilePath& data_directory,
                       bool force_close) override;
 
   void AbortTransactionsAndCompactDatabase(
       base::OnceCallback<void(leveldb::Status)> callback,
-      const blink::StorageKey& storage_key) override;
+      const storage::BucketLocator& bucket_locator) override;
   void AbortTransactionsForDatabase(
       base::OnceCallback<void(leveldb::Status)> callback,
-      const blink::StorageKey& storage_key) override;
+      const storage::BucketLocator& bucket_locator) override;
 
   void HandleBackingStoreFailure(const blink::StorageKey& storage_key) override;
   void HandleBackingStoreCorruption(
@@ -97,15 +98,16 @@
   void ForceClose(const blink::StorageKey& storage_key,
                   bool delete_in_memory_store) override;
 
-  void ForceSchemaDowngrade(const blink::StorageKey& storage_key) override;
+  void ForceSchemaDowngrade(
+      const storage::BucketLocator& bucket_locator) override;
   V2SchemaCorruptionStatus HasV2SchemaCorruption(
-      const blink::StorageKey& storage_key) override;
+      const storage::BucketLocator& bucket_locator) override;
 
   // Called by the IndexedDBContext destructor so the factory can do cleanup.
   void ContextDestroyed() override;
 
   // Called by the IndexedDBActiveBlobRegistry.
-  void ReportOutstandingBlobs(const blink::StorageKey& storage_key,
+  void ReportOutstandingBlobs(const storage::BucketLocator& bucket_locator,
                               bool blobs_outstanding) override;
 
   // Called by IndexedDBBackingStore when blob files have been cleaned.
@@ -125,10 +127,10 @@
   base::Time GetLastModified(
       const blink::StorageKey& storage_key) const override;
 
-  std::vector<blink::StorageKey> GetOpenBuckets() const;
+  std::vector<storage::BucketLocator> GetOpenBuckets() const;
 
   IndexedDBBucketState* GetBucketFactory(
-      const blink::StorageKey& storage_key) const;
+      const storage::BucketLocator& bucket_locator) const;
 
   // On an OK status, the factory handle is populated. Otherwise (when status is
   // not OK), the `IndexedDBDatabaseError` will be populated. If the status was
@@ -138,7 +140,7 @@
              IndexedDBDatabaseError,
              IndexedDBDataLossInfo,
              /*was_cold_open=*/bool>
-  GetOrOpenBucketFactory(const blink::StorageKey& storage_key,
+  GetOrOpenBucketFactory(const storage::BucketLocator& bucket_locator,
                          const base::FilePath& data_directory,
                          bool create_if_missing);
 
@@ -155,7 +157,7 @@
   virtual std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       TransactionalLevelDBFactory* leveldb_factory,
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext* blob_storage_context,
@@ -197,7 +199,7 @@
              IndexedDBDataLossInfo,
              bool /* is_disk_full */>
   OpenAndVerifyIndexedDBBackingStore(
-      const blink::StorageKey& storage_key,
+      const storage::BucketLocator& bucket_locator,
       base::FilePath data_directory,
       base::FilePath database_path,
       base::FilePath blob_path,
@@ -207,20 +209,21 @@
       bool is_first_attempt,
       bool create_if_missing);
 
-  void RemoveBucketState(const blink::StorageKey& storage_key);
+  void RemoveBucketState(const storage::BucketLocator& bucket_locator);
 
   // Called when the database has been deleted on disk.
   void OnDatabaseDeleted(const blink::StorageKey& storage_key);
 
-  void MaybeRunTasksForBucket(const blink::StorageKey& storage_key);
+  void MaybeRunTasksForBucket(const storage::BucketLocator& bucket_locator);
   void RunTasksForBucket(base::WeakPtr<IndexedDBBucketState> bucket_state);
 
   // Testing helpers, so unit tests don't need to grovel through internal
   // state.
-  bool IsDatabaseOpen(const blink::StorageKey& storage_key,
+  bool IsDatabaseOpen(const storage::BucketLocator& bucket_locator,
                       const std::u16string& name) const;
-  bool IsBackingStoreOpen(const blink::StorageKey& storage_key) const;
-  bool IsBackingStorePendingClose(const blink::StorageKey& storage_key) const;
+  bool IsBackingStoreOpen(const storage::BucketLocator& bucket_locator) const;
+  bool IsBackingStorePendingClose(
+      const storage::BucketLocator& bucket_locator) const;
 
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                     base::trace_event::ProcessMemoryDump* pmd) override;
@@ -233,10 +236,15 @@
   base::Time earliest_sweep_;
   base::Time earliest_compaction_;
 
-  base::flat_map<blink::StorageKey, std::unique_ptr<IndexedDBBucketState>>
+  base::flat_map<storage::BucketLocator, std::unique_ptr<IndexedDBBucketState>>
       factories_per_bucket_;
+  // TODO(crbug.com/1218100): We need to map the StorageKey in BucketLocator to
+  // the (for now) only matching BucketLocator. This is done because calls from
+  // IndexedDBContextImpl don't yet have the BucketLocator in all cases.
+  base::flat_map<blink::StorageKey, storage::BucketLocator>
+      storage_key_to_bucket_locator_;
 
-  std::set<blink::StorageKey> backends_opened_since_startup_;
+  std::set<storage::BucketLocator> backends_opened_since_startup_;
 
   OnDatabaseDeletedCallback call_on_database_deleted_for_testing_;
 
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index c992285..235f48e9 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -90,9 +90,9 @@
       // the deletion of the leveldb state. Once the states are no longer
       // around, delete all of the databases on disk.
       auto open_factory_buckets = factory->GetOpenBuckets();
-      for (const auto& storage_key : open_factory_buckets) {
+      for (const auto& bucket_locator : open_factory_buckets) {
         context_->ForceCloseSync(
-            storage_key,
+            bucket_locator.storage_key,
             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
       }
       // All leveldb databases are closed, and they can be deleted.
@@ -144,7 +144,7 @@
   // is no actual data in the database.
   std::tuple<std::unique_ptr<IndexedDBConnection>,
              scoped_refptr<MockIndexedDBDatabaseCallbacks>>
-  CreateConnectionForDatatabase(const blink::StorageKey& storage_key,
+  CreateConnectionForDatatabase(const storage::BucketLocator& bucket_locator,
                                 const std::u16string& name) {
     auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
     auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
@@ -161,7 +161,7 @@
       base::RunLoop loop;
       callbacks->CallOnUpgradeNeeded(
           base::BindLambdaForTesting([&]() { loop.Quit(); }));
-      factory()->Open(name, std::move(connection), storage_key,
+      factory()->Open(name, std::move(connection), bucket_locator,
                       context()->data_path());
       loop.Run();
     }
@@ -236,21 +236,27 @@
 
   const blink::StorageKey storage_key_1 =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator_1 = storage::BucketLocator();
+  bucket_locator_1.storage_key = storage_key_1;
   const blink::StorageKey storage_key_2 =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:82");
+  auto bucket_locator_2 = storage::BucketLocator();
+  bucket_locator_2.storage_key = storage_key_2;
 
   IndexedDBBucketStateHandle bucket_state1_handle;
   IndexedDBBucketStateHandle bucket_state2_handle;
   leveldb::Status s;
 
   std::tie(bucket_state1_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key_1, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator_1,
+                                        context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state1_handle.IsHeld()) << s.ToString();
   EXPECT_TRUE(s.ok()) << s.ToString();
 
   std::tie(bucket_state2_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key_2, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator_2,
+                                        context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state2_handle.IsHeld()) << s.ToString();
   EXPECT_TRUE(s.ok()) << s.ToString();
@@ -268,22 +274,23 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 
   factory()->ForceClose(storage_key, false);
   RunPostedTasks();
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 }
 
 TEST_F(IndexedDBFactoryTest, ImmediateClose) {
@@ -293,19 +300,20 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
   RunPostedTasks();
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
   EXPECT_EQ(0ul, factory()->GetOpenBuckets().size());
 }
 
@@ -316,28 +324,29 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   // Open a connection & immediately release it to cause the closing sequence to
   // start.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 
   EXPECT_EQ(IndexedDBBucketState::ClosingState::kPreCloseGracePeriod,
-            factory()->GetBucketFactory(storage_key)->closing_stage());
+            factory()->GetBucketFactory(bucket_locator)->closing_stage());
 
   task_environment()->FastForwardBy(base::Seconds(2));
 
   // The factory should be closed, as the pre close tasks are delayed.
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 
   // Move the clock to run the tasks in the next close sequence.
   // NOTE: The constants rate-limiting sweeps and compaction are currently the
@@ -347,28 +356,29 @@
   // Open a connection & immediately release it to cause the closing sequence to
   // start again.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
 
   // Manually execute the timer so that the PreCloseTaskList task doesn't also
   // run.
-  factory()->GetBucketFactory(storage_key)->close_timer()->FireNow();
+  factory()->GetBucketFactory(bucket_locator)->close_timer()->FireNow();
 
   // The pre-close tasks should be running now.
-  ASSERT_TRUE(factory()->GetBucketFactory(storage_key));
+  ASSERT_TRUE(factory()->GetBucketFactory(bucket_locator));
   EXPECT_EQ(IndexedDBBucketState::ClosingState::kRunningPreCloseTasks,
-            factory()->GetBucketFactory(storage_key)->closing_stage());
-  ASSERT_TRUE(factory()->GetBucketFactory(storage_key)->pre_close_task_queue());
+            factory()->GetBucketFactory(bucket_locator)->closing_stage());
+  ASSERT_TRUE(
+      factory()->GetBucketFactory(bucket_locator)->pre_close_task_queue());
   EXPECT_TRUE(factory()
-                  ->GetBucketFactory(storage_key)
+                  ->GetBucketFactory(bucket_locator)
                   ->pre_close_task_queue()
                   ->started());
 
   // Stop sweep by opening a connection.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   EXPECT_FALSE(
@@ -380,31 +390,32 @@
   clock.Advance(IndexedDBBucketState::kMaxEarliestGlobalSweepFromNow);
 
   bucket_state_handle.Release();
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
   EXPECT_EQ(IndexedDBBucketState::ClosingState::kPreCloseGracePeriod,
-            factory()->GetBucketFactory(storage_key)->closing_stage());
+            factory()->GetBucketFactory(bucket_locator)->closing_stage());
 
   // Manually execute the timer so that the PreCloseTaskList task doesn't also
   // run.
-  factory()->GetBucketFactory(storage_key)->close_timer()->FireNow();
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
+  factory()->GetBucketFactory(bucket_locator)->close_timer()->FireNow();
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
   RunPostedTasks();
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 
   //  Finally, move the clock forward so the storage key should allow a sweep.
   clock.Advance(IndexedDBBucketState::kMaxEarliestBucketSweepFromNow);
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   bucket_state_handle.Release();
-  factory()->GetBucketFactory(storage_key)->close_timer()->FireNow();
+  factory()->GetBucketFactory(bucket_locator)->close_timer()->FireNow();
 
-  ASSERT_TRUE(factory()->GetBucketFactory(storage_key));
+  ASSERT_TRUE(factory()->GetBucketFactory(bucket_locator));
   EXPECT_EQ(IndexedDBBucketState::ClosingState::kRunningPreCloseTasks,
-            factory()->GetBucketFactory(storage_key)->closing_stage());
-  ASSERT_TRUE(factory()->GetBucketFactory(storage_key)->pre_close_task_queue());
+            factory()->GetBucketFactory(bucket_locator)->closing_stage());
+  ASSERT_TRUE(
+      factory()->GetBucketFactory(bucket_locator)->pre_close_task_queue());
   EXPECT_TRUE(factory()
-                  ->GetBucketFactory(storage_key)
+                  ->GetBucketFactory(bucket_locator)
                   ->pre_close_task_queue()
                   ->started());
 }
@@ -416,14 +427,15 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   // Open a connection & immediately release it to cause the closing sequence to
   // start.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -454,14 +466,15 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   // Open a connection & immediately release it to cause the closing sequence to
   // start.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -495,13 +508,15 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   // Open a connection & immediately release it to cause the closing sequence to
   // start.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -514,12 +529,13 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   EXPECT_TRUE(StorageBucketFromHandle(bucket_state_handle)
@@ -527,14 +543,14 @@
                   ->is_incognito());
   bucket_state_handle.Release();
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 
   factory()->ForceClose(storage_key, false);
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
 
   factory()->ForceClose(storage_key, true);
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 }
 
 TEST_F(IndexedDBFactoryTest, TooLongOrigin) {
@@ -548,12 +564,13 @@
   const blink::StorageKey too_long_storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://" + origin +
                                                     ":81/");
-
+  auto too_long_bucket_locator = storage::BucketLocator();
+  too_long_bucket_locator.storage_key = too_long_storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(too_long_storage_key,
+      factory()->GetOrOpenBucketFactory(too_long_bucket_locator,
                                         context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_FALSE(bucket_state_handle.IsHeld());
@@ -564,6 +581,8 @@
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
   auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
@@ -575,7 +594,7 @@
       callbacks, db_callbacks,
       transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
       std::move(create_transaction_callback));
-  factory()->Open(u"db", std::move(connection), storage_key,
+  factory()->Open(u"db", std::move(connection), bucket_locator,
                   context()->data_path());
   RunPostedTasks();
 
@@ -588,46 +607,50 @@
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
   // Now simulate shutdown, which should clear all factories.
   factory()->ContextDestroyed();
   EXPECT_FALSE(StorageBucketFromHandle(bucket_state_handle));
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 }
 
 TEST_F(IndexedDBFactoryTest, FactoryForceClose) {
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
   StorageBucketFromHandle(bucket_state_handle)->ForceClose();
   bucket_state_handle.Release();
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
   RunPostedTasks();
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 }
 
 TEST_F(IndexedDBFactoryTest, ConnectionForceClose) {
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
   auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
@@ -639,19 +662,19 @@
       callbacks, db_callbacks,
       transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
       std::move(create_transaction_callback));
-  factory()->Open(u"db", std::move(connection), storage_key,
+  factory()->Open(u"db", std::move(connection), bucket_locator,
                   context()->data_path());
   EXPECT_FALSE(callbacks->connection());
   RunPostedTasks();
   EXPECT_TRUE(callbacks->connection());
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 
   callbacks->connection()->CloseAndReportForceClose();
 
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 
   EXPECT_TRUE(db_callbacks->forced_close_called());
 }
@@ -660,6 +683,8 @@
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
   auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
@@ -677,7 +702,7 @@
     base::RunLoop loop;
     callbacks->CallOnUpgradeNeeded(
         base::BindLambdaForTesting([&]() { loop.Quit(); }));
-    factory()->Open(u"db", std::move(connection), storage_key,
+    factory()->Open(u"db", std::move(connection), bucket_locator,
                     context()->data_path());
     loop.Run();
   }
@@ -690,14 +715,16 @@
 
   EXPECT_TRUE(db_callbacks->forced_close_called());
   // Since there are no more references the factory should be closing.
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 }
 
 TEST_F(IndexedDBFactoryTest, ConnectionCloseDuringUpgrade) {
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
   auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
@@ -715,7 +742,7 @@
     base::RunLoop loop;
     callbacks->CallOnUpgradeNeeded(
         base::BindLambdaForTesting([&]() { loop.Quit(); }));
-    factory()->Open(u"db", std::move(connection), storage_key,
+    factory()->Open(u"db", std::move(connection), bucket_locator,
                     context()->data_path());
     loop.Run();
   }
@@ -728,27 +755,29 @@
       IndexedDBConnection::CloseErrorHandling::kAbortAllReturnLastError);
 
   // Since there are no more references the factory should be closing.
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 }
 
 TEST_F(IndexedDBFactoryTest, DatabaseForceCloseWithFullConnection) {
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
   std::unique_ptr<IndexedDBConnection> connection;
   scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks;
   std::tie(connection, db_callbacks) =
-      CreateConnectionForDatatabase(storage_key, u"db");
+      CreateConnectionForDatatabase(bucket_locator, u"db");
 
   // Force close the database.
   connection->database()->ForceCloseAndRunTasks();
 
   EXPECT_TRUE(db_callbacks->forced_close_called());
   // Since there are no more references the factory should be closing.
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 }
 
 TEST_F(IndexedDBFactoryTest, DeleteDatabase) {
@@ -759,14 +788,16 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
-  factory()->DeleteDatabase(u"db", callbacks, storage_key,
+  factory()->DeleteDatabase(u"db", callbacks, bucket_locator,
                             context()->data_path(),
                             /*force_close=*/false);
 
   // Since there are no more references the factory should be closing.
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 }
 
 TEST_F(IndexedDBFactoryTest, DeleteDatabaseWithForceClose) {
@@ -774,12 +805,14 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   const std::u16string name = u"db";
 
   std::unique_ptr<IndexedDBConnection> connection;
   scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks;
   std::tie(connection, db_callbacks) =
-      CreateConnectionForDatatabase(storage_key, name);
+      CreateConnectionForDatatabase(bucket_locator, name);
 
   base::RunLoop run_loop;
   factory()->CallOnDatabaseDeletedForTesting(base::BindLambdaForTesting(
@@ -791,7 +824,7 @@
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
       /*expect_connection=*/false);
 
-  factory()->DeleteDatabase(name, callbacks, storage_key,
+  factory()->DeleteDatabase(name, callbacks, bucket_locator,
                             context()->data_path(),
                             /*force_close=*/true);
 
@@ -799,8 +832,8 @@
   // isn't force closed, and instead is going through it's shutdown sequence.
   EXPECT_FALSE(connection->IsConnected());
   EXPECT_TRUE(db_callbacks->forced_close_called());
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 
   // Wait until the DB is deleted before tearing down since these concurrent
   // operations may conflict.
@@ -815,12 +848,14 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
 
-  factory()->GetDatabaseInfo(callbacks, storage_key, context()->data_path());
+  factory()->GetDatabaseInfo(callbacks, bucket_locator, context()->data_path());
 
   EXPECT_TRUE(callbacks->info_called());
   // Don't create a factory if one doesn't exist.
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key));
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator));
 }
 
 TEST_F(IndexedDBFactoryTest, GetDatabaseNames_ExistingFactory) {
@@ -831,21 +866,22 @@
 
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
-
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   IndexedDBBucketStateHandle bucket_state_handle;
   leveldb::Status s;
 
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
-  factory()->GetDatabaseInfo(callbacks, storage_key, context()->data_path());
+  factory()->GetDatabaseInfo(callbacks, bucket_locator, context()->data_path());
 
   EXPECT_TRUE(callbacks->info_called());
-  EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
+  EXPECT_TRUE(factory()->GetBucketFactory(bucket_locator));
   // GetDatabaseInfo didn't create the factory, so it shouldn't close it.
-  EXPECT_FALSE(factory()->GetBucketFactory(storage_key)->IsClosing());
+  EXPECT_FALSE(factory()->GetBucketFactory(bucket_locator)->IsClosing());
 }
 
 class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
@@ -886,6 +922,8 @@
           nullptr, mojo::NullAssociatedRemote(), context()->IDBTaskRunner());
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   const std::u16string name(u"name");
   auto create_transaction_callback =
       base::BindOnce(&CreateAndBindTransactionPlaceholder);
@@ -893,7 +931,7 @@
       callbacks, dummy_database_callbacks,
       /*transaction_id=*/1, /*version=*/1,
       std::move(create_transaction_callback));
-  factory()->Open(name, std::move(connection), storage_key,
+  factory()->Open(name, std::move(connection), bucket_locator,
                   context()->data_path());
   EXPECT_TRUE(callbacks->error_called());
   base::RunLoop().RunUntilIdle();
@@ -943,6 +981,8 @@
   SetupContext();
   const blink::StorageKey storage_key =
       blink::StorageKey::CreateFromStringForTesting("http://localhost:81");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
   const std::u16string db_name(u"db");
   const int64_t transaction_id = 1;
 
@@ -964,12 +1004,12 @@
       base::RunLoop loop;
       callbacks->CallOnUpgradeNeeded(
           base::BindLambdaForTesting([&]() { loop.Quit(); }));
-      factory()->Open(db_name, std::move(connection), storage_key,
+      factory()->Open(db_name, std::move(connection), bucket_locator,
                       context()->data_path());
       loop.Run();
     }
     EXPECT_TRUE(callbacks->upgrade_called());
-    EXPECT_TRUE(factory()->IsDatabaseOpen(storage_key, db_name));
+    EXPECT_TRUE(factory()->IsDatabaseOpen(bucket_locator, db_name));
   }
 
   // Finish connecting, then close the connection.
@@ -984,7 +1024,7 @@
     callbacks->connection()->AbortTransactionsAndClose(
         IndexedDBConnection::CloseErrorHandling::kAbortAllReturnLastError);
     RunPostedTasks();
-    EXPECT_FALSE(factory()->IsDatabaseOpen(storage_key, db_name));
+    EXPECT_FALSE(factory()->IsDatabaseOpen(bucket_locator, db_name));
   }
 
   // Open at version < 2, which will fail.
@@ -995,12 +1035,12 @@
     auto connection = std::make_unique<IndexedDBPendingConnection>(
         failed_open_callbacks, db_callbacks2,
         transaction_id, db_version, std::move(create_transaction_callback));
-    factory()->Open(db_name, std::move(connection), storage_key,
+    factory()->Open(db_name, std::move(connection), bucket_locator,
                     context()->data_path());
-    EXPECT_TRUE(factory()->IsDatabaseOpen(storage_key, db_name));
+    EXPECT_TRUE(factory()->IsDatabaseOpen(bucket_locator, db_name));
     RunPostedTasks();
     EXPECT_TRUE(failed_open_callbacks->saw_error());
-    EXPECT_FALSE(factory()->IsDatabaseOpen(storage_key, db_name));
+    EXPECT_FALSE(factory()->IsDatabaseOpen(bucket_locator, db_name));
   }
 }
 
@@ -1029,7 +1069,7 @@
 
 TEST_F(IndexedDBFactoryTest, DataFormatVersion) {
   SetupContext();
-  auto try_open = [this](const blink::StorageKey& storage_key,
+  auto try_open = [this](const storage::BucketLocator& bucket_locator,
                          const IndexedDBDataFormatVersion& version) {
     base::AutoReset<IndexedDBDataFormatVersion> override_version(
         &IndexedDBDataFormatVersion::GetMutableCurrentForTesting(), version);
@@ -1057,7 +1097,7 @@
           base::BindLambdaForTesting([&]() { loop.Quit(); }));
 
       this->factory()->Open(u"test_db", std::move(pending_connection),
-                            storage_key, context()->data_path());
+                            bucket_locator, context()->data_path());
       loop.Run();
 
       // If an upgrade was requested, then commit the upgrade transaction.
@@ -1078,7 +1118,7 @@
       }
     }
     RunPostedTasks();
-    factory()->ForceClose(storage_key, false);
+    factory()->ForceClose(bucket_locator.storage_key, false);
     RunPostedTasks();
     return callbacks->data_loss();
   };
@@ -1096,10 +1136,12 @@
     SCOPED_TRACE(test.origin);
     const blink::StorageKey storage_key =
         blink::StorageKey::CreateFromStringForTesting(test.origin);
+    auto bucket_locator = storage::BucketLocator();
+    bucket_locator.storage_key = storage_key;
     ASSERT_EQ(blink::mojom::IDBDataLoss::None,
-              try_open(storage_key, test.open_version_1));
+              try_open(bucket_locator, test.open_version_1));
     EXPECT_EQ(test.expected_data_loss,
-              try_open(storage_key, test.open_version_2));
+              try_open(bucket_locator, test.open_version_2));
   }
 }
 
diff --git a/content/browser/indexed_db/indexed_db_fake_backing_store.cc b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
index 3a53cb1..18a72cfe 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -11,6 +11,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
+#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
 #include "third_party/blink/public/common/storage_key/storage_key.h"
 
@@ -25,41 +26,49 @@
   return factory.get();
 }
 
+const storage::BucketLocator GetBucketLocator(blink::StorageKey storage_key) {
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = storage_key;
+  return bucket_locator;
+}
+
 }  // namespace
 
 IndexedDBFakeBackingStore::IndexedDBFakeBackingStore()
-    : IndexedDBBackingStore(
-          IndexedDBBackingStore::Mode::kInMemory,
-          GetTransactionalLevelDBFactory(),
-          blink::StorageKey::CreateFromStringForTesting("http://localhost:81"),
-          base::FilePath(),
-          std::unique_ptr<TransactionalLevelDBDatabase>(),
-          /*blob_storage_context=*/nullptr,
-          /*file_system_access_context=*/nullptr,
-          std::make_unique<storage::FilesystemProxy>(
-              storage::FilesystemProxy::UNRESTRICTED,
-              base::FilePath()),
-          BlobFilesCleanedCallback(),
-          ReportOutstandingBlobsCallback(),
-          base::SequencedTaskRunnerHandle::Get()) {}
+    : IndexedDBBackingStore(IndexedDBBackingStore::Mode::kInMemory,
+                            GetTransactionalLevelDBFactory(),
+                            storage::BucketLocator(GetBucketLocator(
+                                blink::StorageKey::CreateFromStringForTesting(
+                                    "http://localhost:81"))),
+                            base::FilePath(),
+                            std::unique_ptr<TransactionalLevelDBDatabase>(),
+                            /*blob_storage_context=*/nullptr,
+                            /*file_system_access_context=*/nullptr,
+                            std::make_unique<storage::FilesystemProxy>(
+                                storage::FilesystemProxy::UNRESTRICTED,
+                                base::FilePath()),
+                            BlobFilesCleanedCallback(),
+                            ReportOutstandingBlobsCallback(),
+                            base::SequencedTaskRunnerHandle::Get()) {}
 IndexedDBFakeBackingStore::IndexedDBFakeBackingStore(
     BlobFilesCleanedCallback blob_files_cleaned,
     ReportOutstandingBlobsCallback report_outstanding_blobs,
     scoped_refptr<base::SequencedTaskRunner> task_runner)
-    : IndexedDBBackingStore(
-          IndexedDBBackingStore::Mode::kOnDisk,
-          GetTransactionalLevelDBFactory(),
-          blink::StorageKey::CreateFromStringForTesting("http://localhost:81"),
-          base::FilePath(),
-          std::unique_ptr<TransactionalLevelDBDatabase>(),
-          /*blob_storage_context=*/nullptr,
-          /*file_system_access_context=*/nullptr,
-          std::make_unique<storage::FilesystemProxy>(
-              storage::FilesystemProxy::UNRESTRICTED,
-              base::FilePath()),
-          std::move(blob_files_cleaned),
-          std::move(report_outstanding_blobs),
-          task_runner) {}
+    : IndexedDBBackingStore(IndexedDBBackingStore::Mode::kOnDisk,
+                            GetTransactionalLevelDBFactory(),
+                            storage::BucketLocator(GetBucketLocator(
+                                blink::StorageKey::CreateFromStringForTesting(
+                                    "http://localhost:81"))),
+                            base::FilePath(),
+                            std::unique_ptr<TransactionalLevelDBDatabase>(),
+                            /*blob_storage_context=*/nullptr,
+                            /*file_system_access_context=*/nullptr,
+                            std::make_unique<storage::FilesystemProxy>(
+                                storage::FilesystemProxy::UNRESTRICTED,
+                                base::FilePath()),
+                            std::move(blob_files_cleaned),
+                            std::move(report_outstanding_blobs),
+                            task_runner) {}
 IndexedDBFakeBackingStore::~IndexedDBFakeBackingStore() = default;
 
 leveldb::Status IndexedDBFakeBackingStore::DeleteDatabase(
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc
index b778a11..85712d6d 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -126,9 +126,9 @@
       // the deletion of the leveldb state. Once the states are no longer
       // around, delete all of the databases on disk.
       auto open_factory_buckets = factory->GetOpenBuckets();
-      for (const auto& storage_key : open_factory_buckets) {
+      for (const auto& bucket_locator : open_factory_buckets) {
         context_->ForceCloseSync(
-            storage_key,
+            bucket_locator.storage_key,
             storage::mojom::ForceCloseReason::FORCE_CLOSE_DELETE_ORIGIN);
       }
       // All leveldb databases are closed, and they can be deleted.
@@ -266,7 +266,7 @@
                 std::make_unique<IndexedDBPendingConnection>(
                     open_callbacks, open_db_callbacks, host_transaction_id,
                     version, std::move(create_transaction_callback1)),
-                kTestStorageKey, context()->data_path());
+                bucket_locator, context()->data_path());
   EXPECT_TRUE(base::DirectoryExists(test_path));
 
   auto create_transaction_callback2 =
@@ -275,7 +275,7 @@
                 std::make_unique<IndexedDBPendingConnection>(
                     closed_callbacks, closed_db_callbacks, host_transaction_id,
                     version, std::move(create_transaction_callback2)),
-                kTestStorageKey, context()->data_path());
+                bucket_locator, context()->data_path());
   RunPostedTasks();
   ASSERT_TRUE(closed_callbacks->connection());
   closed_callbacks->connection()->AbortTransactionsAndClose(
@@ -325,6 +325,8 @@
 TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnCommitFailure) {
   const blink::StorageKey kTestStorageKey =
       blink::StorageKey::CreateFromStringForTesting("http://test/");
+  auto bucket_locator = storage::BucketLocator();
+  bucket_locator.storage_key = kTestStorageKey;
 
   auto* factory =
       static_cast<IndexedDBFactoryImpl*>(context()->GetIDBFactory());
@@ -339,7 +341,7 @@
       callbacks, db_callbacks,
       transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
       std::move(create_transaction_callback1));
-  factory->Open(u"db", std::move(connection), kTestStorageKey,
+  factory->Open(u"db", std::move(connection), bucket_locator,
                 context()->data_path());
   RunPostedTasks();
 
@@ -348,14 +350,14 @@
   // ConnectionOpened() is usually called by the dispatcher.
   context()->ConnectionOpened(kTestStorageKey, callbacks->connection());
 
-  EXPECT_TRUE(factory->IsBackingStoreOpen(kTestStorageKey));
+  EXPECT_TRUE(factory->IsBackingStoreOpen(bucket_locator));
 
   // Simulate the write failure.
   leveldb::Status status = leveldb::Status::IOError("Simulated failure");
   factory->HandleBackingStoreFailure(kTestStorageKey);
 
   EXPECT_TRUE(db_callbacks->forced_close_called());
-  EXPECT_FALSE(factory->IsBackingStoreOpen(kTestStorageKey));
+  EXPECT_FALSE(factory->IsBackingStoreOpen(bucket_locator));
 }
 
 TEST(LeveledLockManager, TestRangeDifferences) {
diff --git a/content/browser/indexed_db/mock_indexed_db_factory.h b/content/browser/indexed_db/mock_indexed_db_factory.h
index 0f0b369..107cfeb 100644
--- a/content/browser/indexed_db/mock_indexed_db_factory.h
+++ b/content/browser/indexed_db/mock_indexed_db_factory.h
@@ -29,7 +29,7 @@
                     const base::FilePath& data_directory));
   MOCK_METHOD3(GetDatabaseInfo,
                void(scoped_refptr<IndexedDBCallbacks> callbacks,
-                    const blink::StorageKey& storage_key,
+                    const storage::BucketLocator& bucket_locator,
                     const base::FilePath& data_directory));
   MOCK_METHOD4(OpenProxy,
                void(const std::u16string& name,
@@ -39,33 +39,34 @@
   // Googlemock can't deal with move-only types, so *Proxy() is a workaround.
   void Open(const std::u16string& name,
             std::unique_ptr<IndexedDBPendingConnection> connection,
-            const blink::StorageKey& storage_key,
+            const storage::BucketLocator& bucket_locator,
             const base::FilePath& data_directory) override {
-    OpenProxy(name, connection.get(), storage_key, data_directory);
+    OpenProxy(name, connection.get(), bucket_locator.storage_key,
+              data_directory);
   }
   MOCK_METHOD5(DeleteDatabase,
                void(const std::u16string& name,
                     scoped_refptr<IndexedDBCallbacks> callbacks,
-                    const blink::StorageKey& storage_key,
+                    const storage::BucketLocator& bucket_locator,
                     const base::FilePath& data_directory,
                     bool force_close));
   MOCK_METHOD2(AbortTransactionsAndCompactDatabaseProxy,
                void(base::OnceCallback<void(leveldb::Status)>* callback,
-                    const blink::StorageKey& storage_key));
+                    const storage::BucketLocator& bucket_locator));
   void AbortTransactionsAndCompactDatabase(
       base::OnceCallback<void(leveldb::Status)> callback,
-      const blink::StorageKey& storage_key) override {
+      const storage::BucketLocator& bucket_locator) override {
     base::OnceCallback<void(leveldb::Status)>* callback_ref = &callback;
-    AbortTransactionsAndCompactDatabaseProxy(callback_ref, storage_key);
+    AbortTransactionsAndCompactDatabaseProxy(callback_ref, bucket_locator);
   }
   MOCK_METHOD2(AbortTransactionsForDatabaseProxy,
                void(base::OnceCallback<void(leveldb::Status)>* callback,
-                    const blink::StorageKey& storage_key));
+                    const storage::BucketLocator& bucket_locator));
   void AbortTransactionsForDatabase(
       base::OnceCallback<void(leveldb::Status)> callback,
-      const blink::StorageKey& storage_key) override {
+      const storage::BucketLocator& bucket_locator) override {
     base::OnceCallback<void(leveldb::Status)>* callback_ref = &callback;
-    AbortTransactionsForDatabaseProxy(callback_ref, storage_key);
+    AbortTransactionsForDatabaseProxy(callback_ref, bucket_locator);
   }
 
   MOCK_METHOD1(HandleBackingStoreFailure,
@@ -81,9 +82,10 @@
                void(const blink::StorageKey& storage_key,
                     bool delete_in_memory_store));
   MOCK_METHOD1(ForceSchemaDowngrade,
-               void(const blink::StorageKey& storage_key));
-  MOCK_METHOD1(HasV2SchemaCorruption,
-               V2SchemaCorruptionStatus(const blink::StorageKey& storage_key));
+               void(const storage::BucketLocator& bucket_locator));
+  MOCK_METHOD1(
+      HasV2SchemaCorruption,
+      V2SchemaCorruptionStatus(const storage::BucketLocator& bucket_locator));
   MOCK_METHOD0(ContextDestroyed, void());
 
   MOCK_METHOD1(BlobFilesCleaned, void(const blink::StorageKey& storage_key));
@@ -98,7 +100,7 @@
                      base::Time(const blink::StorageKey& storage_key));
 
   MOCK_METHOD2(ReportOutstandingBlobs,
-               void(const blink::StorageKey& storage_key,
+               void(const storage::BucketLocator& bucket_locator,
                     bool blobs_outstanding));
 
   MOCK_METHOD1(NotifyIndexedDBListChanged,
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index 9fce4766..61a79a9 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -707,6 +707,9 @@
 // image.  CORB allows, because CORB doesn't care about
 // 'application/octet-stream'.
 IMG_TEST(nosniff_png, "nosniff.png.octet-stream", kShouldBeSniffedAndAllowed)
+// Like nosniff_png above, except that ORB v0.1 allows because HLS/m3u8 doesn't
+// sniff as HTML/XML/JSON.
+IMG_TEST(m3u8_octet_stream, "m3u8.octet-stream", kShouldBeSniffedAndAllowed)
 
 class CrossSiteDocumentBlockingTest
     : public CrossSiteDocumentBlockingTestBase,
diff --git a/content/browser/quota/quota_internals_browsertest.cc b/content/browser/quota/quota_internals_browsertest.cc
index 072d517..74e5ea2 100644
--- a/content/browser/quota/quota_internals_browsertest.cc
+++ b/content/browser/quota/quota_internals_browsertest.cc
@@ -81,6 +81,10 @@
   EXPECT_TRUE(
       ExecJsInWebUI("document.getElementsByClassName('syncable-global-and-"
                     "unlimited-usage') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.body.innerHTML.search('Temp Pool Size') >= 0;"));
+  EXPECT_TRUE(
+      ExecJsInWebUI("document.getElementsByClassName('temp-pool-size') >= 0;"));
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 146d630..c1e92ac 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -6419,7 +6419,7 @@
 }
 
 void RenderFrameHostImpl::ScrollRectToVisibleInParentFrame(
-    const gfx::Rect& rect_to_scroll,
+    const gfx::RectF& rect_to_scroll,
     blink::mojom::ScrollIntoViewParamsPtr params) {
   RenderFrameProxyHost* proxy = GetProxyToParent();
   if (!proxy)
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index f95fcd6c..12d8979 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2048,7 +2048,7 @@
       blink::mojom::SuddenTerminationDisablerType disabler_type) override;
   void HadStickyUserActivationBeforeNavigationChanged(bool value) override;
   void ScrollRectToVisibleInParentFrame(
-      const gfx::Rect& rect_to_scroll,
+      const gfx::RectF& rect_to_scroll,
       blink::mojom::ScrollIntoViewParamsPtr params) override;
   void BubbleLogicalScrollInParentFrame(
       blink::mojom::ScrollDirection direction,
diff --git a/content/browser/renderer_host/render_frame_proxy_host.cc b/content/browser/renderer_host/render_frame_proxy_host.cc
index 4b98cb64..48c875f 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.cc
+++ b/content/browser/renderer_host/render_frame_proxy_host.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
 #include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace content {
 
@@ -384,7 +385,7 @@
 }
 
 void RenderFrameProxyHost::ScrollRectToVisible(
-    const gfx::Rect& rect_to_scroll,
+    const gfx::RectF& rect_to_scroll,
     blink::mojom::ScrollIntoViewParamsPtr params) {
   GetAssociatedRemoteFrame()->ScrollRectToVisible(rect_to_scroll,
                                                   std::move(params));
diff --git a/content/browser/renderer_host/render_frame_proxy_host.h b/content/browser/renderer_host/render_frame_proxy_host.h
index 13a8e4e..81ef06d 100644
--- a/content/browser/renderer_host/render_frame_proxy_host.h
+++ b/content/browser/renderer_host/render_frame_proxy_host.h
@@ -31,6 +31,7 @@
 
 namespace gfx {
 class Rect;
+class RectF;
 }
 
 namespace perfetto {
@@ -177,7 +178,7 @@
   // element in the frame's parent. Calling this continues a scroll started in
   // the frame's current process. |rect_to_scroll| is with respect to the
   // coordinates of the originating frame in OOPIF process.
-  void ScrollRectToVisible(const gfx::Rect& rect_to_scroll,
+  void ScrollRectToVisible(const gfx::RectF& rect_to_scroll,
                            blink::mojom::ScrollIntoViewParamsPtr params);
 
   // Sets render frame proxy created state. If |created| is false, any existing
diff --git a/content/browser/resources/quota/quota_internals.html b/content/browser/resources/quota/quota_internals.html
index 1b0c33a..3a90df83 100644
--- a/content/browser/resources/quota/quota_internals.html
+++ b/content/browser/resources/quota/quota_internals.html
@@ -28,6 +28,7 @@
               <th>Temporary Storage Usage</th>
               <th>Persistent Storage Usage</th>
               <th>Syncable Storage Usage</th>
+              <th>Temp Pool Size</th>
             </thead>
             <tbody id="listeners-tbody"></tbody>
           </table>
@@ -84,6 +85,7 @@
         <td class="temporary-global-and-unlimited-usage"></td>
         <td class="persistent-global-and-unlimited-usage"></td>
         <td class="syncable-global-and-unlimited-usage"></td>
+        <td class="temp-pool-size"></td>
       </tr>
     </template>
     <template id="eviction-row">
diff --git a/content/browser/resources/quota/quota_internals.ts b/content/browser/resources/quota/quota_internals.ts
index acb5840..bc0ebf0 100644
--- a/content/browser/resources/quota/quota_internals.ts
+++ b/content/browser/resources/quota/quota_internals.ts
@@ -79,8 +79,8 @@
   return currentTotalUsageObj.hostUsage.toString();
 }
 
-async function renderDiskAvailability() {
-  const result = await getProxy().getDiskAvailability();
+async function renderDiskAvailabilityAndTempPoolSize() {
+  const result = await getProxy().getDiskAvailabilityAndTempPoolSize();
 
   const rowTemplate: HTMLTemplateElement =
       document.body.querySelector<HTMLTemplateElement>('#listener-row')!;
@@ -93,11 +93,15 @@
   const availableSpaceBytes =
       (Number(result.availableSpace) / (1024 ** 3)).toFixed(2);
   const totalSpaceBytes = (Number(result.totalSpace) / (1024 ** 3)).toFixed(2);
+  const tempPoolSizeBytes =
+      (Number(result.tempPoolSize) / (1024 ** 3)).toFixed(2);
 
   listenerRow.querySelector('.total-space')!.textContent =
       `${totalSpaceBytes} GB`;
   listenerRow.querySelector('.available-space')!.textContent =
       `${availableSpaceBytes} GB`;
+  listenerRow.querySelector('.temp-pool-size')!.textContent =
+      `${tempPoolSizeBytes} GB`;
 
   tableBody.append(listenerRow);
 }
@@ -282,7 +286,7 @@
 }
 
 document.addEventListener('DOMContentLoaded', () => {
-  renderDiskAvailability();
+  renderDiskAvailabilityAndTempPoolSize();
   renderEvictionStats();
   renderGlobalUsage();
   renderUsageAndQuotaStats();
diff --git a/content/browser/resources/quota/quota_internals_browser_proxy.ts b/content/browser/resources/quota/quota_internals_browser_proxy.ts
index 2de2575e..448f30a 100644
--- a/content/browser/resources/quota/quota_internals_browser_proxy.ts
+++ b/content/browser/resources/quota/quota_internals_browser_proxy.ts
@@ -18,9 +18,10 @@
   'lastModified': Time,
 };
 
-type GetDiskAvailabilityResult = {
+type GetDiskAvailabilityAndTempPoolSizeResult = {
   totalSpace: bigint,
   availableSpace: bigint,
+  tempPoolSize: bigint,
 };
 
 type GetHostUsageForInternalsResult = {
@@ -74,8 +75,9 @@
 export class QuotaInternalsBrowserProxy {
   private handler = QuotaInternalsHandler.getRemote();
 
-  getDiskAvailability(): Promise<GetDiskAvailabilityResult> {
-    return this.handler.getDiskAvailability();
+  getDiskAvailabilityAndTempPoolSize():
+      Promise<GetDiskAvailabilityAndTempPoolSizeResult> {
+    return this.handler.getDiskAvailabilityAndTempPoolSize();
   }
 
   getGlobalUsage(storageType: string): Promise<GetGlobalUsageResult> {
diff --git a/content/browser/service_worker/service_worker_new_script_fetcher.cc b/content/browser/service_worker/service_worker_new_script_fetcher.cc
index 373cceb..15a3d07 100644
--- a/content/browser/service_worker/service_worker_new_script_fetcher.cc
+++ b/content/browser/service_worker/service_worker_new_script_fetcher.cc
@@ -106,7 +106,6 @@
 
   // Notify to DevTools that the request for fetching the service worker script
   // is about to start. It fires `Network.onRequestWillBeSent` event.
-  request.devtools_request_id = version_->reporting_source().ToString();
   devtools_instrumentation::OnServiceWorkerMainScriptRequestWillBeSent(
       requesting_frame_id_, context_.wrapper(), version_->version_id(),
       request);
diff --git a/content/browser/site_per_process_scroll_browsertest.cc b/content/browser/site_per_process_scroll_browsertest.cc
index a7c9502..34738a2 100644
--- a/content/browser/site_per_process_scroll_browsertest.cc
+++ b/content/browser/site_per_process_scroll_browsertest.cc
@@ -371,8 +371,11 @@
 
 #if BUILDFLAG(IS_ANDROID)
   // The reason for Android specific code is that
-  // AutoZoomFocusedNodeToLegibleScale is in blink's WebSettings and difficult
-  // to access from here. It so happens that the setting is on for Android.
+  // AutoZoomFocusedEditableToLegibleScale is in blink's WebSettings and
+  // difficult to access from here. It so happens that the setting is on for
+  // Android.
+  // TODO(bokan): This is accessible now but this test will soon be replaced.
+  // crbug.com/1296183.
 
   // A lower bound on the ratio of page scale factor after scroll. The actual
   // value depends on minReadableCaretHeight / caret_bounds.Height(). The page
diff --git a/content/public/test/fake_remote_frame.cc b/content/public/test/fake_remote_frame.cc
index 217093b..aebeeee 100644
--- a/content/public/test/fake_remote_frame.cc
+++ b/content/public/test/fake_remote_frame.cc
@@ -72,7 +72,7 @@
     blink::mojom::ResourceTimingInfoPtr timing) {}
 
 void FakeRemoteFrame::ScrollRectToVisible(
-    const gfx::Rect& rect,
+    const gfx::RectF& rect,
     blink::mojom::ScrollIntoViewParamsPtr params) {}
 
 void FakeRemoteFrame::DidStartLoading() {}
diff --git a/content/public/test/fake_remote_frame.h b/content/public/test/fake_remote_frame.h
index 8362047d..b348e01 100644
--- a/content/public/test/fake_remote_frame.h
+++ b/content/public/test/fake_remote_frame.h
@@ -71,7 +71,7 @@
       blink::mojom::ResourceTimingInfoPtr timing) override;
 
   void ScrollRectToVisible(
-      const gfx::Rect& rect,
+      const gfx::RectF& rect,
       blink::mojom::ScrollIntoViewParamsPtr params) override;
   void DidStartLoading() override;
   void DidStopLoading() override;
diff --git a/content/test/data/site_isolation/m3u8.octet-stream b/content/test/data/site_isolation/m3u8.octet-stream
new file mode 100644
index 0000000..b9123dd
--- /dev/null
+++ b/content/test/data/site_isolation/m3u8.octet-stream
@@ -0,0 +1,9 @@
+#EXTM3U
+#<!DOCTYPE html>
+#<html lang="en">
+#EXT-X-VERSION:3
+#EXT-X-TARGETDURATION:1
+#EXT-X-MEDIA-SEQUENCE:0
+#EXTINF:0.934267,
+bear0.ts
+#EXT-X-ENDLIST
diff --git a/content/test/data/site_isolation/m3u8.octet-stream.mock-http-headers b/content/test/data/site_isolation/m3u8.octet-stream.mock-http-headers
new file mode 100644
index 0000000..37ae0ad
--- /dev/null
+++ b/content/test/data/site_isolation/m3u8.octet-stream.mock-http-headers
@@ -0,0 +1,2 @@
+HTTP/1.1 200 OK
+Content-Type: application/octet-stream
diff --git a/docs/memory-infra/README.md b/docs/memory-infra/README.md
index b4213d76..b37661c 100644
--- a/docs/memory-infra/README.md
+++ b/docs/memory-infra/README.md
@@ -91,7 +91,7 @@
 
 <!-- TODO(primiano): Improve this. https://crbug.com/??? -->
 
-[oilpan]:     /third_party/blink/renderer/platform/heap/BlinkGCDesign.md
+[oilpan]:     /third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md
 [discardable]:base/memory/discardable_memory.h
 [cc-memory]:  probe-cc.md
 [gpu-memory]: probe-gpu.md
diff --git a/extensions/browser/browsertest_util.cc b/extensions/browser/browsertest_util.cc
index 36d59cb..99888bc 100644
--- a/extensions/browser/browsertest_util.cc
+++ b/extensions/browser/browsertest_util.cc
@@ -5,91 +5,223 @@
 #include "extensions/browser/browsertest_util.h"
 
 #include "base/callback.h"
+#include "base/json/json_reader.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/service_worker_test_helpers.h"
-#include "extension_registry.h"
 #include "extensions/browser/extension_host.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/process_manager.h"
+#include "extensions/common/manifest_handlers/background_info.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
 namespace browsertest_util {
 
+namespace {
+
+// Returns a log-friendly script string.
+std::string GetScriptToLog(const std::string& script) {
+  // The maximum script size for which to print on failure.
+  static constexpr int kMaxFailingScriptSizeToLog = 1000;
+  return (script.size() < kMaxFailingScriptSizeToLog) ? script
+                                                      : "<script too large>";
+}
+
+}  // namespace
+
+BackgroundScriptExecutor::BackgroundScriptExecutor(
+    content::BrowserContext* browser_context)
+    : browser_context_(browser_context),
+      registry_(ExtensionRegistry::Get(browser_context_)),
+      process_manager_(ProcessManager::Get(browser_context_)) {}
+
+BackgroundScriptExecutor::~BackgroundScriptExecutor() = default;
+
+base::Value BackgroundScriptExecutor::ExecuteScript(
+    const ExtensionId& extension_id,
+    const std::string& script,
+    ScriptUserActivation script_user_activation) {
+  ExecuteScriptAsync(extension_id, script, script_user_activation);
+  return WaitForResult();
+}
+
+// static
+base::Value BackgroundScriptExecutor::ExecuteScript(
+    content::BrowserContext* browser_context,
+    const ExtensionId& extension_id,
+    const std::string& script,
+    ScriptUserActivation script_user_activation) {
+  return BackgroundScriptExecutor(browser_context)
+      .ExecuteScript(extension_id, script, script_user_activation);
+}
+
+bool BackgroundScriptExecutor::ExecuteScriptAsync(
+    const ExtensionId& extension_id,
+    const std::string& script,
+    ScriptUserActivation script_user_activation) {
+  extension_ = registry_->enabled_extensions().GetByID(extension_id);
+  script_ = script;
+  if (!extension_) {
+    AddTestFailure("No enabled extension with id: " + extension_id);
+    return false;
+  }
+
+  if (BackgroundInfo::IsServiceWorkerBased(extension_)) {
+    background_type_ = BackgroundType::kServiceWorker;
+    DCHECK_EQ(ScriptUserActivation::kDontActivate, script_user_activation)
+        << "Cannot provide a user gesture to service worker scripts";
+    return ExecuteScriptInServiceWorker();
+  }
+
+  if (BackgroundInfo::HasBackgroundPage(extension_)) {
+    background_type_ = BackgroundType::kPage;
+    return ExecuteScriptInBackgroundPage(script_user_activation);
+  }
+
+  AddTestFailure(
+      "Attempting to execute a background script for an extension"
+      " with no background context");
+  return false;
+}
+
+// static
+bool BackgroundScriptExecutor::ExecuteScriptAsync(
+    content::BrowserContext* browser_context,
+    const ExtensionId& extension_id,
+    const std::string& script,
+    ScriptUserActivation script_user_activation) {
+  return BackgroundScriptExecutor(browser_context)
+      .ExecuteScriptAsync(extension_id, script, script_user_activation);
+}
+
+base::Value BackgroundScriptExecutor::WaitForResult() {
+  DCHECK(background_type_);
+
+  if (background_type_ == BackgroundType::kServiceWorker) {
+    base::RunLoop run_loop;
+    quit_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+    return std::move(result_);
+  }
+
+  DCHECK_EQ(BackgroundType::kPage, *background_type_);
+  DCHECK(message_queue_);
+  std::string next_message;
+  if (!message_queue_->WaitForMessage(&next_message)) {
+    AddTestFailure("Failed to wait for message");
+    return base::Value();
+  }
+  absl::optional<base::Value> value =
+      base::JSONReader::Read(next_message, base::JSON_ALLOW_TRAILING_COMMAS);
+  if (!value) {
+    AddTestFailure("Received bad message: " + next_message);
+    return base::Value();
+  }
+  return std::move(*value);
+}
+
+bool BackgroundScriptExecutor::ExecuteScriptInServiceWorker() {
+  std::vector<WorkerId> worker_ids =
+      process_manager_->GetServiceWorkersForExtension(extension_->id());
+  if (worker_ids.size() != 1u) {
+    AddTestFailure("Incorrect number of workers registered for extension");
+    return false;
+  }
+  content::ServiceWorkerContext* service_worker_context =
+      util::GetStoragePartitionForExtensionId(extension_->id(),
+                                              browser_context_)
+          ->GetServiceWorkerContext();
+  service_worker_context->ExecuteScriptForTest(  // IN-TEST
+      script_, worker_ids[0].version_id,
+      base::BindOnce(&BackgroundScriptExecutor::OnServiceWorkerResult,
+                     weak_factory_.GetWeakPtr()));
+  return true;
+}
+
+bool BackgroundScriptExecutor::ExecuteScriptInBackgroundPage(
+    ScriptUserActivation script_user_activation) {
+  message_queue_ = std::make_unique<content::DOMMessageQueue>();
+
+  ExtensionHost* host =
+      process_manager_->GetBackgroundHostForExtension(extension_->id());
+  if (!host) {
+    AddTestFailure("Extension does not have an active background page");
+    return false;
+  }
+
+  if (script_user_activation == ScriptUserActivation::kActivate) {
+    content::ExecuteScriptAsync(host->host_contents(), script_);
+  } else {
+    NOTREACHED() << "Not yet supported. Use ExecuteScriptInBackgroundPage().";
+  }
+  return true;
+}
+
+void BackgroundScriptExecutor::OnServiceWorkerResult(
+    base::Value result,
+    const absl::optional<std::string>& error) {
+  ASSERT_FALSE(error) << *error;
+  result_ = std::move(result);
+  if (quit_closure_)
+    std::move(quit_closure_).Run();
+}
+
+void BackgroundScriptExecutor::AddTestFailure(const std::string& message) {
+  ADD_FAILURE() << "Background script execution failed: " << message
+                << ". Extension: "
+                << (extension_ ? extension_->name() : "<not found>")
+                << ", script: " << GetScriptToLog(script_);
+}
+
 std::string ExecuteScriptInBackgroundPage(
     content::BrowserContext* context,
     const std::string& extension_id,
     const std::string& script,
     ScriptUserActivation script_user_activation) {
-  ExtensionHost* host =
-      ProcessManager::Get(context)->GetBackgroundHostForExtension(extension_id);
-  if (!host) {
-    ADD_FAILURE() << "Extension " << extension_id << " has no background page.";
+  // BackgroundScriptExecutor does not yet support kDontActivate.
+  // TODO(https://crbug.com/1319642): Make it so.
+  if (script_user_activation == ScriptUserActivation::kDontActivate) {
+    ExtensionHost* host =
+        ProcessManager::Get(context)->GetBackgroundHostForExtension(
+            extension_id);
+    if (!host) {
+      ADD_FAILURE() << "Extension " << extension_id
+                    << " has no background page.";
+      return std::string();
+    }
+
+    std::string result;
+    bool success = content::ExecuteScriptWithoutUserGestureAndExtractString(
+        host->host_contents(), script, &result);
+    if (!success) {
+      ADD_FAILURE() << "Executing script failed: " << GetScriptToLog(script);
+      result.clear();
+    }
+    return result;
+  }
+
+  DCHECK_EQ(ScriptUserActivation::kActivate, script_user_activation);
+
+  base::Value value = BackgroundScriptExecutor::ExecuteScript(
+      context, extension_id, script, script_user_activation);
+  if (!value.is_string()) {
+    ADD_FAILURE() << "Bad return value: " << value.type()
+                  << "; script: " << GetScriptToLog(script);
     return "";
   }
 
-  std::string result;
-  bool success;
-  if (script_user_activation == ScriptUserActivation::kActivate) {
-    success = content::ExecuteScriptAndExtractString(host->host_contents(),
-                                                     script, &result);
-  } else {
-    DCHECK_EQ(script_user_activation, ScriptUserActivation::kDontActivate);
-    success = content::ExecuteScriptWithoutUserGestureAndExtractString(
-        host->host_contents(), script, &result);
-  }
-
-  // The maximum script size for which to print on failure.
-  constexpr int kMaxFailingScriptSizeToLog = 1000;
-  if (!success) {
-    std::string message_detail = script.length() < kMaxFailingScriptSizeToLog
-                                     ? script
-                                     : "<script too large>";
-    ADD_FAILURE() << "Executing script failed: " << message_detail;
-    result.clear();
-  }
-  return result;
+  return value.GetString();
 }
 
 bool ExecuteScriptInBackgroundPageNoWait(content::BrowserContext* context,
                                          const std::string& extension_id,
                                          const std::string& script) {
-  ExtensionHost* host =
-      ProcessManager::Get(context)->GetBackgroundHostForExtension(extension_id);
-  if (!host) {
-    ADD_FAILURE() << "Extension " << extension_id << " has no background page.";
-    return false;
-  }
-  content::ExecuteScriptAsync(host->host_contents(), script);
-  return true;
-}
-
-void ExecuteScriptInServiceWorker(
-    content::BrowserContext* browser_context,
-    const std::string& extension_id,
-    const std::string& script,
-    base::OnceCallback<void(base::Value)> callback) {
-  ProcessManager* process_manager = ProcessManager::Get(browser_context);
-  ASSERT_TRUE(process_manager);
-  std::vector<WorkerId> worker_ids =
-      process_manager->GetServiceWorkersForExtension(extension_id);
-  ASSERT_EQ(1u, worker_ids.size())
-      << "Incorrect number of workers registered for extension.";
-  content::ServiceWorkerContext* service_worker_context =
-      util::GetStoragePartitionForExtensionId(extension_id, browser_context)
-          ->GetServiceWorkerContext();
-  auto callback_adapter =
-      [](base::OnceCallback<void(base::Value)> original_callback,
-         base::Value value, const absl::optional<std::string>& error) {
-        ASSERT_FALSE(error.has_value()) << *error;
-        std::move(original_callback).Run(std::move(value));
-      };
-  service_worker_context->ExecuteScriptForTest(  // IN-TEST
-      script, worker_ids[0].version_id,
-      base::BindOnce(callback_adapter, std::move(callback)));
+  return BackgroundScriptExecutor::ExecuteScriptAsync(
+      context, extension_id, script, ScriptUserActivation::kActivate);
 }
 
 void StopServiceWorkerForExtensionGlobalScope(content::BrowserContext* context,
diff --git a/extensions/browser/browsertest_util.h b/extensions/browser/browsertest_util.h
index e6e6fd87..beb0543 100644
--- a/extensions/browser/browsertest_util.h
+++ b/extensions/browser/browsertest_util.h
@@ -5,19 +5,25 @@
 #ifndef EXTENSIONS_BROWSER_BROWSERTEST_UTIL_H_
 #define EXTENSIONS_BROWSER_BROWSERTEST_UTIL_H_
 
+#include <memory>
 #include <string>
 
 #include "base/callback_forward.h"
-
-namespace base {
-class Value;
-}  // namespace base
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/test/browser_test_utils.h"
+#include "extensions/common/extension_id.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace content {
 class BrowserContext;
 }  // namespace content
 
 namespace extensions {
+class Extension;
+class ExtensionRegistry;
+class ProcessManager;
+
 namespace browsertest_util {
 
 // Determine if a user activation notification should be triggered before
@@ -27,6 +33,111 @@
   kDontActivate,
 };
 
+// A helper class to execute a script in an extension's background context,
+// either its service worker or its (possibly lazy) background page.
+// Returning results:
+//  Currently, results are returned differently for service worker-based and
+//  background page-based extensions.
+//  - The result for a service worker-based extension is the result of the
+//    final line of the execution - the same as you might get if you were to
+//    enter the script into devtools.
+//  - The result for a background page-based extension is instead returned via
+//    domAutomationController.send().
+// TODO(https://crbug.com/1319642): This should change. This class isn't as
+// helpful if the same script cannot be used in both service worker-based and
+// background page-based extensions.
+// This class is designed for single-use executions.
+class BackgroundScriptExecutor {
+ public:
+  explicit BackgroundScriptExecutor(content::BrowserContext* browser_context);
+  ~BackgroundScriptExecutor();
+
+  // Executes the given `script` and waits for execution to complete, returning
+  // the result. `script_user_activation` is used to determine whether the
+  // script executes with a user gesture, and must be be `kDontActivate` for
+  // service worker-based extensions.
+  base::Value ExecuteScript(const ExtensionId& extension_id,
+                            const std::string& script,
+                            ScriptUserActivation script_user_activation =
+                                ScriptUserActivation::kDontActivate);
+  // Static variant of the above.
+  static base::Value ExecuteScript(content::BrowserContext* browser_context,
+                                   const ExtensionId& extension_id,
+                                   const std::string& script,
+                                   ScriptUserActivation script_user_activation =
+                                       ScriptUserActivation::kDontActivate);
+
+  // Executes the given `script` and returns immediately, without waiting for
+  // the script to finish. `script_user_activation` is used to determine
+  // whether the script executes with a user gesture, and must be
+  // `kDontActivate` for service worker-based extensions.
+  bool ExecuteScriptAsync(const ExtensionId& extension_id,
+                          const std::string& script,
+                          ScriptUserActivation script_user_activation =
+                              ScriptUserActivation::kDontActivate);
+  // Static variant of the above.
+  static bool ExecuteScriptAsync(content::BrowserContext* browser_context,
+                                 const ExtensionId& extension_id,
+                                 const std::string& script,
+                                 ScriptUserActivation script_user_activation =
+                                     ScriptUserActivation::kDontActivate);
+
+  // Waits for the result of the script execution; for use with
+  // `ExecuteScriptAsync()`.
+  base::Value WaitForResult();
+
+ private:
+  enum class BackgroundType {
+    kServiceWorker,
+    kPage,
+  };
+
+  // Helper method to execute the script in a service worker context.
+  bool ExecuteScriptInServiceWorker();
+
+  // Helper method to execute the script in a background page context.
+  bool ExecuteScriptInBackgroundPage(
+      ScriptUserActivation script_user_activation);
+
+  // Method invoked when the service worker script has finished executing.
+  void OnServiceWorkerResult(base::Value result,
+                             const absl::optional<std::string>& error);
+
+  // Method to ADD_FAILURE() to the currently-running test with the given
+  // `message` and other debugging info, like the injected script and associated
+  // extension.
+  void AddTestFailure(const std::string& message);
+
+  // The associated BrowserContext. Must outlive this object.
+  content::BrowserContext* const browser_context_;
+  // The associated ExtensionRegistry; tied to `browser_context_`.
+  ExtensionRegistry* const registry_;
+  // The associated ProcessManager; tied to `browser_context_`.
+  ProcessManager* const process_manager_;
+
+  // The type of background context the extension uses; lazily instantiated in
+  // ExecuteScript*().
+  absl::optional<BackgroundType> background_type_;
+
+  // The DOMMessageQueue used for retrieving results from background page-based
+  // extensions.
+  std::unique_ptr<content::DOMMessageQueue> message_queue_;
+
+  // The returned result; only used for service worker-based extensions.
+  base::Value result_;
+  // A quit closure for a running RunLoop; only used for service worker-based
+  // extensions.
+  base::OnceClosure quit_closure_;
+
+  // The associated Extension.
+  const Extension* extension_ = nullptr;
+
+  // The script to inject.
+  std::string script_;
+
+  base::WeakPtrFactory<BackgroundScriptExecutor> weak_factory_{this};
+};
+
 // Waits until |script| calls "window.domAutomationController.send(result)",
 // where |result| is a string, and returns |result|. Fails the test and returns
 // an empty string if |extension_id| isn't installed in |context| or doesn't
@@ -48,15 +159,6 @@
                                          const std::string& extension_id,
                                          const std::string& script);
 
-// Executes the given `script` in the context of the background service worker
-// registered by the extension with the given `extension_id`. If non-empty,
-// `callback` is invoked with the result of the execution.
-void ExecuteScriptInServiceWorker(
-    content::BrowserContext* browser_context,
-    const std::string& extension_id,
-    const std::string& script,
-    base::OnceCallback<void(base::Value)> callback);
-
 // Synchronously stops the service worker registered by the extension with the
 // given `extension_id` at global scope. The extension must be installed and
 // enabled.
diff --git a/extensions/browser/browsertest_util_browsertest.cc b/extensions/browser/browsertest_util_browsertest.cc
index 29f105fb..5d8c8953 100644
--- a/extensions/browser/browsertest_util_browsertest.cc
+++ b/extensions/browser/browsertest_util_browsertest.cc
@@ -88,7 +88,7 @@
   EXPECT_NONFATAL_FAILURE(
       EXPECT_FALSE(ExecuteScriptInBackgroundPageNoWait(
           browser_context(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "")),
-      "Extension aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has no background page.");
+      "No enabled extension with id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
 }
 
 }  // namespace
diff --git a/extensions/browser/event_listener_map_unittest.cc b/extensions/browser/event_listener_map_unittest.cc
index 09de5f8..27b29f0b 100644
--- a/extensions/browser/event_listener_map_unittest.cc
+++ b/extensions/browser/event_listener_map_unittest.cc
@@ -517,7 +517,8 @@
   };
   auto filter_list = std::make_unique<ListValue>();
   for (const TestCase& test_case : kTestCases)
-    filter_list->Append(CreateHostSuffixFilter(test_case.filter_host_suffix));
+    filter_list->Append(base::Value::FromUniquePtrValue(
+        CreateHostSuffixFilter(test_case.filter_host_suffix)));
 
   DictionaryValue filtered_listeners;
   filtered_listeners.Set(kEvent1Name, std::move(filter_list));
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc
index c68a49c..f5f44d6 100644
--- a/extensions/browser/event_router.cc
+++ b/extensions/browser/event_router.cc
@@ -1188,7 +1188,7 @@
     filtered_events->GetListWithoutPathExpansion(event_name, &filter_list);
   }
 
-  filter_list->Append(filter->CreateDeepCopy());
+  filter_list->Append(filter->Clone());
 }
 
 void EventRouter::OnExtensionLoaded(content::BrowserContext* browser_context,
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index cd336e40..6daf54e 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -840,10 +840,9 @@
       i != permissions.end(); ++i) {
     std::unique_ptr<base::Value> detail(i->ToValue());
     if (detail) {
-      auto tmp(std::make_unique<base::DictionaryValue>());
-      tmp->SetKey(i->name(),
-                  base::Value::FromUniquePtrValue(std::move(detail)));
-      values->Append(std::move(tmp));
+      base::Value::Dict tmp;
+      tmp.Set(i->name(), base::Value::FromUniquePtrValue(std::move(detail)));
+      values->Append(base::Value(std::move(tmp)));
     } else {
       values->Append(i->name());
     }
diff --git a/infra/archive_config/lacros64-archive-rel.json b/infra/archive_config/lacros64-archive-rel.json
index 30e4d1e..ca6ab11 100644
--- a/infra/archive_config/lacros64-archive-rel.json
+++ b/infra/archive_config/lacros64-archive-rel.json
@@ -2,7 +2,7 @@
    "archive_datas":[
       {
          "files":[
-            "chrome.stripped",
+            "chrome",
             "chrome_100_percent.pak",
             "chrome_200_percent.pak",
             "chrome_crashpad_handler",
@@ -19,12 +19,6 @@
          "file_globs": [
             "locales/*.pak"
          ],
-         "rename_files":[
-            {
-               "from_file":"chrome.stripped",
-               "to_file":"chrome"
-            }
-         ],
          "dirs":[
             "WidevineCdm"
          ],
@@ -34,17 +28,6 @@
       },
       {
          "files":[
-            "chrome.debug"
-         ],
-         "dirs": [
-            "locales"
-         ],
-         "gcs_bucket":"chromium-browser-snapshots",
-         "gcs_path":"lacros64/{%position%}/lacros_debug.zip",
-         "archive_type":"ARCHIVE_TYPE_ZIP"
-      },
-      {
-         "files":[
             "metadata.json"
          ],
          "gcs_bucket":"chromium-browser-snapshots",
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index b6af483..e1c9edb 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -306,7 +306,7 @@
   std::string locale("en");
   autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
       web_state(), autofill_client_.get(), /*autofill_agent=*/nil, locale,
-      autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      autofill::AutofillManager::EnableDownloadManager(false));
 
   accessory_mediator_ =
       [[FormInputAccessoryMediator alloc] initWithConsumer:nil
diff --git a/ios/chrome/browser/autofill/autofill_tab_helper.mm b/ios/chrome/browser/autofill/autofill_tab_helper.mm
index de67257..9174af6a 100644
--- a/ios/chrome/browser/autofill/autofill_tab_helper.mm
+++ b/ios/chrome/browser/autofill/autofill_tab_helper.mm
@@ -60,7 +60,7 @@
   autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
       web_state, autofill_client_.get(), autofill_agent_,
       GetApplicationContext()->GetApplicationLocale(),
-      autofill::BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      autofill::AutofillManager::EnableDownloadManager(true));
 }
 
 void AutofillTabHelper::WebStateDestroyed(web::WebState* web_state) {
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm
index 4d9b9b6..fd54b66 100644
--- a/ios/chrome/browser/autofill/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -231,7 +231,7 @@
   std::string locale("en");
   autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
       web_state(), autofill_client_.get(), /*autofill_agent=*/nil, locale,
-      autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+      autofill::AutofillManager::EnableDownloadManager(false));
 }
 
 void FormStructureBrowserTest::TearDown() {
diff --git a/ios/chrome/browser/find_in_page/find_tab_helper.mm b/ios/chrome/browser/find_in_page/find_tab_helper.mm
index 6a732cad..acc76ef 100644
--- a/ios/chrome/browser/find_in_page/find_tab_helper.mm
+++ b/ios/chrome/browser/find_in_page/find_tab_helper.mm
@@ -9,6 +9,7 @@
 #include "base/metrics/user_metrics_action.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
 #import "ios/chrome/browser/find_in_page/find_in_page_model.h"
+#import "ios/web/public/navigation/navigation_context.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -102,6 +103,9 @@
 void FindTabHelper::DidFinishNavigation(
     web::WebState* web_state,
     web::NavigationContext* navigation_context) {
+  if (navigation_context->IsSameDocument()) {
+    return;
+  }
   if (IsFindUIActive()) {
     StopFinding();
   }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm b/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm
index 48a02b6..c807ee1f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/full_card_requester_unittest.mm
@@ -98,7 +98,7 @@
     std::string locale("en");
     autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
         web_state(), autofill_client_.get(), nil, locale,
-        autofill::BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+        autofill::AutofillManager::EnableDownloadManager(false));
   }
 
   void TearDown() override {
diff --git a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
index f0de9bc..d130420 100644
--- a/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/qr_scanner/qr_scanner_view_controller_egtest.mm
@@ -416,7 +416,13 @@
 
 // Tests that the close button, camera preview, viewport caption, and the torch
 // button are visible if the camera is available. The preview is delayed.
-- (void)testQRScannerUIIsShown {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testQRScannerUIIsShown testQRScannerUIIsShown
+#else
+#define MAYBE_testQRScannerUIIsShown DISABLED_testQRScannerUIIsShown
+#endif
+- (void)MAYBE_testQRScannerUIIsShown {
   id cameraControllerMock =
       [QRScannerAppInterface cameraControllerMockWithAuthorizationStatus:
                                  AVAuthorizationStatusAuthorized];
@@ -511,7 +517,15 @@
 
 // Tests that the torch button is disabled when the camera reports that torch
 // became unavailable.
-- (void)testTorchButtonIsDisabledWhenTorchBecomesUnavailable {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testTorchButtonIsDisabledWhenTorchBecomesUnavailable \
+  testTorchButtonIsDisabledWhenTorchBecomesUnavailable
+#else
+#define MAYBE_testTorchButtonIsDisabledWhenTorchBecomesUnavailable \
+  DISABLED_testTorchButtonIsDisabledWhenTorchBecomesUnavailable
+#endif
+- (void)MAYBE_testTorchButtonIsDisabledWhenTorchBecomesUnavailable {
   id cameraControllerMock =
       [QRScannerAppInterface cameraControllerMockWithAuthorizationStatus:
                                  AVAuthorizationStatusAuthorized];
@@ -537,7 +551,13 @@
 
 // Tests that a UIAlertController is presented instead of the
 // QRScannerViewController if the camera is unavailable.
-- (void)testCameraUnavailableDialog {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testCameraUnavailableDialog testCameraUnavailableDialog
+#else
+#define MAYBE_testCameraUnavailableDialog DISABLED_testCameraUnavailableDialog
+#endif
+- (void)MAYBE_testCameraUnavailableDialog {
   UIViewController* bvc = QRScannerAppInterface.currentBrowserViewController;
   NSError* error =
       [QRScannerAppInterface assertModalOfClass:@"QRScannerViewController"
@@ -564,7 +584,15 @@
 
 // Tests that a UIAlertController is presented by the QRScannerViewController if
 // the camera state changes after the QRScannerViewController is presented.
-- (void)testDialogIsDisplayedIfCameraStateChanges {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testDialogIsDisplayedIfCameraStateChanges \
+  testDialogIsDisplayedIfCameraStateChanges
+#else
+#define MAYBE_testDialogIsDisplayedIfCameraStateChanges \
+  DISABLED_testDialogIsDisplayedIfCameraStateChanges
+#endif
+- (void)MAYBE_testDialogIsDisplayedIfCameraStateChanges {
   id cameraControllerMock =
       [QRScannerAppInterface cameraControllerMockWithAuthorizationStatus:
                                  AVAuthorizationStatusAuthorized];
@@ -598,7 +626,14 @@
 
 // Tests that a new dialog replaces an old dialog if the camera state changes.
 // TODO(crbug.com/1019211): Re-enable test on iOS12.
-- (void)testDialogIsReplacedIfCameraStateChanges {
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testDialogIsReplacedIfCameraStateChanges \
+  testDialogIsReplacedIfCameraStateChanges
+#else
+#define MAYBE_testDialogIsReplacedIfCameraStateChanges \
+  DISABLED_testDialogIsReplacedIfCameraStateChanges
+#endif
+- (void)MAYBE_testDialogIsReplacedIfCameraStateChanges {
   id cameraControllerMock =
       [QRScannerAppInterface cameraControllerMockWithAuthorizationStatus:
                                  AVAuthorizationStatusAuthorized];
@@ -637,7 +672,15 @@
 }
 
 // Tests that an error dialog is dismissed if the camera becomes available.
-- (void)testDialogDismissedIfCameraBecomesAvailable {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testDialogDismissedIfCameraBecomesAvailable \
+  testDialogDismissedIfCameraBecomesAvailable
+#else
+#define MAYBE_testDialogDismissedIfCameraBecomesAvailable \
+  DISABLED_testDialogDismissedIfCameraBecomesAvailable
+#endif
+- (void)MAYBE_testDialogDismissedIfCameraBecomesAvailable {
   id cameraControllerMock =
       [QRScannerAppInterface cameraControllerMockWithAuthorizationStatus:
                                  AVAuthorizationStatusAuthorized];
@@ -723,7 +766,15 @@
 
 // Test that the correct page is loaded if the scanner result is a URL which is
 // then manually edited when VoiceOver is enabled.
-- (void)testReceivingQRScannerURLResultWithVoiceOver {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testReceivingQRScannerURLResultWithVoiceOver \
+  testReceivingQRScannerURLResultWithVoiceOver
+#else
+#define MAYBE_testReceivingQRScannerURLResultWithVoiceOver \
+  DISABLED_testReceivingQRScannerURLResultWithVoiceOver
+#endif
+- (void)MAYBE_testReceivingQRScannerURLResultWithVoiceOver {
   id cameraControllerMock =
       [QRScannerAppInterface cameraControllerMockWithAuthorizationStatus:
                                  AVAuthorizationStatusAuthorized];
@@ -759,7 +810,14 @@
 }
 
 // Test that the correct page is loaded if the scanner result is a URL.
-- (void)testReceivingQRScannerURLResult {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testReceivingQRScannerURLResult testReceivingQRScannerURLResult
+#else
+#define MAYBE_testReceivingQRScannerURLResult \
+  DISABLED_testReceivingQRScannerURLResult
+#endif
+- (void)MAYBE_testReceivingQRScannerURLResult {
   [self doTestReceivingResult:_testURL.GetContent()
                      response:kTestURLResponse
                          edit:nil];
@@ -767,7 +825,14 @@
 
 // Test that the URL is sanitized and the correct page is loaded if the scanner
 // result is a URL with forbidden characters.
-- (void)testForbiddenCharactersRemoved {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testForbiddenCharactersRemoved testForbiddenCharactersRemoved
+#else
+#define MAYBE_testForbiddenCharactersRemoved \
+  DISABLED_testForbiddenCharactersRemoved
+#endif
+- (void)MAYBE_testForbiddenCharactersRemoved {
   [self doTestReceivingResult:self.testServer->base_url().GetContent() +
                               kTestURLForbiddenCharacters
               sanitizedResult:_testURL.GetContent()
@@ -777,7 +842,15 @@
 
 // Test that the correct page is loaded if the scanner result is a URL which is
 // then manually edited.
-- (void)testReceivingQRScannerURLResultAndEditingTheURL {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testReceivingQRScannerURLResultAndEditingTheURL \
+  testReceivingQRScannerURLResultAndEditingTheURL
+#else
+#define MAYBE_testReceivingQRScannerURLResultAndEditingTheURL \
+  DISABLED_testReceivingQRScannerURLResultAndEditingTheURL
+#endif
+- (void)MAYBE_testReceivingQRScannerURLResultAndEditingTheURL {
   // TODO(crbug.com/753098): Re-enable this test on iPad once grey_typeText
   // works.
   if ([ChromeEarlGrey isIPadIdiom]) {
@@ -790,13 +863,29 @@
 }
 
 // Test that the correct page is loaded if the scanner result is a search query.
-- (void)testReceivingQRScannerSearchQueryResult {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testReceivingQRScannerSearchQueryResult \
+  testReceivingQRScannerSearchQueryResult
+#else
+#define MAYBE_testReceivingQRScannerSearchQueryResult \
+  DISABLED_testReceivingQRScannerSearchQueryResult
+#endif
+- (void)MAYBE_testReceivingQRScannerSearchQueryResult {
   [self doTestReceivingResult:kTestQuery response:kTestQueryResponse edit:nil];
 }
 
 // Test that the correct page is loaded if the scanner result is a search query
 // which is then manually edited.
-- (void)testReceivingQRScannerSearchQueryResultAndEditingTheQuery {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testReceivingQRScannerSearchQueryResultAndEditingTheQuery \
+  testReceivingQRScannerSearchQueryResultAndEditingTheQuery
+#else
+#define MAYBE_testReceivingQRScannerSearchQueryResultAndEditingTheQuery \
+  DISABLED_testReceivingQRScannerSearchQueryResultAndEditingTheQuery
+#endif
+- (void)MAYBE_testReceivingQRScannerSearchQueryResultAndEditingTheQuery {
   [self doTestReceivingResult:kTestQuery
                      response:kTestQueryEditedResponse
                          edit:@"\bedited"];
@@ -804,7 +893,15 @@
 
 // Test that the correct page is loaded if the scanner result is a not supported
 // URL.
-- (void)testReceivingQRScannerLoadDataResult {
+// TODO(crbug.com/1320518): Flaky on iOS device.
+#if TARGET_IPHONE_SIMULATOR
+#define MAYBE_testReceivingQRScannerLoadDataResult \
+  testReceivingQRScannerLoadDataResult
+#else
+#define MAYBE_testReceivingQRScannerLoadDataResult \
+  DISABLED_testReceivingQRScannerLoadDataResult
+#endif
+- (void)MAYBE_testReceivingQRScannerLoadDataResult {
   [self doTestReceivingResult:kTestDataURL
               sanitizedResult:kTestSanitizedDataURL
                      response:kTestDataURLResponse
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index bb2af30..284367e33 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -135,7 +135,7 @@
 
     autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
         _webState, _autofillClient.get(), self, applicationLocale,
-        autofill::BrowserAutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
+        autofill::AutofillManager::EnableDownloadManager(true));
 
     _passwordManagerClient = std::move(passwordManagerClient);
     _passwordManagerClient->set_bridge(self);
diff --git a/media/audio/audio_features.cc b/media/audio/audio_features.cc
index cdf58b0..add3bfd 100644
--- a/media/audio/audio_features.cc
+++ b/media/audio/audio_features.cc
@@ -6,7 +6,6 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 
 namespace features {
 
@@ -38,7 +37,7 @@
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
 const base::Feature kCrOSSystemAEC{"CrOSSystemAECWithBoardTuningsAllowed",
                                    base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kCrOSSystemAECDeactivatedGroups{
diff --git a/media/audio/audio_features.h b/media/audio/audio_features.h
index 6dd296b6..e167c6e 100644
--- a/media/audio/audio_features.h
+++ b/media/audio/audio_features.h
@@ -7,7 +7,6 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "media/base/media_export.h"
 
 namespace features {
@@ -20,7 +19,7 @@
 MEDIA_EXPORT extern const base::Feature kUseAAudioDriver;
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
 MEDIA_EXPORT extern const base::Feature kCrOSSystemAEC;
 MEDIA_EXPORT extern const base::Feature kCrOSSystemAECDeactivatedGroups;
 MEDIA_EXPORT extern const base::Feature kCrOSEnforceSystemAecNsAgc;
diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc
index 46f69f6..09de28a 100644
--- a/media/audio/audio_manager_base.cc
+++ b/media/audio/audio_manager_base.cc
@@ -27,7 +27,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 #include "base/logging.h"
-#include "build/chromeos_buildflags.h"
 #include "media/audio/audio_input_stream_data_interceptor.h"
 
 namespace media {
@@ -347,7 +346,7 @@
   std::string output_device_id =
       AudioDeviceDescription::IsDefaultDevice(device_id)
           ?
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
           // On ChromeOS, it is expected that, if the default device is given,
           // no specific device ID should be used since the actual output device
           // should change dynamically if the system default device changes.
diff --git a/media/base/video_encoder.cc b/media/base/video_encoder.cc
index 73ae8ea..dffeaa0 100644
--- a/media/base/video_encoder.cc
+++ b/media/base/video_encoder.cc
@@ -4,6 +4,7 @@
 
 #include "media/base/video_encoder.h"
 
+#include "base/cxx17_backports.h"
 #include "base/numerics/clamped_math.h"
 #include "media/base/video_frame.h"
 
@@ -20,10 +21,10 @@
 
   // Scale default bitrate to the given frame size and fps
   base::ClampedNumeric<uint64_t> result = kDefaultBitrateForHD30fps;
-  result *= std::clamp(framerate, 1u, 300u);
-  result *= std::clamp(frame_size.GetArea(), 1, kMaxArea);
+  result *= base::clamp(framerate, 1u, 300u);
+  result *= base::clamp(frame_size.GetArea(), 1, kMaxArea);
   result /= kHDArea * 30u;  // HD resolution, 30 fps
-  return std::clamp(result.RawValue(), kMinBitrate, kMaxBitrate);
+  return base::clamp(result.RawValue(), kMinBitrate, kMaxBitrate);
 }
 
 VideoEncoderOutput::VideoEncoderOutput() = default;
@@ -41,4 +42,4 @@
 VideoEncoder::PendingEncode::PendingEncode(PendingEncode&&) = default;
 VideoEncoder::PendingEncode::~PendingEncode() = default;
 
-}  // namespace media
\ No newline at end of file
+}  // namespace media
diff --git a/media/cast/sender/external_video_encoder.cc b/media/cast/sender/external_video_encoder.cc
index 91780c98..0c3320d2 100644
--- a/media/cast/sender/external_video_encoder.cc
+++ b/media/cast/sender/external_video_encoder.cc
@@ -9,7 +9,7 @@
 #include <sstream>
 #include <utility>
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "base/cpu.h"                     // nogncheck
 #include "base/no_destructor.h"           // nogncheck
 #include "third_party/re2/src/re2/re2.h"  // nogncheck
@@ -71,7 +71,7 @@
     base::StringPiece receiver_model_name,
     const std::vector<media::VideoEncodeAccelerator::SupportedProfile>&
         profiles) {
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
   // NOTE: the hardware encoder on some Chrome OS devices does not play well
   // with Vizio TVs. See https://crbug.com/1238774 for more information.
   // Vizio uses the TV model string for the receiver model name.
@@ -101,12 +101,12 @@
         profiles) {
 // TODO(b/169533953): Look into chromecast fails to decode bitstreams produced
 // by the AMD HW encoder.
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
   static const base::NoDestructor<base::CPU> cpuid;
   static const bool is_amd = cpuid->vendor_name() == "AuthenticAMD";
   if (is_amd)
     return false;
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 // TODO(crbug.com/1015482): Look into why H.264 hardware encoder on MacOS is
 // broken.
diff --git a/media/cast/sender/external_video_encoder_unittest.cc b/media/cast/sender/external_video_encoder_unittest.cc
index 65021a07..d20f6a4 100644
--- a/media/cast/sender/external_video_encoder_unittest.cc
+++ b/media/cast/sender/external_video_encoder_unittest.cc
@@ -6,11 +6,12 @@
 
 #include <stdint.h>
 
+#include "build/build_config.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_types.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "base/cpu.h"  // nogncheck
 #endif
 
@@ -104,7 +105,7 @@
 
   for (const char* model_name : kVizioTvModelNames) {
     constexpr bool should_recommend =
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
         false;
 #else
         true;
@@ -127,7 +128,7 @@
   for (const char* model_name : kFirstPartyModelNames) {
 // On ChromeOS only, disable hardware encoder on AMD chipsets due to
 // failure on Chromecast chipsets to decode.
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
     if (base::CPU().vendor_name() == "AuthenticAMD") {
       EXPECT_FALSE(ExternalVideoEncoder::IsRecommended(
           CODEC_VIDEO_H264, std::string(model_name), kValidVeaProfiles));
diff --git a/media/gpu/test/video_frame_validator.cc b/media/gpu/test/video_frame_validator.cc
index 09bb5d33..5195b04 100644
--- a/media/gpu/test/video_frame_validator.cc
+++ b/media/gpu/test/video_frame_validator.cc
@@ -16,7 +16,6 @@
 #include "base/strings/string_util.h"
 #include "base/system/sys_info.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/buildflags.h"
 #include "media/gpu/macros.h"
@@ -245,7 +244,7 @@
 MD5VideoFrameValidator::Validate(scoped_refptr<const VideoFrame> frame,
                                  size_t frame_index) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(validator_thread_sequence_checker_);
-#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#if BUILDFLAG(IS_CHROMEOS)
   // b/149808895: There is a bug in the synchronization on mapped buffers, which
   // causes the frame validation failure. The bug is due to some missing i915
   // patches in kernel v3.18. The bug will be fixed if the kernel is upreved to
@@ -265,7 +264,7 @@
     if (is_skylake)
       usleep(10);
   }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
   if (frame->format() != validation_format_) {
     frame = ConvertVideoFrame(frame.get(), validation_format_);
   }
diff --git a/net/cert/multi_threaded_cert_verifier.cc b/net/cert/multi_threaded_cert_verifier.cc
index c32b30f..d4e137f 100644
--- a/net/cert/multi_threaded_cert_verifier.cc
+++ b/net/cert/multi_threaded_cert_verifier.cc
@@ -265,12 +265,24 @@
   // Not yet implemented.
   DCHECK(config.additional_untrusted_authorities.empty());
 #else
+  // Construct a temporary list and then swap that into the member variable, to
+  // be polite to any verifications that might be in progress in a background
+  // thread. This ensures that, at least for certs that are present in both the
+  // old and new config, there will not be a time when the refcount drops to
+  // zero. For the case where a cert was in the old config and is not in the
+  // new config, it might be removed while a verification is still going on
+  // that might be able to use it. Oh well. Ideally the list should be passed
+  // into CertVerifyProc as noted by the TODO(https://crbug.com/978854), since
+  // the workers could then keep a reference to the appropriate certs as long
+  // as they need.
+  net::ScopedCERTCertificateList temp_certs;
   for (const auto& cert : config.additional_untrusted_authorities) {
-    ScopedCERTCertificate x509_cert =
+    ScopedCERTCertificate nss_cert =
         x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
-    DCHECK(x509_cert);
-    temp_certs_.push_back(std::move(x509_cert));
+    if (nss_cert)
+      temp_certs.push_back(std::move(nss_cert));
   }
+  temp_certs_ = std::move(temp_certs);
 #endif
 
   config_ = config;
diff --git a/storage/browser/blob/blob_memory_controller.cc b/storage/browser/blob/blob_memory_controller.cc
index 9143dfc..4ad4f7d 100644
--- a/storage/browser/blob/blob_memory_controller.cc
+++ b/storage/browser/blob/blob_memory_controller.cc
@@ -1123,13 +1123,7 @@
 void BlobMemoryController::GrantMemoryAllocations(
     std::vector<scoped_refptr<ShareableBlobDataItem>>* items,
     size_t total_bytes) {
-  // These metrics let us calculate the global distribution of blob storage by
-  // subtracting the histograms.
-  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.StorageSizeBeforeAppend",
-                          blob_memory_used_ / 1024);
   blob_memory_used_ += total_bytes;
-  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.StorageSizeAfterAppend",
-                          blob_memory_used_ / 1024);
 
   for (auto& item : *items) {
     item->set_state(ShareableBlobDataItem::QUOTA_GRANTED);
@@ -1142,14 +1136,7 @@
 void BlobMemoryController::RevokeMemoryAllocation(uint64_t item_id,
                                                   size_t length) {
   DCHECK_LE(length, blob_memory_used_);
-
-  // These metrics let us calculate the global distribution of blob storage by
-  // subtracting the histograms.
-  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.StorageSizeBeforeAppend",
-                          blob_memory_used_ / 1024);
   blob_memory_used_ -= length;
-  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.StorageSizeAfterAppend",
-                          blob_memory_used_ / 1024);
 
   auto iterator = populated_memory_items_.Get(item_id);
   if (iterator != populated_memory_items_.end()) {
diff --git a/storage/browser/quota/quota_internals.mojom b/storage/browser/quota/quota_internals.mojom
index fc6b351c..97b369f 100644
--- a/storage/browser/quota/quota_internals.mojom
+++ b/storage/browser/quota/quota_internals.mojom
@@ -30,9 +30,11 @@
 // Interface for controlling Quota Internals.
 // Hosted on "chrome://quota-internals" for WebUI content::QuotaInternalsUI.
 interface QuotaInternalsHandler {
-    // Returns the total and available disk space in bits for a user,
-    // which is then converted to bytes and displayed on the Quota Internals UI.
-    GetDiskAvailability() => (uint64 total_space, uint64 available_space);
+    // Returns the total disk space, available disk space,
+    // and temporary pool size, which is then converted to bytes
+    // and displayed on the Quota Internals UI.
+    GetDiskAvailabilityAndTempPoolSize() =>
+        (int64 total_space, int64 available_space, int64 temp_pool_size);
 
     // Returns the following statistics:
     // Errors on Getting Usage and Quota, Evicted Buckets, Evicted Rounds
diff --git a/storage/browser/quota/quota_manager_impl.cc b/storage/browser/quota/quota_manager_impl.cc
index d0fd02f..425c954a 100644
--- a/storage/browser/quota/quota_manager_impl.cc
+++ b/storage/browser/quota/quota_manager_impl.cc
@@ -1446,20 +1446,67 @@
   internals_handlers_receivers_.Add(this, std::move(receiver));
 }
 
-void QuotaManagerImpl::GetDiskAvailability(
-    GetDiskAvailabilityCallback callback) {
+void QuotaManagerImpl::GetDiskAvailabilityAndTempPoolSize(
+    GetDiskAvailabilityAndTempPoolSizeCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(callback);
 
+  auto info = std::make_unique<AccumulateQuotaInternalsInfo>();
+  auto* info_ptr = info.get();
+
+  base::RepeatingClosure barrier = base::BarrierClosure(
+      2, base::BindOnce(
+             &QuotaManagerImpl::FinallySendDiskAvailabilityAndTempPoolSize,
+             weak_factory_.GetWeakPtr(), std::move(callback), std::move(info)));
+
+  // base::Unretained usage is safe here because BarrierClosure holds
+  // the std::unque_ptr that keeps AccumulateQuotaInternalsInfo alive, and the
+  // BarrierClosure will outlive the UpdateQuotaInternalsDiskAvailability
+  // and UpdateQuotaInternalsTempPoolSpace closures.
   GetStorageCapacity(base::BindOnce(
-      [](GetDiskAvailabilityCallback callback, int64_t total_space,
-         int64_t available_space) {
-        DCHECK(callback);
-        DCHECK_GE(total_space, 0);
-        DCHECK_GE(available_space, 0);
-        std::move(callback).Run(total_space, available_space);
-      },
-      std::move(callback)));
+      &QuotaManagerImpl::UpdateQuotaInternalsDiskAvailability,
+      weak_factory_.GetWeakPtr(), barrier, base::Unretained(info_ptr)));
+  GetQuotaSettings(base::BindOnce(
+      &QuotaManagerImpl::UpdateQuotaInternalsTempPoolSpace,
+      weak_factory_.GetWeakPtr(), barrier, base::Unretained(info_ptr)));
+}
+
+void QuotaManagerImpl::UpdateQuotaInternalsDiskAvailability(
+    base::OnceClosure barrier_callback,
+    AccumulateQuotaInternalsInfo* info,
+    int64_t total_space,
+    int64_t available_space) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GE(total_space, 0);
+  DCHECK_GE(total_space, available_space);
+
+  info->total_space = total_space;
+  info->available_space = available_space;
+
+  std::move(barrier_callback).Run();
+}
+
+void QuotaManagerImpl::UpdateQuotaInternalsTempPoolSpace(
+    base::OnceClosure barrier_callback,
+    AccumulateQuotaInternalsInfo* info,
+    const QuotaSettings& settings) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GE(settings.pool_size, 0);
+  info->temp_pool_size = settings.pool_size;
+
+  std::move(barrier_callback).Run();
+}
+
+void QuotaManagerImpl::FinallySendDiskAvailabilityAndTempPoolSize(
+    GetDiskAvailabilityAndTempPoolSizeCallback callback,
+    std::unique_ptr<AccumulateQuotaInternalsInfo> info) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_GE(info->total_space, 0);
+  DCHECK_GE(info->total_space, info->available_space);
+  DCHECK_GE(info->temp_pool_size, 0);
+
+  std::move(callback).Run(info->total_space, info->available_space,
+                          info->temp_pool_size);
 }
 
 void QuotaManagerImpl::GetStatistics(GetStatisticsCallback callback) {
diff --git a/storage/browser/quota/quota_manager_impl.h b/storage/browser/quota/quota_manager_impl.h
index 9082281..9fbd22e 100644
--- a/storage/browser/quota/quota_manager_impl.h
+++ b/storage/browser/quota/quota_manager_impl.h
@@ -105,6 +105,12 @@
   }
 };
 
+struct AccumulateQuotaInternalsInfo {
+  int64_t total_space = 0;
+  int64_t available_space = 0;
+  int64_t temp_pool_size = 0;
+};
+
 // Entry point into the Quota System
 //
 // Each StoragePartition has exactly one QuotaManagerImpl instance, which
@@ -352,7 +358,8 @@
                              base::OnceClosure callback);
 
   // storage::mojom::QuotaInternalsHandler implementation
-  void GetDiskAvailability(GetDiskAvailabilityCallback callback) override;
+  void GetDiskAvailabilityAndTempPoolSize(
+      GetDiskAvailabilityAndTempPoolSizeCallback callback) override;
   void GetStatistics(GetStatisticsCallback callback) override;
   void RetrieveBucketsTable(RetrieveBucketsTableCallback callback) override;
   void GetHostUsageForInternals(
@@ -549,6 +556,16 @@
       GetHostUsageForInternalsCallback callback,
       int64_t usage,
       blink::mojom::UsageBreakdownPtr usage_breakdown);
+  void UpdateQuotaInternalsDiskAvailability(base::OnceClosure barrier_callback,
+                                            AccumulateQuotaInternalsInfo* info,
+                                            int64_t total_space,
+                                            int64_t available_space);
+  void UpdateQuotaInternalsTempPoolSpace(base::OnceClosure barrier_callback,
+                                         AccumulateQuotaInternalsInfo* info,
+                                         const QuotaSettings& settings);
+  void FinallySendDiskAvailabilityAndTempPoolSize(
+      GetDiskAvailabilityAndTempPoolSizeCallback callback,
+      std::unique_ptr<AccumulateQuotaInternalsInfo> info);
 
   // Runs BucketDataDeleter which calls QuotaClients to clear data for the
   // bucket. Once the task is complete, calls the QuotaDatabase to delete the
diff --git a/storage/browser/quota/quota_manager_unittest.cc b/storage/browser/quota/quota_manager_unittest.cc
index 70e037b..ffb14fc 100644
--- a/storage/browser/quota/quota_manager_unittest.cc
+++ b/storage/browser/quota/quota_manager_unittest.cc
@@ -2631,6 +2631,23 @@
   EXPECT_EQ(2, perm_result);
 }
 
+TEST_F(QuotaManagerImplTest, GetDiskAvailabilityAndTempPoolSize) {
+  const int kPoolSize = 1000;
+  const int kPerHostQuota = kPoolSize / 5;
+  SetQuotaSettings(kPoolSize, kPerHostQuota, 0);
+  storage::StorageCapacityResult storage_capacity = GetStorageCapacity();
+
+  base::test::TestFuture<int64_t, int64_t, int64_t> quota_internals_future;
+  quota_manager_impl()->GetDiskAvailabilityAndTempPoolSize(
+      quota_internals_future.GetCallback());
+  std::tuple quota_internals_result = quota_internals_future.Take();
+
+  EXPECT_EQ(storage_capacity.total_space, std::get<0>(quota_internals_result));
+  EXPECT_EQ(storage_capacity.available_space,
+            std::get<1>(quota_internals_result));
+  EXPECT_EQ(kPoolSize, std::get<2>(quota_internals_result));
+}
+
 TEST_F(QuotaManagerImplTest, NotifyAndLRUBucket) {
   static const ClientBucketData kData[] = {
       {"http://a.com/", kDefaultBucketName, kTemp, 0},
diff --git a/styleguide/c++/c++-features.md b/styleguide/c++/c++-features.md
index d209c53..dfe8aa3 100644
--- a/styleguide/c++/c++-features.md
+++ b/styleguide/c++/c++-features.md
@@ -11,8 +11,9 @@
 The C++ language has in recent years received an updated standard every three
 years (C++11, C++14, etc.). For various reasons, Chromium does not immediately
 allow new features on the publication of such a standard. Instead, once
-toolchain support is sufficient, a standard is declared "initially supported",
-with new language/library features banned pending discussion.
+Chromium supports the toolchain to a certain extent (e.g., build support is
+ready), a standard is declared "_initially supported_", with new
+language/library features banned pending discussion but not yet allowed.
 
 You can propose changing the status of a feature by sending an email to
 [cxx@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/cxx).
@@ -710,6 +711,26 @@
 [Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/YhlF_sTDSc0/m/QMzf42BtAAAJ)
 ***
 
+### std::conjunction/std::disjunction/std::negation <sup>[allowed]</sup>
+
+```c++
+template<typename T, typename... Ts>
+std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>>
+func(T, Ts...) { ...
+```
+
+**Description:** Performs logical operations on type traits.
+
+**Documentation:**
+[std::conjunction](https://en.cppreference.com/w/cpp/types/conjunction),
+[std::disjunction](https://en.cppreference.com/w/cpp/types/disjunction),
+[std::negation](https://en.cppreference.com/w/cpp/types/negation)
+
+**Notes:**
+*** promo
+[Discussion thread](https://groups.google.com/a/chromium.org/g/cxx/c/YhlF_sTDSc0/m/QMzf42BtAAAJ)
+***
+
 ## C++17 Banned Library Features {#library-blocklist-17}
 
 The following C++17 library features are not allowed in the Chromium codebase.
@@ -1276,26 +1297,6 @@
 None
 ***
 
-### std::conjunction/std::disjunction/std::negation <sup>[tbd]</sup>
-
-```c++
-template<typename T, typename... Ts>
-std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>>
-func(T, Ts...) { ...
-```
-
-**Description:** Performs logical operations on type traits.
-
-**Documentation:**
-[std::conjunction](https://en.cppreference.com/w/cpp/types/conjunction),
-[std::disjunction](https://en.cppreference.com/w/cpp/types/disjunction),
-[std::negation](https://en.cppreference.com/w/cpp/types/negation)
-
-**Notes:**
-*** promo
-None
-***
-
 ### std::is_swappable <sup>[tbd]</sup>
 
 ```c++
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index bd656cf1e..a7bd349 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8498,7 +8498,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -9002,7 +9002,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 400e5948..9da70231 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46099,7 +46099,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46603,7 +46603,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47111,7 +47111,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47615,7 +47615,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48190,7 +48190,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48694,7 +48694,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49269,7 +49269,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49773,7 +49773,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.23"
+              "revision": "version:102.0.5005.24"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index a128043..7e4b780 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -1721,46 +1721,6 @@
           "shards": 14
         },
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgpu_cts",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enable-dawn-backend-validation",
-          "--retry-limit=3"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgpu_cts_with_validation_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "display_attached": "1",
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac-12.1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 14
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   },
@@ -2910,46 +2870,6 @@
           "shards": 14
         },
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
-          "webgpu_cts",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
-          "--enable-dawn-backend-validation",
-          "--retry-limit=3"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "webgpu_cts_with_validation_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "display_attached": "1",
-              "gpu": "1002:6821",
-              "hidpi": "1",
-              "os": "Mac-12.1",
-              "pool": "chromium.tests.gpu"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 14
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.json b/testing/buildbot/chromium.json
index ed0ad58..b6121a1 100644
--- a/testing/buildbot/chromium.json
+++ b/testing/buildbot/chromium.json
@@ -24,10 +24,7 @@
   "lacros64-archive-rel": {
     "additional_compile_targets": [
       "chrome",
-      "lacros_version_metadata",
-      "linux_symbols",
-      "symupload",
-      "strip_chrome_binary"
+      "lacros_version_metadata"
     ]
   },
   "linux-archive-dbg": {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index a7704caf..74c6759 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -449,7 +449,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.23',
+          'revision': 'version:102.0.5005.24',
         }
       ],
     },
@@ -593,7 +593,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.23',
+          'revision': 'version:102.0.5005.24',
         }
       ],
     },
@@ -737,7 +737,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.23',
+          'revision': 'version:102.0.5005.24',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 758c79f..512e78ca 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -389,9 +389,6 @@
         'additional_compile_targets': [
           'chrome',
           'lacros_version_metadata',
-          'linux_symbols',
-          'symupload',
-          'strip_chrome_binary',
         ],
         'os_type': 'chromeos',
       },
@@ -2329,7 +2326,7 @@
           'mac_retina_amd_gpu_stable',
         ],
         'test_suites': {
-          'gpu_telemetry_tests': 'gpu_dawn_telemetry_cts_regular_and_validation',
+          'gpu_telemetry_tests': 'gpu_dawn_telemetry_cts_regular',
           'gtest_tests': 'gpu_dawn_integration_gtests_passthrough',
           'isolated_scripts': 'gpu_dawn_isolated_scripts',
         },
@@ -2380,7 +2377,7 @@
           'mac_retina_amd_gpu_stable',
         ],
         'test_suites': {
-          'gpu_telemetry_tests': 'gpu_dawn_telemetry_cts_regular_and_validation',
+          'gpu_telemetry_tests': 'gpu_dawn_telemetry_cts_regular',
           'gtest_tests': 'gpu_dawn_integration_gtests_passthrough',
           'isolated_scripts': 'gpu_dawn_isolated_scripts',
         },
diff --git a/third_party/blink/common/web_preferences/web_preferences.cc b/third_party/blink/common/web_preferences/web_preferences.cc
index c7c9811d..6d51f6a7 100644
--- a/third_party/blink/common/web_preferences/web_preferences.cc
+++ b/third_party/blink/common/web_preferences/web_preferences.cc
@@ -116,6 +116,7 @@
       viewport_enabled(false),
 #if BUILDFLAG(IS_ANDROID)
       viewport_meta_enabled(true),
+      auto_zoom_focused_editable_to_legible_scale(true),
       shrinks_viewport_contents_to_fit(true),
       viewport_style(mojom::ViewportStyle::kMobile),
       always_show_context_menu_on_touch(false),
@@ -123,6 +124,7 @@
       main_frame_resizes_are_orientation_changes(true),
 #else
       viewport_meta_enabled(false),
+      auto_zoom_focused_editable_to_legible_scale(false),
       shrinks_viewport_contents_to_fit(false),
       viewport_style(mojom::ViewportStyle::kDefault),
       always_show_context_menu_on_touch(true),
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
index 5412add..187fd8d 100644
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
@@ -132,6 +132,8 @@
   out->supports_multiple_windows = data.supports_multiple_windows();
   out->viewport_enabled = data.viewport_enabled();
   out->viewport_meta_enabled = data.viewport_meta_enabled();
+  out->auto_zoom_focused_editable_to_legible_scale =
+      data.auto_zoom_focused_editable_to_legible_scale();
   out->shrinks_viewport_contents_to_fit =
       data.shrinks_viewport_contents_to_fit();
   out->smooth_scroll_for_find_enabled = data.smooth_scroll_for_find_enabled();
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
index d636845..59947bfd 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -135,6 +135,7 @@
   bool supports_multiple_windows;
   bool viewport_enabled;
   bool viewport_meta_enabled;
+  bool auto_zoom_focused_editable_to_legible_scale;
 
   // If true - Blink will clamp the minimum scale factor to the content width,
   // preventing zoom beyond the visible content. This is really only needed if
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
index 00bbcda4..bd425a5 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
@@ -370,6 +370,11 @@
     return r.viewport_meta_enabled;
   }
 
+  static bool auto_zoom_focused_editable_to_legible_scale(
+      const blink::web_pref::WebPreferences& r) {
+    return r.auto_zoom_focused_editable_to_legible_scale;
+  }
+
   static bool shrinks_viewport_contents_to_fit(
       const blink::web_pref::WebPreferences& r) {
     return r.shrinks_viewport_contents_to_fit;
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 6455c64..77db5a3c 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -320,7 +320,7 @@
   HadStickyUserActivationBeforeNavigationChanged(bool has_gesture);
 
   // Sent by a local root to request scrolling in its parent process.
-  ScrollRectToVisibleInParentFrame(gfx.mojom.Rect rect_to_scroll,
+  ScrollRectToVisibleInParentFrame(gfx.mojom.RectF rect_to_scroll,
                                    ScrollIntoViewParams params);
 
   // Sent by a local root to continue bubbling a logical scroll in its parent
@@ -1091,7 +1091,7 @@
 
   // Sent to the remote frame placeholder in the parent process to request
   // scrolling.
-  ScrollRectToVisible(gfx.mojom.Rect rect, ScrollIntoViewParams params);
+  ScrollRectToVisible(gfx.mojom.RectF rect, ScrollIntoViewParams params);
 
   // Notifies this remote frame that its corresponding document has started
   // loading.
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
index 47c1ccc..738ac64 100644
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
@@ -188,6 +188,7 @@
   bool supports_multiple_windows;
   bool viewport_enabled;
   bool viewport_meta_enabled;
+  bool auto_zoom_focused_editable_to_legible_scale;
 
   // If true - Blink will clamp the minimum scale factor to the content width,
   // preventing zoom beyond the visible content. This is really only needed if
diff --git a/third_party/blink/public/web/web_settings.h b/third_party/blink/public/web/web_settings.h
index 8fd3204..ee890fb 100644
--- a/third_party/blink/public/web/web_settings.h
+++ b/third_party/blink/public/web/web_settings.h
@@ -113,7 +113,7 @@
   virtual void SetAntialiased2dCanvasEnabled(bool) = 0;
   virtual void SetAntialiasedClips2dCanvasEnabled(bool) = 0;
   virtual void SetAutoplayPolicy(mojom::AutoplayPolicy) = 0;
-  virtual void SetAutoZoomFocusedNodeToLegibleScale(bool) = 0;
+  virtual void SetAutoZoomFocusedEditableToLegibleScale(bool) = 0;
   virtual void SetCaretBrowsingEnabled(bool) = 0;
   virtual void SetClobberUserAgentInitialScaleQuirk(bool) = 0;
   virtual void SetCookieEnabled(bool) = 0;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 9ef772e..ec414d7 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7161,33 +7161,42 @@
   return nullptr;
 }
 
-bool Document::PopupShowing() const {
-  return !popup_element_stack_.IsEmpty();
+bool Document::PopupOrHintShowing() const {
+  return !popup_and_hint_stack_.IsEmpty();
+}
+bool Document::HintShowing() const {
+  return !popup_and_hint_stack_.IsEmpty() &&
+         (popup_and_hint_stack_.back()->PopupType() == PopupValueType::kHint);
 }
 
-void Document::HideTopmostPopupElement() {
+void Document::HideTopmostPopupOrHint() {
   DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
-  if (popup_element_stack_.IsEmpty())
+  if (popup_and_hint_stack_.IsEmpty())
     return;
-  popup_element_stack_.back()->hidePopup();
+  popup_and_hint_stack_.back()->hidePopup();
 }
 
 void Document::HideAllPopupsUntil(const Element* endpoint) {
   DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
-  while (!popup_element_stack_.IsEmpty() &&
-         popup_element_stack_.back() != endpoint) {
-    popup_element_stack_.back()->hidePopup();
+  while (!popup_and_hint_stack_.IsEmpty() &&
+         popup_and_hint_stack_.back() != endpoint) {
+    popup_and_hint_stack_.back()->hidePopup();
   }
 }
 
-void Document::HidePopupIfShowing(const Element* popup) {
+void Document::HidePopupIfShowing(Element* popup) {
   DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
-  if (!popup_element_stack_.Contains(popup))
+  DCHECK(popup->HasValidPopupAttribute());
+  if (!popup->popupOpen())
     return;
-  HideAllPopupsUntil(popup);
-  DCHECK(!popup_element_stack_.IsEmpty() &&
-         popup_element_stack_.back() == popup);
-  HideTopmostPopupElement();
+  if (popup->PopupType() == PopupValueType::kAsync) {
+    popup->hidePopup();
+  } else {
+    HideAllPopupsUntil(popup);
+    DCHECK(!popup_and_hint_stack_.IsEmpty() &&
+           popup_and_hint_stack_.back() == popup);
+    HideTopmostPopupOrHint();
+  }
 }
 
 void Document::exitPointerLock() {
@@ -7941,7 +7950,7 @@
   visitor->Trace(lists_invalidated_at_document_);
   visitor->Trace(node_lists_);
   visitor->Trace(top_layer_elements_);
-  visitor->Trace(popup_element_stack_);
+  visitor->Trace(popup_and_hint_stack_);
   visitor->Trace(load_event_delay_timer_);
   visitor->Trace(plugin_loading_timer_);
   visitor->Trace(elem_sheet_);
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 8823fcc..539824d 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1492,17 +1492,18 @@
 
   HTMLDialogElement* ActiveModalDialog() const;
 
-  HeapVector<Member<Element>>& PopupElementStack() {
-    return popup_element_stack_;
+  HeapVector<Member<Element>>& PopupAndHintStack() {
+    return popup_and_hint_stack_;
   }
-  bool PopupShowing() const;
-  void HideTopmostPopupElement();
+  bool PopupOrHintShowing() const;
+  bool HintShowing() const;
+  void HideTopmostPopupOrHint();
   // This hides all visible popups up to, but not including,
   // |endpoint|. If |endpoint| is nullptr, all popups are hidden.
   void HideAllPopupsUntil(const Element* endpoint);
   // This hides the provided popup, if it is showing. This will also
   // hide all popups above |popup| in the popup stack.
-  void HidePopupIfShowing(const Element* popup);
+  void HidePopupIfShowing(Element* popup);
 
   // A non-null template_document_host_ implies that |this| was created by
   // EnsureTemplateDocument().
@@ -2289,10 +2290,11 @@
   // stack and is thus the one that will be visually on top.
   HeapVector<Member<Element>> top_layer_elements_;
 
-  // The stack of currently-displayed Popup elements, which contain the `popup`
-  // attribute. Elements in the stack go from earliest (bottom-most) to latest
-  // (top-most).
-  HeapVector<Member<Element>> popup_element_stack_;
+  // The stack of currently-displayed Popup (and Hint) elements, which are
+  // elements that have either `popup=popup` or `popup=hint`. Elements in the
+  // stack go from earliest (bottom-most) to latest (top-most). If there is a
+  // hint in the stack, it is at the top.
+  HeapVector<Member<Element>> popup_and_hint_stack_;
 
   int load_event_delay_count_;
 
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 2316273..d8d749ff 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2355,10 +2355,17 @@
   } else if (EqualIgnoringASCIICase(value, kPopupTypeValueAsync)) {
     type = PopupValueType::kAsync;
   } else {
+    type = PopupValueType::kNone;
+  }
+  if (HasValidPopupAttribute()) {
+    if (PopupType() == type)
+      return;
+    // If the popup type is changing, hide it.
+    hidePopup();
+  }
+  if (type == PopupValueType::kNone) {
     if (HasValidPopupAttribute()) {
-      // If the popup is changing from valid to invalid, hide it and remove the
-      // PopupData.
-      hidePopup();
+      // If the popup is changing from valid to invalid, remove the PopupData.
       GetElementRareData()->RemovePopupData();
     }
     // TODO(masonf) This console message might be too much log spam. Though
@@ -2382,6 +2389,9 @@
 PopupData* Element::GetPopupData() const {
   return HasRareData() ? GetElementRareData()->GetPopupData() : nullptr;
 }
+PopupValueType Element::PopupType() const {
+  return GetPopupData() ? GetPopupData()->type() : PopupValueType::kNone;
+}
 
 bool Element::popupOpen() const {
   DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
@@ -2394,13 +2404,21 @@
   DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
   if (!HasValidPopupAttribute() || popupOpen() || !isConnected())
     return;
-  // Only hide popups up to this popup's ancestral popup.
-  GetDocument().HideAllPopupsUntil(NearestOpenAncestralPopup(this));
+  if (PopupType() == PopupValueType::kPopup ||
+      PopupType() == PopupValueType::kHint) {
+    if (GetDocument().HintShowing()) {
+      GetDocument().HideTopmostPopupOrHint();
+    }
+    if (PopupType() == PopupValueType::kPopup) {
+      // Only hide other popups up to this popup's ancestral popup.
+      GetDocument().HideAllPopupsUntil(NearestOpenAncestralPopup(this));
+    }
+    // Add this popup to the stack.
+    auto& stack = GetDocument().PopupAndHintStack();
+    DCHECK(!stack.Contains(this));
+    stack.push_back(this);
+  }
   GetPopupData()->setOpen(true);
-  // Add this popup to the stack, and the top layer.
-  auto& stack = GetDocument().PopupElementStack();
-  DCHECK(!stack.Contains(this));
-  stack.push_back(this);
   GetDocument().AddToTopLayer(this);
   PseudoStateChanged(CSSSelector::kPseudoPopupOpen);
   SetPopupFocusOnShow();
@@ -2418,11 +2436,14 @@
   GetPopupData()->setOpen(false);
   GetPopupData()->setInvoker(nullptr);
   GetPopupData()->setNeedsRepositioningForSelectMenu(false);
-  GetDocument().HideAllPopupsUntil(this);
-  // Remove this popup from the stack and the top layer.
-  auto& stack = GetDocument().PopupElementStack();
-  DCHECK(stack.back() == this);
-  stack.pop_back();
+  if (PopupType() == PopupValueType::kPopup ||
+      PopupType() == PopupValueType::kHint) {
+    GetDocument().HideAllPopupsUntil(this);
+    // Remove this popup from the stack and the top layer.
+    auto& stack = GetDocument().PopupAndHintStack();
+    DCHECK(stack.back() == this);
+    stack.pop_back();
+  }
   GetDocument().RemoveFromTopLayer(this);
   PseudoStateChanged(CSSSelector::kPseudoPopupOpen);
   // Queue the hide event.
@@ -2520,7 +2541,9 @@
   HeapHashMap<Member<const Element>, int> popup_position;
   int indx = 0;
   Document& document = start_node->GetDocument();
-  for (auto popup : document.PopupElementStack()) {
+  for (auto popup : document.PopupAndHintStack()) {
+    DCHECK(popup->PopupType() == PopupValueType::kPopup ||
+           popup->PopupType() == PopupValueType::kHint);
     popup_position.Set(popup, indx++);
     if (const auto* anchor = popup->anchorElement())
       anchors_and_invokers.Set(anchor, popup);
@@ -2552,7 +2575,9 @@
     if (!current_element)
       continue;
     if (current_element->HasValidPopupAttribute() &&
-        current_element->GetPopupData()->open()) {
+        current_element->GetPopupData()->open() &&
+        (current_element->PopupType() == PopupValueType::kPopup ||
+         current_element->PopupType() == PopupValueType::kHint)) {
       // Case #1: a showing popup.
       update_highest(current_element);
     } else if (anchors_and_invokers.Contains(current_element)) {
@@ -2602,7 +2627,7 @@
   if (!target_node)
     return;
   auto& document = target_node->GetDocument();
-  DCHECK(document.PopupShowing());
+  DCHECK(document.PopupOrHintShowing());
   const AtomicString& event_type = event.type();
   if (event_type == event_type_names::kMousedown) {
     // - Hide everything up to the clicked element. We do this on mousedown,
@@ -2614,8 +2639,8 @@
   } else if (event_type == event_type_names::kKeydown) {
     const KeyboardEvent* key_event = DynamicTo<KeyboardEvent>(event);
     if (key_event && key_event->key() == "Escape") {
-      // Escape key just pops the topmost <popup> off the stack.
-      document.HideTopmostPopupElement();
+      // Escape key just pops the topmost popup or hint off the stack.
+      document.HideTopmostPopupOrHint();
     }
   } else if (event_type == event_type_names::kFocusin) {
     // If we focus an element, hide all popups that don't contain that element.
@@ -2959,14 +2984,16 @@
     GetPopupData()->setHadInitiallyOpenWhenParsed(false);
     GetDocument()
         .GetTaskRunner(TaskType::kDOMManipulation)
-        ->PostTask(FROM_HERE, WTF::Bind(
-                                  [](Element* popup) {
-                                    if (popup && popup->isConnected() &&
-                                        !popup->GetDocument().PopupShowing()) {
-                                      popup->showPopup();
-                                    }
-                                  },
-                                  WrapWeakPersistent(this)));
+        ->PostTask(FROM_HERE,
+                   WTF::Bind(
+                       [](Element* popup) {
+                         if (popup && popup->isConnected() &&
+                             (popup->PopupType() == PopupValueType::kAsync ||
+                              !popup->GetDocument().PopupOrHintShowing())) {
+                           popup->showPopup();
+                         }
+                       },
+                       WrapWeakPersistent(this)));
   }
 
   TreeScope& scope = insertion_point.GetTreeScope();
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 7f6abc3..e0b667f 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -541,6 +541,7 @@
   void UpdatePopupAttribute(String);
   bool HasValidPopupAttribute() const;
   PopupData* GetPopupData() const;
+  PopupValueType PopupType() const;
   bool popupOpen() const;
   void showPopup();
   void hidePopup();
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index c42cd48..be50925 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -2864,7 +2864,7 @@
 
 void Node::HandleLocalEvents(Event& event) {
   if (UNLIKELY(IsDocumentNode())) {
-    if (GetDocument().PopupShowing() &&
+    if (GetDocument().PopupOrHintShowing() &&
         (event.eventPhase() == Event::kCapturingPhase ||
          event.eventPhase() == Event::kAtTarget)) {
       DCHECK(RuntimeEnabledFeatures::HTMLPopupAttributeEnabled());
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index e6252416..00752ec 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -75,7 +75,6 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input/context_menu_allowed_scope.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
-#include "third_party/blink/renderer/core/layout/deferred_shaping.h"
 #include "third_party/blink/renderer/core/layout/hit_test_request.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
@@ -173,14 +172,6 @@
     force_locks = DisplayLockUtilities::ScopedForcedUpdate(
         base.AnchorNode(), DisplayLockContext::ForcedPhase::kLayout);
   }
-  // If the following UpdateStyleAndLayout() registered new shaping-deferred
-  // elements, it would be slow because of ScopedForcedUpdate instances.
-  // Without this DeferredShapingDisallowScope, the UpdateStyleAndLayout()
-  // takes 200 seconds in editing/deleting/delete-many-lines-of-text.html
-  // with a debug build.
-  absl::optional<DeferredShapingDisallowScope> disallow_deferred;
-  if (auto* frame_view = GetDocument().View())
-    disallow_deferred.emplace(*frame_view);
   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kSelection);
   return ComputeVisibleSelectionInDOMTree();
 }
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.cc b/third_party/blink/renderer/core/exported/web_settings_impl.cc
index c831e5f..d67f8d0 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.cc
@@ -43,7 +43,7 @@
     : settings_(settings),
       dev_tools_emulator_(dev_tools_emulator),
       render_v_sync_notification_enabled_(false),
-      auto_zoom_focused_node_to_legible_scale_(false),
+      auto_zoom_focused_editable_to_legible_scale_(false),
       support_deprecated_target_density_dpi_(false),
       viewport_meta_non_user_scalable_quirk_(false),
       clobber_user_agent_initial_scale_quirk_(false) {
@@ -138,10 +138,10 @@
       static_cast<blink::AutoplayPolicy::Type>(policy));
 }
 
-void WebSettingsImpl::SetAutoZoomFocusedNodeToLegibleScale(
-    bool auto_zoom_focused_node_to_legible_scale) {
-  auto_zoom_focused_node_to_legible_scale_ =
-      auto_zoom_focused_node_to_legible_scale;
+void WebSettingsImpl::SetAutoZoomFocusedEditableToLegibleScale(
+    bool auto_zoom_focused_editable_to_legible_scale) {
+  auto_zoom_focused_editable_to_legible_scale_ =
+      auto_zoom_focused_editable_to_legible_scale;
 }
 
 void WebSettingsImpl::SetTextAutosizingEnabled(bool enabled) {
diff --git a/third_party/blink/renderer/core/exported/web_settings_impl.h b/third_party/blink/renderer/core/exported/web_settings_impl.h
index 1e0216e1..14cec5a 100644
--- a/third_party/blink/renderer/core/exported/web_settings_impl.h
+++ b/third_party/blink/renderer/core/exported/web_settings_impl.h
@@ -64,7 +64,7 @@
   void SetAlwaysShowContextMenuOnTouch(bool) override;
   void SetAntialiased2dCanvasEnabled(bool) override;
   void SetAntialiasedClips2dCanvasEnabled(bool) override;
-  void SetAutoZoomFocusedNodeToLegibleScale(bool) override;
+  void SetAutoZoomFocusedEditableToLegibleScale(bool) override;
   void SetClobberUserAgentInitialScaleQuirk(bool) override;
   void SetCookieEnabled(bool) override;
   void SetCaretBrowsingEnabled(bool) override;
@@ -229,8 +229,8 @@
   bool RenderVSyncNotificationEnabled() const {
     return render_v_sync_notification_enabled_;
   }
-  bool AutoZoomFocusedNodeToLegibleScale() const {
-    return auto_zoom_focused_node_to_legible_scale_;
+  bool AutoZoomFocusedEditableToLegibleScale() const {
+    return auto_zoom_focused_editable_to_legible_scale_;
   }
   bool DoubleTapToZoomEnabled() const;
   bool SupportDeprecatedTargetDensityDPI() const {
@@ -251,7 +251,7 @@
   Settings* settings_;
   Persistent<DevToolsEmulator> dev_tools_emulator_;
   bool render_v_sync_notification_enabled_;
-  bool auto_zoom_focused_node_to_legible_scale_;
+  bool auto_zoom_focused_editable_to_legible_scale_;
   bool support_deprecated_target_density_dpi_;
   // This quirk is to maintain compatibility with Android apps built on
   // the Android SDK prior to and including version 18. Presumably, this
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index e48550e7..9704d8d8 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1631,7 +1631,6 @@
   settings->SetAccessibilityFontScaleFactor(prefs.font_scale_factor);
   settings->SetDeviceScaleAdjustment(prefs.device_scale_adjustment);
   web_view_impl->SetIgnoreViewportTagScaleLimits(prefs.force_enable_zoom);
-  settings->SetAutoZoomFocusedNodeToLegibleScale(true);
   settings->SetDefaultVideoPosterURL(
       WebString::FromASCII(prefs.default_video_poster_url.spec()));
   settings->SetSupportDeprecatedTargetDensityDPI(
@@ -1682,6 +1681,8 @@
   settings->SetViewportEnabled(prefs.viewport_enabled);
   settings->SetViewportMetaEnabled(prefs.viewport_meta_enabled);
   settings->SetViewportStyle(prefs.viewport_style);
+  settings->SetAutoZoomFocusedEditableToLegibleScale(
+      prefs.auto_zoom_focused_editable_to_legible_scale);
 
   settings->SetLoadWithOverviewMode(prefs.initialize_at_minimum_page_scale);
   settings->SetMainFrameResizesAreOrientationChanges(
@@ -2037,7 +2038,7 @@
 
 bool WebViewImpl::ShouldZoomToLegibleScale(const Element& element) {
   bool zoom_into_legible_scale =
-      web_settings_->AutoZoomFocusedNodeToLegibleScale() &&
+      web_settings_->AutoZoomFocusedEditableToLegibleScale() &&
       !GetPage()->GetVisualViewport().ShouldDisableDesktopWorkarounds();
 
   if (zoom_into_legible_scale) {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 5e86258..674cfd6b5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -3851,9 +3851,7 @@
              ->CanAccess(GetFrame().GetSecurityContext()->GetSecurityOrigin()));
   PhysicalRect new_rect = ConvertToRootFrame(rect_to_scroll);
   GetFrame().GetLocalFrameHostRemote().ScrollRectToVisibleInParentFrame(
-      gfx::Rect(new_rect.X().ToInt(), new_rect.Y().ToInt(),
-                new_rect.Width().ToInt(), new_rect.Height().ToInt()),
-      std::move(params));
+      gfx::RectF(new_rect), std::move(params));
 }
 
 bool LocalFrameView::AllowedToPropagateScrollIntoView(
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index 87d457f1..de7c268c 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -67,6 +67,7 @@
 #include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/rect_f.h"
 
 namespace blink {
 
@@ -706,7 +707,7 @@
 }
 
 void RemoteFrame::ScrollRectToVisible(
-    const gfx::Rect& rect_to_scroll,
+    const gfx::RectF& rect_to_scroll,
     mojom::blink::ScrollIntoViewParamsPtr params) {
   Element* owner_element = DeprecatedLocalOwner();
   LayoutObject* owner_object = owner_element->GetLayoutObject();
@@ -719,11 +720,7 @@
 
   // Schedule the scroll.
   PhysicalRect absolute_rect = owner_object->LocalToAncestorRect(
-      PhysicalRect(LayoutUnit(rect_to_scroll.x()),
-                   LayoutUnit(rect_to_scroll.y()),
-                   LayoutUnit(rect_to_scroll.width()),
-                   LayoutUnit(rect_to_scroll.height())),
-      owner_object->View());
+      PhysicalRect::EnclosingRect(rect_to_scroll), owner_object->View());
 
   if (!params->zoom_into_rect ||
       !owner_object->GetDocument().GetFrame()->LocalFrameRoot().IsMainFrame()) {
diff --git a/third_party/blink/renderer/core/frame/remote_frame.h b/third_party/blink/renderer/core/frame/remote_frame.h
index 62f804c..a319c996 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.h
+++ b/third_party/blink/renderer/core/frame/remote_frame.h
@@ -180,7 +180,7 @@
       mojom::blink::ResourceTimingInfoPtr timing,
       const String& server_timing_values) final;
   void ScrollRectToVisible(
-      const gfx::Rect& rect_to_scroll,
+      const gfx::RectF& rect_to_scroll,
       mojom::blink::ScrollIntoViewParamsPtr params) override;
   void DidStartLoading() override;
   void DidStopLoading() override;
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc
index 3088351..b53a3c2 100644
--- a/third_party/blink/renderer/core/frame/web_frame_test.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -4083,7 +4083,7 @@
       .SetTextAutosizingEnabled(false);
   web_view_helper.GetWebView()
       ->GetSettings()
-      ->SetAutoZoomFocusedNodeToLegibleScale(true);
+      ->SetAutoZoomFocusedEditableToLegibleScale(true);
   web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
 
   float initial_scale = web_view_helper.GetWebView()->PageScaleFactor();
@@ -12052,7 +12052,7 @@
         true);
     WebView().GetPage()->GetSettings().SetViewportStyle(
         mojom::blink::ViewportStyle::kMobile);
-    WebView().GetSettings()->SetAutoZoomFocusedNodeToLegibleScale(true);
+    WebView().GetSettings()->SetAutoZoomFocusedEditableToLegibleScale(true);
     WebView().GetSettings()->SetShrinksViewportContentToFit(true);
     WebView().SetDefaultPageScaleLimits(0.25f, 5);
   }
@@ -12326,7 +12326,7 @@
   WebView().EnableFakePageScaleAnimationForTesting(true);
   WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
   WebView().GetPage()->GetSettings().SetViewportEnabled(false);
-  WebView().GetSettings()->SetAutoZoomFocusedNodeToLegibleScale(true);
+  WebView().GetSettings()->SetAutoZoomFocusedEditableToLegibleScale(true);
 
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
@@ -12433,7 +12433,7 @@
   WebView().EnableFakePageScaleAnimationForTesting(true);
   WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
   WebView().GetPage()->GetSettings().SetViewportEnabled(false);
-  WebView().GetSettings()->SetAutoZoomFocusedNodeToLegibleScale(true);
+  WebView().GetSettings()->SetAutoZoomFocusedEditableToLegibleScale(true);
 
   SimRequest request("https://example.com/test.html", "text/html");
   LoadURL("https://example.com/test.html");
diff --git a/third_party/blink/renderer/core/inspector/identifiers_factory.cc b/third_party/blink/renderer/core/inspector/identifiers_factory.cc
index ca1b318..06eed19 100644
--- a/third_party/blink/renderer/core/inspector/identifiers_factory.cc
+++ b/third_party/blink/renderer/core/inspector/identifiers_factory.cc
@@ -29,10 +29,13 @@
 #include "base/process/process_handle.h"
 #include "third_party/blink/renderer/core/dom/dom_node_ids.h"
 #include "third_party/blink/renderer/core/dom/weak_identifier_map.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/inspector/inspected_frames.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
+#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -44,6 +47,22 @@
 }
 
 // static
+String IdentifiersFactory::RequestId(ExecutionContext* execution_context,
+                                     uint64_t identifier) {
+  if (!identifier)
+    return String();
+  auto* worker_global_scope = DynamicTo<WorkerGlobalScope>(execution_context);
+  if (worker_global_scope &&
+      worker_global_scope->MainResourceIdentifier() == identifier) {
+    return String(worker_global_scope->GetDevToolsToken().ToString());
+  }
+  auto* window = DynamicTo<LocalDOMWindow>(execution_context);
+  if (window && window->document())
+    return RequestId(window->document()->Loader(), identifier);
+  return AddProcessIdPrefixTo(identifier);
+}
+
+// static
 String IdentifiersFactory::RequestId(DocumentLoader* loader,
                                      uint64_t identifier) {
   if (!identifier)
@@ -55,7 +74,7 @@
 
 // static
 String IdentifiersFactory::SubresourceRequestId(uint64_t identifier) {
-  return RequestId(nullptr, identifier);
+  return RequestId(static_cast<ExecutionContext*>(nullptr), identifier);
 }
 
 // static
diff --git a/third_party/blink/renderer/core/inspector/identifiers_factory.h b/third_party/blink/renderer/core/inspector/identifiers_factory.h
index 5325b85..34ea7ef 100644
--- a/third_party/blink/renderer/core/inspector/identifiers_factory.h
+++ b/third_party/blink/renderer/core/inspector/identifiers_factory.h
@@ -34,6 +34,7 @@
 namespace blink {
 
 class DocumentLoader;
+class ExecutionContext;
 class Frame;
 class LocalFrame;
 class Node;
@@ -45,8 +46,14 @@
  public:
   static String CreateIdentifier();
 
+  static String RequestId(ExecutionContext*, uint64_t identifier);
+
+  // Same as above, but used when it's inconvenient to get the ExecutionContext
+  // or it does not exist. Prefer using RequestId with ExecutionContext. (See
+  // above.)
   static String RequestId(DocumentLoader*, uint64_t identifier);
-  // Same as above, but can only be used on reuquests that are guaranteed
+
+  // Same as RequestId, but can only be used on reuquests that are guaranteed
   // to be subresources, not main resource.
   static String SubresourceRequestId(uint64_t identifier);
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index f07a1bb7..bd329017 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -1067,8 +1067,7 @@
                           ResourceResponse(), options, type,
                           base::TimeTicks::Now());
 
-  String request_id =
-      IdentifiersFactory::RequestId(loader, request.InspectorId());
+  String request_id = RequestId(loader, request.InspectorId());
 
   // Conversion Measurement API triggers recording of conversions
   // as redirects to a `/.well-known/register-conversion` url.
@@ -1094,12 +1093,22 @@
     DocumentLoader* loader,
     uint64_t identifier,
     ResourceLoadPriority load_priority) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
   GetFrontend()->resourceChangedPriority(
       request_id, ResourcePriorityJSON(load_priority),
       base::TimeTicks::Now().since_origin().InSecondsF());
 }
 
+String InspectorNetworkAgent::RequestId(DocumentLoader* loader,
+                                        uint64_t identifier) {
+  // It's difficult to go from a loader to an execution context, and in the case
+  // of iframes the loader that is resolved via |GetTargetExecutionContext()| is
+  // not the intended loader
+  if (loader)
+    return IdentifiersFactory::RequestId(loader, identifier);
+  return IdentifiersFactory::RequestId(GetTargetExecutionContext(), identifier);
+}
+
 void InspectorNetworkAgent::WillSendRequestInternal(
     DocumentLoader* loader,
     const KURL& fetch_context_url,
@@ -1109,8 +1118,7 @@
     InspectorPageAgent::ResourceType type,
     base::TimeTicks timestamp) {
   String loader_id = IdentifiersFactory::LoaderId(loader);
-  String request_id =
-      IdentifiersFactory::RequestId(loader, request.InspectorId());
+  String request_id = RequestId(loader, request.InspectorId());
   NetworkResourcesData::ResourceData const* data =
       resources_data_->Data(request_id);
   // Support for POST request redirect.
@@ -1235,8 +1243,8 @@
   // The loader parameter is for generating a browser generated ID for a browser
   // initiated request. We pass it null here because we are reporting a renderer
   // generated ID for a renderer initiated request.
-  request.SetDevToolsId(IdentifiersFactory::RequestId(/* loader */ nullptr,
-                                                      request.InspectorId()));
+  request.SetDevToolsId(
+      IdentifiersFactory::SubresourceRequestId(request.InspectorId()));
 }
 
 void InspectorNetworkAgent::PrepareRequest(DocumentLoader* loader,
@@ -1330,8 +1338,7 @@
 
 void InspectorNetworkAgent::MarkResourceAsCached(DocumentLoader* loader,
                                                  uint64_t identifier) {
-  GetFrontend()->requestServedFromCache(
-      IdentifiersFactory::RequestId(loader, identifier));
+  GetFrontend()->requestServedFromCache(RequestId(loader, identifier));
 }
 
 void InspectorNetworkAgent::DidReceiveResourceResponse(
@@ -1339,7 +1346,7 @@
     DocumentLoader* loader,
     const ResourceResponse& response,
     const Resource* cached_resource) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
   bool is_not_modified = response.HttpStatusCode() == 304;
 
   bool resource_is_empty = true;
@@ -1363,6 +1370,14 @@
     type = saved_type;
   }
 
+  // Main Worker requests are initiated in the browser, so saved_type will not
+  // be found. We therefore must explicitly set it.
+  if (worker_global_scope_ &&
+      worker_global_scope_->MainResourceIdentifier() == identifier) {
+    DCHECK(saved_type == InspectorPageAgent::kOtherResource);
+    type = InspectorPageAgent::kScriptResource;
+  }
+
   // Resources are added to NetworkResourcesData as a WeakMember here and
   // removed in willDestroyResource() called in the prefinalizer of Resource.
   // Because NetworkResourceData retains weak references only, it
@@ -1411,7 +1426,7 @@
                                            DocumentLoader* loader,
                                            const char* data,
                                            uint64_t data_length) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
 
   if (data) {
     NetworkResourcesData::ResourceData const* resource_data =
@@ -1434,7 +1449,7 @@
 void InspectorNetworkAgent::DidReceiveBlob(uint64_t identifier,
                                            DocumentLoader* loader,
                                            scoped_refptr<BlobDataHandle> blob) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
   resources_data_->BlobReceived(request_id, std::move(blob));
 }
 
@@ -1442,7 +1457,7 @@
     DocumentLoader* loader,
     uint64_t identifier,
     size_t encoded_data_length) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
   resources_data_->AddPendingEncodedDataLength(request_id, encoded_data_length);
 }
 
@@ -1453,7 +1468,7 @@
     int64_t encoded_data_length,
     int64_t decoded_body_length,
     bool should_report_corb_blocking) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
   NetworkResourcesData::ResourceData const* resource_data =
       resources_data_->Data(request_id);
 
@@ -1500,7 +1515,7 @@
     DocumentLoader* loader,
     const ResourceError& error,
     const base::UnguessableToken& devtools_frame_or_worker_token) {
-  String request_id = IdentifiersFactory::RequestId(loader, identifier);
+  String request_id = RequestId(loader, identifier);
 
   // A Trust Token redemption can be served from cache if a valid
   // Signed-Redemption-Record is present. In this case the request is aborted
@@ -1784,7 +1799,7 @@
                                                     const char* payload,
                                                     size_t payload_length) {
   GetFrontend()->webSocketFrameSent(
-      IdentifiersFactory::RequestId(nullptr, identifier),
+      IdentifiersFactory::SubresourceRequestId(identifier),
       base::TimeTicks::Now().since_origin().InSecondsF(),
       WebSocketMessageToProtocol(op_code, masked, payload, payload_length));
 }
@@ -1793,7 +1808,7 @@
     uint64_t identifier,
     const String& error_message) {
   GetFrontend()->webSocketFrameError(
-      IdentifiersFactory::RequestId(nullptr, identifier),
+      IdentifiersFactory::SubresourceRequestId(identifier),
       base::TimeTicks::Now().since_origin().InSecondsF(), error_message);
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index d4179073..603900d 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -267,6 +267,7 @@
   String NavigationInitiatorInfo(LocalFrame*);
 
  private:
+  String RequestId(DocumentLoader*, uint64_t identifier);
   void Enable();
   void WillSendRequestInternal(DocumentLoader*,
                                const KURL& fetch_context_url,
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 111646169..3aa3ab51 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -1198,7 +1198,8 @@
 void inspector_parse_script_event::Data(perfetto::TracedValue context,
                                         uint64_t identifier,
                                         const String& url) {
-  String request_id = IdentifiersFactory::RequestId(nullptr, identifier);
+  String request_id = IdentifiersFactory::RequestId(
+      static_cast<ExecutionContext*>(nullptr), identifier);
   auto dict = std::move(context).WriteDictionary();
   dict.Add("requestId", request_id);
   dict.Add("url", url);
@@ -1207,7 +1208,8 @@
 void inspector_deserialize_script_event::Data(perfetto::TracedValue context,
                                               uint64_t identifier,
                                               const String& url) {
-  String request_id = IdentifiersFactory::RequestId(nullptr, identifier);
+  String request_id = IdentifiersFactory::RequestId(
+      static_cast<ExecutionContext*>(nullptr), identifier);
   auto dict = std::move(context).WriteDictionary();
   dict.Add("requestId", request_id);
   dict.Add("url", url);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
index d3834ae1..0b53850 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
@@ -84,8 +84,9 @@
 
 String NGBlockBreakToken::ToString() const {
   StringBuilder string_builder;
-  string_builder.Append(NGBreakToken::ToString());
-  string_builder.Append(" consumed:");
+  string_builder.Append("(");
+  string_builder.Append(InputNode().ToString());
+  string_builder.Append(") consumed:");
   string_builder.Append(ConsumedBlockSize().ToString());
   string_builder.Append("px");
 
diff --git a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
index 62ac8a5..0ff770ea 100644
--- a/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.cc
@@ -55,8 +55,12 @@
   if (worker_main_script_load_params) {
     DCHECK_EQ(level_, ModuleGraphLevel::kTopLevelModuleFetch);
 
-    fetch_params.MutableResourceRequest().SetInspectorId(
-        CreateUniqueIdentifier());
+    auto identifier = CreateUniqueIdentifier();
+    if (global_scope_->IsServiceWorkerGlobalScope()) {
+      global_scope_->SetMainResoureIdentifier(identifier);
+    }
+
+    fetch_params.MutableResourceRequest().SetInspectorId(identifier);
     worker_main_script_loader_ = MakeGarbageCollected<WorkerMainScriptLoader>();
     worker_main_script_loader_->Start(
         fetch_params, std::move(worker_main_script_load_params),
diff --git a/third_party/blink/renderer/core/page/focus_controller_test.cc b/third_party/blink/renderer/core/page/focus_controller_test.cc
index e7b97424..deec58d 100644
--- a/third_party/blink/renderer/core/page/focus_controller_test.cc
+++ b/third_party/blink/renderer/core/page/focus_controller_test.cc
@@ -180,4 +180,29 @@
                           password, mojom::blink::FocusType::kBackward));
 }
 
+// Ignore a checkbox to streamline form submission.
+TEST_F(FocusControllerTest, NextFocusableElementForIME_Checkbox) {
+  GetDocument().body()->setInnerHTML(
+      "<form>"
+      "  <input type='text' id='username'>"
+      "  <input type='password' id='password'>"
+      "  <input type='checkbox' id='remember-me'>"
+      "  <input type='submit' value='Login'>"
+      "</form>");
+  Element* username = GetElementById("username");
+  Element* password = GetElementById("password");
+  ASSERT_TRUE(username);
+  ASSERT_TRUE(password);
+
+  EXPECT_EQ(password, GetFocusController().NextFocusableElementForIME(
+                          username, mojom::blink::FocusType::kForward));
+  EXPECT_EQ(nullptr, GetFocusController().NextFocusableElementForIME(
+                         username, mojom::blink::FocusType::kBackward));
+
+  EXPECT_EQ(nullptr, GetFocusController().NextFocusableElementForIME(
+                         password, mojom::blink::FocusType::kForward));
+  EXPECT_EQ(username, GetFocusController().NextFocusableElementForIME(
+                          password, mojom::blink::FocusType::kBackward));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.cc b/third_party/blink/renderer/core/script/classic_pending_script.cc
index d376fc47..8e2d70b 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.cc
+++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -262,13 +262,29 @@
   auto* script_resource = To<ScriptResource>(GetResource());
   CHECK(!cache_consumer_);
   cache_consumer_ = script_resource->TakeCacheConsumer();
+
   if (cache_consumer_) {
-    AdvanceReadyState(kWaitingForCacheConsumer);
-    // TODO(leszeks): Decide whether kNetworking is the right task type here.
-    cache_consumer_->NotifyClientWaiting(
-        this,
-        element->GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
-  } else {
+    ExecutionContext* execution_context = element->GetExecutionContext();
+    // Only wait for the cache consume if there is an execution context it can
+    // notify us on.
+    if (execution_context) {
+      AdvanceReadyState(kWaitingForCacheConsumer);
+      // TODO(leszeks): Decide whether kNetworking is the right task type here.
+      cache_consumer_->NotifyClientWaiting(
+          this, execution_context->GetTaskRunner(TaskType::kNetworking));
+
+    } else {
+      // Otherwise (probably when our Document isn't active anymore),
+      // we can simply drop the cache consumer and let it be cleaned up by the
+      // GC; no one is waiting on it so it's safe to just drop it without
+      // needing to cancel or dispose it in any way.
+      cache_consumer_ = nullptr;
+    }
+  }
+
+  if (!cache_consumer_) {
+    // Either there was never a cache consume, or it was dropped. Either way, we
+    // are ready.
     AdvanceReadyState(kReady);
   }
 }
diff --git a/third_party/blink/renderer/core/script/classic_script.cc b/third_party/blink/renderer/core/script/classic_script.cc
index 370492dd..aa392ab8 100644
--- a/third_party/blink/renderer/core/script/classic_script.cc
+++ b/third_party/blink/renderer/core/script/classic_script.cc
@@ -206,10 +206,4 @@
   return result.GetResultType() == ScriptEvaluationResult::ResultType::kSuccess;
 }
 
-std::pair<size_t, size_t> ClassicScript::GetClassicScriptSizes() const {
-  size_t cached_metadata_size =
-      CacheHandler() ? CacheHandler()->GetCodeCacheSize() : 0;
-  return std::pair<size_t, size_t>(SourceText().length(), cached_metadata_size);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/script/classic_script.h b/third_party/blink/renderer/core/script/classic_script.h
index 36ad947..dc2ea24 100644
--- a/third_party/blink/renderer/core/script/classic_script.h
+++ b/third_party/blink/renderer/core/script/classic_script.h
@@ -117,8 +117,6 @@
     return mojom::blink::ScriptType::kClassic;
   }
 
-  std::pair<size_t, size_t> GetClassicScriptSizes() const override;
-
   const ParkableString source_text_;
 
   // The URL of the script, which is primarily intended for DevTools
diff --git a/third_party/blink/renderer/core/script/module_script.cc b/third_party/blink/renderer/core/script/module_script.cc
index 7434dce..f89fa1a 100644
--- a/third_party/blink/renderer/core/script/module_script.cc
+++ b/third_party/blink/renderer/core/script/module_script.cc
@@ -148,10 +148,6 @@
   return V8ScriptRunner::EvaluateModule(this, std::move(rethrow_errors));
 }
 
-std::pair<size_t, size_t> ModuleScript::GetClassicScriptSizes() const {
-  return std::pair<size_t, size_t>(0, 0);
-}
-
 std::ostream& operator<<(std::ostream& stream,
                          const ModuleScript& module_script) {
   stream << "ModuleScript[" << &module_script;
diff --git a/third_party/blink/renderer/core/script/module_script.h b/third_party/blink/renderer/core/script/module_script.h
index abe4d76..95a1ad2 100644
--- a/third_party/blink/renderer/core/script/module_script.h
+++ b/third_party/blink/renderer/core/script/module_script.h
@@ -76,8 +76,6 @@
   }
   bool RunScriptOnWorkerOrWorklet(WorkerOrWorkletGlobalScope&) override;
 
-  std::pair<size_t, size_t> GetClassicScriptSizes() const override;
-
   friend class ModuleTreeLinkerTestModulator;
 
   // https://html.spec.whatwg.org/C/#settings-object
diff --git a/third_party/blink/renderer/core/script/script.h b/third_party/blink/renderer/core/script/script.h
index 14e139b..36d0259 100644
--- a/third_party/blink/renderer/core/script/script.h
+++ b/third_party/blink/renderer/core/script/script.h
@@ -75,13 +75,6 @@
   const ScriptFetchOptions& FetchOptions() const { return fetch_options_; }
   const KURL& BaseURL() const { return base_url_; }
 
-  // Returns a pair of (script's size, cached metadata's size) only for classic
-  // scripts. This is used only for metrics via
-  // ServiceWorkerGlobalScopeProxy::WillEvaluateClassicScript().
-  // TODO(asamidoi, hiroshige): Remove this once the metrics are no longer
-  // referenced.
-  virtual std::pair<size_t, size_t> GetClassicScriptSizes() const = 0;
-
  protected:
   explicit Script(const ScriptFetchOptions& fetch_options, const KURL& base_url)
       : fetch_options_(fetch_options), base_url_(base_url) {}
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
index 318985d9..420e6ff0 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.cc
@@ -80,7 +80,7 @@
     bool value) {}
 
 void FakeLocalFrameHost::ScrollRectToVisibleInParentFrame(
-    const gfx::Rect& rect_to_scroll,
+    const gfx::RectF& rect_to_scroll,
     blink::mojom::blink::ScrollIntoViewParamsPtr params) {}
 
 void FakeLocalFrameHost::BubbleLogicalScrollInParentFrame(
diff --git a/third_party/blink/renderer/core/testing/fake_local_frame_host.h b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
index 100c82c..56f5693 100644
--- a/third_party/blink/renderer/core/testing/fake_local_frame_host.h
+++ b/third_party/blink/renderer/core/testing/fake_local_frame_host.h
@@ -59,7 +59,7 @@
       blink::mojom::SuddenTerminationDisablerType disabler_type) override;
   void HadStickyUserActivationBeforeNavigationChanged(bool value) override;
   void ScrollRectToVisibleInParentFrame(
-      const gfx::Rect& rect_to_scroll,
+      const gfx::RectF& rect_to_scroll,
       blink::mojom::blink::ScrollIntoViewParamsPtr params) override;
   void BubbleLogicalScrollInParentFrame(
       blink::mojom::blink::ScrollDirection direction,
diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
index 0563cb1..961f30e 100644
--- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
+++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.cc
@@ -143,7 +143,8 @@
     base::OnceClosure finished_callback,
     RejectCoepUnsafeNone reject_coep_unsafe_none,
     mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
-        blob_url_loader_factory) {
+        blob_url_loader_factory,
+    absl::optional<uint64_t> main_script_identifier) {
   DCHECK(fetch_client_settings_object_fetcher);
   DCHECK(response_callback || finished_callback);
   response_callback_ = std::move(response_callback);
@@ -161,7 +162,15 @@
   // Use WorkerMainScriptLoader to load the main script for dedicated workers
   // (PlzDedicatedWorker) and shared workers.
   if (worker_main_script_load_params) {
-    request.SetInspectorId(CreateUniqueIdentifier());
+    auto* worker_global_scope = DynamicTo<WorkerGlobalScope>(execution_context);
+    DCHECK(worker_global_scope);
+    if (main_script_identifier.has_value()) {
+      worker_global_scope->SetMainResoureIdentifier(
+          main_script_identifier.value());
+      request.SetInspectorId(main_script_identifier.value());
+    } else {
+      request.SetInspectorId(CreateUniqueIdentifier());
+    }
     request.SetReferrerString(Referrer::NoReferrer());
     request.SetPriority(ResourceLoadPriority::kHigh);
     FetchParameters fetch_params(
diff --git a/third_party/blink/renderer/core/workers/worker_classic_script_loader.h b/third_party/blink/renderer/core/workers/worker_classic_script_loader.h
index 0c3ad27..ea4e1de 100644
--- a/third_party/blink/renderer/core/workers/worker_classic_script_loader.h
+++ b/third_party/blink/renderer/core/workers/worker_classic_script_loader.h
@@ -98,7 +98,8 @@
       RejectCoepUnsafeNone reject_coep_unsafe_none =
           RejectCoepUnsafeNone(false),
       mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>
-          blob_url_loader_factory = {});
+          blob_url_loader_factory = {},
+      absl::optional<uint64_t> main_script_identifier = absl::nullopt);
 
   // This will immediately invoke |finishedCallback| if
   // LoadTopLevelScriptAsynchronously() is in progress.
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.cc b/third_party/blink/renderer/core/workers/worker_global_scope.cc
index de8bcea..6fc69bc 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -493,8 +493,7 @@
 
   switch (worker_script_->GetScriptType()) {
     case mojom::blink::ScriptType::kClassic: {
-      auto sizes = worker_script_->GetClassicScriptSizes();
-      ReportingProxy().WillEvaluateClassicScript(sizes.first, sizes.second);
+      ReportingProxy().WillEvaluateClassicScript();
       break;
     }
     case mojom::blink::ScriptType::kModule:
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index caa9288..253d1667 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -234,6 +234,15 @@
 
   bool IsUrlValid() { return url_.IsValid(); }
 
+  void SetMainResoureIdentifier(uint64_t identifier) {
+    DCHECK(!main_resource_identifier_.has_value());
+    main_resource_identifier_ = identifier;
+  }
+
+  absl::optional<uint64_t> MainResourceIdentifier() const {
+    return main_resource_identifier_;
+  }
+
  protected:
   WorkerGlobalScope(std::unique_ptr<GlobalScopeCreationParams>,
                     WorkerThread*,
@@ -332,6 +341,11 @@
   std::unique_ptr<WorkerMainScriptLoadParameters>
       worker_main_script_load_params_for_modules_;
 
+  // |main_resource_identifier_| is used to track main script that was started
+  // in the browser process. This field not having a value does not imply
+  // anything.
+  absl::optional<uint64_t> main_resource_identifier_;
+
   // This is the interface that handles generated code cache
   // requests both to fetch code cache when loading resources.
   std::unique_ptr<CodeCacheHost> code_cache_host_;
diff --git a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
index eef5502..070bf6c5 100644
--- a/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
+++ b/third_party/blink/renderer/core/workers/worker_reporting_proxy.h
@@ -86,8 +86,7 @@
   virtual void DidFailToFetchModuleScript() {}
 
   // Invoked when the main classic script is about to be evaluated.
-  virtual void WillEvaluateClassicScript(size_t script_size,
-                                         size_t cached_metadata_size) {}
+  virtual void WillEvaluateClassicScript() {}
 
   // Invoked when an imported classic script is about to be evaluated.
   virtual void WillEvaluateImportedClassicScript(size_t script_size,
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test.cc b/third_party/blink/renderer/core/workers/worker_thread_test.cc
index 98f01461..748aa69 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread_test.cc
@@ -99,7 +99,7 @@
               DidCreateWorkerGlobalScope(_))
       .Times(1);
   EXPECT_CALL(*nested_worker_helper->reporting_proxy,
-              WillEvaluateClassicScriptMock(_, _))
+              WillEvaluateClassicScriptMock())
       .Times(1);
   EXPECT_CALL(*nested_worker_helper->reporting_proxy,
               DidEvaluateTopLevelScript(true))
@@ -188,8 +188,7 @@
  protected:
   void ExpectReportingCalls() {
     EXPECT_CALL(*reporting_proxy_, DidCreateWorkerGlobalScope(_)).Times(1);
-    EXPECT_CALL(*reporting_proxy_, WillEvaluateClassicScriptMock(_, _))
-        .Times(1);
+    EXPECT_CALL(*reporting_proxy_, WillEvaluateClassicScriptMock()).Times(1);
     EXPECT_CALL(*reporting_proxy_, DidEvaluateTopLevelScript(true)).Times(1);
     EXPECT_CALL(*reporting_proxy_, WillDestroyWorkerGlobalScope()).Times(1);
     EXPECT_CALL(*reporting_proxy_, DidTerminateWorkerThread()).Times(1);
@@ -197,7 +196,7 @@
 
   void ExpectReportingCallsForWorkerPossiblyTerminatedBeforeInitialization() {
     EXPECT_CALL(*reporting_proxy_, DidCreateWorkerGlobalScope(_)).Times(1);
-    EXPECT_CALL(*reporting_proxy_, WillEvaluateClassicScriptMock(_, _))
+    EXPECT_CALL(*reporting_proxy_, WillEvaluateClassicScriptMock())
         .Times(AtMost(1));
     EXPECT_CALL(*reporting_proxy_, DidEvaluateTopLevelScript(_))
         .Times(AtMost(1));
@@ -208,8 +207,7 @@
 
   void ExpectReportingCallsForWorkerForciblyTerminated() {
     EXPECT_CALL(*reporting_proxy_, DidCreateWorkerGlobalScope(_)).Times(1);
-    EXPECT_CALL(*reporting_proxy_, WillEvaluateClassicScriptMock(_, _))
-        .Times(1);
+    EXPECT_CALL(*reporting_proxy_, WillEvaluateClassicScriptMock()).Times(1);
     EXPECT_CALL(*reporting_proxy_, DidEvaluateTopLevelScript(false)).Times(1);
     EXPECT_CALL(*reporting_proxy_, WillDestroyWorkerGlobalScope()).Times(1);
     EXPECT_CALL(*reporting_proxy_, DidTerminateWorkerThread()).Times(1);
diff --git a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
index 3c155cb..3f43c2c 100644
--- a/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
+++ b/third_party/blink/renderer/core/workers/worker_thread_test_helper.h
@@ -188,17 +188,15 @@
   ~MockWorkerReportingProxy() override = default;
 
   MOCK_METHOD1(DidCreateWorkerGlobalScope, void(WorkerOrWorkletGlobalScope*));
-  MOCK_METHOD2(WillEvaluateClassicScriptMock,
-               void(size_t scriptSize, size_t cachedMetadataSize));
+  MOCK_METHOD0(WillEvaluateClassicScriptMock, void());
   MOCK_METHOD1(DidEvaluateTopLevelScript, void(bool success));
   MOCK_METHOD0(DidCloseWorkerGlobalScope, void());
   MOCK_METHOD0(WillDestroyWorkerGlobalScope, void());
   MOCK_METHOD0(DidTerminateWorkerThread, void());
 
-  void WillEvaluateClassicScript(size_t script_size,
-                                 size_t cached_metadata_size) override {
+  void WillEvaluateClassicScript() override {
     script_evaluation_event_.Signal();
-    WillEvaluateClassicScriptMock(script_size, cached_metadata_size);
+    WillEvaluateClassicScriptMock();
   }
 
   void WaitUntilScriptEvaluation() { script_evaluation_event_.Wait(); }
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
index 83545fd..a7ed42bd 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_global_scope_test.cc
@@ -105,8 +105,9 @@
                           base::WaitableEvent* waitable_event) {
     ASSERT_TRUE(thread->IsCurrentThread());
     auto* global_scope = To<AnimationWorkletGlobalScope>(thread->GlobalScope());
-    ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                    ->RunScriptOnWorkerOrWorklet(*global_scope));
+    ClassicScript::CreateUnspecifiedScript(source_code)
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
 
     waitable_event->Signal();
   }
@@ -126,8 +127,9 @@
               animate () {}
             });
           )JS";
-      ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                      ->RunScriptOnWorkerOrWorklet(*global_scope));
+      ClassicScript::CreateUnspecifiedScript(source_code)
+          ->RunScriptOnScriptState(
+              global_scope->ScriptController()->GetScriptState());
 
       AnimatorDefinition* definition =
           global_scope->FindDefinitionForTest("test");
@@ -138,8 +140,9 @@
       // registerAnimator() with a null class definition should fail to define
       // an animator.
       String source_code = "registerAnimator('null', null);";
-      ASSERT_FALSE(ClassicScript::CreateUnspecifiedScript(source_code)
-                       ->RunScriptOnWorkerOrWorklet(*global_scope));
+      ClassicScript::CreateUnspecifiedScript(source_code)
+          ->RunScriptOnScriptState(
+              global_scope->ScriptController()->GetScriptState());
       EXPECT_FALSE(global_scope->FindDefinitionForTest("null"));
     }
 
@@ -190,8 +193,9 @@
               }
             });
         )JS";
-    ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                    ->RunScriptOnWorkerOrWorklet(*global_scope));
+    ClassicScript::CreateUnspecifiedScript(source_code)
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
 
     EXPECT_FALSE(RunScriptAndGetBoolean(
         global_scope, "Function('return this')().constructed"))
@@ -249,8 +253,9 @@
             registerAnimator('stateless_animator', Stateless);
             registerAnimator('foo', Foo);
         )JS";
-    ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                    ->RunScriptOnWorkerOrWorklet(*global_scope));
+    ClassicScript::CreateUnspecifiedScript(source_code)
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
 
     AnimatorDefinition* first_definition =
         global_scope->FindDefinitionForTest("stateful_animator");
@@ -278,7 +283,8 @@
               }
             });
           )JS")
-        ->RunScriptOnWorkerOrWorklet(*global_scope);
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
 
     // Passing a new input state with a new animation id should cause the
     // worklet to create and animate an animator.
@@ -316,7 +322,8 @@
               }
             });
           )JS")
-        ->RunScriptOnWorkerOrWorklet(*global_scope);
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
 
     cc::WorkletAnimationId animation_id = {1, 1};
     AnimationWorkletInput state;
@@ -363,7 +370,8 @@
               }
             });
           )JS")
-        ->RunScriptOnWorkerOrWorklet(*global_scope);
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
 
     cc::WorkletAnimationId animation_id = {1, 1};
     AnimationWorkletInput state;
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_test.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_test.cc
index 6e12d61..3e8c5a5 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client_test.cc
@@ -11,6 +11,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
@@ -177,10 +178,12 @@
           registerAnimator('stateless_animator', Stateless);
       )JS";
 
-    ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                    ->RunScriptOnWorkerOrWorklet(*first_global_scope));
-    ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                    ->RunScriptOnWorkerOrWorklet(*second_global_scope));
+    ClassicScript::CreateUnspecifiedScript(source_code)
+        ->RunScriptOnScriptState(
+            first_global_scope->ScriptController()->GetScriptState());
+    ClassicScript::CreateUnspecifiedScript(source_code)
+        ->RunScriptOnScriptState(
+            second_global_scope->ScriptController()->GetScriptState());
 
     std::unique_ptr<AnimationWorkletInput> state =
         std::make_unique<AnimationWorkletInput>();
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
index e3bbccc2..69bcbe3 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h"
 
 #include "base/synchronization/waitable_event.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
@@ -80,8 +81,9 @@
               paint (ctx, size) {}
             });
           )JS";
-      ASSERT_TRUE(ClassicScript::CreateUnspecifiedScript(source_code)
-                      ->RunScriptOnWorkerOrWorklet(*global_scope));
+      ClassicScript::CreateUnspecifiedScript(source_code)
+          ->RunScriptOnScriptState(
+              global_scope->ScriptController()->GetScriptState());
       CSSPaintDefinition* definition = global_scope->FindDefinition("test");
       ASSERT_TRUE(definition);
     }
@@ -90,8 +92,9 @@
       // registerPaint() with a null class definition should fail to define a
       // painter.
       String source_code = "registerPaint('null', null);";
-      ASSERT_FALSE(ClassicScript::CreateUnspecifiedScript(source_code)
-                       ->RunScriptOnWorkerOrWorklet(*global_scope));
+      ClassicScript::CreateUnspecifiedScript(source_code)
+          ->RunScriptOnScriptState(
+              global_scope->ScriptController()->GetScriptState());
       EXPECT_FALSE(global_scope->FindDefinition("null"));
     }
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
index f039806..670c1960 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
@@ -10,6 +10,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/test/test_simple_task_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/css/cssom/cross_thread_style_value.h"
 #include "third_party/blink/renderer/core/css/cssom/css_paint_worklet_input.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
@@ -74,7 +75,8 @@
     // standard flow.
     ClassicScript::CreateUnspecifiedScript(
         "registerPaint('add_global_scope', class { paint() { } });")
-        ->RunScriptOnWorkerOrWorklet(*worker_thread->GlobalScope());
+        ->RunScriptOnScriptState(
+            worker_thread->GlobalScope()->ScriptController()->GetScriptState());
     waitable_event->Signal();
   }
 
@@ -212,7 +214,8 @@
   for (const auto& global_scope : proxy_client->GetGlobalScopesForTesting()) {
     ClassicScript::CreateUnspecifiedScript(
         "registerPaint('foo', class { paint() { } });")
-        ->RunScriptOnWorkerOrWorklet(*global_scope);
+        ->RunScriptOnScriptState(
+            global_scope->ScriptController()->GetScriptState());
   }
 
   PaintWorkletStylePropertyMap::CrossThreadData data;
@@ -259,13 +262,15 @@
         static get inputProperties() { return ['border-image', 'color']; }
         paint() { }
       });)JS")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes[0]);
+      ->RunScriptOnScriptState(
+          global_scopes[0]->ScriptController()->GetScriptState());
   EXPECT_NE(document_definition_map.at("test1"), nullptr);
   ClassicScript::CreateUnspecifiedScript(R"JS(registerPaint('test1', class {
         static get inputProperties() { return ['left']; }
         paint() { }
       });)JS")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes[1]);
+      ->RunScriptOnScriptState(
+          global_scopes[1]->ScriptController()->GetScriptState());
   EXPECT_EQ(document_definition_map.at("test1"), nullptr);
 
   // Differing custom properties.
@@ -273,25 +278,29 @@
         static get inputProperties() { return ['--foo', '--bar']; }
         paint() { }
       });)JS")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes[0]);
+      ->RunScriptOnScriptState(
+          global_scopes[0]->ScriptController()->GetScriptState());
   EXPECT_NE(document_definition_map.at("test2"), nullptr);
   ClassicScript::CreateUnspecifiedScript(R"JS(registerPaint('test2', class {
         static get inputProperties() { return ['--zoinks']; }
         paint() { }
       });)JS")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes[1]);
+      ->RunScriptOnScriptState(
+          global_scopes[1]->ScriptController()->GetScriptState());
   EXPECT_EQ(document_definition_map.at("test2"), nullptr);
 
   // Differing alpha values. The default is 'true'.
   ClassicScript::CreateUnspecifiedScript(
       "registerPaint('test3', class { paint() { } });")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes[0]);
+      ->RunScriptOnScriptState(
+          global_scopes[0]->ScriptController()->GetScriptState());
   EXPECT_NE(document_definition_map.at("test3"), nullptr);
   ClassicScript::CreateUnspecifiedScript(R"JS(registerPaint('test3', class {
         static get contextOptions() { return {alpha: false}; }
         paint() { }
       });)JS")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes[1]);
+      ->RunScriptOnScriptState(
+          global_scopes[1]->ScriptController()->GetScriptState());
   EXPECT_EQ(document_definition_map.at("test3"), nullptr);
 
   waitable_event->Signal();
@@ -353,14 +362,16 @@
   for (wtf_size_t i = 0; i < global_scopes.size() - 1; i++) {
     ClassicScript::CreateUnspecifiedScript(
         "registerPaint('foo', class { paint() { } });")
-        ->RunScriptOnWorkerOrWorklet(*global_scopes[i]);
+        ->RunScriptOnScriptState(
+            global_scopes[i]->ScriptController()->GetScriptState());
     EXPECT_FALSE(fake_runner.TaskHasBeenPosted());
   }
 
   // Now register the final one; the task should then be posted.
   ClassicScript::CreateUnspecifiedScript(
       "registerPaint('foo', class { paint() { } });")
-      ->RunScriptOnWorkerOrWorklet(*global_scopes.back());
+      ->RunScriptOnScriptState(
+          global_scopes.back()->ScriptController()->GetScriptState());
   EXPECT_TRUE(fake_runner.TaskHasBeenPosted());
 
   waitable_event->Signal();
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
index abb3811..2ea3a2a 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -8,6 +8,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/css/css_syntax_definition.h"
 #include "third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
@@ -121,7 +122,8 @@
   PaintWorkletGlobalScope* global_scope = GetProxy()->global_scope();
   ClassicScript::CreateUnspecifiedScript(
       "registerPaint('foo', class { paint() { } });")
-      ->RunScriptOnWorkerOrWorklet(*global_scope);
+      ->RunScriptOnScriptState(
+          global_scope->ScriptController()->GetScriptState());
 
   CSSPaintDefinition* definition = global_scope->FindDefinition("foo");
   ASSERT_TRUE(definition);
@@ -152,7 +154,8 @@
   PaintWorkletGlobalScope* global_scope = GetProxy()->global_scope();
   ClassicScript::CreateUnspecifiedScript(
       "registerPaint('foo', class { paint() { } });")
-      ->RunScriptOnWorkerOrWorklet(*global_scope);
+      ->RunScriptOnScriptState(
+          global_scope->ScriptController()->GetScriptState());
 
   CSSPaintImageGeneratorImpl* generator =
       static_cast<CSSPaintImageGeneratorImpl*>(
@@ -271,21 +274,21 @@
         paint() {}
       });)JS";
 
-  ClassicScript::CreateUnspecifiedScript(foo0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(foo0)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("foo"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
-  ClassicScript::CreateUnspecifiedScript(foo1)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(foo1)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   // foo0 and foo1 have the same name but different definitions, therefore
   // this definition must become invalid.
   EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
-  ClassicScript::CreateUnspecifiedScript(bar)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(bar)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("bar"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
@@ -296,8 +299,8 @@
   if (!RuntimeEnabledFeatures::OffMainThreadCSSPaintEnabled())
     EXPECT_CALL(*observer, PaintImageGeneratorReady).Times(1);
 
-  ClassicScript::CreateUnspecifiedScript(bar)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(bar)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
 }
@@ -392,14 +395,14 @@
       });)JS";
 
   // Definition invalidated before cross thread check
-  ClassicScript::CreateUnspecifiedScript(foo0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(foo0)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("foo"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
-  ClassicScript::CreateUnspecifiedScript(foo1)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(foo1)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
@@ -417,14 +420,14 @@
   EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
   // Definition invalidated by cross thread check
-  ClassicScript::CreateUnspecifiedScript(bar0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(bar0)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("bar"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
 
-  ClassicScript::CreateUnspecifiedScript(bar0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(bar0)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
 
@@ -444,8 +447,8 @@
   EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("bar"));
 
   // Definition invalidated by second main thread call after cross thread check
-  ClassicScript::CreateUnspecifiedScript(loo0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(loo0)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("loo"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("loo"));
@@ -463,8 +466,8 @@
 
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("loo"));
 
-  ClassicScript::CreateUnspecifiedScript(loo1)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(loo1)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   // Although the first main thread call and the cross thread definition are the
   // same, the second main thread call differs so the definition must become
@@ -472,8 +475,8 @@
   EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("loo"));
 
   // Definition invalidated by cross thread check before second main thread call
-  ClassicScript::CreateUnspecifiedScript(gar0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(gar0)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("gar"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("gar"));
@@ -490,8 +493,8 @@
 
   EXPECT_FALSE(paint_worklet_to_test->GetDocumentDefinitionMap().at("gar"));
 
-  ClassicScript::CreateUnspecifiedScript(gar0)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(gar0)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   // Although the main thread definitions were the same, the definition sent
   // cross thread differed from the main thread definitions so it must stay
@@ -528,14 +531,14 @@
         paint() {}
       });)JS";
 
-  ClassicScript::CreateUnspecifiedScript(foo)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[0]);
+  ClassicScript::CreateUnspecifiedScript(foo)->RunScriptOnScriptState(
+      global_scopes[0]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(global_scopes[0]->FindDefinition("foo"));
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
-  ClassicScript::CreateUnspecifiedScript(foo)->RunScriptOnWorkerOrWorklet(
-      *global_scopes[1]);
+  ClassicScript::CreateUnspecifiedScript(foo)->RunScriptOnScriptState(
+      global_scopes[1]->ScriptController()->GetScriptState());
 
   EXPECT_TRUE(paint_worklet_to_test->GetDocumentDefinitionMap().at("foo"));
 
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 5f9ba6b..7c1b2c7 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -136,6 +136,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
 #include "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
@@ -319,7 +320,8 @@
                 WrapPersistent(classic_script_loader)),
       WTF::Bind(&ServiceWorkerGlobalScope::DidFetchClassicScript,
                 WrapWeakPersistent(this), WrapPersistent(classic_script_loader),
-                stack_id));
+                stack_id),
+      RejectCoepUnsafeNone(false), {}, CreateUniqueIdentifier());
 }
 
 void ServiceWorkerGlobalScope::FetchAndRunModuleScript(
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
index 80d300c..884f8a5 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.cc
@@ -168,9 +168,7 @@
   Client().FailedToFetchModuleScript();
 }
 
-void ServiceWorkerGlobalScopeProxy::WillEvaluateClassicScript(
-    size_t script_size,
-    size_t cached_metadata_size) {
+void ServiceWorkerGlobalScopeProxy::WillEvaluateClassicScript() {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
   TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(
       "ServiceWorker", "ServiceWorkerGlobalScopeProxy::EvaluateTopLevelScript",
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
index 2057e3a..d2f154c 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope_proxy.h
@@ -119,8 +119,7 @@
   void DidFetchScript() override;
   void DidFailToFetchClassicScript() override;
   void DidFailToFetchModuleScript() override;
-  void WillEvaluateClassicScript(size_t script_size,
-                                 size_t cached_metadata_size) override;
+  void WillEvaluateClassicScript() override;
   void WillEvaluateImportedClassicScript(size_t script_size,
                                          size_t cached_metadata_size) override;
   void WillEvaluateModuleScript() override;
diff --git a/third_party/blink/renderer/platform/heap/README.md b/third_party/blink/renderer/platform/heap/README.md
index 8cd0dea..9a2f133 100644
--- a/third_party/blink/renderer/platform/heap/README.md
+++ b/third_party/blink/renderer/platform/heap/README.md
@@ -4,5 +4,4 @@
 
 ## Documentation
 
-* [API reference](BlinkGCAPIReference.md)
-* [GC design](BlinkGCDesign.md)
+* [GC design and API reference](BlinkGCAPIReference.md)
diff --git a/third_party/blink/renderer/platform/wtf/Allocator.md b/third_party/blink/renderer/platform/wtf/Allocator.md
index 2d7471c..7a3d52b0 100644
--- a/third_party/blink/renderer/platform/wtf/Allocator.md
+++ b/third_party/blink/renderer/platform/wtf/Allocator.md
@@ -19,7 +19,7 @@
 * HeapVector<T>, HeapHashSet<T>, HeapHashMap<T, U> etc
 
 The implementation is in platform/heap/.
-See [BlinkGCDesign.md](../heap/BlinkGCDesign.md) to learn the design.
+See [BlinkGCAPIReference.md](../heap/BlinkGCAPIReference.md) to learn the design.
 
 ### PartitionAlloc
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index b78a6f0..3367963 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -13,6 +13,7 @@
 # Tests that fail in legacy but pass in NG
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-039.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-multicol/column-fill-balance-orthog-block-001.html [ Failure ]
 crbug.com/626703 virtual/prerender/wpt_internal/prerender/unload-on-prerender-cross-origin-subframe-navigation.html [ Timeout ]
@@ -1100,6 +1101,12 @@
 crbug.com/591099 external/wpt/css/css-overflow/webkit-line-clamp-038.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-overflow/webkit-line-clamp-039.html [ Failure ]
 
+# Scrollable overflow changes in NG
+crbug.com/591099 external/wpt/css/cssom-view/scrollWidthHeight.xht [ Failure ]
+crbug.com/591099 external/wpt/css/css-overflow/orthogonal-flow-with-inline-end-margin.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-overflow/overflow-padding.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-overflow/scrollable-overflow-padding.html [ Failure ]
+
 # White space, absolute position and paint timing failures from June 5, 2020
 crbug.com/591099 external/wpt/paint-timing/fcp-only/fcp-pseudo-element-visibility.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-position/position-absolute-center-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index f9ee3b82..c77774a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -104,6 +104,10 @@
 # Tested by paint/background/root-element-background-transparency.html for now.
 external/wpt/css/compositing/root-element-background-transparency.html [ Failure ]
 
+# The following deal with failed main service worker requests
+crbug.com/1304795 http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-failures.js [ Skip ]
+crbug.com/1304795 http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-failures.js [ Skip ]
+
 # ====== Synchronous, budgeted HTML parser tests from here ========================
 # See crbug.com/1254921 for details.
 # These tests either started failing outright, or became flaky, when the new HTML
@@ -1221,12 +1225,6 @@
 
 crbug.com/722825 media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Pass Timeout ]
 
-#### crbug.com/783229 overflow
-crbug.com/724701 overflow/overflow-basic-004.html [ Failure ]
-
-# Temporary failure as trying to ship inline-end padding for overflow.
-crbug.com/1245722 external/wpt/css/cssom-view/scrollWidthHeight.xht [ Failure ]
-
 crbug.com/846753 [ Mac ] http/tests/media/reload-after-dialog.html [ Failure Pass Timeout ]
 
 ### external/wpt/css/css-tables/
@@ -1442,10 +1440,6 @@
 crbug.com/993813 [ Mac ] external/wpt/css/css-overflow/webkit-line-clamp-025.html [ Failure ]
 crbug.com/993813 external/wpt/css/css-overflow/webkit-line-clamp-029.html [ Failure ]
 
-# Temporary failures while we evaluate the web-compat of adding inline-end padding for overflow.
-crbug.com/1245722 external/wpt/css/css-overflow/overflow-padding.html [ Failure ]
-crbug.com/1245722 external/wpt/css/css-overflow/orthogonal-flow-with-inline-end-margin.html [ Failure ]
-
 # Lack of support for font-language-override
 crbug.com/481430 external/wpt/css/css-fonts/font-language-override-01.html [ Failure ]
 crbug.com/481430 external/wpt/css/css-fonts/font-language-override-02.html [ Failure ]
@@ -7731,4 +7725,4 @@
 crbug.com/1288741 external/wpt/scroll-to-text-fragment/find-range-from-text-directive.html [ Pass Timeout ]
 
 # Sheriff 2022-04-27
-crbug.com/1320418 [ Win7 ] external/wpt/dom/nodes/Document-createEvent.https.html [ Failure ]
\ No newline at end of file
+crbug.com/1320418 [ Win7 ] external/wpt/dom/nodes/Document-createEvent.https.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 706e160..6accb25 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -818,10 +818,10 @@
     "bases": [
       "external/wpt/web-locks/partitioned-web-locks.tentative.https.html",
       "external/wpt/workers/shared-worker-partitioned.tentative.html",
-      "external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https.html",
-      "external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https.html",
-      "external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https.html",
-      "external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https.html",
+      "external/wpt/service-workers/service-worker/partitioned.tentative.https.html",
+      "external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https.html",
+      "external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https.html",
+      "external/wpt/service-workers/service-worker/partitioned-claim.tentative.https.html",
       "external/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html",
       "external/wpt/webmessaging/broadcastchannel/cross-partition.https.tentative.html",
       "external/wpt/IndexedDB/idb-partitioned-basic.tentative.sub.html",
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index e1a185c..2abcdff 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 457b92323d501458a2b4023d0022a35308ab0b07
+Version: 905502574b6c12bf568dd13cc96c223f5cf3b5cf
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 7627e58..562f30d 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -81108,6 +81108,32 @@
         {}
        ]
       ],
+      "multi-line-column-flex-fragmentation-048.html": [
+       "82800413950cb29fa1ba2be1f6fc90f82a1631a3",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "multi-line-column-flex-fragmentation-049.html": [
+       "0ede74a118c4be49056e8b55dd3f0fac89536096",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "multi-line-row-flex-fragmentation-001.html": [
        "ba6b0103e447994a2778cbd9ea356a490f62b8fb",
        [
@@ -82525,6 +82551,84 @@
         {}
        ]
       ],
+      "single-line-column-flex-fragmentation-050.html": [
+       "ae683e938c29a6a8224e506d789284c79ab1ea69",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-column-flex-fragmentation-051.html": [
+       "9fc2a6e44261a494a12f341a8b116164cc6244ec",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-column-flex-fragmentation-052.html": [
+       "faa0d1305554e8693f3fcbb61da3b55eb01fc7c3",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-column-flex-fragmentation-053.html": [
+       "524870e4fc9e49fcb17ee56ff516c5d5dd973ee2",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-column-flex-fragmentation-054.html": [
+       "60c130ed39ffdfb60eb5a9e1b0cab92f2c964bf5",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "single-line-column-flex-fragmentation-055.html": [
+       "2958e6167abcf0df60bae8070b61388406e3c9db",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "single-line-row-flex-fragmentation-001.html": [
        "379327a4aff584192f076d3f9d8f3ead232c0f5c",
        [
@@ -82617,7 +82721,7 @@
        ]
       ],
       "single-line-row-flex-fragmentation-008.html": [
-       "562cb61fa5d03628349d8bd2c7a8c08f89fe540a",
+       "dfade0ff4b9544c3a82b5ab608ac671042b88248",
        [
         null,
         [
@@ -82630,7 +82734,7 @@
        ]
       ],
       "single-line-row-flex-fragmentation-009.html": [
-       "b51c7883a6475cef7455ab500503b6bce76900ea",
+       "8dbaa6925fa07dbfc60e161bd7ad346aa8735797",
        [
         null,
         [
@@ -148522,7 +148626,7 @@
       ]
      ],
      "highlight-painting-003.html": [
-      "89a279370ccdc93d8bffc2897a4b3989788827e1",
+      "e4f061f0b5d1fba48ceb99dd6078a9010293c0d2",
       [
        null,
        [
@@ -148535,7 +148639,7 @@
       ]
      ],
      "highlight-painting-004.html": [
-      "7abccb5245392322e27f97b53319ce70093e0386",
+      "26ded65f871645841d1376ae24d12a4d8c99b29d",
       [
        null,
        [
@@ -148659,8 +148763,8 @@
        {}
       ]
      ],
-     "highlight-paired-cascade-004.tentative.html": [
-      "cc1d4daa971b65bc861f1b3c67b7c5523dba867b",
+     "highlight-paired-cascade-004.html": [
+      "61e2b7d7f0735e75a34b043354a60ac58659083b",
       [
        null,
        [
@@ -148672,8 +148776,8 @@
        {}
       ]
      ],
-     "highlight-paired-cascade-005.tentative.html": [
-      "4d402292eb5ff7238c174a151df5bfb59b3a57b8",
+     "highlight-paired-cascade-005.html": [
+      "50677c811e78fc81496d462e00fc4977e5d29ff9",
       [
        null,
        [
@@ -149982,7 +150086,7 @@
       ]
      ],
      "textpath-selection-011.html": [
-      "3d7254839ad3ee6f112f244de1ee25011e2807ea",
+      "1e84519b0e572bec3135e3ece3da7f9ada51034a",
       [
        null,
        [
@@ -211242,6 +211346,19 @@
        {}
       ]
      ],
+     "abs-pos-replaced-vrl-001.html": [
+      "54838a0e0e8cc2149889a2011ce471da1ece3723",
+      [
+       null,
+       [
+        [
+         "/css/css-writing-modes/abs-pos-replaced-vrl-001-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "abs-pos-with-replaced-child.html": [
       "c68de4fb57b32f810ab1668d98d27e0209428a0e",
       [
@@ -279647,7 +279764,7 @@
       []
      ],
      "textpath-selection-011-ref.html": [
-      "6950c32f1d5b54eef47a54d518962ed8b2a0dede",
+      "6d07c71bc49f308d106c3c30a428576fc760c227",
       []
      ]
     },
@@ -290614,6 +290731,10 @@
       "6dae429b24e8067be40527616dc4c9d8da79078e",
       []
      ],
+     "abs-pos-replaced-vrl-001-ref.html": [
+      "35f8d909c9cdae7e7df11b7ee25e792b212ea502",
+      []
+     ],
      "astral-bidi": {
       "adlam-ref.html": [
        "d039127044e0bc9744bc358441da559cdd73bf81",
@@ -303700,10 +303821,6 @@
         "10565628132bf8b12c936316eb96f3f84fa57024",
         []
        ],
-       "007-expected.txt": [
-        "54102b25e5f04966e23aa8e944f7c0603c0c9af7",
-        []
-       ],
        "008.js": [
         "96a1fe5d4a6f7e6fd548abf218777dac4fc15bb7",
         []
@@ -325766,7 +325883,7 @@
      []
     ],
     "preload-error.sub-expected.txt": [
-     "f9e2190f9043294f9287992dcf4a76a41463c435",
+     "e9361bfadb6adc99c3e2dc7b3ab831b9e242a581",
      []
     ],
     "preload-in-data-doc-ref.html": [
@@ -337732,6 +337849,10 @@
       "9b321f4c4bd2e8fe042bd0fdbaac05a9b2ca6dfc",
       []
      ],
+     "opaque-origin-expected.txt": [
+      "3a5c2ffe73c81c2ab474909924caf7197bc2cc92",
+      []
+     ],
      "resources": {
       "cross-origin.html": [
        "5078b6fc8e46f556fe1b86c7e1aa5a141c132be1",
@@ -438980,6 +439101,85 @@
          {}
         ]
        ],
+       "event-order": {
+        "after-load-hash-twice.html": [
+         "75889ef5175fa6b44281a4d5a138f8a02064baff",
+         [
+          null,
+          {}
+         ]
+        ],
+        "after-load-hash.html": [
+         "f74d716d914da86976125e89595f1c9197823132",
+         [
+          null,
+          {}
+         ]
+        ],
+        "after-load-pushState.html": [
+         "4f9f3dad473f99d6f363e6bddfa4c3d03c6a9722",
+         [
+          null,
+          {}
+         ]
+        ],
+        "after-load-replaceState.html": [
+         "28148ff7b2d34a1a4236ac4fe073e3d3fd8cec3b",
+         [
+          null,
+          {}
+         ]
+        ],
+        "before-load-hash-twice.html": [
+         "7c8df118437d0c075b8468e9a4abef787c09a673",
+         [
+          null,
+          {}
+         ]
+        ],
+        "before-load-hash.html": [
+         "97c4636fad7c51cb07eb71cab70048df0b4df6ee",
+         [
+          null,
+          {}
+         ]
+        ],
+        "before-load-pushState.html": [
+         "a08afa474f0ea496b184dc255e2d9e635edf29d0",
+         [
+          null,
+          {}
+         ]
+        ],
+        "before-load-replaceState.html": [
+         "10d30038fb855a81ff7f9703d09f2b5cab909f30",
+         [
+          null,
+          {}
+         ]
+        ],
+        "pushState-inside-popstate.html": [
+         "35ada116edd35dd950783b1426b2d5d64698d864",
+         [
+          null,
+          {}
+         ]
+        ],
+        "same-document-traverse-immediate.html": [
+         "51ea20b289dd01e25b64238447fc1df7df27cf12",
+         [
+          null,
+          {}
+         ]
+        ],
+        "same-document-traverse-wait.html": [
+         "39bc760ff73de645bd513bf45c68236ee4c20bb8",
+         [
+          null,
+          {}
+         ]
+        ]
+       },
        "events.html": [
         "d5ff83fac07e23347287652d28699ec7f3877e61",
         [
@@ -504139,7 +504339,7 @@
      ]
     ],
     "preload-error.sub.html": [
-     "e67684a5fe16d07ab76085cf50594570c225a818",
+     "e2ae13170b5ca3bcd6915ae2a708293ed9ca90c8",
      [
       null,
       {
@@ -551243,7 +551443,7 @@
       ]
      ],
      "opaque-origin.html": [
-      "3f30873be227fad2e50e383387c0606ba74aa02c",
+      "831abc622c137029934c2af8b972f24a848364f9",
       [
        null,
        {}
@@ -551364,6 +551564,24 @@
        {}
       ]
      ],
+     "detached-iframe.window.js": [
+      "c1effaf141b7246320883e293b58dabbc3572123",
+      [
+       "webmessaging/message-channels/detached-iframe.window.html",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "MessageChannel in a detached iframe test"
+         ],
+         [
+          "script",
+          "/service-workers/service-worker/resources/test-helpers.sub.js"
+         ]
+        ]
+       }
+      ]
+     ],
      "dictionary-transferrable.any.js": [
       "bf49fddb99271fb16262a8029a97ba52041300eb",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/orthogonal-flow-with-inline-end-margin.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/orthogonal-flow-with-inline-end-margin.html
index 534dff976..f0af1fe3 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-overflow/orthogonal-flow-with-inline-end-margin.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/orthogonal-flow-with-inline-end-margin.html
@@ -11,6 +11,6 @@
   test(function() {
     var container = document.getElementById("container");
     assert_equals(container.scrollWidth, 100);
-    assert_equals(container.scrollHeight, 100);
-  }, "Only trailing *block* margins should affect overflow");
+    assert_equals(container.scrollHeight, 300);
+  });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-padding.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-padding.html
index a77cac3..2d6efc39 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-padding.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-padding.html
@@ -49,8 +49,8 @@
 </style>
 <body onload="runTest()">
 <p><span style="background:green">green</span> blocks get scrollbars, <span style="background:yellow">yellow</span> do not.</p>
-<p>Block child gets only block padding.</p>
-<div class="container" data-scrollbar="v">
+<p>Block child gets block and inline padding.</p>
+<div class="container" data-scrollbar="hv">
   <div class="big"></div>
 </div>
 <div class="container" data-scrollbar="">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollable-overflow-padding.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollable-overflow-padding.html
new file mode 100644
index 0000000..6a37f73
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollable-overflow-padding.html
@@ -0,0 +1,183 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scrollable">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/129">
+<style>
+scrollable-container {
+  display: block;
+  width: 100px;
+  height: 100px;
+  overflow: scroll;
+  padding: 10px 5px;
+  line-height: 0;
+}
+
+scrollable-container > div {
+  position: relative;
+  writing-mode: horizontal-tb;
+  direction: ltr;
+  outline: solid red 2px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<body onload="checkLayout('scrollable-container')">
+
+<!--
+  All of these tests have a child which contributes to the "alignment rectangle" ("infow-bounds" in Blink).
+  However doesn't directly contribute to the scrollable-overflow as it is relative-positioned before the
+  "scroll origin" edge(s).
+
+  All of these tests have an inline/block end margin (wrt. the parent writing-mode + direction).
+-->
+
+<!-- HTB + LTR -->
+<div style="writing-mode: horizontal-tb; direction: ltr;">
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-right: 50px; left: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-right: 50px; left: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <!-- For this specific case collapsed-margins affect the size of the alignment rectangle. -->
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; margin-bottom: 100px; top: -1000px;">
+      <div style="height: 200px; margin-bottom: -50px;"></div>
+    </div>
+  </scrollable-container>
+</div>
+
+<!-- HTB + RTL -->
+<div style="writing-mode: horizontal-tb; direction: rtl;">
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-left: 50px; right: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-left: 50px; right: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; margin-bottom: 100px; top: -1000px;">
+      <div style="height: 200px; margin-bottom: -50px;"></div>
+    </div>
+  </scrollable-container>
+</div>
+
+<!-- VRL + LTR -->
+<div style="writing-mode: vertical-rl; direction: ltr;">
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-left: 50px; right: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-left: 50px; right: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="height: 0; margin-left: 100px; right: -1000px;">
+      <div style="width: 200px; margin-left: -50px;"></div>
+    </div>
+  </scrollable-container>
+</div>
+
+<!-- VRL + RTL -->
+<div style="writing-mode: vertical-rl; direction: rtl;">
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-left: 50px; right: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-top: 50px; bottom: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-left: 50px; right: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-top: 50px; bottom: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="height: 0; margin-left: 100px; right: -1000px;">
+      <div style="width: 200px; margin-left: -50px;"></div>
+    </div>
+  </scrollable-container>
+</div>
+
+<!-- VLR + LTR -->
+<div style="writing-mode: vertical-lr; direction: ltr;">
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-right: 50px; left: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-right: 50px; left: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-bottom: 50px; top: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="height: 0; margin-right: 100px; left: -1000px;">
+      <div style="width: 200px; margin-right: -50px;"></div>
+    </div>
+  </scrollable-container>
+</div>
+
+<!-- VLR + RTL -->
+<div style="writing-mode: vertical-rl; direction: rtl;">
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-right: 50px; left: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-top: 50px; bottom: -1000px;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="width: 200px; height: 0; margin-right: 50px; left: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-height="270">
+    <div style="width: 0; height: 200px; margin-top: 50px; bottom: -1000px; display: inline-block;"></div>
+  </scrollable-container>
+
+  <scrollable-container data-expected-scroll-width="260">
+    <div style="height: 0; margin-right: 100px; left: -1000px;">
+      <div style="width: 200px; margin-right: -50px;"></div>
+    </div>
+  </scrollable-container>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001-ref.html
new file mode 100644
index 0000000..35f8d909c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<style>
+  .cb {
+    position: relative;
+  }
+  audio, video, canvas, iframe, svg {
+    border: 1px solid blue;
+    position: absolute;
+    /* This value corresponds to the difference between wrapper
+       divs' width in the testcase. It causes the elements to
+       be shifted right to prevent the test from spuriously
+       passing by just placing them on the leftmost side. */
+    left: 100px;
+  }
+</style>
+<body>
+  <div class="cb">
+    <audio controls></audio>
+    <video controls style="top: 50px;"></video>
+    <canvas style="top: 210px;"></canvas>
+    <iframe style="top: 370px;"></iframe>
+    <svg style="top: 530px;" width="300" height="20"></svg>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001.html b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001.html
new file mode 100644
index 0000000..54838a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-writing-modes/abs-pos-replaced-vrl-001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="David Shin" href="mailto:dshin@mozilla.com">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://drafts.csswg.org/css-writing-modes-3/#block-flow">
+<meta name="assert" content="This test checks that absolutely positioned elements are positioned correctly, when their writing mode is different from that of their containing block." />
+<link rel="match" href="abs-pos-replaced-vrl-001-ref.html">
+<style>
+  .vert-cb {
+    position: relative;
+    width: 250px;
+    writing-mode: vertical-rl;
+  }
+  .horiz-parent {
+    width: 150px;
+    writing-mode: horizontal-tb;
+  }
+  audio, video, canvas, iframe, svg {
+    position: absolute;
+    border: 1px solid blue;
+  }
+</style>
+<body>
+  <div class="vert-cb">
+    <div class="horiz-parent">
+      <audio controls></audio>
+      <video controls style="top: 50px;"></video>
+      <canvas style="top: 210px;"></canvas>
+      <iframe style="top: 370px;"></iframe>
+      <!-- Note: Including width/height attrs in order to test SVG without
+           hitting https://bugzilla.mozilla.org/show_bug.cgi?id=1766304 -->
+      <svg style="top: 530px;" width="300" height="20"></svg>
+    </div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollWidthHeight.xht b/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollWidthHeight.xht
index 77b01db..1893051 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollWidthHeight.xht
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/scrollWidthHeight.xht
@@ -78,54 +78,33 @@
                 assert_equals(elemOverflow.clientHeight, 90);
             }, "elemOverflow.clientHeight is the height of the padding edge");
 
-            /* This test differs from the spec. Opera and Webkit meet the spec, IE9 and Firefox
-               give the result here. It seems that in this case Opera and Webkit place
-               the padding-bottom below elemOverflow's child (i.e. below elemOverflow's bottom border);
-               you can scroll to it. IE9 and Firefox do not. I believe this is a Webkit/Opera bug
-               (If you remove overflow:hidden then the padding-bottom moves back to be above the bottom
-               border, as expected.)
-               The underlying issue seems to be whether bottom padding on a scrollable element is
-               always placed at the element's bottom border and not scrolled, or else deemed to
-               belong to the scrolled content and placed below the scrolled element's children.
-               Commenting out for now, because this is not really a CSSOM issue, but an issue
-               over the layout of elements with 'overflow'.
-
             test(function(){
-                assert_equals(elemOverflow.scrollHeight, 150);
-            }, "elemOverflow.scrollHeight is the height of its scrolled contents (ignoring padding, since we overflowed)");
-            */
+                assert_equals(elemOverflow.scrollHeight, 200);
+            }, "elemOverflow.scrollHeight is the height of its scrolled contents (including padding)");
 
             test(function(){
                 assert_equals(elemOverflow.clientWidth, 240);
             }, "elemOverflow.clientWidth is the width of the padding edge");
 
-            /* This test differs from the spec. All major browsers give the result here, ignoring
-               the right padding.
-            */
             test(function(){
-                assert_equals(elemOverflow.scrollWidth, 250);
-            }, "elemOverflow.scrollHeight is the width of its scrolled contents (ignoring padding, since we overflowed)");
+                assert_equals(elemOverflow.scrollWidth, 290);
+            }, "elemOverflow.scrollHeight is the width of its scrolled contents (including padding)");
 
             test(function(){
                 assert_equals(elemNestedOverflow.clientHeight, 90);
             }, "elemNestedOverflow.clientHeight is the height of the padding edge");
 
-            /* This test differs from the spec. All major browsers give the result here.
-            */
             test(function(){
                 assert_equals(elemNestedOverflow.scrollHeight, 150);
-            }, "elemNestedOverflow.scrollHeight is the height of its scrolled contents (ignoring padding, since we overflowed)");
+            }, "elemNestedOverflow.scrollHeight is the height of its scrolled contents (ignoring padding)");
 
             test(function(){
                 assert_equals(elemNestedOverflow.clientWidth, 240);
             }, "elemNestedOverflow.clientWidth is the height of the padding edge");
 
-            /* This test differs from the spec. All major browsers give the result here, ignoring
-               the right padding.
-            */
             test(function(){
                 assert_equals(elemNestedOverflow.scrollWidth, 250);
-            }, "elemNestedOverflow.scrollWidth is the width of its scrolled contents (ignoring padding, since we overflowed)");
+            }, "elemNestedOverflow.scrollWidth is the width of its scrolled contents (ignoring padding)");
 
         ]]></script>
     </body>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-attribute-basic.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-attribute-basic.tentative.html
index 30df4e5..1760dd78 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-attribute-basic.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-attribute-basic.tentative.html
@@ -87,24 +87,7 @@
     assert_equals(popup.getAttribute('popup'),'invalid');
     popup.popup='';
     assert_equals(popup.getAttribute('popup'),'');
-  },'IDL attribute reflection')
-
-  test(() => {
-    const popup = createPopup();
-    assertIsFunctionalPopup(popup);
-
-    popup.setAttribute('popup','hint'); // Change popup type
-    assertIsFunctionalPopup(popup);
-
-    popup.setAttribute('popup','invalid'); // Change popup type to something invalid
-    assertNotAPopup(popup);
-
-    popup.popup = 'hint'; // Change popup type via IDL
-    assertIsFunctionalPopup(popup);
-
-    popup.popup = 'invalid'; // Make invalid via IDL
-    assertNotAPopup(popup);
-  },'Changing attribute values for popup should work')
+  },'IDL attribute reflection');
 
   test(() => {
     const popup = createPopup();
@@ -121,5 +104,48 @@
     assertIsFunctionalPopup(popup);
     popup.popup = 'PoPuP';
     assertIsFunctionalPopup(popup);
-  },'Popup attribute value should be case insensitive')
+  },'Popup attribute value should be case insensitive');
+
+  test(() => {
+    const popup = createPopup();
+    assertIsFunctionalPopup(popup);
+    popup.setAttribute('popup','hint'); // Change popup type
+    assertIsFunctionalPopup(popup);
+    popup.setAttribute('popup','invalid'); // Change popup type to something invalid
+    assertNotAPopup(popup);
+    popup.popup = 'hint'; // Change popup type via IDL
+    assertIsFunctionalPopup(popup);
+    popup.popup = 'invalid'; // Make invalid via IDL
+    assertNotAPopup(popup);
+  },'Changing attribute values for popup should work');
+
+  test(() => {
+    const popup = createPopup();
+    popup.showPopup();
+    assert_true(popup.matches(':popup-open'));
+    popup.setAttribute('popup','hint'); // Change popup type
+    assert_false(popup.matches(':popup-open'));
+    popup.showPopup();
+    assert_true(popup.matches(':popup-open'));
+    popup.setAttribute('popup','async');
+    assert_false(popup.matches(':popup-open'));
+    popup.showPopup();
+    assert_true(popup.matches(':popup-open'));
+    popup.setAttribute('popup','invalid');
+    assert_false(popup.matches(':popup-open'));
+  },'Changing attribute values should close open popups');
+
+
+  ["popup","hint","async"].forEach(type => {
+    test(() => {
+      const popup = createPopup();
+      popup.setAttribute('popup',type);
+      popup.showPopup();
+      assert_true(popup.matches(':popup-open'));
+      popup.remove();
+      assert_false(popup.matches(':popup-open'));
+      document.body.appendChild(popup);
+      assert_false(popup.matches(':popup-open'));
+    },`Removing a visible popup=${type} element from the document should close the popup`);
+  });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-initiallyopen.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-initiallyopen.tentative.html
index ecbc655..9396e01 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-initiallyopen.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-initiallyopen.tentative.html
@@ -8,8 +8,12 @@
 <div popup=popup id=p1 initiallyopen>This is a popup, which should be open upon load</div>
 <script></script> <!-- Possibly yield the parser, just to double-check -->
 <div popup=popup id=p2 initiallyopen>This is a second popup with initiallyopen, which should NOT be open upon load</div>
+<div popup=hint id=p2b initiallyopen>This is a hint popup with initiallyopen, which should NOT be open upon load</div>
 <div popup=popup id=p3>Also not visible</div>
 
+<div popup=async id=p4 initiallyopen>This is an async popup with initiallyopen, which should be open upon load</div>
+<div popup=async id=p5 initiallyopen>This is an async popup with initiallyopen, which should be open upon load</div>
+
 <script>
   requestAnimationFrame(() => {
     requestAnimationFrame(() => {
@@ -21,6 +25,10 @@
         assert_true(p2.hasAttribute('initiallyopen'),'initiallyopen should be present/true, even if not opened');
         assert_true(p2.initiallyOpen,'initiallyopen should be present/true, even if not opened');
 
+        assert_false(p2b.matches(':popup-open'),'Only the first popup/hint with initiallyopen should be open on load');
+        assert_true(p4.matches(':popup-open'),'initiallyopen should open all async popups');
+        assert_true(p5.matches(':popup-open'),'initiallyopen should open all async popups');
+
         assert_false(p3.matches(':popup-open'));
         p3.setAttribute('initiallyopen','');
         assert_false(p3.matches(':popup-open'), 'Changing initiallyopen should not affect open status');
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-light-dismiss.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-light-dismiss.tentative.html
index fbf19ea8..2a5110b 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-light-dismiss.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-light-dismiss.tentative.html
@@ -388,3 +388,49 @@
   assert_false(convPopup4.matches(':popup-open'));
 },'Ensure circular/convoluted ancestral relationships are functional, with a direct showPopup()');
 </script>
+
+
+<div popup=popup id=p10>Popup</div>
+<div popup=hint id=p11>Hint</div>
+<div popup=async id=p12>Async</div>
+<style>
+  #p10 {top:100px;}
+  #p11 {top:200px;}
+  #p12 {top:300px;}
+</style>
+<script>
+promise_test(async () => {
+  const popup = document.querySelector('#p10');
+  const hint = document.querySelector('#p11');
+  const async = document.querySelector('#p12');
+  // All three can be open at once, if shown in this order:
+  popup.showPopup();
+  hint.showPopup();
+  async.showPopup();
+  assert_true(popup.matches(':popup-open'));
+  assert_true(hint.matches(':popup-open'));
+  assert_true(async.matches(':popup-open'));
+  // The hint was opened last, so clicking it shouldn't close anything:
+  await clickOn(hint);
+  assert_true(popup.matches(':popup-open'),'popup should stay open');
+  assert_true(hint.matches(':popup-open'),'hint should stay open');
+  assert_true(async.matches(':popup-open'),'async does not light dismiss');
+  // Clicking outside should close the hint and popup, but not the async:
+  await clickOn(outside);
+  assert_false(popup.matches(':popup-open'),'popup should close');
+  assert_false(hint.matches(':popup-open'),'hint should close');
+  assert_true(async.matches(':popup-open'),'async does not light dismiss');
+  async.hidePopup();
+  assert_false(async.matches(':popup-open'));
+  popup.showPopup();
+  hint.showPopup();
+  assert_true(popup.matches(':popup-open'));
+  assert_true(hint.matches(':popup-open'));
+  // Clicking on the popup should close the hint:
+  await clickOn(popup);
+  assert_true(popup.matches(':popup-open'),'popup should stay open');
+  assert_false(hint.matches(':popup-open'),'hint should light dismiss');
+  popup.hidePopup();
+  assert_false(popup.matches(':popup-open'));
+},'Light dismiss of mixed popup types');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-types.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-types.tentative.html
new file mode 100644
index 0000000..74a1b916
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popups/popup-types.tentative.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel=help href="https://open-ui.org/components/popup.research.explainer">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=popup popup=popup>Popup</div>
+<div id=hint popup=hint>Hint</div>
+<div id=async popup=async>Async</div>
+<div id=async2 popup=async>Async</div>
+
+<script>
+function assert_state_1(popupOpen,hintOpen,asyncOpen,async2Open) {
+  assert_equals(popup.matches(':popup-open'),popupOpen,'popup open state is incorrect');
+  assert_equals(hint.matches(':popup-open'),hintOpen,'hint open state is incorrect');
+  assert_equals(async.matches(':popup-open'),asyncOpen,'async open state is incorrect');
+  assert_equals(async2.matches(':popup-open'),async2Open,'async2 open state is incorrect');
+}
+test(() => {
+  assert_state_1(false,false,false,false);
+  popup.showPopup();
+  assert_state_1(true,false,false,false);
+  hint.showPopup();
+  assert_state_1(true,true,false,false);
+  async.showPopup();
+  assert_state_1(true,true,true,false);
+  async2.showPopup();
+  assert_state_1(true,true,true,true);
+  hint.hidePopup();
+  assert_state_1(true,false,true,true);
+  popup.hidePopup();
+  assert_state_1(false,false,true,true);
+  popup.showPopup();
+  hint.showPopup();
+  assert_state_1(true,true,true,true);
+  popup.hidePopup();
+  assert_state_1(false,false,true,true);
+  async.hidePopup();
+  assert_state_1(false,false,false,true);
+  async2.hidePopup();
+  assert_state_1(false,false,false,false);
+},'hints and asyncs do not close popups');
+
+test(() => {
+  assert_state_1(false,false,false,false);
+  hint.showPopup();
+  async.showPopup();
+  async2.showPopup();
+  assert_state_1(false,true,true,true);
+  popup.showPopup();
+  assert_state_1(true,false,true,true);
+  popup.hidePopup();
+  assert_state_1(false,false,true,true);
+  async.hidePopup();
+  async2.hidePopup();
+  assert_state_1(false,false,false,false);
+},'popups close hints but not asyncs');
+</script>
+
+<div id=popup1 popup=popup>Popup 1
+  <div id=popup2 popup=popup>Popup 2
+    <p id=anchorid>Anchor</p>
+    <div id=popup3 popup=popup>Popup 3</div>
+  </div>
+</div>
+<div id=hint2 popup=hint anchor=anchorid>Hint anchored to popup</div>
+<script>
+function assert_state_2(popup1Open,popup2Open,popup3Open,hintOpen) {
+  assert_equals(popup1.matches(':popup-open'),popup1Open,'popup1 open state is incorrect');
+  assert_equals(popup2.matches(':popup-open'),popup2Open,'popup2 open state is incorrect');
+  assert_equals(popup3.matches(':popup-open'),popup3Open,'popup3 open state is incorrect');
+  assert_equals(hint2.matches(':popup-open'),hintOpen,'hint2 open state is incorrect');
+}
+test(() => {
+  assert_state_2(false,false,false,false);
+  popup1.showPopup();
+  popup2.showPopup();
+  popup3.showPopup();
+  assert_state_2(true,true,true,false);
+  hint2.showPopup();
+  assert_state_2(true,true,true,true);
+  popup3.hidePopup(); // Should close the hint
+  assert_state_2(true,true,false,false);
+  popup1.hidePopup();
+  assert_state_2(false,false,false,false);
+},'hint, even with popup ancestor, closes with first popup');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-claim.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https-expected.txt
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-claim.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-claim.tentative.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-claim.tentative.https.html
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https-expected.txt
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https.html
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https-expected.txt
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https.html
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https-expected.txt
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned.tentative.https.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https.html
rename to third_party/blink/web_tests/external/wpt/service-workers/service-worker/partitioned.tentative.https.html
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/helper.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/helper.js
new file mode 100644
index 0000000..d22710b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/helper.js
@@ -0,0 +1,55 @@
+(function() {
+class NetworkLifecycleObserver {
+  constructor(dp) {
+    this._dp = dp;
+    this._dp.Network.enable();
+    this._dp.Fetch.enable();
+    this._dp.Fetch.onRequestPaused(e => {
+      this._dp.Fetch.continueRequest({
+        requestId: e.params.requestId,
+      });
+    });
+  }
+
+  async waitForCompletion(urlPattern) {
+    const [willBeSentRequestId, pausedNetworkId, responseReceivedRequestId] =
+        await Promise.all([
+          this._dp.Network
+              .onceRequestWillBeSent(
+                  evt => urlPattern.test(evt.params.request?.url))
+              .then(evt => evt.params.requestId)
+              .then(async requestId => {
+                await Promise.race([
+                  this._dp.Network.onLoadingFinished(
+                      evt => evt.params.requestId === requestId),
+                  this._dp.Network.onLoadingFailed(
+                      evt => evt.params.requestId === requestId),
+                ]);
+
+                return requestId;
+              }),
+          this._dp.Fetch
+              .onceRequestPaused(
+                  evt => urlPattern.test(evt.params.request?.url))
+              .then(evt => evt.params.networkId),
+          this._dp.Network
+              .onceResponseReceived(
+                  evt => urlPattern.test(evt.params.response?.url))
+              .then(evt => evt.params.requestId),
+        ]);
+
+    if (willBeSentRequestId && willBeSentRequestId === pausedNetworkId &&
+        pausedNetworkId === responseReceivedRequestId)
+      return `[${
+          urlPattern}] OK: All expected network events found and align with one another!`;
+
+    return `[${
+        urlPattern}] FAIL: requestId was empty or did not align with other network events. Network.requestWillBeSent.params.requestId (${
+        willBeSentRequestId}), Fetch.requestPaused.params.networkId (${
+        pausedNetworkId}), Network.responseReceived.params.requestId (${
+        responseReceivedRequestId})`;
+  }
+}
+
+return NetworkLifecycleObserver;
+})()
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/imported-classic.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/imported-classic.js
new file mode 100644
index 0000000..4190c54
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/imported-classic.js
@@ -0,0 +1,2 @@
+console.log('imported-classic.js running');
+CLASSIC_EXPORTED_VALUE = 'CLASSIC';
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/imported-module.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/imported-module.js
new file mode 100644
index 0000000..040f888
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/imported-module.js
@@ -0,0 +1,3 @@
+console.log('imported-module.js running');
+const MODULE_EXPORTED_VALUE = 'MODULE';
+export default MODULE_EXPORTED_VALUE
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/main.html b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/main.html
new file mode 100644
index 0000000..efe8518d
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/main.html
@@ -0,0 +1,15 @@
+<script type="module">
+  const params = new URLSearchParams(location.search);
+  const type = params.get('type');
+  let url = `sw-${type}.js`;
+  if (params.get('fail') === '404')
+    url = `not-going-to-be-found/${url}`;
+
+  navigator.serviceWorker.ready.then(registration => {
+    registration.active.postMessage(`${type} init`);
+  });
+  window.completionMessage = new Promise(resolve => {
+    navigator.serviceWorker.addEventListener('message', evt => { resolve(evt.data) });
+  });
+  await navigator.serviceWorker.register(url, { type });
+</script>
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/sw-classic.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/sw-classic.js
new file mode 100644
index 0000000..cb2b5a9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/sw-classic.js
@@ -0,0 +1,12 @@
+console.log('Importing imported-classic');
+importScripts('./imported-classic.js');
+const IMPORTED = CLASSIC_EXPORTED_VALUE;
+console.log('Finished importing imported-classic');
+
+addEventListener('message', async event => {
+  console.log('worker got message');
+  event.source.postMessage([
+    event.data, `imported value: ${IMPORTED}`,
+    `fetch within worker: ${await fetch('/404-me').then(res => res.statusText)}`
+  ]);
+});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/sw-module.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/sw-module.js
new file mode 100644
index 0000000..0555eb0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/resources/service-workers/sw-module.js
@@ -0,0 +1,12 @@
+console.log('Importing imported-module');
+import MODULE_EXPORTED_VALUE from './imported-module.js';
+const IMPORTED = MODULE_EXPORTED_VALUE;
+console.log('Finished importing imported-module');
+
+addEventListener('message', async event => {
+  console.log('worker got message');
+  event.source.postMessage([
+    event.data, `imported value: ${IMPORTED}`,
+    `fetch within worker: ${await fetch('/404-me').then(res => res.statusText)}`
+  ]);
+});
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-expected.txt
new file mode 100644
index 0000000..e5a795c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-expected.txt
@@ -0,0 +1,17 @@
+Tests that Network events are fired for service workers and they align (classic)
+[browser] Request to http://127.0.0.1:8000/inspector-protocol/fetch/resources/service-workers/main.html?type=classic, type: Document
+[browser] Request to http://127.0.0.1:8000/inspector-protocol/fetch/resources/service-workers/sw-classic.js, type: Other
+[browser] Request to http://127.0.0.1:8000/inspector-protocol/fetch/resources/service-workers/imported-classic.js, type: Script
+[browser] Request to http://127.0.0.1:8000/404-me, type: XHR
+
+Results from Running the Page:
+	classic init
+	-> imported value: CLASSIC
+	-> fetch within worker: Not Found
+
+Network Events and Alignment:
+[/main\.html/] OK: All expected network events found and align with one another!
+[/sw-classic\.js/] OK: All expected network events found and align with one another!
+[/imported-classic\.js/] OK: All expected network events found and align with one another!
+[/404-me/] OK: All expected network events found and align with one another!
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-failures.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-failures.js
new file mode 100644
index 0000000..89fc877
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic-failures.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  const {session, dp} = await testRunner.startBlank(
+      `Tests that Network events are fired for 404'd service workers and they align (classic)`);
+  testRunner.startDumpingProtocolMessages();
+  const FetchHelper = await testRunner.loadScript('resources/fetch-test.js');
+  const NetworkLifecycleObserver =
+      await testRunner.loadScript('resources/service-workers/helper.js');
+
+  await dp.Target.setAutoAttach(
+      {autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
+
+  let workerNetworkObserver = new Promise(resolve => {
+    dp.Target.onAttachedToTarget(async event => {
+      const wdp = session.createChild(event.params.sessionId).protocol;
+      resolve(new NetworkLifecycleObserver(wdp));
+      wdp.Runtime.runIfWaitingForDebugger();
+    });
+  });
+
+  const pageObserver = new NetworkLifecycleObserver(dp);
+  const observers = [
+    pageObserver.waitForCompletion(/main\.html/),
+    workerNetworkObserver.then(o => Promise.all([
+      o.waitForCompletion(/sw-classic\.js/),
+    ]))
+  ];
+
+  const globalFetcher = new FetchHelper(testRunner, testRunner.browserP());
+  globalFetcher.setLogPrefix('[browser] ');
+  await globalFetcher.enable();
+  globalFetcher.onRequest().continueRequest({});
+
+  await session.navigate(
+      './resources/service-workers/main.html?type=classic&fail=404');
+  testRunner.log(`\nNetwork Events and Alignment:`)
+  testRunner.log((await Promise.all(observers)).flat().join(`\n`));
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic.js
new file mode 100644
index 0000000..34c3851
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-classic.js
@@ -0,0 +1,42 @@
+(async function(testRunner) {
+  const {session, dp} = await testRunner.startBlank(
+      `Tests that Network events are fired for service workers and they align (classic)`);
+
+  const FetchHelper = await testRunner.loadScript('resources/fetch-test.js');
+  const NetworkLifecycleObserver =
+      await testRunner.loadScript('resources/service-workers/helper.js');
+
+  await dp.Target.setAutoAttach(
+      {autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
+
+  let workerNetworkObserver = new Promise(resolve => {
+    dp.Target.onAttachedToTarget(async event => {
+      const wdp = session.createChild(event.params.sessionId).protocol;
+      resolve(new NetworkLifecycleObserver(wdp));
+      wdp.Runtime.runIfWaitingForDebugger();
+    });
+  });
+
+  const pageObserver = new NetworkLifecycleObserver(dp);
+  const observers = [
+    pageObserver.waitForCompletion(/main\.html/),
+    workerNetworkObserver.then(o => Promise.all([
+      o.waitForCompletion(/sw-classic\.js/),
+      o.waitForCompletion(/imported-classic\.js/),
+      o.waitForCompletion(/404-me/),
+    ]))
+  ];
+
+  const globalFetcher = new FetchHelper(testRunner, testRunner.browserP());
+  globalFetcher.setLogPrefix('[browser] ');
+  await globalFetcher.enable();
+  globalFetcher.onRequest().continueRequest({});
+
+  await session.navigate('./resources/service-workers/main.html?type=classic');
+  const messages = await session.evaluateAsync('window.completionMessage');
+  testRunner.log(
+      `\nResults from Running the Page:\n\t${messages.join(`\n\t-> `)}`);
+  testRunner.log(`\nNetwork Events and Alignment:`)
+  testRunner.log((await Promise.all(observers)).flat().join(`\n`));
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-expected.txt
new file mode 100644
index 0000000..68d92a4
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-expected.txt
@@ -0,0 +1,17 @@
+Tests that Network events are fired for service workers and they align (module)
+[browser] Request to http://127.0.0.1:8000/inspector-protocol/fetch/resources/service-workers/main.html?type=module, type: Document
+[browser] Request to http://127.0.0.1:8000/inspector-protocol/fetch/resources/service-workers/sw-module.js, type: Other
+[browser] Request to http://127.0.0.1:8000/inspector-protocol/fetch/resources/service-workers/imported-module.js, type: Other
+[browser] Request to http://127.0.0.1:8000/404-me, type: XHR
+
+Results from Running the Page:
+	module init
+	-> imported value: MODULE
+	-> fetch within worker: Not Found
+
+Network Events and Alignment:
+[/main\.html/] OK: All expected network events found and align with one another!
+[/sw-module\.js/] OK: All expected network events found and align with one another!
+[/imported-module\.js/] OK: All expected network events found and align with one another!
+[/404-me/] OK: All expected network events found and align with one another!
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-failures.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-failures.js
new file mode 100644
index 0000000..a1f11a77
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module-failures.js
@@ -0,0 +1,38 @@
+(async function(testRunner) {
+  const {session, dp} = await testRunner.startBlank(
+      `Tests that Network events are fired for 404'd service workers and they align (module)`);
+  testRunner.startDumpingProtocolMessages();
+  const FetchHelper = await testRunner.loadScript('resources/fetch-test.js');
+  const NetworkLifecycleObserver =
+      await testRunner.loadScript('resources/service-workers/helper.js');
+
+  await dp.Target.setAutoAttach(
+      {autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
+
+  let workerNetworkObserver = new Promise(resolve => {
+    dp.Target.onAttachedToTarget(async event => {
+      const wdp = session.createChild(event.params.sessionId).protocol;
+      resolve(new NetworkLifecycleObserver(wdp));
+      wdp.Runtime.runIfWaitingForDebugger();
+    });
+  });
+
+  const pageObserver = new NetworkLifecycleObserver(dp);
+  const observers = [
+    pageObserver.waitForCompletion(/main\.html/),
+    workerNetworkObserver.then(o => Promise.all([
+      o.waitForCompletion(/sw-module\.js/),
+    ]))
+  ];
+
+  const globalFetcher = new FetchHelper(testRunner, testRunner.browserP());
+  globalFetcher.setLogPrefix('[browser] ');
+  await globalFetcher.enable();
+  globalFetcher.onRequest().continueRequest({});
+
+  await session.navigate(
+      './resources/service-workers/main.html?type=module&fail=404');
+  testRunner.log(`\nNetwork Events and Alignment:`)
+  testRunner.log((await Promise.all(observers)).flat().join(`\n`));
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module.js
new file mode 100644
index 0000000..7eec6586
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-network-id-alignment-module.js
@@ -0,0 +1,42 @@
+(async function(testRunner) {
+  const {session, dp} = await testRunner.startBlank(
+      `Tests that Network events are fired for service workers and they align (module)`);
+
+  const FetchHelper = await testRunner.loadScript('resources/fetch-test.js');
+  const NetworkLifecycleObserver =
+      await testRunner.loadScript('resources/service-workers/helper.js');
+
+  await dp.Target.setAutoAttach(
+      {autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
+
+  let workerNetworkObserver = new Promise(resolve => {
+    dp.Target.onAttachedToTarget(async event => {
+      const wdp = session.createChild(event.params.sessionId).protocol;
+      resolve(new NetworkLifecycleObserver(wdp));
+      wdp.Runtime.runIfWaitingForDebugger();
+    });
+  });
+
+  const pageObserver = new NetworkLifecycleObserver(dp);
+  const observers = [
+    pageObserver.waitForCompletion(/main\.html/),
+    workerNetworkObserver.then(o => Promise.all([
+      o.waitForCompletion(/sw-module\.js/),
+      o.waitForCompletion(/imported-module\.js/),
+      o.waitForCompletion(/404-me/),
+    ]))
+  ];
+
+  const globalFetcher = new FetchHelper(testRunner, testRunner.browserP());
+  globalFetcher.setLogPrefix('[browser] ');
+  await globalFetcher.enable();
+  globalFetcher.onRequest().continueRequest({});
+
+  await session.navigate('./resources/service-workers/main.html?type=module');
+  const messages = await session.evaluateAsync('window.completionMessage');
+  testRunner.log(
+      `\nResults from Running the Page:\n\t${messages.join(`\n\t-> `)}`);
+  testRunner.log(`\nNetwork Events and Alignment:`)
+  testRunner.log((await Promise.all(observers)).flat().join(`\n`));
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id-expected.txt
deleted file mode 100644
index 579245a..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Tests that networkId present on Fetch.requestPaused for initial Service Worker script request
-OK: init: networkId === requestId
-OK: subFetch: networkId === requestId
-
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js b/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js
deleted file mode 100644
index 5a701e2..0000000
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/fetch/service-worker-request-paused-network-id.js
+++ /dev/null
@@ -1,75 +0,0 @@
-(async function(testRunner) {
-  const {page, session, dp} = await testRunner.startBlank(
-      `Tests that networkId present on Fetch.requestPaused for initial Service Worker script request`);
-
-  const assertNetworkIdAlignment = (description, willBeSentEvent, pausedEvent) => {
-    const {params: {requestId}} = willBeSentEvent;
-    const {params: {networkId}} = pausedEvent;
-    if (networkId && networkId === requestId) {
-      testRunner.log(`OK: ${description}: networkId === requestId`)
-      return;
-    }
-
-    testRunner.fail(`FAIL: ${
-        description}: networkId !== requestId or one or more ids are missing (${
-        networkId} vs. ${requestId})`);
-  };
-
-  await dp.Target.setAutoAttach(
-      {autoAttach: true, waitForDebuggerOnStart: true, flatten: true});
-
-  let workerProtocol = new Promise(resolve => {
-    dp.Target.onAttachedToTarget(async event => {
-      const wdp = session.createChild(event.params.sessionId).protocol;
-      resolve(wdp);
-
-      wdp.Fetch.onRequestPaused(e => {
-        wdp.Fetch.continueRequest({
-          requestId: e.params.requestId,
-        });
-      });
-      wdp.Network.enable();
-      wdp.Fetch.enable({patterns: [{urlPattern: '*'}]});
-      wdp.Runtime.runIfWaitingForDebugger();
-    });
-  });
-
-  await dp.Network.enable();
-  await dp.Fetch.enable({patterns: [{urlPattern: '*'}]});
-
-  dp.Fetch.onRequestPaused(event => {
-    dp.Fetch.continueRequest({
-      requestId: event.params.requestId,
-    });
-  });
-
-  const [[
-    initScriptWillBeSentEvent, initScriptPausedEvent, subFetchWillBeSentEvent,
-    subFetchPausedEvent
-  ]] =
-      await Promise.all([
-        workerProtocol.then(wdp => Promise.all([
-          wdp.Network.onceRequestWillBeSent(
-              e => e.params.request.url.endsWith(
-                  '/service-worker-with-fetch.js')),
-          wdp.Fetch.onceRequestPaused(
-              e => e.params.request.url.endsWith(
-                  '/service-worker-with-fetch.js')),
-          wdp.Network.onceRequestWillBeSent(
-              e => e.params.request.url.endsWith(
-                  '/request-within-service-worker')),
-          wdp.Fetch.onceRequestPaused(
-              e => e.params.request.url.endsWith(
-                  '/request-within-service-worker')),
-        ])),
-        page.navigate(
-            testRunner.url('./resources/service-worker-with-fetch.html')),
-      ]);
-
-  assertNetworkIdAlignment(
-      'init', initScriptWillBeSentEvent, initScriptPausedEvent);
-  assertNetworkIdAlignment(
-      'subFetch', subFetchWillBeSentEvent, subFetchPausedEvent);
-
-  testRunner.completeTest();
-})
diff --git a/third_party/blink/web_tests/overflow/overflow-basic-004.html b/third_party/blink/web_tests/overflow/overflow-basic-004.html
deleted file mode 100644
index 409af161..0000000
--- a/third_party/blink/web_tests/overflow/overflow-basic-004.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!doctype html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<style>
-#container {
-  overflow: auto;
-  border: 1px solid black;
-  width: 200px;
-  height: 100px;
-}
-#target {
-  width: 300px;
-  height: 200px;
-  position: relative;
-  top: -50px;
-  left: -40px;
-  background-color: rgba(0, 255, 0, 0.3);
-}
-</style>
-<p>Overflow, with position:relative. position:relative affects overflow.</p>
-<div id="container">
-  <div id="target"></div>
-</div>
-<script>
-
-var container = document.querySelector('#container');
-var target = document.querySelector('#target');
-
-test(function() {
-  var targetStyle = window.getComputedStyle(target);
-  assert_equals(container.scrollWidth,
-    target.offsetWidth + parseInt(targetStyle.left),
-    "width");
-  assert_equals(container.scrollHeight,
-    target.offsetHeight + parseInt(targetStyle.top),
-    "height");
-}, "relative overflow");
-
-test(function() {
-  target.style.top = "-66px";
-  var targetStyle = window.getComputedStyle(target);
-  assert_equals(container.scrollWidth,
-    target.offsetWidth + parseInt(targetStyle.left),
-    "width");
-  assert_equals(container.scrollHeight,
-    target.offsetHeight + parseInt(targetStyle.top),
-    "height");
-}, "relative overflow, after target move");
-</script>
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-claim.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker-claim.tentative.https-expected.txt
rename to third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-claim.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker-getRegistrations.tentative.https-expected.txt
rename to third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-getRegistrations.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker-matchAll.tentative.https-expected.txt
rename to third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-matchAll.tentative.https-expected.txt
diff --git a/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https-expected.txt b/third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned.tentative.https-expected.txt
similarity index 100%
rename from third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned-service-worker.tentative.https-expected.txt
rename to third_party/blink/web_tests/virtual/third-party-storage-partitioning/external/wpt/service-workers/service-worker/partitioned.tentative.https-expected.txt
diff --git a/third_party/ipcz/src/BUILD.gn b/third_party/ipcz/src/BUILD.gn
index a38b1a01..2b5f1532c 100644
--- a/third_party/ipcz/src/BUILD.gn
+++ b/third_party/ipcz/src/BUILD.gn
@@ -161,6 +161,11 @@
   visibility = [ ":*" ]
   public = [
     "ipcz/api_object.h",
+    "ipcz/driver_memory.h",
+    "ipcz/driver_memory_mapping.h",
+    "ipcz/driver_object.h",
+    "ipcz/driver_transport.h",
+    "ipcz/message_internal.h",
     "ipcz/node.h",
     "ipcz/parcel.h",
     "ipcz/parcel_queue.h",
@@ -168,15 +173,26 @@
     "ipcz/router.h",
     "ipcz/sequence_number.h",
     "ipcz/sequenced_queue.h",
+    "ipcz/test_messages.h",
   ]
   sources = [
     "ipcz/api_object.cc",
+    "ipcz/driver_memory.cc",
+    "ipcz/driver_memory_mapping.cc",
+    "ipcz/driver_object.cc",
+    "ipcz/driver_transport.cc",
     "ipcz/link_side.cc",
     "ipcz/link_side.h",
     "ipcz/link_type.cc",
     "ipcz/link_type.h",
     "ipcz/local_router_link.cc",
     "ipcz/local_router_link.h",
+    "ipcz/message_internal.cc",
+    "ipcz/message_macros/message_declaration_macros.h",
+    "ipcz/message_macros/message_definition_macros.h",
+    "ipcz/message_macros/message_params_declaration_macros.h",
+    "ipcz/message_macros/message_params_declaration_macros.h",
+    "ipcz/message_macros/undef_message_macros.h",
     "ipcz/node.cc",
     "ipcz/node_name.cc",
     "ipcz/node_name.h",
@@ -185,6 +201,8 @@
     "ipcz/portal.cc",
     "ipcz/router.cc",
     "ipcz/router_link.h",
+    "ipcz/test_messages.cc",
+    "ipcz/test_messages_generator.h",
   ]
   public_deps = [
     ":ipcz_header",
@@ -218,9 +236,15 @@
 
   sources = [
     "api_test.cc",
+    "ipcz/driver_memory_test.cc",
+    "ipcz/driver_object_test.cc",
+    "ipcz/driver_transport_test.cc",
+    "ipcz/message_internal_test.cc",
     "ipcz/parcel_queue_test.cc",
     "ipcz/sequenced_queue_test.cc",
     "reference_drivers/single_process_reference_driver_test.cc",
+    "test/mock_driver.cc",
+    "test/mock_driver.h",
     "test/test_base.cc",
     "test/test_base.h",
     "util/ref_counted_test.cc",
@@ -228,6 +252,7 @@
   ]
 
   deps = [
+    "//testing/gmock",
     "//testing/gtest",
     "//third_party/abseil-cpp:absl",
   ]
diff --git a/third_party/ipcz/src/ipcz/OWNERS b/third_party/ipcz/src/ipcz/OWNERS
new file mode 100644
index 0000000..0afdfaa
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/OWNERS
@@ -0,0 +1,4 @@
+per-file *_messages.cc=set noparent
+per-file *_messages.cc=file://ipc/SECURITY_OWNERS
+per-file *_messages*.h=set noparent
+per-file *_messages*.h=file://ipc/SECURITY_OWNERS
diff --git a/third_party/ipcz/src/ipcz/driver_memory.cc b/third_party/ipcz/src/ipcz/driver_memory.cc
new file mode 100644
index 0000000..feb9ef1
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_memory.cc
@@ -0,0 +1,68 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_memory.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <limits>
+#include <utility>
+
+#include "ipcz/ipcz.h"
+#include "ipcz/node.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz {
+
+DriverMemory::DriverMemory() = default;
+
+DriverMemory::DriverMemory(DriverObject memory) : memory_(std::move(memory)) {
+  if (memory_.is_valid()) {
+    IpczSharedMemoryInfo info = {.size = sizeof(info)};
+    IpczResult result = memory_.node()->driver().GetSharedMemoryInfo(
+        memory_.handle(), IPCZ_NO_FLAGS, nullptr, &info);
+    ABSL_ASSERT(result == IPCZ_RESULT_OK);
+    size_ = info.region_num_bytes;
+  }
+}
+
+DriverMemory::DriverMemory(Ref<Node> node, size_t num_bytes)
+    : size_(num_bytes) {
+  ABSL_ASSERT(num_bytes > 0);
+  IpczDriverHandle handle;
+  IpczResult result = node->driver().AllocateSharedMemory(
+      num_bytes, IPCZ_NO_FLAGS, nullptr, &handle);
+  ABSL_ASSERT(result == IPCZ_RESULT_OK);
+  memory_ = DriverObject(std::move(node), handle);
+}
+
+DriverMemory::DriverMemory(DriverMemory&& other) = default;
+
+DriverMemory& DriverMemory::operator=(DriverMemory&& other) = default;
+
+DriverMemory::~DriverMemory() = default;
+
+DriverMemory DriverMemory::Clone() {
+  ABSL_ASSERT(is_valid());
+
+  IpczDriverHandle handle;
+  IpczResult result = memory_.node()->driver().DuplicateSharedMemory(
+      memory_.handle(), 0, nullptr, &handle);
+  ABSL_ASSERT(result == IPCZ_RESULT_OK);
+
+  return DriverMemory(DriverObject(memory_.node(), handle));
+}
+
+DriverMemoryMapping DriverMemory::Map() {
+  ABSL_ASSERT(is_valid());
+  void* address;
+  IpczDriverHandle mapping_handle;
+  IpczResult result = memory_.node()->driver().MapSharedMemory(
+      memory_.handle(), 0, nullptr, &address, &mapping_handle);
+  ABSL_ASSERT(result == IPCZ_RESULT_OK);
+  return DriverMemoryMapping(memory_.node()->driver(), mapping_handle, address,
+                             size_);
+}
+
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/driver_memory.h b/third_party/ipcz/src/ipcz/driver_memory.h
new file mode 100644
index 0000000..67d5e2f0
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_memory.h
@@ -0,0 +1,59 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_DRIVER_MEMORY_H_
+#define IPCZ_SRC_IPCZ_DRIVER_MEMORY_H_
+
+#include <cstddef>
+
+#include "ipcz/driver_memory_mapping.h"
+#include "ipcz/driver_object.h"
+#include "ipcz/ipcz.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+
+class Node;
+
+// Scoped wrapper around a shared memory region allocated and manipulated
+// through an ipcz driver.
+class DriverMemory {
+ public:
+  DriverMemory();
+
+  // Takes ownership of an existing driver memory object.
+  explicit DriverMemory(DriverObject memory);
+
+  // Asks the node to allocate a new driver shared memory region of at least
+  // `num_bytes` in size.
+  DriverMemory(Ref<Node> node, size_t num_bytes);
+
+  DriverMemory(DriverMemory&& other);
+  DriverMemory& operator=(DriverMemory&& other);
+
+  ~DriverMemory();
+
+  bool is_valid() const { return memory_.is_valid(); }
+  size_t size() const { return size_; }
+
+  DriverObject& driver_object() { return memory_; }
+
+  DriverObject TakeDriverObject() { return std::move(memory_); }
+
+  // Asks the driver to clone this memory object and return a new one which
+  // references the same underlying memory region.
+  DriverMemory Clone();
+
+  // Asks the driver to map this memory object into the process's address space
+  // and returns a scoper to control the mapping's lifetime.
+  DriverMemoryMapping Map();
+
+ private:
+  DriverObject memory_;
+  size_t size_ = 0;
+};
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_DRIVER_MEMORY_H_
diff --git a/third_party/ipcz/src/ipcz/driver_memory_mapping.cc b/third_party/ipcz/src/ipcz/driver_memory_mapping.cc
new file mode 100644
index 0000000..d2c04f1
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_memory_mapping.cc
@@ -0,0 +1,51 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_memory_mapping.h"
+
+#include <algorithm>
+
+namespace ipcz {
+
+DriverMemoryMapping::DriverMemoryMapping() = default;
+
+DriverMemoryMapping::DriverMemoryMapping(const IpczDriver& driver,
+                                         IpczDriverHandle mapping_handle,
+                                         void* address,
+                                         size_t size)
+    : driver_(driver),
+      mapping_(mapping_handle),
+      address_(address),
+      size_(size) {}
+
+DriverMemoryMapping::DriverMemoryMapping(DriverMemoryMapping&& other)
+    : driver_(other.driver_),
+      mapping_(std::exchange(other.mapping_, IPCZ_INVALID_DRIVER_HANDLE)),
+      address_(std::exchange(other.address_, nullptr)),
+      size_(std::exchange(other.size_, 0)) {}
+
+DriverMemoryMapping& DriverMemoryMapping::operator=(
+    DriverMemoryMapping&& other) {
+  Unmap();
+  driver_ = other.driver_;
+  mapping_ = std::exchange(other.mapping_, IPCZ_INVALID_DRIVER_HANDLE);
+  address_ = std::exchange(other.address_, nullptr);
+  size_ = std::exchange(other.size_, 0);
+  return *this;
+}
+
+DriverMemoryMapping::~DriverMemoryMapping() {
+  Unmap();
+}
+
+void DriverMemoryMapping::Unmap() {
+  if (is_valid()) {
+    driver_.Close(mapping_, 0, nullptr);
+    mapping_ = IPCZ_INVALID_DRIVER_HANDLE;
+    address_ = nullptr;
+    size_ = 0;
+  }
+}
+
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/driver_memory_mapping.h b/third_party/ipcz/src/ipcz/driver_memory_mapping.h
new file mode 100644
index 0000000..525f350
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_memory_mapping.h
@@ -0,0 +1,60 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_DRIVER_MEMORY_MAPPING_H_
+#define IPCZ_SRC_IPCZ_DRIVER_MEMORY_MAPPING_H_
+
+#include "ipcz/ipcz.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+
+namespace ipcz {
+
+// Scoped wrapper around a driver-controlled shared memory region mapping.
+class DriverMemoryMapping {
+ public:
+  DriverMemoryMapping();
+
+  // Tracks the driver-produced handle and base address of an active memory
+  // mapping.
+  DriverMemoryMapping(const IpczDriver& driver,
+                      IpczDriverHandle mapping_handle,
+                      void* address,
+                      size_t size);
+
+  DriverMemoryMapping(DriverMemoryMapping&& other);
+  DriverMemoryMapping(const DriverMemoryMapping&) = delete;
+  DriverMemoryMapping& operator=(DriverMemoryMapping&& other);
+  DriverMemoryMapping& operator=(const DriverMemoryMapping&) = delete;
+  ~DriverMemoryMapping();
+
+  bool is_valid() const { return mapping_ != IPCZ_INVALID_DRIVER_HANDLE; }
+
+  // Returns the base address of this mapping. Returns null if the mapping is
+  // invalid.
+  void* address() const { return address_; }
+
+  // Returns the address at `offset` bytes from this mapping's base address. iT
+  // is an error to call this with an `offset` greater than or equal to the
+  // mapped region's size.
+  void* address_at(size_t offset) const {
+    ABSL_ASSERT(offset < size_);
+    return static_cast<uint8_t*>(address_) + offset;
+  }
+
+  absl::Span<uint8_t> bytes() const {
+    return {static_cast<uint8_t*>(address_), size_};
+  }
+
+ private:
+  void Unmap();
+
+  IpczDriver driver_;
+  IpczDriverHandle mapping_;
+  void* address_;
+  size_t size_;
+};
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_DRIVER_MEMORY_MAPPING_H_
diff --git a/third_party/ipcz/src/ipcz/driver_memory_test.cc b/third_party/ipcz/src/ipcz/driver_memory_test.cc
new file mode 100644
index 0000000..900ccb1
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_memory_test.cc
@@ -0,0 +1,160 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_memory.h"
+
+#include "ipcz/driver_memory_mapping.h"
+#include "ipcz/node.h"
+#include "test/mock_driver.h"
+#include "test/test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+namespace {
+
+using testing::_;
+using testing::Return;
+
+class DriverMemoryTest : public test::TestBase {
+ public:
+  DriverMemoryTest() = default;
+  ~DriverMemoryTest() override = default;
+
+  test::MockDriver& driver() { return driver_; }
+  const Ref<Node>& node() const { return node_; }
+
+ private:
+  ::testing::StrictMock<test::MockDriver> driver_;
+  const Ref<Node> node_{MakeRefCounted<Node>(Node::Type::kNormal,
+                                             test::kMockDriver,
+                                             IPCZ_INVALID_DRIVER_HANDLE)};
+};
+
+TEST_F(DriverMemoryTest, Invalid) {
+  DriverMemory memory;
+  EXPECT_FALSE(memory.is_valid());
+}
+
+TEST_F(DriverMemoryTest, AcquireFromObject) {
+  constexpr IpczDriverHandle kHandle = 1234;
+  constexpr size_t kSize = 64;
+  DriverObject object(node(), kHandle);
+
+  // Constructing a new DriverMemory over a generic DriverObject must query the
+  // underlying object for its size.
+  EXPECT_CALL(driver(), GetSharedMemoryInfo(kHandle, _, _, _))
+      .WillOnce([](IpczDriverHandle handle, uint32_t, const void*,
+                   IpczSharedMemoryInfo* info) {
+        info->region_num_bytes = kSize;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  // DriverMemory must reflect the size returned by the driver.
+  DriverMemory memory(std::move(object));
+  EXPECT_EQ(kSize, memory.size());
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _));
+}
+
+TEST_F(DriverMemoryTest, Allocate) {
+  constexpr IpczDriverHandle kHandle = 54321;
+  constexpr size_t kSize = 256;
+
+  // Constructing a new DriverMemory with a size should allocate a new shared
+  // memory region through the driver.
+  EXPECT_CALL(driver(), AllocateSharedMemory(kSize, _, _, _))
+      .WillOnce([&](size_t num_bytes, uint32_t, const void*,
+                    IpczDriverHandle* handle) {
+        *handle = kHandle;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  DriverMemory memory(node(), kSize);
+  EXPECT_EQ(kHandle, memory.driver_object().handle());
+  EXPECT_EQ(kSize, memory.size());
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _));
+}
+
+TEST_F(DriverMemoryTest, Clone) {
+  constexpr IpczDriverHandle kHandle = 54321;
+  constexpr IpczDriverHandle kDupe = 1234;
+  constexpr size_t kSize = 256;
+
+  EXPECT_CALL(driver(), AllocateSharedMemory(kSize, _, _, _))
+      .WillOnce([&](size_t num_bytes, uint32_t, const void*,
+                    IpczDriverHandle* handle) {
+        *handle = kHandle;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  DriverMemory memory(node(), kSize);
+
+  EXPECT_CALL(driver(), DuplicateSharedMemory(kHandle, _, _, _))
+      .WillOnce([&](IpczDriverHandle memory, uint32_t, const void*,
+                    IpczDriverHandle* new_handle) {
+        *new_handle = kDupe;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+  EXPECT_CALL(driver(), GetSharedMemoryInfo(kDupe, _, _, _))
+      .WillOnce([](IpczDriverHandle handle, uint32_t, const void*,
+                   IpczSharedMemoryInfo* info) {
+        info->region_num_bytes = kSize;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  DriverMemory clone = memory.Clone();
+  EXPECT_TRUE(clone.is_valid());
+  EXPECT_EQ(kDupe, clone.driver_object().handle());
+  EXPECT_EQ(kSize, clone.size());
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _));
+  EXPECT_CALL(driver(), Close(kDupe, _, _));
+}
+
+TEST_F(DriverMemoryTest, Map) {
+  constexpr IpczDriverHandle kHandle = 424242;
+  constexpr IpczDriverHandle kMapping = 777;
+  constexpr size_t kSize = 64;
+
+  uint8_t data[64] = {0};
+  void* kMappingAddress = &data[0];
+
+  EXPECT_CALL(driver(), AllocateSharedMemory(kSize, _, _, _))
+      .WillOnce([&](size_t num_bytes, uint32_t, const void*,
+                    IpczDriverHandle* handle) {
+        *handle = kHandle;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  DriverMemory memory(node(), kSize);
+
+  EXPECT_CALL(driver(), MapSharedMemory(kHandle, _, _, _, _))
+      .WillOnce([&](IpczDriverHandle memory, uint32_t, const void*, void** addr,
+                    IpczDriverHandle* mapping) {
+        *addr = kMappingAddress;
+        *mapping = kMapping;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  DriverMemoryMapping mapping = memory.Map();
+  EXPECT_TRUE(mapping.is_valid());
+  EXPECT_EQ(kMappingAddress, mapping.address());
+  EXPECT_EQ(&data[0], mapping.bytes().data());
+  EXPECT_EQ(kSize, mapping.bytes().size());
+
+  EXPECT_CALL(driver(), Close(kMapping, _, _));
+  EXPECT_CALL(driver(), Close(kHandle, _, _));
+}
+
+}  // namespace
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/driver_object.cc b/third_party/ipcz/src/ipcz/driver_object.cc
new file mode 100644
index 0000000..63194bf
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_object.cc
@@ -0,0 +1,120 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_object.h"
+
+#include <cstdint>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "ipcz/driver_transport.h"
+#include "ipcz/ipcz.h"
+#include "ipcz/node.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "third_party/abseil-cpp/absl/container/inlined_vector.h"
+
+namespace ipcz {
+
+DriverObject::DriverObject() = default;
+
+DriverObject::DriverObject(Ref<Node> node, IpczDriverHandle handle)
+    : node_(std::move(node)), handle_(handle) {}
+
+DriverObject::DriverObject(DriverObject&& other)
+    : node_(std::move(other.node_)), handle_(other.handle_) {
+  other.handle_ = IPCZ_INVALID_DRIVER_HANDLE;
+}
+
+DriverObject& DriverObject::operator=(DriverObject&& other) {
+  reset();
+  node_ = std::move(other.node_);
+  handle_ = other.handle_;
+  other.handle_ = IPCZ_INVALID_DRIVER_HANDLE;
+  return *this;
+}
+
+DriverObject::~DriverObject() {
+  reset();
+}
+
+void DriverObject::reset() {
+  if (is_valid()) {
+    node_->driver().Close(handle_, IPCZ_NO_FLAGS, nullptr);
+    node_.reset();
+    handle_ = IPCZ_INVALID_DRIVER_HANDLE;
+  }
+}
+
+IpczDriverHandle DriverObject::release() {
+  IpczDriverHandle handle = handle_;
+  handle_ = IPCZ_INVALID_DRIVER_HANDLE;
+  node_.reset();
+  return handle;
+}
+
+bool DriverObject::IsSerializable() const {
+  if (!is_valid()) {
+    return false;
+  }
+
+  const IpczResult result = node_->driver().Serialize(
+      handle_, IPCZ_INVALID_DRIVER_HANDLE, IPCZ_NO_FLAGS, nullptr, nullptr,
+      nullptr, nullptr, nullptr);
+  return result == IPCZ_RESULT_ABORTED ||
+         result == IPCZ_RESULT_RESOURCE_EXHAUSTED;
+}
+
+bool DriverObject::CanTransmitOn(const DriverTransport& transport) const {
+  if (!is_valid()) {
+    return false;
+  }
+
+  const IpczResult result = node_->driver().Serialize(
+      handle_, transport.driver_object().handle(), IPCZ_NO_FLAGS, nullptr,
+      nullptr, nullptr, nullptr, nullptr);
+  return result == IPCZ_RESULT_RESOURCE_EXHAUSTED;
+}
+
+DriverObject::SerializedDimensions DriverObject::GetSerializedDimensions(
+    const DriverTransport& transport) const {
+  DriverObject::SerializedDimensions dimensions = {};
+  IpczResult result = node_->driver().Serialize(
+      handle_, transport.driver_object().handle(), IPCZ_NO_FLAGS, nullptr,
+      nullptr, &dimensions.num_bytes, nullptr, &dimensions.num_driver_handles);
+  ABSL_ASSERT(result == IPCZ_RESULT_RESOURCE_EXHAUSTED);
+  ABSL_ASSERT(dimensions.num_bytes > 0 || dimensions.num_driver_handles > 0);
+  return dimensions;
+}
+
+void DriverObject::Serialize(const DriverTransport& transport,
+                             absl::Span<uint8_t> data,
+                             absl::Span<IpczDriverHandle> handles) {
+  size_t num_bytes = data.size();
+  size_t num_handles = handles.size();
+  IpczResult result = node_->driver().Serialize(
+      handle_, transport.driver_object().handle(), IPCZ_NO_FLAGS, nullptr,
+      data.data(), &num_bytes, handles.data(), &num_handles);
+  ABSL_ASSERT(result == IPCZ_RESULT_OK);
+  release();
+}
+
+// static
+DriverObject DriverObject::Deserialize(
+    const DriverTransport& transport,
+    absl::Span<const uint8_t> data,
+    absl::Span<const IpczDriverHandle> handles) {
+  IpczDriverHandle handle;
+  const Ref<Node>& node = transport.driver_object().node();
+  IpczResult result = node->driver().Deserialize(
+      data.data(), data.size(), handles.data(), handles.size(),
+      transport.driver_object().handle(), IPCZ_NO_FLAGS, nullptr, &handle);
+  if (result != IPCZ_RESULT_OK) {
+    return DriverObject();
+  }
+
+  return DriverObject(node, handle);
+}
+
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/driver_object.h b/third_party/ipcz/src/ipcz/driver_object.h
new file mode 100644
index 0000000..d87409f
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_object.h
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_DRIVER_OBJECT_H_
+#define IPCZ_SRC_IPCZ_DRIVER_OBJECT_H_
+
+#include <cstdint>
+
+#include "ipcz/ipcz.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+
+class DriverTransport;
+class Node;
+
+// Owns an IpczDriverHandle and exposes a generic interface for serialization
+// and deserialization through the driver.
+class DriverObject {
+ public:
+  DriverObject();
+  DriverObject(Ref<Node> node, IpczDriverHandle handle);
+  DriverObject(DriverObject&&);
+  DriverObject& operator=(DriverObject&&);
+  ~DriverObject();
+
+  const Ref<Node>& node() const { return node_; }
+  IpczDriverHandle handle() const { return handle_; }
+
+  void reset();
+  IpczDriverHandle release();
+
+  bool is_valid() const { return handle_ != IPCZ_INVALID_DRIVER_HANDLE; }
+
+  // Indicates whether this DriverObject can be serialized into a collection of
+  // data and/or transmissible subobjects for transmission over a driver
+  // transport.
+  bool IsSerializable() const;
+
+  // Indicates whether this DriverObject is (either as-is, or after some
+  // serialization) transmissible over the identified transport.
+  bool CanTransmitOn(const DriverTransport& transport) const;
+
+  // Returns the data and transmissible handle capacity required to serialize
+  // this object for transmission over the identified transport. Must only be
+  // called on a valid object which is transmissible over that transport.
+  struct SerializedDimensions {
+    size_t num_bytes;
+    size_t num_driver_handles;
+  };
+  SerializedDimensions GetSerializedDimensions(
+      const DriverTransport& transport) const;
+
+  // Serializes this object into `data` and `handles` for imminent transmission
+  // over `transport`. Both input spans must be at least large enough to support
+  // the object's serialized dimensions. Handles placed into `handles` will be
+  // transmissible by the driver without further serialization. Must only be
+  // called on valid objects which are known to be serializable and
+  // transmissible over `transport`.
+  void Serialize(const DriverTransport& transport,
+                 absl::Span<uint8_t> data,
+                 absl::Span<IpczDriverHandle> handles);
+
+  // Attempts to deserialize a driver object from a series of bytes and
+  // transmissible driver objects produced by a prior call to Serialize() and
+  // received via `transport`. Returns a valid DriverObject on success, or an
+  // invalid DriverObject on failure.
+  static DriverObject Deserialize(const DriverTransport& transport,
+                                  absl::Span<const uint8_t> data,
+                                  absl::Span<const IpczDriverHandle> handles);
+
+ private:
+  Ref<Node> node_;
+  IpczDriverHandle handle_ = IPCZ_INVALID_DRIVER_HANDLE;
+};
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_DRIVER_OBJECT_H_
diff --git a/third_party/ipcz/src/ipcz/driver_object_test.cc b/third_party/ipcz/src/ipcz/driver_object_test.cc
new file mode 100644
index 0000000..6da736fd
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_object_test.cc
@@ -0,0 +1,190 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_object.h"
+
+#include "ipcz/driver_transport.h"
+#include "ipcz/node.h"
+#include "test/mock_driver.h"
+#include "test/test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+namespace {
+
+using testing::_;
+using testing::Return;
+
+class DriverObjectTest : public test::TestBase {
+ public:
+  DriverObjectTest() = default;
+  ~DriverObjectTest() override = default;
+
+  test::MockDriver& driver() { return driver_; }
+  const Ref<Node>& node() const { return node_; }
+
+ private:
+  ::testing::StrictMock<test::MockDriver> driver_;
+  const Ref<Node> node_{MakeRefCounted<Node>(Node::Type::kNormal,
+                                             test::kMockDriver,
+                                             IPCZ_INVALID_DRIVER_HANDLE)};
+};
+
+TEST_F(DriverObjectTest, Invalid) {
+  DriverObject object;
+  EXPECT_FALSE(object.is_valid());
+  EXPECT_EQ(nullptr, object.node().get());
+  EXPECT_EQ(IPCZ_INVALID_DRIVER_HANDLE, object.handle());
+  EXPECT_FALSE(object.IsSerializable());
+
+  DriverObject other = std::move(object);
+  EXPECT_FALSE(object.is_valid());
+  EXPECT_EQ(nullptr, other.node().get());
+  EXPECT_FALSE(other.is_valid());
+  EXPECT_FALSE(other.IsSerializable());
+}
+
+TEST_F(DriverObjectTest, Move) {
+  constexpr IpczDriverHandle kHandle = 42;
+  DriverObject object(node(), kHandle);
+
+  EXPECT_EQ(node(), object.node());
+  EXPECT_TRUE(object.is_valid());
+  EXPECT_EQ(kHandle, object.handle());
+
+  DriverObject other = std::move(object);
+  EXPECT_EQ(node(), other.node());
+  EXPECT_TRUE(other.is_valid());
+  EXPECT_EQ(kHandle, other.handle());
+  EXPECT_FALSE(object.is_valid());
+  EXPECT_EQ(nullptr, object.node().get());
+  EXPECT_EQ(IPCZ_INVALID_DRIVER_HANDLE, object.handle());
+
+  // Note that releasing prevents DriverObject from invoking the driver's
+  // Close() on destruction.
+  EXPECT_EQ(kHandle, other.release());
+}
+
+TEST_F(DriverObjectTest, Reset) {
+  // Both explicit reset and implicit reset (i.e. DriverObject destruction) must
+  // invoke Close().
+
+  constexpr IpczDriverHandle kHandle = 5;
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _))
+      .WillOnce(Return(IPCZ_RESULT_OK))
+      .RetiresOnSaturation();
+  { DriverObject object(node(), kHandle); }
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _))
+      .WillOnce(Return(IPCZ_RESULT_OK))
+      .RetiresOnSaturation();
+  {
+    DriverObject object(node(), kHandle);
+    object.reset();
+  }
+}
+
+TEST_F(DriverObjectTest, SerializableObject) {
+  constexpr IpczDriverHandle kHandle = 5;
+
+  DriverObject object(node(), kHandle);
+
+  EXPECT_CALL(driver(), Serialize(kHandle, _, _, _, _, _, _, _))
+      .WillOnce(Return(IPCZ_RESULT_ABORTED))
+      .RetiresOnSaturation();
+  EXPECT_TRUE(object.IsSerializable());
+
+  EXPECT_CALL(driver(), Serialize(kHandle, _, _, _, _, _, _, _))
+      .WillOnce(Return(IPCZ_RESULT_RESOURCE_EXHAUSTED))
+      .RetiresOnSaturation();
+  EXPECT_TRUE(object.IsSerializable());
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _)).WillOnce(Return(IPCZ_RESULT_OK));
+}
+
+TEST_F(DriverObjectTest, UnserializableObject) {
+  constexpr IpczDriverHandle kHandle = 5;
+
+  DriverObject object(node(), kHandle);
+
+  EXPECT_CALL(driver(), Serialize(kHandle, _, _, _, _, _, _, _))
+      .WillOnce(Return(IPCZ_RESULT_INVALID_ARGUMENT));
+  EXPECT_FALSE(object.IsSerializable());
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _)).WillOnce(Return(IPCZ_RESULT_OK));
+}
+
+TEST_F(DriverObjectTest, CanTransmit) {
+  constexpr IpczDriverHandle kTransport = 42;
+  constexpr IpczDriverHandle kHandle = 5;
+
+  auto transport =
+      MakeRefCounted<DriverTransport>(DriverObject(node(), kTransport));
+  DriverObject object(node(), kHandle);
+
+  EXPECT_CALL(driver(), Serialize(kHandle, kTransport, _, _, _, _, _, _))
+      .WillOnce(Return(IPCZ_RESULT_RESOURCE_EXHAUSTED));
+
+  EXPECT_TRUE(object.CanTransmitOn(*transport));
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _)).WillOnce(Return(IPCZ_RESULT_OK));
+  EXPECT_CALL(driver(), Close(kTransport, _, _))
+      .WillOnce(Return(IPCZ_RESULT_OK));
+}
+
+TEST_F(DriverObjectTest, CannotTransmit) {
+  constexpr IpczDriverHandle kTransport = 42;
+  constexpr IpczDriverHandle kHandle = 5;
+
+  auto transport =
+      MakeRefCounted<DriverTransport>(DriverObject(node(), kTransport));
+  DriverObject object(node(), kHandle);
+
+  EXPECT_CALL(driver(), Serialize(kHandle, kTransport, _, _, _, _, _, _))
+      .WillOnce(Return(IPCZ_RESULT_PERMISSION_DENIED));
+
+  EXPECT_FALSE(object.CanTransmitOn(*transport));
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _)).WillOnce(Return(IPCZ_RESULT_OK));
+  EXPECT_CALL(driver(), Close(kTransport, _, _))
+      .WillOnce(Return(IPCZ_RESULT_OK));
+}
+
+TEST_F(DriverObjectTest, GetSerializedDimensions) {
+  constexpr IpczDriverHandle kHandle = 5;
+  constexpr IpczDriverHandle kTransport = 42;
+
+  auto transport =
+      MakeRefCounted<DriverTransport>(DriverObject(node(), kTransport));
+  DriverObject object(node(), kHandle);
+
+  constexpr size_t kNumBytes = 3;
+  constexpr size_t kNumHandles = 7;
+  EXPECT_CALL(driver(), Serialize(kHandle, kTransport, _, _, _, _, _, _))
+      .WillOnce([&](IpczDriverHandle handle, IpczDriverHandle transport,
+                    uint32_t flags, const void* options, void* data,
+                    size_t* num_bytes, IpczDriverHandle* handles,
+                    size_t* num_handles) {
+        EXPECT_EQ(nullptr, data);
+        EXPECT_EQ(nullptr, handles);
+        *num_bytes = kNumBytes;
+        *num_handles = kNumHandles;
+        return IPCZ_RESULT_RESOURCE_EXHAUSTED;
+      })
+      .RetiresOnSaturation();
+
+  DriverObject::SerializedDimensions dimensions =
+      object.GetSerializedDimensions(*transport);
+  EXPECT_EQ(kNumBytes, dimensions.num_bytes);
+  EXPECT_EQ(kNumHandles, dimensions.num_driver_handles);
+
+  EXPECT_CALL(driver(), Close(kHandle, _, _)).WillOnce(Return(IPCZ_RESULT_OK));
+  EXPECT_CALL(driver(), Close(kTransport, _, _))
+      .WillOnce(Return(IPCZ_RESULT_OK));
+}
+
+}  // namespace
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/driver_transport.cc b/third_party/ipcz/src/ipcz/driver_transport.cc
new file mode 100644
index 0000000..719d78a3
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_transport.cc
@@ -0,0 +1,110 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_transport.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+
+#include "ipcz/ipcz.h"
+#include "ipcz/node.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+
+namespace {
+
+IpczResult IPCZ_API NotifyTransport(IpczHandle transport,
+                                    const void* data,
+                                    size_t num_bytes,
+                                    const IpczDriverHandle* driver_handles,
+                                    size_t num_driver_handles,
+                                    IpczTransportActivityFlags flags,
+                                    const void* options) {
+  DriverTransport* t = DriverTransport::FromHandle(transport);
+  if (!t) {
+    return IPCZ_RESULT_INVALID_ARGUMENT;
+  }
+
+  if (flags & IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED) {
+    const Ref<DriverTransport> doomed_transport =
+        DriverTransport::TakeFromHandle(transport);
+    return IPCZ_RESULT_OK;
+  }
+
+  if (flags & IPCZ_TRANSPORT_ACTIVITY_ERROR) {
+    t->NotifyError();
+    return IPCZ_RESULT_OK;
+  }
+
+  return t->Notify(DriverTransport::Message(
+      absl::MakeSpan(static_cast<const uint8_t*>(data), num_bytes),
+      absl::MakeSpan(driver_handles, num_driver_handles)));
+}
+
+}  // namespace
+
+DriverTransport::Message::Message(Data data) : data(data) {}
+
+DriverTransport::Message::Message(Data data,
+                                  absl::Span<const IpczDriverHandle> handles)
+    : data(data), handles(handles) {}
+
+DriverTransport::Message::Message(const Message&) = default;
+
+DriverTransport::Message& DriverTransport::Message::operator=(const Message&) =
+    default;
+
+DriverTransport::Message::~Message() = default;
+
+DriverTransport::DriverTransport(DriverObject transport)
+    : transport_(std::move(transport)) {}
+
+DriverTransport::~DriverTransport() = default;
+
+IpczDriverHandle DriverTransport::Release() {
+  return transport_.release();
+}
+
+IpczResult DriverTransport::Activate() {
+  // Acquire a self-reference, balanced in NotifyTransport() when the driver
+  // invokes its activity handler with IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED.
+  IpczHandle handle = ReleaseAsHandle(WrapRefCounted(this));
+  return transport_.node()->driver().ActivateTransport(
+      transport_.handle(), handle, NotifyTransport, IPCZ_NO_FLAGS, nullptr);
+}
+
+IpczResult DriverTransport::Deactivate() {
+  return transport_.node()->driver().DeactivateTransport(
+      transport_.handle(), IPCZ_NO_FLAGS, nullptr);
+}
+
+IpczResult DriverTransport::TransmitMessage(const Message& message) {
+  return transport_.node()->driver().Transmit(
+      transport_.handle(), message.data.data(), message.data.size(),
+      message.handles.data(), message.handles.size(), IPCZ_NO_FLAGS, nullptr);
+}
+
+IpczResult DriverTransport::Notify(const Message& message) {
+  ABSL_ASSERT(listener_);
+  return listener_->OnTransportMessage(message);
+}
+
+void DriverTransport::NotifyError() {
+  ABSL_ASSERT(listener_);
+  listener_->OnTransportError();
+}
+
+IpczResult DriverTransport::Close() {
+  // Applications should not close transport handles provided to the driver
+  // by ActivateTransport(). These handles are automatically closed on
+  // deactivation by ipcz, or when the driver signals an unrecoverable error via
+  // IPCZ_TRANSPORT_ACTIVITY_ERROR.
+  return IPCZ_RESULT_INVALID_ARGUMENT;
+}
+
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/driver_transport.h b/third_party/ipcz/src/ipcz/driver_transport.h
new file mode 100644
index 0000000..79e1600
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_transport.h
@@ -0,0 +1,131 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_DRIVER_TRANSPORT_H_
+#define IPCZ_SRC_IPCZ_DRIVER_TRANSPORT_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+#include <vector>
+
+#include "ipcz/api_object.h"
+#include "ipcz/driver_object.h"
+#include "ipcz/ipcz.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+
+class Node;
+
+// Encapsulates shared ownership of a transport endpoint created by an ipcz
+// driver. The driver calls into this object to notify ipcz of incoming messages
+// on the transport, and ipcz calls into this object to submit outgoing messages
+// for transmission by the driver.
+class DriverTransport
+    : public APIObjectImpl<DriverTransport, APIObject::kTransport> {
+ public:
+  using Pair = std::pair<Ref<DriverTransport>, Ref<DriverTransport>>;
+  using Data = absl::Span<const uint8_t>;
+
+  // A view into a transport message. Does not own the underlying data or
+  // handles.
+  struct Message {
+    explicit Message(Data data);
+    Message(Data data, absl::Span<const IpczDriverHandle> handles);
+    Message(const Message&);
+    Message& operator=(const Message&);
+    ~Message();
+
+    Data data;
+    absl::Span<const IpczDriverHandle> handles;
+  };
+
+  // A Listener to receive message and error events from the driver.
+  class Listener {
+   public:
+    virtual ~Listener() = default;
+
+    // Accepts a raw message from the transport. Note that this is called
+    // without *any* validation of the size or content of `message`.
+    virtual IpczResult OnTransportMessage(const Message& message) = 0;
+
+    // Indicates that some unrecoverable error has occurred with the transport.
+    virtual void OnTransportError() = 0;
+  };
+
+  // Constructs a new DriverTransport object over the driver-created transport
+  // handle in `transport`.
+  explicit DriverTransport(DriverObject transport);
+
+  // Set the object handling any incoming message or error notifications. This
+  // is only safe to set before Activate() is called, or from within one of the
+  // Listener methods when invoked by this DriverTransport (because invocations
+  // are mutually exclusive). `listener` must outlive this DriverTransport.
+  void set_listener(Listener* listener) { listener_ = listener; }
+
+  // Exposes the underlying driver handle for this transport.
+  const DriverObject& driver_object() const { return transport_; }
+
+  // Releases ownership of the underlying driver transport, returning it to the
+  // caller. After this call, the DriverTransport object is reset and
+  // `driver_object()` will return an invalid object.
+  DriverObject TakeDriverObject() {
+    ABSL_ASSERT(!listener_);
+    return std::move(transport_);
+  }
+
+  // Releases the driver handle so that it's no longer controlled by this
+  // DriverTranport.
+  IpczDriverHandle Release();
+
+  // Begins listening on the transport for incoming data and driver objects.
+  // Once this is called, the transport's Listener may be invoked by the driver
+  // at any time from arbitrary threads, as determined by the driver
+  // implementation itself. The driver will continue listening on this transport
+  // until Deactivate() is called or an unrecoverable error is encountered.
+  IpczResult Activate();
+
+  // Requests that the driver cease listening for incoming data and driver
+  // objects on this transport. Once a transport is deactivated, it can never be
+  // reactivated.
+  IpczResult Deactivate();
+
+  // Asks the driver to submit the data and driver objects in `message` for
+  // transmission from this transport endpoint to the opposite endpoint.
+  IpczResult TransmitMessage(const Message& message);
+
+  // Templated helper for transmitting macro-generated ipcz messages. This
+  // performs any necessary in-place serialization of driver objects before
+  // transmitting.
+  template <typename T>
+  IpczResult Transmit(T& message) {
+    if (!message.Serialize(*this)) {
+      return IPCZ_RESULT_INVALID_ARGUMENT;
+    }
+    return TransmitMessage(
+        Message(message.data_view(), message.transmissible_driver_handles()));
+  }
+
+  // Invoked by the driver any time this transport receives data and driver
+  // handles to be passed back into ipcz.
+  IpczResult Notify(const Message& message);
+  void NotifyError();
+
+  // APIObject:
+  IpczResult Close() override;
+
+ private:
+  ~DriverTransport() override;
+
+  DriverObject transport_;
+
+  Listener* listener_ = nullptr;
+};
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_DRIVER_TRANSPORT_H_
diff --git a/third_party/ipcz/src/ipcz/driver_transport_test.cc b/third_party/ipcz/src/ipcz/driver_transport_test.cc
new file mode 100644
index 0000000..76bac51
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/driver_transport_test.cc
@@ -0,0 +1,216 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/driver_transport.h"
+
+#include <cstdint>
+#include <functional>
+#include <string>
+#include <string_view>
+#include <utility>
+
+#include "ipcz/driver_memory.h"
+#include "ipcz/driver_memory_mapping.h"
+#include "ipcz/driver_object.h"
+#include "ipcz/node.h"
+#include "test/mock_driver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+namespace {
+
+using ::testing::_;
+using ::testing::Return;
+
+DriverTransport::Message MakeMessage(std::string_view s) {
+  return DriverTransport::Message(
+      absl::MakeSpan(reinterpret_cast<const uint8_t*>(s.data()), s.size()));
+}
+
+std::string_view MessageAsString(const DriverTransport::Message& message) {
+  return std::string_view(reinterpret_cast<const char*>(message.data.data()),
+                          message.data.size());
+}
+
+class DriverTransportTest : public testing::Test {
+ public:
+  DriverTransportTest() = default;
+  ~DriverTransportTest() override = default;
+
+  test::MockDriver& driver() { return driver_; }
+
+  std::pair<Ref<DriverTransport>, Ref<DriverTransport>> CreateTransportPair(
+      IpczDriverHandle transport0,
+      IpczDriverHandle transport1) {
+    return {MakeRefCounted<DriverTransport>(DriverObject(node_, transport0)),
+            MakeRefCounted<DriverTransport>(DriverObject(node_, transport1))};
+  }
+
+ private:
+  ::testing::StrictMock<test::MockDriver> driver_;
+  Ref<Node> node_{MakeRefCounted<Node>(Node::Type::kNormal,
+                                       test::kMockDriver,
+                                       IPCZ_INVALID_DRIVER_HANDLE)};
+};
+
+class TestListener : public DriverTransport::Listener {
+ public:
+  using MessageHandler =
+      std::function<IpczResult(const DriverTransport::Message&)>;
+  using ErrorHandler = std::function<void()>;
+
+  explicit TestListener(MessageHandler message_handler,
+                        ErrorHandler error_handler = nullptr)
+      : message_handler_(std::move(message_handler)),
+        error_handler_(std::move(error_handler)) {}
+  ~TestListener() override = default;
+
+  IpczResult OnTransportMessage(
+      const DriverTransport::Message& message) override {
+    return message_handler_(message);
+  }
+
+  void OnTransportError() override {
+    if (error_handler_) {
+      error_handler_();
+    }
+  }
+
+ private:
+  MessageHandler message_handler_;
+  ErrorHandler error_handler_;
+};
+
+TEST_F(DriverTransportTest, Activation) {
+  constexpr IpczDriverHandle kTransport0 = 5;
+  constexpr IpczDriverHandle kTransport1 = 42;
+  auto [a, b] = CreateTransportPair(kTransport0, kTransport1);
+
+  IpczHandle ipcz_transport = IPCZ_INVALID_HANDLE;
+  IpczTransportActivityHandler activity_handler = nullptr;
+  EXPECT_CALL(driver(), ActivateTransport(kTransport1, _, _, _, _))
+      .WillOnce([&](IpczDriverHandle driver_transport, IpczHandle transport,
+                    IpczTransportActivityHandler handler, uint32_t flags,
+                    const void* options) {
+        ipcz_transport = transport;
+        activity_handler = handler;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  // Verify that activation of a DriverTransport feeds the driver an activity
+  // handler and valid ipcz handle to use when notifying ipcz of incoming
+  // communications.
+  b->Activate();
+  EXPECT_NE(IPCZ_INVALID_HANDLE, ipcz_transport);
+  EXPECT_TRUE(activity_handler);
+
+  // And verify that the activity handler actually invokes the transport's
+  // Listener.
+
+  const std::string kTestMessage = "hihihihi";
+  bool received = false;
+  TestListener listener([&](const DriverTransport::Message& message) {
+    EXPECT_EQ(kTestMessage, MessageAsString(message));
+    received = true;
+    return IPCZ_RESULT_OK;
+  });
+  b->set_listener(&listener);
+
+  EXPECT_FALSE(received);
+  EXPECT_EQ(
+      IPCZ_RESULT_OK,
+      activity_handler(ipcz_transport, kTestMessage.data(), kTestMessage.size(),
+                       nullptr, 0, IPCZ_NO_FLAGS, nullptr));
+  EXPECT_TRUE(received);
+
+  // Normal shutdown involves ipcz calling Deactivate() on the DriverTransport.
+  // This should result in a call to DeactivateTransport() on the driver.
+
+  EXPECT_CALL(driver(), DeactivateTransport(kTransport1, _, _))
+      .WillOnce(Return(IPCZ_RESULT_OK));
+
+  EXPECT_CALL(driver(), Close(kTransport1, _, _));
+  EXPECT_CALL(driver(), Close(kTransport0, _, _));
+  b->Deactivate();
+
+  // The driver must also release its handle to ipcz' DriverTransport, which it
+  // does by an invocation of the activity handler like this. Without this, we'd
+  // be left with a dangling reference to the DriverTransport.
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            activity_handler(ipcz_transport, nullptr, 0, nullptr, 0,
+                             IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED, nullptr));
+}
+
+TEST_F(DriverTransportTest, Error) {
+  constexpr IpczDriverHandle kTransport0 = 5;
+  constexpr IpczDriverHandle kTransport1 = 42;
+  auto [a, b] = CreateTransportPair(kTransport0, kTransport1);
+
+  IpczHandle ipcz_transport = IPCZ_INVALID_HANDLE;
+  IpczTransportActivityHandler activity_handler = nullptr;
+  EXPECT_CALL(driver(), ActivateTransport(kTransport1, _, _, _, _))
+      .WillOnce([&](IpczDriverHandle driver_transport, IpczHandle transport,
+                    IpczTransportActivityHandler handler, uint32_t flags,
+                    const void* options) {
+        ipcz_transport = transport;
+        activity_handler = handler;
+        return IPCZ_RESULT_OK;
+      })
+      .RetiresOnSaturation();
+
+  b->Activate();
+
+  bool observed_error = false;
+  TestListener listener(
+      [&](const DriverTransport::Message& message) {
+        ABSL_ASSERT(false);
+        return IPCZ_RESULT_INVALID_ARGUMENT;
+      },
+      [&] { observed_error = true; });
+
+  b->set_listener(&listener);
+
+  // Verify that a driver invoking the activity handler with
+  // IPCZ_TRANSPORT_ACTIVITY_ERROR results in an error notification on the
+  // DriverTransport's Listener. This implies deactivation on the ipcz side, so
+  // no call to Deactivate() is necessary.
+
+  EXPECT_FALSE(observed_error);
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            activity_handler(ipcz_transport, nullptr, 0, nullptr, 0,
+                             IPCZ_TRANSPORT_ACTIVITY_ERROR, nullptr));
+  EXPECT_TRUE(observed_error);
+
+  // Even after signaling an error, the driver must also signal deactivation on
+  // its side, to release the DriverTransport handle it holds.
+  EXPECT_EQ(IPCZ_RESULT_OK,
+            activity_handler(ipcz_transport, nullptr, 0, nullptr, 0,
+                             IPCZ_TRANSPORT_ACTIVITY_DEACTIVATED, nullptr));
+
+  EXPECT_CALL(driver(), Close(kTransport1, _, _));
+  EXPECT_CALL(driver(), Close(kTransport0, _, _));
+}
+
+TEST_F(DriverTransportTest, Transmit) {
+  constexpr IpczDriverHandle kTransport0 = 5;
+  constexpr IpczDriverHandle kTransport1 = 42;
+  auto [a, b] = CreateTransportPair(kTransport0, kTransport1);
+
+  const std::string kTestMessage = "hihihihi";
+  EXPECT_CALL(driver(),
+              Transmit(kTransport0, kTestMessage.data(), kTestMessage.size(),
+                       nullptr, 0, IPCZ_NO_FLAGS, nullptr))
+      .WillOnce(Return(IPCZ_RESULT_OK));
+
+  a->TransmitMessage(MakeMessage(kTestMessage));
+
+  EXPECT_CALL(driver(), Close(kTransport1, _, _));
+  EXPECT_CALL(driver(), Close(kTransport0, _, _));
+}
+
+}  // namespace
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/message_internal.cc b/third_party/ipcz/src/ipcz/message_internal.cc
new file mode 100644
index 0000000..8619a5f
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_internal.cc
@@ -0,0 +1,359 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/message_internal.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <utility>
+
+#include "ipcz/driver_object.h"
+#include "ipcz/driver_transport.h"
+#include "ipcz/ipcz.h"
+#include "third_party/abseil-cpp/absl/container/inlined_vector.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+#include "util/safe_math.h"
+
+namespace ipcz::internal {
+
+namespace {
+
+// Helper to transform a driver object attached to `message` into its serialized
+// form within the message by running it through the driver's serializer.
+//
+// Metadata is placed into a DriverObjectData structure at `data_offset` bytes
+// from the begining of the message. Serialized data bytes are stored in an
+// array appended to `message` and referenced by the DriverObjectData, and any
+// transmissible handles emitted by the driver are appended to
+// `transmissible_handles`, with relevant index and count also stashed in the
+// DriverObjectData.
+IpczResult SerializeDriverObject(
+    uint32_t data_offset,
+    const DriverTransport& transport,
+    MessageBase& message,
+    absl::InlinedVector<IpczDriverHandle, 2>& transmissible_handles) {
+  DriverObjectData* data =
+      reinterpret_cast<DriverObjectData*>(&message.data_view()[data_offset]);
+  DriverObject object =
+      std::move(message.driver_objects()[data->first_driver_handle]);
+  if (!object.is_valid()) {
+    // This is not a valid driver handle and it cannot be serialized.
+    data->num_driver_handles = 0;
+    return IPCZ_RESULT_INVALID_ARGUMENT;
+  }
+
+  // NOTE: `data` may be invalid after the allocation below. It's nulled here to
+  // help catch accidental reuse.
+  data = nullptr;
+
+  uint32_t driver_data_array = 0;
+  DriverObject::SerializedDimensions dimensions =
+      object.GetSerializedDimensions(transport);
+  if (dimensions.num_bytes > 0) {
+    driver_data_array = message.AllocateArray<uint8_t>(dimensions.num_bytes);
+  }
+
+  const uint32_t first_handle =
+      static_cast<uint32_t>(transmissible_handles.size());
+  data = reinterpret_cast<DriverObjectData*>(&message.data_view()[data_offset]);
+  absl::Span<uint8_t> driver_data =
+      message.GetArrayView<uint8_t>(driver_data_array);
+  data->driver_data_array = driver_data_array;
+  data->num_driver_handles = dimensions.num_driver_handles;
+  data->first_driver_handle = first_handle;
+
+  transmissible_handles.resize(transmissible_handles.size() +
+                               dimensions.num_driver_handles);
+
+  auto handles_view = absl::MakeSpan(transmissible_handles);
+  object.Serialize(
+      transport, driver_data,
+      handles_view.subspan(first_handle, dimensions.num_driver_handles));
+  return IPCZ_RESULT_OK;
+}
+
+// Returns `true` if and only if it will be safe to use GetArrayView() to access
+// the contents of a serialized array beginning at `array_offset` bytes from
+// the start of `message`, where each element is `element_size` bytes wide.
+bool IsArrayValid(MessageBase& message,
+                  uint32_t array_offset,
+                  size_t element_size) {
+  if (array_offset == 0) {
+    return true;
+  }
+
+  const absl::Span<uint8_t> data = message.data_view();
+  if (array_offset >= data.size()) {
+    return false;
+  }
+
+  size_t bytes_available = data.size() - array_offset;
+  if (bytes_available < sizeof(ArrayHeader)) {
+    return false;
+  }
+
+  ArrayHeader& header = *reinterpret_cast<ArrayHeader*>(&data[array_offset]);
+  if (bytes_available < header.num_bytes ||
+      header.num_bytes < sizeof(ArrayHeader)) {
+    return false;
+  }
+
+  size_t max_num_elements =
+      (header.num_bytes - sizeof(ArrayHeader)) / element_size;
+  if (header.num_elements > max_num_elements) {
+    return false;
+  }
+
+  return true;
+}
+
+// Deserializes a driver object encoded within `message`, appending the object
+// for later retrieval via driver_objects() or TakeDriverObject().
+bool DeserializeDriverObject(MessageBase& message,
+                             DriverObjectData& object_data,
+                             absl::Span<const IpczDriverHandle> handles,
+                             const DriverTransport& transport) {
+  if (!IsArrayValid(message, object_data.driver_data_array, sizeof(uint8_t))) {
+    return false;
+  }
+
+  auto driver_data =
+      message.GetArrayView<uint8_t>(object_data.driver_data_array);
+  if (object_data.num_driver_handles > handles.size()) {
+    return false;
+  }
+
+  if (handles.size() - object_data.num_driver_handles <
+      object_data.first_driver_handle) {
+    return false;
+  }
+
+  DriverObject object = DriverObject::Deserialize(
+      transport, driver_data,
+      handles.subspan(object_data.first_driver_handle,
+                      object_data.num_driver_handles));
+  if (!object.is_valid()) {
+    return false;
+  }
+
+  message.AppendDriverObject(std::move(object), object_data);
+  return true;
+}
+
+}  // namespace
+
+MessageBase::MessageBase(uint8_t message_id, size_t params_size)
+    : data_(sizeof(MessageHeader) + params_size),
+      message_id_(message_id),
+      params_size_(params_size) {
+  MessageHeader& h = header();
+  h.size = sizeof(h);
+  h.version = 0;
+  h.message_id = message_id;
+}
+
+MessageBase::~MessageBase() = default;
+
+uint32_t MessageBase::AllocateGenericArray(size_t element_size,
+                                           size_t num_elements) {
+  if (num_elements == 0) {
+    return 0;
+  }
+  size_t offset = Align(data_.size());
+  size_t num_bytes = Align(
+      CheckAdd(sizeof(ArrayHeader), CheckMul(element_size, num_elements)));
+  data_.resize(CheckAdd(offset, num_bytes));
+  ArrayHeader& header = *reinterpret_cast<ArrayHeader*>(&data_[offset]);
+  header.num_bytes = checked_cast<uint32_t>(num_bytes);
+  header.num_elements = checked_cast<uint32_t>(num_elements);
+  return offset;
+}
+
+uint32_t MessageBase::AppendDriverObjects(absl::Span<DriverObject> objects) {
+  const uint32_t array_param = AllocateArray<DriverObjectData>(objects.size());
+  const absl::Span<DriverObjectData> object_data =
+      GetArrayView<DriverObjectData>(array_param);
+  for (size_t i = 0; i < objects.size(); ++i) {
+    AppendDriverObject(std::move(objects[i]), object_data[i]);
+  }
+  return array_param;
+}
+
+void MessageBase::AppendDriverObject(DriverObject object,
+                                     DriverObjectData& data) {
+  // This is only a placeholder used later by Serialize() to locate the
+  // serializable object within `driver_objects_`. Serialize() will then fill in
+  // this structure with more appropriate metadata pertaining to the object's
+  // serialized encoding.
+  data.driver_data_array = 0;
+  data.first_driver_handle = checked_cast<uint32_t>(driver_objects_.size());
+  data.num_driver_handles = 1;
+  driver_objects_.push_back(std::move(object));
+}
+
+DriverObject MessageBase::TakeDriverObject(const DriverObjectData& data) {
+  // When properly deserialized, every logical driver object field in a message
+  // should correspond to a single attached DriverObject. This is validated
+  // during deserialization, so these assertions are safe.
+  ABSL_ASSERT(data.num_driver_handles == 1);
+  ABSL_ASSERT(driver_objects_.size() > data.first_driver_handle);
+  return std::move(driver_objects_[data.first_driver_handle]);
+}
+
+bool MessageBase::CanTransmitOn(const DriverTransport& transport) {
+  for (DriverObject& object : driver_objects_) {
+    if (!object.CanTransmitOn(transport)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void MessageBase::Serialize(absl::Span<const ParamMetadata> params,
+                            const DriverTransport& transport) {
+  ABSL_ASSERT(CanTransmitOn(transport));
+  absl::InlinedVector<IpczDriverHandle, 2> transmissible_handles;
+  for (const auto& param : params) {
+    switch (param.type) {
+      case ParamType::kDriverObject: {
+        IpczResult result = SerializeDriverObject(
+            GetDataOffset(&GetParamValueAt<DriverObjectData>(param.offset)),
+            transport, *this, transmissible_handles);
+        ABSL_ASSERT(result == IPCZ_RESULT_OK);
+        break;
+      }
+
+      case ParamType::kDriverObjectArray: {
+        const uint32_t array_data_offset =
+            GetParamValueAt<uint32_t>(param.offset);
+        const size_t num_objects =
+            GetArrayView<DriverObjectData>(array_data_offset).size();
+        for (size_t i = 0; i < num_objects; ++i) {
+          // Note that the address of this array can move on each iteration, as
+          // SerializeDriverObject may need to reallocate the data buffer. Hence
+          // we resolve it from the array offset each time.
+          auto data = GetArrayView<DriverObjectData>(array_data_offset);
+          IpczResult result = SerializeDriverObject(
+              GetDataOffset(&data[i]), transport, *this, transmissible_handles);
+          ABSL_ASSERT(result == IPCZ_RESULT_OK);
+        }
+        break;
+      }
+
+      default:
+        // No additional work needed to serialize plain data or data array
+        // fields.
+        break;
+    }
+  }
+
+  // Basic consistency check: all driver objects must have been taken and
+  // serialized.
+  for (const auto& object : driver_objects_) {
+    ABSL_ASSERT(!object.is_valid());
+  }
+
+  transmissible_driver_handles_ = std::move(transmissible_handles);
+}
+
+bool MessageBase::DeserializeFromTransport(
+    size_t params_size,
+    uint32_t params_current_version,
+    absl::Span<const ParamMetadata> params_metadata,
+    absl::Span<const uint8_t> data,
+    absl::Span<const IpczDriverHandle> handles,
+    const DriverTransport& transport) {
+  // Copy the data into a local message object to avoid any TOCTOU issues in
+  // case `data` is in unsafe shared memory.
+  data_.resize(data.size());
+  memcpy(data_.data(), data.data(), data.size());
+
+  // Validate the header. The message must at least be large enough to encode a
+  // v0 MessageHeader, and the encoded header size and version must make sense
+  // (e.g. version 0 size must be sizeof(MessageHeader))
+  if (data_.size() < sizeof(MessageHeaderV0)) {
+    return false;
+  }
+
+  const auto& message_header =
+      *reinterpret_cast<const MessageHeaderV0*>(data_.data());
+  if (message_header.version == 0) {
+    if (message_header.size != sizeof(MessageHeaderV0)) {
+      return false;
+    }
+  } else {
+    if (message_header.size < sizeof(MessageHeaderV0)) {
+      return false;
+    }
+  }
+
+  if (message_header.size > data_.size()) {
+    return false;
+  }
+
+  // Validate parameter data. There must be at least enough bytes following the
+  // header to encode a StructHeader and to account for all parameter data.
+
+  absl::Span<uint8_t> params_data = params_data_view();
+  if (params_data.size() < sizeof(StructHeader)) {
+    return false;
+  }
+
+  StructHeader& params_header =
+      *reinterpret_cast<StructHeader*>(params_data.data());
+  if (params_current_version < params_header.version) {
+    params_header.version = params_current_version;
+  }
+
+  // The param struct's header claims to consist of more data than is present in
+  // the message. Not good.
+  if (params_data.size() < params_header.size) {
+    return false;
+  }
+
+  // Finally, validate each parameter and unpack driver objects.
+  for (const ParamMetadata& param : params_metadata) {
+    if (param.offset >= params_header.size ||
+        param.offset + param.size > params_header.size) {
+      return false;
+    }
+
+    if (param.array_element_size > 0) {
+      const uint32_t array_offset =
+          *reinterpret_cast<uint32_t*>(&params_data[param.offset]);
+      if (!IsArrayValid(*this, array_offset, param.array_element_size)) {
+        return false;
+      }
+    }
+
+    switch (param.type) {
+      case ParamType::kDriverObject:
+        if (!DeserializeDriverObject(
+                *this, GetParamValueAt<DriverObjectData>(param.offset), handles,
+                transport)) {
+          return false;
+        }
+        break;
+
+      case ParamType::kDriverObjectArray: {
+        auto objects = GetArrayView<DriverObjectData>(
+            GetParamValueAt<uint32_t>(param.offset));
+        for (DriverObjectData& object : objects) {
+          if (!DeserializeDriverObject(*this, object, handles, transport)) {
+            return false;
+          }
+        }
+        break;
+      }
+
+      default:
+        break;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace ipcz::internal
diff --git a/third_party/ipcz/src/ipcz/message_internal.h b/third_party/ipcz/src/ipcz/message_internal.h
new file mode 100644
index 0000000..7132cde2
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_internal.h
@@ -0,0 +1,398 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_MESSAGE_INTERNAL_H_
+#define IPCZ_SRC_IPCZ_MESSAGE_INTERNAL_H_
+
+#include <cstdint>
+
+#include "ipcz/driver_memory.h"
+#include "ipcz/driver_object.h"
+#include "ipcz/ipcz.h"
+#include "ipcz/sequence_number.h"
+#include "third_party/abseil-cpp/absl/container/inlined_vector.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/abseil-cpp/absl/types/span.h"
+#include "util/ref_counted.h"
+#include "util/safe_math.h"
+
+namespace ipcz {
+
+class DriverTransport;
+class Node;
+
+namespace internal {
+
+// All wire structures defined in this header should be declared between this
+// line and the corresponding #pragma pack(pop). Fields in these structures
+// should manually aligned with explicit padding fields as needed.
+#pragma pack(push, 1)
+
+// Header which begins all messages. The header layout is versioned for
+// extensibility and long-term support.
+struct IPCZ_ALIGN(8) MessageHeader {
+  // The size of the header in bytes.
+  uint8_t size;
+
+  // The header version in use by this message.
+  uint8_t version;
+
+  // Message ID assigned as part of a message's type definition via
+  // IPCZ_MSG_BEGIN().
+  uint8_t message_id;
+
+  // Reserved for future use. Must be zero.
+  uint8_t reserved[5];
+
+  // Used for sequencing messages along a NodeLink to preserve end-to-end
+  // ordering, as NodeLink messages may be transmitted either across a driver
+  // transport or queues in shared memory.
+  SequenceNumber sequence_number;
+};
+static_assert(sizeof(MessageHeader) == 16, "Unexpected size");
+
+using MessageHeaderV0 = MessageHeader;
+using LatestMessageHeaderVersion = MessageHeaderV0;
+
+// Header encoding metadata about a structure within a message.
+struct IPCZ_ALIGN(8) StructHeader {
+  // The size of the structure in bytes.
+  uint32_t size;
+
+  // The version number of the structure, which may be used to differentiate
+  // between different versions of the same encoded size.
+  uint32_t version;
+};
+static_assert(sizeof(StructHeader) == 8, "Unexpected size");
+
+// Header encoding metadata about any array within a message.
+struct IPCZ_ALIGN(8) ArrayHeader {
+  // The total number of bytes occupied by the array, including this header and
+  // any padding for 8-byte alignment.
+  uint32_t num_bytes;
+
+  // The number of elements encoded for the array. Elements are packed into the
+  // message immediately following this header.
+  uint32_t num_elements;
+};
+
+// Each serialized driver object is represented as an array of bytes and an
+// array of driver handles. This structure describes both arrays for a single
+// driver object. For every object attached to a message, there one of these
+// structs.
+struct IPCZ_ALIGN(8) DriverObjectData {
+  // Array index of the byte array which contains serialized data for this
+  // driver object. This is specifically the byte index into the enclosing
+  // message where the array's ArrayHeader can be found.
+  uint32_t driver_data_array;
+
+  // Every message carries a single unified array of attached driver handles.
+  // This is index of the first driver handle relevant to a specific driver
+  // object attachment.
+  uint16_t first_driver_handle;
+
+  // The number of driver handles belonging to this driver object, starting at
+  // the index of `first_driver_handle` within the message's driver handle
+  // array.
+  uint16_t num_driver_handles;
+};
+
+// End of wire structure definitions. Anything below this line is not meant to
+// be encoded into messages.
+#pragma pack(pop)
+
+// Message macros emit metadata structures which are used at runtime to help
+// validate an encoded message during deserialization. This conveys the kind of
+// each field within the message's parameter structure.
+enum class ParamType {
+  // A parameter encoded inline within the message's primary parameter struct.
+  kData,
+
+  // A parameter encoded as a 32-bit index elsewhere in the message. This index
+  // points to encoded array contents, beginning with an ArrayHeader.
+  kDataArray,
+
+  // A parameter encoded as a single DriverObjectData structure, referring to
+  // a single driver object attached to the message.
+  kDriverObject,
+
+  // A parameter encoded as a 32-bit index to an array elsewhere in the message.
+  // The array contains zero or more DriverObjectData structures, and the
+  // message parameter corresponds to a collection of driver objects attached to
+  // the message.
+  kDriverObjectArray,
+};
+
+// Metadata about a single parameter declared within a message via one of the
+// IPCZ_MSG_PARAM* macros.
+struct ParamMetadata {
+  // The offset of this parameter from the start of the macro-generated
+  // parameters structure, including the StructHeader itself.
+  size_t offset;
+
+  // The size of the data expected at `offset` in order for the field to be
+  // deserializable.
+  size_t size;
+
+  // If this is an array-typed field, this is the encoded size of each array
+  // element expected.
+  size_t array_element_size;
+
+  // The generic type of this parameter. See ParamType above.
+  ParamType type;
+};
+
+// Base class for all ipcz-internal wire messages to be transmitted across a
+// NodeLink. This provides helpers for appending and extracting dynamic message
+// contents in addition to the base parameter structure for a given message.
+// This should not be used directly, but should instead be used via a specific
+// instance of the derived Message<T> helper below.
+class IPCZ_ALIGN(8) MessageBase {
+ public:
+  MessageBase(uint8_t message_id, size_t params_size);
+  ~MessageBase();
+
+  MessageHeader& header() {
+    return *reinterpret_cast<MessageHeader*>(data_.data());
+  }
+
+  const MessageHeader& header() const {
+    return *reinterpret_cast<const MessageHeader*>(data_.data());
+  }
+
+  absl::Span<uint8_t> data_view() { return absl::MakeSpan(data_); }
+  absl::Span<uint8_t> params_data_view() {
+    return absl::MakeSpan(&data_[header().size], data_.size() - header().size);
+  }
+  absl::Span<DriverObject> driver_objects() {
+    return absl::MakeSpan(driver_objects_);
+  }
+  absl::Span<IpczDriverHandle> transmissible_driver_handles() {
+    return absl::MakeSpan(transmissible_driver_handles_);
+  }
+
+  // Allocates additional storage in this message to hold an array of
+  // `num_elements`, each with a size of `element_size` bytes. The allocated
+  // storage includes space for an ArrayHeader and padding to an 8-byte
+  // boundary. Returns the offset into the message payload where the ArrayHeader
+  // begins. Storage for each element follows contiguously from there.
+  uint32_t AllocateGenericArray(size_t element_size, size_t num_elements);
+
+  // Simple template helper for AllocateGenericArray, using the size of the
+  // type argument as the element size.
+  template <typename ElementType>
+  uint32_t AllocateArray(size_t num_elements) {
+    return AllocateGenericArray(sizeof(ElementType), num_elements);
+  }
+
+  // Allocates additional storage in this message for an array of driver
+  // objects, with each consisting of some number of bytes and driver handles.
+  // Each driver object is described in the message by a DriverObjectData
+  // structure, and this allocates an array of those structures. Similar to
+  // AllocateGenericArray, this returns the index of that array's header within
+  // the message.
+  //
+  // The objects in `objects` are stashed in this message and will not be fully
+  // encoded until Serialize() is called.
+  uint32_t AppendDriverObjects(absl::Span<DriverObject> objects);
+
+  // Appends storage for a single driver object and stores it within this
+  // message. `data` is updated to track the index of the attached object within
+  // `driver_objects_`. This does not serialize `object` yet.
+  //
+  // When Serialize() is called on the message, any attached objects will be
+  // serialized at that time, and any encoded DriverObjectData structures will
+  // be updated to reflect details of the serialized object encoding.
+  void AppendDriverObject(DriverObject object, DriverObjectData& data);
+
+  // Takes ownership of a DriverObject that was attached to this message, given
+  // an encoded DriverObjectData struct. This is only to be used on deserialized
+  // messages.
+  DriverObject TakeDriverObject(const DriverObjectData& data);
+
+  // Returns the address of the first element of an array whose header begins
+  // at `offset` bytes from the beginning of this message.
+  void* GetArrayData(size_t offset) {
+    // NOTE: Any offset plugged into this method must be validated ahead of
+    // time.
+    ABSL_ASSERT(CheckAdd(offset, sizeof(ArrayHeader)) <= data_.size());
+    ArrayHeader& header = *reinterpret_cast<ArrayHeader*>(&data_[offset]);
+    return &header + 1;
+  }
+
+  // Template helper which returns a view into a serialized array's contents,
+  // given an array whose header begins at `offset` bytes from the beginning of
+  // this message. If `offset` is zero, this returns an empty span.
+  template <typename ElementType>
+  absl::Span<ElementType> GetArrayView(size_t offset) {
+    if (!offset) {
+      return {};
+    }
+
+    // NOTE: Any offset plugged into this method must be validated ahead of
+    // time.
+    ABSL_ASSERT(CheckAdd(offset, sizeof(ArrayHeader)) <= data_.size());
+    ArrayHeader& header = *reinterpret_cast<ArrayHeader*>(&data_[offset]);
+
+    // The ArrayHeader itself must also have been validated already to ensure
+    // that the span of array contents will not exceed the bounds of `data_`.
+    ABSL_ASSERT(CheckAdd(CheckMul(sizeof(ElementType),
+                                  static_cast<size_t>(header.num_elements)),
+                         sizeof(ArrayHeader)) <= data_.size());
+    return absl::MakeSpan(reinterpret_cast<ElementType*>(&header + 1),
+                          header.num_elements);
+  }
+
+  // Helper to retrieve a typed value from the message given an absolute byte
+  // offset from the start of the message.
+  template <typename T>
+  T& GetValueAt(size_t data_offset) {
+    // NOTE: Any offset plugged into this method must be validated ahead of
+    // time.
+    ABSL_ASSERT(CheckAdd(data_offset, sizeof(T)) <= data_.size());
+    return *reinterpret_cast<T*>(&data_[data_offset]);
+  }
+
+  // Helper to retrieve a typed value from the message given a byte offset from
+  // the start of the message's parameter data. Note the distinction between
+  // this and GetValueAt(), as this offset is taken from the end of the message
+  // header, while GetValueAt() (and most other offset-diven methods here)
+  // interprets the offset as relative to the beginning of the message itself.
+  //
+  // This method is used in conjunection with parameter metadata generated by
+  // macros at compile-time.
+  template <typename T>
+  T& GetParamValueAt(size_t param_offset) {
+    // NOTE: Any offset plugged into this method must be validated ahead of
+    // time.
+    ABSL_ASSERT(CheckAdd(param_offset, sizeof(T)) <= params_data_view().size());
+    return GetValueAt<T>(GetDataOffset(&params_data_view()[param_offset]));
+  }
+
+  // Checks and indicates whether this message can be transmitted over
+  // `transport`, which depends on whether the driver is able to transmit all of
+  // the attached driver objects over that transport.
+  bool CanTransmitOn(const DriverTransport& transport);
+
+  // Attempts to finalize a message for transit over `transport`, potentially
+  // mutating the message data in-place. Returns true iff sucessful.
+  //
+  // NOTE: It is invalid to call this on a message for which
+  // `CanTransmitOn(transport)` does not return true and doing so results in
+  // unspecified behavior.
+  void Serialize(absl::Span<const ParamMetadata> params,
+                 const DriverTransport& transport);
+
+ protected:
+  // Returns `x` aligned above to the nearest 8-byte boundary.
+  constexpr size_t Align(size_t x) { return (x + 7) & ~7; }
+
+  // Returns the relative offset of an address which falls within the message's
+  // data payload.
+  uint32_t GetDataOffset(const void* data) {
+    return static_cast<uint32_t>(static_cast<const uint8_t*>(data) -
+                                 data_.data());
+  }
+
+  // Attempts to deserialize a message from raw `data` and `handles` into `this`
+  // message object, given the `params_size`, `params_current_version` and
+  // `params_metadata`, which are all generated from message macros at build
+  // time to describe a specific ipcz-internal message.
+  //
+  // `transport` is the transport from which the incoming data and handles were
+  // received.
+  bool DeserializeFromTransport(size_t params_size,
+                                uint32_t params_current_version,
+                                absl::Span<const ParamMetadata> params_metadata,
+                                absl::Span<const uint8_t> data,
+                                absl::Span<const IpczDriverHandle> handles,
+                                const DriverTransport& transport);
+
+  // Raw serialized data for this message. This always begins with MessageHeader
+  // (or potentially some newer or older version thereof), whose actual size
+  // is determined by the header's `size` field. After that many bytes, a
+  // parameters structure immediately follows, as generated by an invocation of
+  // IPCZ_MSG_BEGIN()/IPCZ_MSG_END(). After fixed parameters, any number of
+  // dynamicaly inlined allocations may follow (e.g. for array contents,
+  // driver objects, etc.)
+  absl::InlinedVector<uint8_t, 128> data_;
+
+  // Collection of DriverObjects attached to this message. These are attached
+  // while building a message (e.g. by calling AppendDriverObject), and they are
+  // consumed by Serialize() to encode the objects for transmission. Serialized
+  // objects are represented by some combination of data within the message,
+  // and zero or more transmissible driver handles which accumulate in
+  // `transmissible_driver_handles_` during serialization.
+  //
+  // On deserialization, driver object data and transmissible handles are fed
+  // back to the driver and used to reconstruct this list. Deserialized objects
+  // may be extracted from the message by calling TakeDriverObject().
+  //
+  // Since each driver object may serialize to any number of bytes and
+  // transmissible handles, there is generally NOT a 1:1 correpsondence between
+  // this list and `transmissible_driver_handles_`.
+  absl::InlinedVector<DriverObject, 2> driver_objects_;
+
+  // Collection of driver handles which the driver knows how to transmit as-is,
+  // in conjunction with (but out-of-band from) the payload in `data_`. This
+  // is populated by Serialize() if the driver emits any transmissible handles
+  // as outputs when serializing any of the objects in `driver_objects_`.
+  //
+  // On deserialization this set of handles is consumed; and in combination with
+  // encoded object data, is used by the driver to reconstruct
+  // `driver_objects_`.
+  //
+  // Since each driver object may serialize to any number of bytes and
+  // transmissible handles, there is generally NOT a 1:1 correpsondence between
+  // this list and `driver_objects_`.
+  absl::InlinedVector<IpczDriverHandle, 2> transmissible_driver_handles_;
+
+  // Basic constant attributes of this message, as constructed or deserialized.
+  const uint8_t message_id_;
+  const uint32_t params_size_;
+};
+
+// Template helper to wrap the MessageBase type for a specific macro-generated
+// parameter structure. This primarily exists for safe, convenient construction
+// of message payloads with correct header information and no leaky padding
+// bits, as well as for convenient access to parameters within size-validated,
+// deserialized messages.
+//
+// When an IPCZ_MSG_BEGIN() macro is used to declare a new Foo message, it will
+// emit both a msg::Foo_Params structure for the fixed wire data of the message
+// parameters, as well as a msg::Foo which is an alias for an instance of this
+// template, namely Message<msg::Foo_Params>.
+template <typename ParamDataType>
+class Message : public MessageBase {
+ public:
+  Message() : MessageBase(ParamDataType::kId, sizeof(ParamDataType)) {
+    ParamDataType& p = *(new (&params()) ParamDataType());
+    p.header.size = sizeof(p);
+    p.header.version = ParamDataType::kVersion;
+  }
+
+  ~Message() = default;
+
+  // Convenient accessors for the message's main parameters struct, whose
+  // location depends on the size of the header. Note that because this may be
+  // used to access parameters within messages using a newer or older header
+  // than what's defined above in MessageHeader, we index based on the header's
+  // encoded size rather than the compile-time size of MessageHeader.
+  //
+  // If this Message was deserialized from the wire, it must already have been
+  // validated to have an enough space for `header().size` bytes plus the size
+  // if ParamDataType.
+  ParamDataType& params() {
+    return *reinterpret_cast<ParamDataType*>(&data_[header().size]);
+  }
+
+  const ParamDataType& params() const {
+    return *reinterpret_cast<const ParamDataType*>(&data_[header().size]);
+  }
+};
+
+}  // namespace internal
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_IPCZ_MESSAGE_INTERNAL_H_
diff --git a/third_party/ipcz/src/ipcz/message_internal_test.cc b/third_party/ipcz/src/ipcz/message_internal_test.cc
new file mode 100644
index 0000000..834578e
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_internal_test.cc
@@ -0,0 +1,327 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/message_internal.h"
+
+#include <cstdint>
+#include <queue>
+#include <utility>
+#include <vector>
+
+#include "ipcz/driver_object.h"
+#include "ipcz/driver_transport.h"
+#include "ipcz/ipcz.h"
+#include "ipcz/node.h"
+#include "ipcz/test_messages.h"
+#include "test/mock_driver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "util/ref_counted.h"
+
+namespace ipcz {
+namespace {
+
+using testing::_;
+
+constexpr IpczDriverHandle kTransportHandle = 42;
+
+// Structure used to temporarily store messages transmitted through a transport,
+// so that tests can inspect and/or deserialize them later.
+struct ReceivedMessage {
+  std::vector<uint8_t> data;
+  std::vector<IpczDriverHandle> handles;
+
+  DriverTransport::Message AsTransportMessage() {
+    return DriverTransport::Message(absl::MakeSpan(data),
+                                    absl::MakeSpan(handles));
+  }
+};
+
+class MessageInternalTest : public testing::Test {
+ public:
+  MessageInternalTest() {
+    // All serialized messages transmitted through `transport()` will be
+    // captured directly in `received_messages_`.
+    EXPECT_CALL(driver(), Transmit(kTransportHandle, _, _, _, _, _, _))
+        .WillRepeatedly([&](IpczDriverHandle driver_transport, const void* data,
+                            size_t num_bytes, const IpczDriverHandle* handles,
+                            size_t num_handles, uint32_t, const void*) {
+          const uint8_t* bytes = static_cast<const uint8_t*>(data);
+          received_messages_.push(
+              {{bytes, bytes + num_bytes}, {handles, handles + num_handles}});
+          return IPCZ_RESULT_OK;
+        });
+
+    // For convenient automation when exercising DriverObject transmission, all
+    // driver handles in these tests are treated as 32-bit values. Their
+    // "serialized" form is the same value decomposed: the high 16-bits are the
+    // serialized data bytes, and the low 16-bits are treated as a new
+    // transmissible driver handle.
+    EXPECT_CALL(driver(), Serialize(_, kTransportHandle, _, _, _, _, _, _))
+        .WillRepeatedly([&](IpczDriverHandle handle, IpczDriverHandle transport,
+                            uint32_t, const void*, void* data,
+                            size_t* num_bytes, IpczDriverHandle* handles,
+                            size_t* num_handles) {
+          const size_t data_capacity = num_bytes ? *num_bytes : 0;
+          const size_t handle_capacity = num_handles ? *num_handles : 0;
+          if (num_bytes) {
+            *num_bytes = 2;
+          }
+          if (num_handles) {
+            *num_handles = 1;
+          }
+          if (!data || !handles || data_capacity < 2 || handle_capacity < 1) {
+            return IPCZ_RESULT_RESOURCE_EXHAUSTED;
+          }
+          static_cast<uint16_t*>(data)[0] = static_cast<uint16_t>(handle >> 16);
+          handles[0] = handle & 0xffff;
+          return IPCZ_RESULT_OK;
+        });
+
+    // "Deserialization" reverses the process above: 2 data bytes are expected
+    // and 1 transmissible handle is expected, and these are combined into a
+    // single new driver handle value to represent the deserialized object.
+    EXPECT_CALL(driver(), Deserialize(_, _, _, _, kTransportHandle, _, _, _))
+        .WillRepeatedly([&](const void* data, size_t num_bytes,
+                            const IpczDriverHandle* handles, size_t num_handles,
+                            IpczDriverHandle transport, uint32_t, const void*,
+                            IpczDriverHandle* handle) {
+          if (reject_driver_objects_) {
+            return IPCZ_RESULT_INVALID_ARGUMENT;
+          }
+
+          ABSL_ASSERT(num_bytes == 2);
+          ABSL_ASSERT(num_handles == 1);
+          const uint16_t data_value = static_cast<const uint16_t*>(data)[0];
+          *handle =
+              (static_cast<IpczDriverHandle>(data_value) << 16) | handles[0];
+          return IPCZ_RESULT_OK;
+        });
+  }
+
+  ~MessageInternalTest() override {
+    EXPECT_CALL(driver_, Close(kTransportHandle, _, _));
+  }
+
+  test::MockDriver& driver() { return driver_; }
+  const Ref<Node>& node() const { return node_; }
+  DriverTransport& transport() { return *transport_; }
+
+  void set_reject_driver_objects(bool reject) {
+    reject_driver_objects_ = reject;
+  }
+
+  size_t GetReceivedMessageCount() const { return received_messages_.size(); }
+
+  ReceivedMessage TakeNextReceivedMessage() {
+    ABSL_ASSERT(!received_messages_.empty());
+    ReceivedMessage message = std::move(received_messages_.front());
+    received_messages_.pop();
+    return message;
+  }
+
+ private:
+  ::testing::StrictMock<test::MockDriver> driver_;
+  const Ref<Node> node_{MakeRefCounted<Node>(Node::Type::kNormal,
+                                             test::kMockDriver,
+                                             IPCZ_INVALID_DRIVER_HANDLE)};
+  const Ref<DriverTransport> transport_{
+      MakeRefCounted<DriverTransport>(DriverObject(node_, kTransportHandle))};
+  std::queue<ReceivedMessage> received_messages_;
+  bool reject_driver_objects_ = false;
+};
+
+TEST_F(MessageInternalTest, BasicMessage) {
+  test::msg::BasicTestMessage in;
+  EXPECT_GE(sizeof(internal::MessageHeaderV0), in.header().size);
+  EXPECT_EQ(0u, in.header().version);
+  EXPECT_EQ(test::msg::BasicTestMessage::kId, in.header().message_id);
+  EXPECT_EQ(0u, in.header().reserved[0]);
+  EXPECT_EQ(0u, in.header().reserved[1]);
+  EXPECT_EQ(0u, in.header().reserved[2]);
+  EXPECT_EQ(0u, in.header().reserved[3]);
+  EXPECT_EQ(0u, in.header().reserved[4]);
+  EXPECT_EQ(SequenceNumber(0), in.header().sequence_number);
+  EXPECT_EQ(0u, in.header().size % 8u);
+  EXPECT_EQ(0u, in.params().foo);
+  EXPECT_EQ(0u, in.params().bar);
+  in.params().foo = 5;
+  in.params().bar = 7;
+
+  EXPECT_EQ(0u, GetReceivedMessageCount());
+  transport().Transmit(in);
+  EXPECT_EQ(1u, GetReceivedMessageCount());
+
+  test::msg::BasicTestMessage out;
+  ReceivedMessage serialized = TakeNextReceivedMessage();
+  EXPECT_TRUE(out.Deserialize(serialized.AsTransportMessage(), transport()));
+  EXPECT_EQ(5u, out.params().foo);
+  EXPECT_EQ(7u, out.params().bar);
+}
+
+TEST_F(MessageInternalTest, DataArray) {
+  test::msg::MessageWithDataArray in;
+  in.params().values = in.AllocateArray<uint64_t>(3);
+
+  absl::Span<uint64_t> values = in.GetArrayView<uint64_t>(in.params().values);
+  values[0] = 11;
+  values[1] = 13;
+  values[2] = 17;
+
+  transport().Transmit(in);
+
+  test::msg::MessageWithDataArray out;
+  ReceivedMessage serialized = TakeNextReceivedMessage();
+  EXPECT_TRUE(out.Deserialize(serialized.AsTransportMessage(), transport()));
+
+  values = out.GetArrayView<uint64_t>(out.params().values);
+  ASSERT_EQ(3u, values.size());
+  EXPECT_EQ(11u, values[0]);
+  EXPECT_EQ(13u, values[1]);
+  EXPECT_EQ(17u, values[2]);
+}
+
+TEST_F(MessageInternalTest, DriverObject) {
+  constexpr IpczDriverHandle kObjectHandle = 0x12345678;
+
+  test::msg::MessageWithDriverObject in;
+  in.AppendDriverObject(DriverObject(node(), kObjectHandle),
+                        in.params().object);
+
+  transport().Transmit(in);
+
+  test::msg::MessageWithDriverObject out;
+  ReceivedMessage serialized = TakeNextReceivedMessage();
+  EXPECT_TRUE(out.Deserialize(serialized.AsTransportMessage(), transport()));
+
+  DriverObject object = out.TakeDriverObject(out.params().object);
+  EXPECT_EQ(kObjectHandle, object.release());
+}
+
+TEST_F(MessageInternalTest, DriverObjectArray) {
+  constexpr IpczDriverHandle kObjectHandles[] = {0x12345678, 0x5a5aa5a5,
+                                                 0x42425555};
+  DriverObject in_objects[std::size(kObjectHandles)];
+  for (size_t i = 0; i < std::size(kObjectHandles); ++i) {
+    in_objects[i] = DriverObject(node(), kObjectHandles[i]);
+  }
+
+  test::msg::MessageWithDriverObjectArray in;
+  in.params().objects = in.AppendDriverObjects(in_objects);
+
+  transport().Transmit(in);
+
+  test::msg::MessageWithDriverObjectArray out;
+  ReceivedMessage serialized = TakeNextReceivedMessage();
+  EXPECT_TRUE(out.Deserialize(serialized.AsTransportMessage(), transport()));
+
+  auto objects_data =
+      out.GetArrayView<internal::DriverObjectData>(out.params().objects);
+  EXPECT_EQ(3u, objects_data.size());
+  for (size_t i = 0; i < objects_data.size(); ++i) {
+    EXPECT_EQ(kObjectHandles[i],
+              out.TakeDriverObject(objects_data[i]).release());
+  }
+}
+
+TEST_F(MessageInternalTest, ShortMessage) {
+  test::msg::BasicTestMessage m;
+  EXPECT_FALSE(m.Deserialize(
+      DriverTransport::Message(m.data_view().subspan(0, 4)), transport()));
+}
+
+TEST_F(MessageInternalTest, ShortHeader) {
+  test::msg::BasicTestMessage m;
+  m.header().size = sizeof(internal::MessageHeaderV0) - 1;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, HeaderOverflow) {
+  test::msg::BasicTestMessage m;
+  m.header().size = 255;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, ShortParamsHeader) {
+  test::msg::BasicTestMessage m;
+  EXPECT_FALSE(m.Deserialize(DriverTransport::Message(m.data_view().subspan(
+                                 0, sizeof(internal::MessageHeader) + 1)),
+                             transport()));
+}
+
+TEST_F(MessageInternalTest, ShortPrams) {
+  test::msg::BasicTestMessage m;
+  m.params().header.size = 1;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, ParamsOverflow) {
+  test::msg::BasicTestMessage m;
+  m.params().header.size = 100000;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, ArrayOffsetOverflow) {
+  test::msg::MessageWithDataArray m;
+  m.params().values = 10000000;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, ArraySizeOverflow) {
+  test::msg::MessageWithDataArray m;
+  m.params().values = m.AllocateArray<uint64_t>(10);
+
+  auto& header = m.GetValueAt<internal::ArrayHeader>(m.params().values);
+  header.num_bytes = 1000000;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, ArrayElementsOverflow) {
+  test::msg::MessageWithDataArray m;
+  m.params().values = m.AllocateArray<uint64_t>(10);
+
+  auto& header = m.GetValueAt<internal::ArrayHeader>(m.params().values);
+  header.num_elements = 1000000;
+  EXPECT_FALSE(
+      m.Deserialize(DriverTransport::Message(m.data_view()), transport()));
+}
+
+TEST_F(MessageInternalTest, MalformedDriverObject) {
+  constexpr IpczDriverHandle kObjectHandle = 0x12345678;
+  test::msg::MessageWithDriverObject in;
+  in.AppendDriverObject(DriverObject(node(), kObjectHandle),
+                        in.params().object);
+  transport().Transmit(in);
+
+  // Force driver object deserialization to fail. This must result in failure of
+  // overall message deserialization.
+  set_reject_driver_objects(true);
+  ReceivedMessage message = TakeNextReceivedMessage();
+  test::msg::MessageWithDriverObject out;
+  EXPECT_FALSE(out.Deserialize(message.AsTransportMessage(), transport()));
+}
+
+TEST_F(MessageInternalTest, TolerateNewerVersion) {
+  test::msg::BasicTestMessageV1 in;
+  in.params().foo = 1;
+  in.params().bar = 2;
+  in.params().baz = 3;
+  in.params().qux = 4;
+  transport().Transmit(in);
+
+  test::msg::BasicTestMessage out;
+  ReceivedMessage message = TakeNextReceivedMessage();
+  EXPECT_TRUE(out.Deserialize(message.AsTransportMessage(), transport()));
+  EXPECT_EQ(1u, out.params().foo);
+  EXPECT_EQ(2u, out.params().bar);
+}
+
+}  // namespace
+}  // namespace ipcz
diff --git a/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h b/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h
new file mode 100644
index 0000000..d3c1489b
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_macros/message_declaration_macros.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+
+#define IPCZ_MSG_ID(x) static constexpr uint8_t kId = x
+#define IPCZ_MSG_VERSION(x) static constexpr uint32_t kVersion = x
+
+#define IPCZ_MSG_BEGIN(name, id_decl, version_decl)      \
+  class name : public internal::Message<name##_Params> { \
+   public:                                               \
+    using ParamsType = name##_Params;                    \
+    id_decl;                                             \
+    version_decl;                                        \
+    name();                                              \
+    ~name();                                             \
+    bool Serialize(const DriverTransport& transport);    \
+    bool Deserialize(const DriverTransport::Message&,    \
+                     const DriverTransport& transport);  \
+                                                         \
+    static constexpr internal::ParamMetadata kMetadata[] = {
+#define IPCZ_MSG_END() \
+  }                    \
+  ;                    \
+  }                    \
+  ;
+
+#define IPCZ_MSG_PARAM(type, name)                          \
+  {offsetof(ParamsType, name), sizeof(ParamsType::name), 0, \
+   internal::ParamType::kData},
+#define IPCZ_MSG_PARAM_ARRAY(type, name)                               \
+  {offsetof(ParamsType, name), sizeof(ParamsType::name), sizeof(type), \
+   internal::ParamType::kDataArray},
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT(name)                  \
+  {offsetof(ParamsType, name), sizeof(ParamsType::name), 0, \
+   internal::ParamType::kDriverObject},
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT_ARRAY(name)         \
+  {offsetof(ParamsType, name), sizeof(ParamsType::name), \
+   sizeof(internal::DriverObjectData),                   \
+   internal::ParamType::kDriverObjectArray},
diff --git a/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h b/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h
new file mode 100644
index 0000000..4f5008fe
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_macros/message_definition_macros.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+
+#define IPCZ_MSG_ID(x)
+#define IPCZ_MSG_VERSION(x)
+
+#define IPCZ_MSG_BEGIN(name, id_decl, version_decl)                          \
+  name::name() = default;                                                    \
+  name::~name() = default;                                                   \
+  bool name::Serialize(const DriverTransport& transport) {                   \
+    if (!CanTransmitOn(transport)) {                                         \
+      return false;                                                          \
+    }                                                                        \
+    MessageBase::Serialize(kMetadata, transport);                            \
+    return true;                                                             \
+  }                                                                          \
+  bool name::Deserialize(const DriverTransport::Message& message,            \
+                         const DriverTransport& transport) {                 \
+    return DeserializeFromTransport(sizeof(ParamsType), kVersion,            \
+                                    absl::MakeSpan(kMetadata), message.data, \
+                                    message.handles, transport);             \
+  }                                                                          \
+  constexpr internal::ParamMetadata name::kMetadata[];
+
+#define IPCZ_MSG_END()
+
+#define IPCZ_MSG_PARAM(type, name)
+#define IPCZ_MSG_PARAM_ARRAY(type, name)
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT(name)
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT_ARRAY(name)
diff --git a/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h b/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h
new file mode 100644
index 0000000..6a14364
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_macros/message_params_declaration_macros.h
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+
+#define IPCZ_MSG_ID(x) static constexpr uint8_t kId = x
+#define IPCZ_MSG_VERSION(x) static constexpr uint32_t kVersion = x
+
+#define IPCZ_MSG_BEGIN(name, id_decl, version_decl) \
+  struct IPCZ_ALIGN(8) name##_Params {              \
+    name##_Params();                                \
+    ~name##_Params();                               \
+    id_decl;                                        \
+    version_decl;                                   \
+    internal::StructHeader header;
+
+#define IPCZ_MSG_END() \
+  }                    \
+  ;
+
+#define IPCZ_MSG_PARAM(type, name) type name;
+#define IPCZ_MSG_PARAM_ARRAY(type, name) uint32_t name;
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT(name) internal::DriverObjectData name;
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT_ARRAY(name) uint32_t name;
diff --git a/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h b/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h
new file mode 100644
index 0000000..87bc72d1
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_macros/message_params_definition_macros.h
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+
+#define IPCZ_MSG_ID(x)
+#define IPCZ_MSG_VERSION(x)
+
+#define IPCZ_MSG_BEGIN(name, id_decl, version_decl) \
+  name##_Params::name##_Params() = default;         \
+  name##_Params::~name##_Params() = default;
+
+#define IPCZ_MSG_END()
+
+#define IPCZ_MSG_PARAM(type, name)
+#define IPCZ_MSG_PARAM_ARRAY(type, name)
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT(name)
+#define IPCZ_MSG_PARAM_DRIVER_OBJECT_ARRAY(name)
diff --git a/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h b/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h
new file mode 100644
index 0000000..0d7a7d7
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/message_macros/undef_message_macros.h
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+
+#undef IPCZ_MSG_ID
+#undef IPCZ_MSG_VERSION
+#undef IPCZ_MSG_BEGIN
+#undef IPCZ_MSG_END
+#undef IPCZ_MSG_PARAM
+#undef IPCZ_MSG_PARAM_ARRAY
+#undef IPCZ_MSG_PARAM_DRIVER_OBJECT
+#undef IPCZ_MSG_PARAM_DRIVER_OBJECT_ARRAY
diff --git a/third_party/ipcz/src/ipcz/test_messages.cc b/third_party/ipcz/src/ipcz/test_messages.cc
new file mode 100644
index 0000000..e082c7b
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/test_messages.cc
@@ -0,0 +1,25 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipcz/test_messages.h"
+
+#include "third_party/abseil-cpp/absl/types/span.h"
+
+namespace ipcz::test::msg {
+
+#pragma pack(push, 1)
+
+// clang-format off
+#include "ipcz/message_macros/message_params_definition_macros.h"
+#include "ipcz/test_messages_generator.h"
+#include "ipcz/message_macros/undef_message_macros.h"
+
+#include "ipcz/message_macros/message_definition_macros.h"
+#include "ipcz/test_messages_generator.h"
+#include "ipcz/message_macros/undef_message_macros.h"
+// clang-format on
+
+#pragma pack(pop)
+
+}  // namespace ipcz::test::msg
diff --git a/third_party/ipcz/src/ipcz/test_messages.h b/third_party/ipcz/src/ipcz/test_messages.h
new file mode 100644
index 0000000..017c17e0
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/test_messages.h
@@ -0,0 +1,33 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_IPCZ_TEST_MESSAGES_H_
+#define IPCZ_SRC_IPCZ_TEST_MESSAGES_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "ipcz/driver_object.h"
+#include "ipcz/driver_transport.h"
+#include "ipcz/message_internal.h"
+
+namespace ipcz::test::msg {
+
+#pragma pack(push, 1)
+
+// clang-format off
+#include "ipcz/message_macros/message_params_declaration_macros.h"
+#include "ipcz/test_messages_generator.h"
+#include "ipcz/message_macros/undef_message_macros.h"
+
+#include "ipcz/message_macros/message_declaration_macros.h"
+#include "ipcz/test_messages_generator.h"
+#include "ipcz/message_macros/undef_message_macros.h"
+// clang-format on
+
+#pragma pack(pop)
+
+}  // namespace ipcz::test::msg
+
+#endif  // IPCZ_SRC_IPCZ_TEST_MESSAGES_H_
diff --git a/third_party/ipcz/src/ipcz/test_messages_generator.h b/third_party/ipcz/src/ipcz/test_messages_generator.h
new file mode 100644
index 0000000..da26e29
--- /dev/null
+++ b/third_party/ipcz/src/ipcz/test_messages_generator.h
@@ -0,0 +1,31 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// no-include-guard-because-multiply-included
+
+IPCZ_MSG_BEGIN(BasicTestMessage, IPCZ_MSG_ID(0), IPCZ_MSG_VERSION(0))
+  IPCZ_MSG_PARAM(uint32_t, foo)
+  IPCZ_MSG_PARAM(uint32_t, bar)
+IPCZ_MSG_END()
+
+IPCZ_MSG_BEGIN(BasicTestMessageV1, IPCZ_MSG_ID(0), IPCZ_MSG_VERSION(1))
+  IPCZ_MSG_PARAM(uint32_t, foo)
+  IPCZ_MSG_PARAM(uint32_t, bar)
+  IPCZ_MSG_PARAM(uint32_t, baz)
+  IPCZ_MSG_PARAM(uint32_t, qux)
+IPCZ_MSG_END()
+
+IPCZ_MSG_BEGIN(MessageWithDataArray, IPCZ_MSG_ID(1), IPCZ_MSG_VERSION(0))
+  IPCZ_MSG_PARAM_ARRAY(uint64_t, values)
+IPCZ_MSG_END()
+
+IPCZ_MSG_BEGIN(MessageWithDriverObject, IPCZ_MSG_ID(2), IPCZ_MSG_VERSION(0))
+  IPCZ_MSG_PARAM_DRIVER_OBJECT(object)
+IPCZ_MSG_END()
+
+IPCZ_MSG_BEGIN(MessageWithDriverObjectArray,
+               IPCZ_MSG_ID(3),
+               IPCZ_MSG_VERSION(0))
+  IPCZ_MSG_PARAM_DRIVER_OBJECT_ARRAY(objects)
+IPCZ_MSG_END()
diff --git a/third_party/ipcz/src/test/mock_driver.cc b/third_party/ipcz/src/test/mock_driver.cc
new file mode 100644
index 0000000..43df7a4
--- /dev/null
+++ b/third_party/ipcz/src/test/mock_driver.cc
@@ -0,0 +1,159 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "test/mock_driver.h"
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+
+namespace ipcz::test {
+
+namespace {
+
+MockDriver*& GetDriverPtr() {
+  static MockDriver* driver = nullptr;
+  return driver;
+}
+
+MockDriver& GetDriver() {
+  MockDriver*& ptr = GetDriverPtr();
+  ABSL_ASSERT(ptr != nullptr);
+  return *ptr;
+}
+
+IpczResult IPCZ_API Close(IpczDriverHandle handle,
+                          uint32_t flags,
+                          const void* options) {
+  return GetDriver().Close(handle, flags, options);
+}
+
+IpczResult IPCZ_API Serialize(IpczDriverHandle handle,
+                              IpczDriverHandle transport,
+                              uint32_t flags,
+                              const void* options,
+                              void* data,
+                              size_t* num_bytes,
+                              IpczDriverHandle* handles,
+                              size_t* num_handles) {
+  return GetDriver().Serialize(handle, transport, flags, options, data,
+                               num_bytes, handles, num_handles);
+}
+
+IpczResult IPCZ_API Deserialize(const void* data,
+                                size_t num_bytes,
+                                const IpczDriverHandle* handles,
+                                size_t num_handles,
+                                IpczDriverHandle transport,
+                                uint32_t flags,
+                                const void* options,
+                                IpczDriverHandle* driver_handle) {
+  return GetDriver().Deserialize(data, num_bytes, handles, num_handles,
+                                 transport, flags, options, driver_handle);
+}
+
+IpczResult IPCZ_API CreateTransports(IpczDriverHandle transport0,
+                                     IpczDriverHandle transport1,
+                                     uint32_t flags,
+                                     const void* options,
+                                     IpczDriverHandle* new_transport0,
+                                     IpczDriverHandle* new_transport1) {
+  return GetDriver().CreateTransports(transport0, transport1, flags, options,
+                                      new_transport0, new_transport1);
+}
+
+IpczResult IPCZ_API ActivateTransport(IpczDriverHandle driver_transport,
+                                      IpczHandle transport,
+                                      IpczTransportActivityHandler handler,
+                                      uint32_t flags,
+                                      const void* options) {
+  return GetDriver().ActivateTransport(driver_transport, transport, handler,
+                                       flags, options);
+}
+
+IpczResult IPCZ_API DeactivateTransport(IpczDriverHandle driver_transport,
+                                        uint32_t flags,
+                                        const void* options) {
+  return GetDriver().DeactivateTransport(driver_transport, flags, options);
+}
+
+IpczResult IPCZ_API Transmit(IpczDriverHandle driver_transport,
+                             const void* data,
+                             size_t num_bytes,
+                             const IpczDriverHandle* handles,
+                             size_t num_handles,
+                             uint32_t flags,
+                             const void* options) {
+  return GetDriver().Transmit(driver_transport, data, num_bytes, handles,
+                              num_handles, flags, options);
+}
+
+IpczResult IPCZ_API AllocateSharedMemory(size_t num_bytes,
+                                         uint32_t flags,
+                                         const void* options,
+                                         IpczDriverHandle* driver_memory) {
+  return GetDriver().AllocateSharedMemory(num_bytes, flags, options,
+                                          driver_memory);
+}
+
+IpczResult GetSharedMemoryInfo(IpczDriverHandle driver_memory,
+                               uint32_t flags,
+                               const void* options,
+                               IpczSharedMemoryInfo* info) {
+  return GetDriver().GetSharedMemoryInfo(driver_memory, flags, options, info);
+}
+
+IpczResult IPCZ_API DuplicateSharedMemory(IpczDriverHandle driver_memory,
+                                          uint32_t flags,
+                                          const void* options,
+                                          IpczDriverHandle* new_driver_memory) {
+  return GetDriver().DuplicateSharedMemory(driver_memory, flags, options,
+                                           new_driver_memory);
+}
+
+IpczResult IPCZ_API MapSharedMemory(IpczDriverHandle driver_memory,
+                                    uint32_t flags,
+                                    const void* options,
+                                    void** address,
+                                    IpczDriverHandle* driver_mapping) {
+  return GetDriver().MapSharedMemory(driver_memory, flags, options, address,
+                                     driver_mapping);
+}
+
+IpczResult IPCZ_API GenerateRandomBytes(size_t num_bytes,
+                                        uint32_t flags,
+                                        const void* options,
+                                        void* buffer) {
+  return GetDriver().GenerateRandomBytes(num_bytes, flags, options, buffer);
+}
+
+}  // namespace
+
+MockDriver::MockDriver() {
+  MockDriver*& ptr = GetDriverPtr();
+  ABSL_ASSERT(ptr == nullptr);
+  ptr = this;
+}
+
+MockDriver::~MockDriver() {
+  MockDriver*& ptr = GetDriverPtr();
+  ABSL_ASSERT(ptr == this);
+  ptr = nullptr;
+}
+
+const IpczDriver kMockDriver = {
+    sizeof(kMockDriver),
+    Close,
+    Serialize,
+    Deserialize,
+    CreateTransports,
+    ActivateTransport,
+    DeactivateTransport,
+    Transmit,
+    AllocateSharedMemory,
+    GetSharedMemoryInfo,
+    DuplicateSharedMemory,
+    MapSharedMemory,
+    GenerateRandomBytes,
+};
+
+}  // namespace ipcz::test
diff --git a/third_party/ipcz/src/test/mock_driver.h b/third_party/ipcz/src/test/mock_driver.h
new file mode 100644
index 0000000..b82e28f
--- /dev/null
+++ b/third_party/ipcz/src/test/mock_driver.h
@@ -0,0 +1,95 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_TEST_MOCK_DRIVER_H_
+#define IPCZ_SRC_TEST_MOCK_DRIVER_H_
+
+#include "ipcz/ipcz.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace ipcz::test {
+
+// A mock implementation of the IpczDriver API which tests can use to precisely
+// introspect driver API invocations made by ipcz. At most one instance of this
+// class may exist at a time, and the kMockDriver driver declared below can only
+// be used while such an instance exists.
+class MockDriver {
+ public:
+  MockDriver();
+  ~MockDriver();
+
+  MOCK_METHOD(IpczResult, Close, (IpczDriverHandle, uint32_t, const void*));
+  MOCK_METHOD(IpczResult,
+              Serialize,
+              (IpczDriverHandle,
+               IpczDriverHandle,
+               uint32_t,
+               const void*,
+               void*,
+               size_t*,
+               IpczDriverHandle*,
+               size_t*));
+  MOCK_METHOD(IpczResult,
+              Deserialize,
+              (const void*,
+               size_t,
+               const IpczDriverHandle*,
+               size_t,
+               IpczDriverHandle,
+               uint32_t,
+               const void*,
+               IpczDriverHandle*));
+  MOCK_METHOD(IpczResult,
+              CreateTransports,
+              (IpczDriverHandle,
+               IpczDriverHandle,
+               uint32_t,
+               const void*,
+               IpczDriverHandle*,
+               IpczDriverHandle*));
+  MOCK_METHOD(IpczResult,
+              ActivateTransport,
+              (IpczDriverHandle,
+               IpczHandle,
+               IpczTransportActivityHandler,
+               uint32_t,
+               const void*));
+  MOCK_METHOD(IpczResult,
+              DeactivateTransport,
+              (IpczDriverHandle, uint32_t, const void*));
+  MOCK_METHOD(IpczResult,
+              Transmit,
+              (IpczDriverHandle,
+               const void*,
+               size_t,
+               const IpczDriverHandle*,
+               size_t,
+               uint32_t,
+               const void*));
+  MOCK_METHOD(IpczResult,
+              AllocateSharedMemory,
+              (size_t, uint32_t, const void*, IpczDriverHandle*));
+  MOCK_METHOD(IpczResult,
+              GetSharedMemoryInfo,
+              (IpczDriverHandle, uint32_t, const void*, IpczSharedMemoryInfo*));
+  MOCK_METHOD(IpczResult,
+              DuplicateSharedMemory,
+              (IpczDriverHandle, uint32_t, const void*, IpczDriverHandle*));
+  MOCK_METHOD(
+      IpczResult,
+      MapSharedMemory,
+      (IpczDriverHandle, uint32_t, const void*, void**, IpczDriverHandle*));
+  MOCK_METHOD(IpczResult,
+              GenerateRandomBytes,
+              (size_t, uint32_t, const void*, void*));
+};
+
+// An ipcz driver which forwards its API invocations to the only currently
+// existing instance of the MockDriver class above. It is an error to elicit
+// driver calls from ipcz while no MockDriver instance exists.
+extern const IpczDriver kMockDriver;
+
+}  // namespace ipcz::test
+
+#endif  // IPCZ_SRC_TEST_MOCK_DRIVER_H_
diff --git a/third_party/ipcz/src/util/safe_math.h b/third_party/ipcz/src/util/safe_math.h
new file mode 100644
index 0000000..15cbb8c5
--- /dev/null
+++ b/third_party/ipcz/src/util/safe_math.h
@@ -0,0 +1,43 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPCZ_SRC_UTIL_SAFE_MATH_
+#define IPCZ_SRC_UTIL_SAFE_MATH_
+
+#include <limits>
+
+#include "third_party/abseil-cpp/absl/base/macros.h"
+#include "third_party/abseil-cpp/absl/base/optimization.h"
+
+namespace ipcz {
+
+template <typename Dst, typename Src>
+constexpr Dst checked_cast(Src value) {
+  // This throws a compile-time error on evaluating the constexpr if it can be
+  // determined at compile-time as failing, otherwise it will fail an
+  // assertion at runtime.
+  ABSL_HARDENING_ASSERT(
+      ABSL_PREDICT_TRUE(value <= std::numeric_limits<Dst>::max()));
+  return static_cast<Dst>(value);
+}
+
+template <typename T>
+constexpr T CheckAdd(T a, T b) {
+  T result;
+  ABSL_HARDENING_ASSERT(
+      !ABSL_PREDICT_FALSE(__builtin_add_overflow(a, b, &result)));
+  return result;
+}
+
+template <typename T>
+constexpr T CheckMul(T a, T b) {
+  T result;
+  ABSL_HARDENING_ASSERT(
+      !ABSL_PREDICT_FALSE(__builtin_mul_overflow(a, b, &result)));
+  return result;
+}
+
+}  // namespace ipcz
+
+#endif  // IPCZ_SRC_UTIL_SAFE_MATH_
diff --git a/third_party/zlib/contrib/optimizations/inflate.c b/third_party/zlib/contrib/optimizations/inflate.c
index 4841cd9..5d69770 100644
--- a/third_party/zlib/contrib/optimizations/inflate.c
+++ b/third_party/zlib/contrib/optimizations/inflate.c
@@ -460,10 +460,10 @@
 
 /* check function to use adler32() for zlib or crc32() for gzip */
 #ifdef GUNZIP
-#  define UPDATE(check, buf, len) \
+#  define UPDATE_CHECK(check, buf, len) \
     (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
 #else
-#  define UPDATE(check, buf, len) adler32(check, buf, len)
+#  define UPDATE_CHECK(check, buf, len) adler32(check, buf, len)
 #endif
 
 /* check macros for header crc */
@@ -735,6 +735,7 @@
                 CRC2(state->check, hold);
             INITBITS();
             state->mode = TIME;
+                /* fallthrough */
         case TIME:
             NEEDBITS(32);
             if (state->head != Z_NULL)
@@ -743,6 +744,7 @@
                 CRC4(state->check, hold);
             INITBITS();
             state->mode = OS;
+                /* fallthrough */
         case OS:
             NEEDBITS(16);
             if (state->head != Z_NULL) {
@@ -753,6 +755,7 @@
                 CRC2(state->check, hold);
             INITBITS();
             state->mode = EXLEN;
+                /* fallthrough */
         case EXLEN:
             if (state->flags & 0x0400) {
                 NEEDBITS(16);
@@ -766,6 +769,7 @@
             else if (state->head != Z_NULL)
                 state->head->extra = Z_NULL;
             state->mode = EXTRA;
+                /* fallthrough */
         case EXTRA:
             if (state->flags & 0x0400) {
                 copy = state->length;
@@ -788,6 +792,7 @@
             }
             state->length = 0;
             state->mode = NAME;
+                /* fallthrough */
         case NAME:
             if (state->flags & 0x0800) {
                 if (have == 0) goto inf_leave;
@@ -809,6 +814,7 @@
                 state->head->name = Z_NULL;
             state->length = 0;
             state->mode = COMMENT;
+                /* fallthrough */
         case COMMENT:
             if (state->flags & 0x1000) {
                 if (have == 0) goto inf_leave;
@@ -829,6 +835,7 @@
             else if (state->head != Z_NULL)
                 state->head->comment = Z_NULL;
             state->mode = HCRC;
+                /* fallthrough */
         case HCRC:
             if (state->flags & 0x0200) {
                 NEEDBITS(16);
@@ -852,6 +859,7 @@
             strm->adler = state->check = ZSWAP32(hold);
             INITBITS();
             state->mode = DICT;
+                /* fallthrough */
         case DICT:
             if (state->havedict == 0) {
                 RESTORE();
@@ -859,8 +867,10 @@
             }
             strm->adler = state->check = adler32(0L, Z_NULL, 0);
             state->mode = TYPE;
+                /* fallthrough */
         case TYPE:
             if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
+                /* fallthrough */
         case TYPEDO:
             if (state->last) {
                 BYTEBITS();
@@ -911,8 +921,10 @@
             INITBITS();
             state->mode = COPY_;
             if (flush == Z_TREES) goto inf_leave;
+                /* fallthrough */
         case COPY_:
             state->mode = COPY;
+                /* fallthrough */
         case COPY:
             copy = state->length;
             if (copy) {
@@ -948,6 +960,7 @@
             Tracev((stderr, "inflate:       table sizes ok\n"));
             state->have = 0;
             state->mode = LENLENS;
+                /* fallthrough */
         case LENLENS:
             while (state->have < state->ncode) {
                 NEEDBITS(3);
@@ -969,6 +982,7 @@
             Tracev((stderr, "inflate:       code lengths ok\n"));
             state->have = 0;
             state->mode = CODELENS;
+                /* fallthrough */
         case CODELENS:
             while (state->have < state->nlen + state->ndist) {
                 for (;;) {
@@ -1052,8 +1066,10 @@
             Tracev((stderr, "inflate:       codes ok\n"));
             state->mode = LEN_;
             if (flush == Z_TREES) goto inf_leave;
+                /* fallthrough */
         case LEN_:
             state->mode = LEN;
+                /* fallthrough */
         case LEN:
             if (have >= INFLATE_FAST_MIN_INPUT &&
                 left >= INFLATE_FAST_MIN_OUTPUT) {
@@ -1104,6 +1120,7 @@
             }
             state->extra = (unsigned)(here.op) & 15;
             state->mode = LENEXT;
+                /* fallthrough */
         case LENEXT:
             if (state->extra) {
                 NEEDBITS(state->extra);
@@ -1114,6 +1131,7 @@
             Tracevv((stderr, "inflate:         length %u\n", state->length));
             state->was = state->length;
             state->mode = DIST;
+                /* fallthrough */
         case DIST:
             for (;;) {
                 here = state->distcode[BITS(state->distbits)];
@@ -1141,6 +1159,7 @@
             state->offset = (unsigned)here.val;
             state->extra = (unsigned)(here.op) & 15;
             state->mode = DISTEXT;
+                /* fallthrough */
         case DISTEXT:
             if (state->extra) {
                 NEEDBITS(state->extra);
@@ -1157,6 +1176,7 @@
 #endif
             Tracevv((stderr, "inflate:         distance %u\n", state->offset));
             state->mode = MATCH;
+                /* fallthrough */
         case MATCH:
             if (left == 0) goto inf_leave;
             copy = out - left;
@@ -1215,7 +1235,7 @@
                 state->total += out;
                 if ((state->wrap & 4) && out)
                     strm->adler = state->check =
-                        UPDATE(state->check, put - out, out);
+                        UPDATE_CHECK(state->check, put - out, out);
                 out = left;
                 if ((state->wrap & 4) && (
 #ifdef GUNZIP
@@ -1231,6 +1251,7 @@
             }
 #ifdef GUNZIP
             state->mode = LENGTH;
+                /* fallthrough */
         case LENGTH:
             if (state->wrap && state->flags) {
                 NEEDBITS(32);
@@ -1244,6 +1265,7 @@
             }
 #endif
             state->mode = DONE;
+                /* fallthrough */
         case DONE:
             ret = Z_STREAM_END;
             goto inf_leave;
@@ -1253,6 +1275,7 @@
         case MEM:
             return Z_MEM_ERROR;
         case SYNC:
+                /* fallthrough */
         default:
             return Z_STREAM_ERROR;
         }
@@ -1288,7 +1311,7 @@
     state->total += out;
     if ((state->wrap & 4) && out)
         strm->adler = state->check =
-            UPDATE(state->check, strm->next_out - out, out);
+            UPDATE_CHECK(state->check, strm->next_out - out, out);
     strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
                       (state->mode == TYPE ? 128 : 0) +
                       (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
diff --git a/third_party/zlib/crc32.c b/third_party/zlib/crc32.c
index 41fe8915..b83d6e4 100644
--- a/third_party/zlib/crc32.c
+++ b/third_party/zlib/crc32.c
@@ -230,7 +230,7 @@
   is just exclusive-or, and multiplying a polynomial by x is a right shift by
   one. If we call the above polynomial p, and represent a byte as the
   polynomial q, also with the lowest power in the most significant bit (so the
-  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+  byte 0xb1 is the polynomial x^7+x^3+x^2+1), then the CRC is (q*x^32) mod p,
   where a mod b means the remainder after dividing a by b.
 
   This calculation is done using the shift-register method of multiplying and
diff --git a/third_party/zlib/gzguts.h b/third_party/zlib/gzguts.h
index 6378d468a..fc712dc 100644
--- a/third_party/zlib/gzguts.h
+++ b/third_party/zlib/gzguts.h
@@ -190,6 +190,7 @@
         /* just for writing */
     int level;              /* compression level */
     int strategy;           /* compression strategy */
+    int reset;              /* true if a reset is pending after a Z_FINISH */
         /* seek request */
     z_off64_t skip;         /* amount to skip (already rewound if backwards) */
     int seek;               /* true if seek request pending */
diff --git a/third_party/zlib/gzlib.c b/third_party/zlib/gzlib.c
index 4838bf0..f6b3b40 100644
--- a/third_party/zlib/gzlib.c
+++ b/third_party/zlib/gzlib.c
@@ -81,6 +81,8 @@
         state->past = 0;            /* have not read past end yet */
         state->how = LOOK;          /* look for gzip header */
     }
+    else                            /* for writing ... */
+        state->reset = 0;           /* no deflateReset pending */
     state->seek = 0;                /* no seek request pending */
     gz_error(state, Z_OK, NULL);    /* clear error */
     state->x.pos = 0;               /* no uncompressed data yet */
diff --git a/third_party/zlib/gzwrite.c b/third_party/zlib/gzwrite.c
index 52381332..85b576b 100644
--- a/third_party/zlib/gzwrite.c
+++ b/third_party/zlib/gzwrite.c
@@ -97,6 +97,15 @@
         return 0;
     }
 
+    /* check for a pending reset */
+    if (state->reset) {
+        /* don't start a new gzip member unless there is data to write */
+        if (strm->avail_in == 0)
+            return 0;
+        deflateReset(strm);
+        state->reset = 0;
+    }
+
     /* run deflate() on provided input until it produces no more output */
     ret = Z_OK;
     do {
@@ -134,7 +143,7 @@
 
     /* if that completed a deflate stream, allow another to start */
     if (flush == Z_FINISH)
-        deflateReset(strm);
+        state->reset = 1;
 
     /* all done, no errors */
     return 0;
diff --git a/third_party/zlib/infback.c b/third_party/zlib/infback.c
index aab14b4..5f56c8c7 100644
--- a/third_party/zlib/infback.c
+++ b/third_party/zlib/infback.c
@@ -477,6 +477,7 @@
             }
             Tracev((stderr, "inflate:       codes ok\n"));
             state->mode = LEN;
+                /* fallthrough */
 
         case LEN:
             /* use inflate_fast() if we have enough input and output */
diff --git a/third_party/zlib/inflate.c b/third_party/zlib/inflate.c
index 7543c33d..71f336f 100644
--- a/third_party/zlib/inflate.c
+++ b/third_party/zlib/inflate.c
@@ -449,10 +449,10 @@
 
 /* check function to use adler32() for zlib or crc32() for gzip */
 #ifdef GUNZIP
-#  define UPDATE(check, buf, len) \
+#  define UPDATE_CHECK(check, buf, len) \
     (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
 #else
-#  define UPDATE(check, buf, len) adler32(check, buf, len)
+#  define UPDATE_CHECK(check, buf, len) adler32(check, buf, len)
 #endif
 
 /* check macros for header crc */
@@ -724,6 +724,7 @@
                 CRC2(state->check, hold);
             INITBITS();
             state->mode = TIME;
+                /* fallthrough */
         case TIME:
             NEEDBITS(32);
             if (state->head != Z_NULL)
@@ -732,6 +733,7 @@
                 CRC4(state->check, hold);
             INITBITS();
             state->mode = OS;
+                /* fallthrough */
         case OS:
             NEEDBITS(16);
             if (state->head != Z_NULL) {
@@ -742,6 +744,7 @@
                 CRC2(state->check, hold);
             INITBITS();
             state->mode = EXLEN;
+                /* fallthrough */
         case EXLEN:
             if (state->flags & 0x0400) {
                 NEEDBITS(16);
@@ -755,6 +758,7 @@
             else if (state->head != Z_NULL)
                 state->head->extra = Z_NULL;
             state->mode = EXTRA;
+                /* fallthrough */
         case EXTRA:
             if (state->flags & 0x0400) {
                 copy = state->length;
@@ -777,6 +781,7 @@
             }
             state->length = 0;
             state->mode = NAME;
+                /* fallthrough */
         case NAME:
             if (state->flags & 0x0800) {
                 if (have == 0) goto inf_leave;
@@ -798,6 +803,7 @@
                 state->head->name = Z_NULL;
             state->length = 0;
             state->mode = COMMENT;
+                /* fallthrough */
         case COMMENT:
             if (state->flags & 0x1000) {
                 if (have == 0) goto inf_leave;
@@ -818,6 +824,7 @@
             else if (state->head != Z_NULL)
                 state->head->comment = Z_NULL;
             state->mode = HCRC;
+                /* fallthrough */
         case HCRC:
             if (state->flags & 0x0200) {
                 NEEDBITS(16);
@@ -841,6 +848,7 @@
             strm->adler = state->check = ZSWAP32(hold);
             INITBITS();
             state->mode = DICT;
+                /* fallthrough */
         case DICT:
             if (state->havedict == 0) {
                 RESTORE();
@@ -848,8 +856,10 @@
             }
             strm->adler = state->check = adler32(0L, Z_NULL, 0);
             state->mode = TYPE;
+                /* fallthrough */
         case TYPE:
             if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave;
+                /* fallthrough */
         case TYPEDO:
             if (state->last) {
                 BYTEBITS();
@@ -900,8 +910,10 @@
             INITBITS();
             state->mode = COPY_;
             if (flush == Z_TREES) goto inf_leave;
+                /* fallthrough */
         case COPY_:
             state->mode = COPY;
+                /* fallthrough */
         case COPY:
             copy = state->length;
             if (copy) {
@@ -937,6 +949,7 @@
             Tracev((stderr, "inflate:       table sizes ok\n"));
             state->have = 0;
             state->mode = LENLENS;
+                /* fallthrough */
         case LENLENS:
             while (state->have < state->ncode) {
                 NEEDBITS(3);
@@ -958,6 +971,7 @@
             Tracev((stderr, "inflate:       code lengths ok\n"));
             state->have = 0;
             state->mode = CODELENS;
+                /* fallthrough */
         case CODELENS:
             while (state->have < state->nlen + state->ndist) {
                 for (;;) {
@@ -1041,8 +1055,10 @@
             Tracev((stderr, "inflate:       codes ok\n"));
             state->mode = LEN_;
             if (flush == Z_TREES) goto inf_leave;
+                /* fallthrough */
         case LEN_:
             state->mode = LEN;
+                /* fallthrough */
         case LEN:
             if (have >= INFLATE_FAST_MIN_INPUT &&
                 left >= INFLATE_FAST_MIN_OUTPUT) {
@@ -1093,6 +1109,7 @@
             }
             state->extra = (unsigned)(here.op) & 15;
             state->mode = LENEXT;
+                /* fallthrough */
         case LENEXT:
             if (state->extra) {
                 NEEDBITS(state->extra);
@@ -1103,6 +1120,7 @@
             Tracevv((stderr, "inflate:         length %u\n", state->length));
             state->was = state->length;
             state->mode = DIST;
+                /* fallthrough */
         case DIST:
             for (;;) {
                 here = state->distcode[BITS(state->distbits)];
@@ -1130,6 +1148,7 @@
             state->offset = (unsigned)here.val;
             state->extra = (unsigned)(here.op) & 15;
             state->mode = DISTEXT;
+                /* fallthrough */
         case DISTEXT:
             if (state->extra) {
                 NEEDBITS(state->extra);
@@ -1146,6 +1165,7 @@
 #endif
             Tracevv((stderr, "inflate:         distance %u\n", state->offset));
             state->mode = MATCH;
+                /* fallthrough */
         case MATCH:
             if (left == 0) goto inf_leave;
             copy = out - left;
@@ -1205,7 +1225,7 @@
                 state->total += out;
                 if ((state->wrap & 4) && out)
                     strm->adler = state->check =
-                        UPDATE(state->check, put - out, out);
+                        UPDATE_CHECK(state->check, put - out, out);
                 out = left;
                 if ((state->wrap & 4) && (
 #ifdef GUNZIP
@@ -1221,6 +1241,7 @@
             }
 #ifdef GUNZIP
             state->mode = LENGTH;
+                /* fallthrough */
         case LENGTH:
             if (state->wrap && state->flags) {
                 NEEDBITS(32);
@@ -1234,6 +1255,7 @@
             }
 #endif
             state->mode = DONE;
+                /* fallthrough */
         case DONE:
             ret = Z_STREAM_END;
             goto inf_leave;
@@ -1243,6 +1265,7 @@
         case MEM:
             return Z_MEM_ERROR;
         case SYNC:
+                /* fallthrough */
         default:
             return Z_STREAM_ERROR;
         }
@@ -1268,7 +1291,7 @@
     state->total += out;
     if ((state->wrap & 4) && out)
         strm->adler = state->check =
-            UPDATE(state->check, strm->next_out - out, out);
+            UPDATE_CHECK(state->check, strm->next_out - out, out);
     strm->data_type = (int)state->bits + (state->last ? 64 : 0) +
                       (state->mode == TYPE ? 128 : 0) +
                       (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0);
diff --git a/third_party/zlib/trees.c b/third_party/zlib/trees.c
index decaeb7..6896067 100644
--- a/third_party/zlib/trees.c
+++ b/third_party/zlib/trees.c
@@ -1091,9 +1091,9 @@
  * Check if the data type is TEXT or BINARY, using the following algorithm:
  * - TEXT if the two conditions below are satisfied:
  *    a) There are no non-portable control characters belonging to the
- *       "black list" (0..6, 14..25, 28..31).
+ *       "block list" (0..6, 14..25, 28..31).
  *    b) There is at least one printable character belonging to the
- *       "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ *       "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
  * - BINARY otherwise.
  * - The following partially-portable control characters form a
  *   "gray list" that is ignored in this detection algorithm:
@@ -1103,19 +1103,19 @@
 local int detect_data_type(s)
     deflate_state *s;
 {
-    /* black_mask is the bit mask of black-listed bytes
+    /* block_mask is the bit mask of block-listed bytes
      * set bits 0..6, 14..25, and 28..31
      * 0xf3ffc07f = binary 11110011111111111100000001111111
      */
-    unsigned long black_mask = 0xf3ffc07fUL;
+    unsigned long block_mask = 0xf3ffc07fUL;
     int n;
 
-    /* Check for non-textual ("black-listed") bytes. */
-    for (n = 0; n <= 31; n++, black_mask >>= 1)
-        if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0))
+    /* Check for non-textual ("block-listed") bytes. */
+    for (n = 0; n <= 31; n++, block_mask >>= 1)
+        if ((block_mask & 1) && (s->dyn_ltree[n].Freq != 0))
             return Z_BINARY;
 
-    /* Check for textual ("white-listed") bytes. */
+    /* Check for textual ("allow-listed") bytes. */
     if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0
             || s->dyn_ltree[13].Freq != 0)
         return Z_TEXT;
@@ -1123,7 +1123,7 @@
         if (s->dyn_ltree[n].Freq != 0)
             return Z_TEXT;
 
-    /* There are no "black-listed" or "white-listed" bytes:
+    /* There are no "block-listed" or "allow-listed" bytes:
      * this stream either is empty or has tolerated ("gray-listed") bytes only.
      */
     return Z_BINARY;
diff --git a/third_party/zlib/zlib.h b/third_party/zlib/zlib.h
index 88961b9..77510ee 100644
--- a/third_party/zlib/zlib.h
+++ b/third_party/zlib/zlib.h
@@ -1304,14 +1304,14 @@
 /*
 ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
 
-     Opens a gzip (.gz) file for reading or writing.  The mode parameter is as
-   in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
-   a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
-   compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
-   for fixed code compression as in "wb9F".  (See the description of
-   deflateInit2 for more information about the strategy parameter.)  'T' will
-   request transparent writing or appending with no compression and not using
-   the gzip format.
+     Open the gzip (.gz) file at path for reading and decompressing, or
+   compressing and writing.  The mode parameter is as in fopen ("rb" or "wb")
+   but can also include a compression level ("wb9") or a strategy: 'f' for
+   filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h",
+   'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression
+   as in "wb9F".  (See the description of deflateInit2 for more information
+   about the strategy parameter.)  'T' will request transparent writing or
+   appending with no compression and not using the gzip format.
 
      "a" can be used instead of "w" to request that the gzip stream that will
    be written be appended to the file.  "+" will result in an error, since
@@ -1341,9 +1341,9 @@
 
 ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
 /*
-     gzdopen associates a gzFile with the file descriptor fd.  File descriptors
-   are obtained from calls like open, dup, creat, pipe or fileno (if the file
-   has been previously opened with fopen).  The mode parameter is as in gzopen.
+     Associate a gzFile with the file descriptor fd.  File descriptors are
+   obtained from calls like open, dup, creat, pipe or fileno (if the file has
+   been previously opened with fopen).  The mode parameter is as in gzopen.
 
      The next call of gzclose on the returned gzFile will also close the file
    descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
@@ -1364,13 +1364,13 @@
 
 ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
 /*
-     Set the internal buffer size used by this library's functions.  The
-   default buffer size is 8192 bytes.  This function must be called after
-   gzopen() or gzdopen(), and before any other calls that read or write the
-   file.  The buffer memory allocation is always deferred to the first read or
-   write.  Three times that size in buffer space is allocated.  A larger buffer
-   size of, for example, 64K or 128K bytes will noticeably increase the speed
-   of decompression (reading).
+     Set the internal buffer size used by this library's functions for file to
+   size.  The default buffer size is 8192 bytes.  This function must be called
+   after gzopen() or gzdopen(), and before any other calls that read or write
+   the file.  The buffer memory allocation is always deferred to the first read
+   or write.  Three times that size in buffer space is allocated.  A larger
+   buffer size of, for example, 64K or 128K bytes will noticeably increase the
+   speed of decompression (reading).
 
      The new buffer size also affects the maximum length for gzprintf().
 
@@ -1380,9 +1380,9 @@
 
 ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
 /*
-     Dynamically update the compression level or strategy.  See the description
-   of deflateInit2 for the meaning of these parameters.  Previously provided
-   data is flushed before the parameter change.
+     Dynamically update the compression level and strategy for file.  See the
+   description of deflateInit2 for the meaning of these parameters. Previously
+   provided data is flushed before applying the parameter changes.
 
      gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not
    opened for writing, Z_ERRNO if there is an error writing the flushed data,
@@ -1391,7 +1391,7 @@
 
 ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
 /*
-     Reads the given number of uncompressed bytes from the compressed file.  If
+     Read and decompress up to len uncompressed bytes from file into buf.  If
    the input file is not in gzip format, gzread copies the given number of
    bytes into the buffer directly from the file.
 
@@ -1422,11 +1422,11 @@
 ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
                                      gzFile file));
 /*
-     Read up to nitems items of size size from file to buf, otherwise operating
-   as gzread() does.  This duplicates the interface of stdio's fread(), with
-   size_t request and return types.  If the library defines size_t, then
-   z_size_t is identical to size_t.  If not, then z_size_t is an unsigned
-   integer type that can contain a pointer.
+     Read and decompress up to nitems items of size size from file into buf,
+   otherwise operating as gzread() does.  This duplicates the interface of
+   stdio's fread(), with size_t request and return types.  If the library
+   defines size_t, then z_size_t is identical to size_t.  If not, then z_size_t
+   is an unsigned integer type that can contain a pointer.
 
      gzfread() returns the number of full items read of size size, or zero if
    the end of the file was reached and a full item could not be read, or if
@@ -1445,18 +1445,16 @@
    file, reseting and retrying on end-of-file, when size is not 1.
 */
 
-ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
-                                voidpc buf, unsigned len));
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file, voidpc buf, unsigned len));
 /*
-     Writes the given number of uncompressed bytes into the compressed file.
-   gzwrite returns the number of uncompressed bytes written or 0 in case of
-   error.
+     Compress and write the len uncompressed bytes at buf to file. gzwrite
+   returns the number of uncompressed bytes written or 0 in case of error.
 */
 
 ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
                                       z_size_t nitems, gzFile file));
 /*
-     gzfwrite() writes nitems items of size size from buf to file, duplicating
+     Compress and write nitems items of size size from buf to file, duplicating
    the interface of stdio's fwrite(), with size_t request and return types.  If
    the library defines size_t, then z_size_t is identical to size_t.  If not,
    then z_size_t is an unsigned integer type that can contain a pointer.
@@ -1469,22 +1467,22 @@
 
 ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
 /*
-     Converts, formats, and writes the arguments to the compressed file under
-   control of the format string, as in fprintf.  gzprintf returns the number of
+     Convert, format, compress, and write the arguments (...) to file under
+   control of the string format, as in fprintf.  gzprintf returns the number of
    uncompressed bytes actually written, or a negative zlib error code in case
    of error.  The number of uncompressed bytes written is limited to 8191, or
    one less than the buffer size given to gzbuffer().  The caller should assure
    that this limit is not exceeded.  If it is exceeded, then gzprintf() will
    return an error (0) with nothing written.  In this case, there may also be a
    buffer overflow with unpredictable consequences, which is possible only if
-   zlib was compiled with the insecure functions sprintf() or vsprintf()
+   zlib was compiled with the insecure functions sprintf() or vsprintf(),
    because the secure snprintf() or vsnprintf() functions were not available.
    This can be determined using zlibCompileFlags().
 */
 
 ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
 /*
-     Writes the given null-terminated string to the compressed file, excluding
+     Compress and write the given null-terminated string s to file, excluding
    the terminating null character.
 
      gzputs returns the number of characters written, or -1 in case of error.
@@ -1492,11 +1490,12 @@
 
 ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
 /*
-     Reads bytes from the compressed file until len-1 characters are read, or a
-   newline character is read and transferred to buf, or an end-of-file
-   condition is encountered.  If any characters are read or if len == 1, the
-   string is terminated with a null character.  If no characters are read due
-   to an end-of-file or len < 1, then the buffer is left untouched.
+     Read and decompress bytes from file into buf, until len-1 characters are
+   read, or until a newline character is read and transferred to buf, or an
+   end-of-file condition is encountered.  If any characters are read or if len
+   is one, the string is terminated with a null character.  If no characters
+   are read due to an end-of-file or len is less than one, then the buffer is
+   left untouched.
 
      gzgets returns buf which is a null-terminated string, or it returns NULL
    for end-of-file or in case of error.  If there was an error, the contents at
@@ -1505,13 +1504,13 @@
 
 ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
 /*
-     Writes c, converted to an unsigned char, into the compressed file.  gzputc
+     Compress and write c, converted to an unsigned char, into file.  gzputc
    returns the value that was written, or -1 in case of error.
 */
 
 ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
 /*
-     Reads one byte from the compressed file.  gzgetc returns this byte or -1
+     Read and decompress one byte from file.  gzgetc returns this byte or -1
    in case of end of file or error.  This is implemented as a macro for speed.
    As such, it does not do all of the checking the other functions do.  I.e.
    it does not check to see if file is NULL, nor whether the structure file
@@ -1520,8 +1519,8 @@
 
 ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
 /*
-     Push one character back onto the stream to be read as the first character
-   on the next read.  At least one character of push-back is allowed.
+     Push c back onto the stream for file to be read as the first character on
+   the next read.  At least one character of push-back is always allowed.
    gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will
    fail if c is -1, and may fail if a character has been pushed but not read
    yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the
@@ -1532,9 +1531,9 @@
 
 ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
 /*
-     Flushes all pending output into the compressed file.  The parameter flush
-   is as in the deflate() function.  The return value is the zlib error number
-   (see function gzerror below).  gzflush is only permitted when writing.
+     Flush all pending output to file.  The parameter flush is as in the
+   deflate() function.  The return value is the zlib error number (see function
+   gzerror below).  gzflush is only permitted when writing.
 
      If the flush parameter is Z_FINISH, the remaining data is written and the
    gzip stream is completed in the output.  If gzwrite() is called again, a new
@@ -1549,8 +1548,8 @@
 ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
                                    z_off_t offset, int whence));
 
-     Sets the starting position for the next gzread or gzwrite on the given
-   compressed file.  The offset represents a number of bytes in the
+     Set the starting position to offset relative to whence for the next gzread
+   or gzwrite on file.  The offset represents a number of bytes in the
    uncompressed data stream.  The whence parameter is defined as in lseek(2);
    the value SEEK_END is not supported.
 
@@ -1567,18 +1566,18 @@
 
 ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
 /*
-     Rewinds the given file. This function is supported only for reading.
+     Rewind file. This function is supported only for reading.
 
-     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET).
 */
 
 /*
 ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
 
-     Returns the starting position for the next gzread or gzwrite on the given
-   compressed file.  This position represents a number of bytes in the
-   uncompressed data stream, and is zero when starting, even if appending or
-   reading a gzip stream from the middle of a file using gzdopen().
+     Return the starting position for the next gzread or gzwrite on file.
+   This position represents a number of bytes in the uncompressed data stream,
+   and is zero when starting, even if appending or reading a gzip stream from
+   the middle of a file using gzdopen().
 
      gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
 */
@@ -1586,22 +1585,22 @@
 /*
 ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
 
-     Returns the current offset in the file being read or written.  This offset
-   includes the count of bytes that precede the gzip stream, for example when
-   appending or when using gzdopen() for reading.  When reading, the offset
-   does not include as yet unused buffered input.  This information can be used
-   for a progress indicator.  On error, gzoffset() returns -1.
+     Return the current compressed (actual) read or write offset of file.  This
+   offset includes the count of bytes that precede the gzip stream, for example
+   when appending or when using gzdopen() for reading.  When reading, the
+   offset does not include as yet unused buffered input.  This information can
+   be used for a progress indicator.  On error, gzoffset() returns -1.
 */
 
 ZEXTERN int ZEXPORT gzeof OF((gzFile file));
 /*
-     Returns true (1) if the end-of-file indicator has been set while reading,
-   false (0) otherwise.  Note that the end-of-file indicator is set only if the
-   read tried to go past the end of the input, but came up short.  Therefore,
-   just like feof(), gzeof() may return false even if there is no more data to
-   read, in the event that the last read request was for the exact number of
-   bytes remaining in the input file.  This will happen if the input file size
-   is an exact multiple of the buffer size.
+     Return true (1) if the end-of-file indicator for file has been set while
+   reading, false (0) otherwise.  Note that the end-of-file indicator is set
+   only if the read tried to go past the end of the input, but came up short.
+   Therefore, just like feof(), gzeof() may return false even if there is no
+   more data to read, in the event that the last read request was for the exact
+   number of bytes remaining in the input file.  This will happen if the input
+   file size is an exact multiple of the buffer size.
 
      If gzeof() returns true, then the read functions will return no more data,
    unless the end-of-file indicator is reset by gzclearerr() and the input file
@@ -1610,7 +1609,7 @@
 
 ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
 /*
-     Returns true (1) if file is being copied directly while reading, or false
+     Return true (1) if file is being copied directly while reading, or false
    (0) if file is a gzip stream being decompressed.
 
      If the input file is empty, gzdirect() will return true, since the input
@@ -1631,8 +1630,8 @@
 
 ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
 /*
-     Flushes all pending output if necessary, closes the compressed file and
-   deallocates the (de)compression state.  Note that once file is closed, you
+     Flush all pending output for file, if necessary, close file and
+   deallocate the (de)compression state.  Note that once file is closed, you
    cannot call gzerror with file, since its structures have been deallocated.
    gzclose must not be called more than once on the same file, just as free
    must not be called more than once on the same allocation.
@@ -1656,10 +1655,10 @@
 
 ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
 /*
-     Returns the error message for the last error which occurred on the given
-   compressed file.  errnum is set to zlib error number.  If an error occurred
-   in the file system and not in the compression library, errnum is set to
-   Z_ERRNO and the application may consult errno to get the exact error code.
+     Return the error message for the last error which occurred on file.
+   errnum is set to zlib error number.  If an error occurred in the file system
+   and not in the compression library, errnum is set to Z_ERRNO and the
+   application may consult errno to get the exact error code.
 
      The application must not modify the returned string.  Future calls to
    this function may invalidate the previously returned string.  If file is
@@ -1672,7 +1671,7 @@
 
 ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
 /*
-     Clears the error and end-of-file flags for file.  This is analogous to the
+     Clear the error and end-of-file flags for file.  This is analogous to the
    clearerr() function in stdio.  This is useful for continuing to read a gzip
    file that is being written concurrently.
 */
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ff7aa055..3004566 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -4086,6 +4086,16 @@
       label="inotify exceeds its limit while processing a filesystem change"/>
 </enum>
 
+<enum name="ArchiveAnalysisResult">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Unspecified"/>
+  <int value="2" label="Valid"/>
+  <int value="3" label="Too large"/>
+  <int value="4" label="Timeout"/>
+  <int value="5" label="Failed to open archive"/>
+  <int value="6" label="Failed to open temporary file"/>
+</enum>
+
 <enum name="ArchivemountError">
   <obsolete>
     Deprecated 10/2021. Replaced by FuseArchiveError.
@@ -52496,6 +52506,7 @@
   <int value="-2002461806" label="ShoppingAssist:enabled"/>
   <int value="-2001869199" label="ShillSandboxing:enabled"/>
   <int value="-2000567059" label="SimplifyHttpsIndicator:enabled"/>
+  <int value="-2000177300" label="JourneysLabel:disabled"/>
   <int value="-1999892428" label="force-ui-direction"/>
   <int value="-1999824144" label="NewDragSpecInLauncher:disabled"/>
   <int value="-1999617045" label="PDFAnnotations:disabled"/>
@@ -53195,6 +53206,7 @@
   <int value="-1536242739" label="security-chip"/>
   <int value="-1535758690" label="AutoplayIgnoreWebAudio:disabled"/>
   <int value="-1535694535" label="PageInfoPerformanceHints:enabled"/>
+  <int value="-1535608870" label="JourneysLabel:enabled"/>
   <int value="-1534970225" label="FilesArchivemount:disabled"/>
   <int value="-1533258008" label="CalculateNativeWinOcclusion:enabled"/>
   <int value="-1532720464" label="WellKnownChangePassword:enabled"/>
@@ -53624,6 +53636,7 @@
   <int value="-1271563519" label="enable-appcontainer"/>
   <int value="-1271441871" label="AmbientColor:enabled"/>
   <int value="-1270634957" label="PhoneHubUseBle:enabled"/>
+  <int value="-1270241570" label="TerminalDev:disabled"/>
   <int value="-1270082532" label="BluetoothRevamp:disabled"/>
   <int value="-1269962982" label="SyncUSSPasswords:disabled"/>
   <int value="-1269093329" label="AndroidOmniboxPreviewsBadge:disabled"/>
@@ -53922,6 +53935,7 @@
   <int value="-1074257709" label="ScalableAppList:enabled"/>
   <int value="-1074107607" label="data-reduction-proxy-experiment"/>
   <int value="-1073479583" label="ShowArcFilesApp:disabled"/>
+  <int value="-1073388569" label="TerminalDev:enabled"/>
   <int value="-1072337227" label="FilesArchivemount2:disabled"/>
   <int value="-1071471678" label="enable-finch-seed-delta-compression"/>
   <int value="-1071043945" label="VerticalSnap:enabled"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index 4b567251..deedbe2 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -3766,16 +3766,17 @@
 </histogram>
 
 <histogram name="Android.WebView.LoadUrl.UrlScheme" enum="WebViewUrlScheme"
-    expires_after="2022-03-29">
+    expires_after="2023-04-19">
   <owner>ntfschr@chromium.org</owner>
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
     Records the scheme for the URL loaded by a browser-initiated navigation.
     Specifically, this is the scheme of the URL passed to loadUrl() (and its
-    overloads), postUrl(), and loadData (this is implicitly &quot;data:&quot;).
-    also records if this value is &quot;empty&quot;, which means either null or
-    &quot;about:blank&quot;. This intentionally excludes loadDataWithBaseURL().
+    overloads), postUrl(), and loadData() (this is implicitly
+    &quot;data:&quot;). also records if this value is &quot;empty&quot;, which
+    means either null or &quot;about:blank&quot;. This intentionally excludes
+    loadDataWithBaseURL().
   </summary>
 </histogram>
 
@@ -3907,6 +3908,9 @@
 
 <histogram name="Android.WebView.onReceivedError.ErrorCode"
     enum="WebViewClientErrorCode" expires_after="2022-05-10">
+  <obsolete>
+    Deprecated in April 2022 (milestone M103).
+  </obsolete>
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -3917,13 +3921,13 @@
 </histogram>
 
 <histogram name="Android.WebView.onReceivedHttpError.StatusCode"
-    enum="HttpResponseCode" expires_after="2022-05-10">
+    enum="HttpResponseCode" expires_after="2023-04-19">
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
-    The WebViewClient http resonse status code as returned by the
-    onReceivedHttpError callback This is recorded regardless of whether the
-    network service is enabled or disabled.
+    The WebViewClient HTTP response status code as returned by the
+    onReceivedHttpError callback. This callback is only called for HTTP errors,
+    so this does not include any successful HTTP responses (code 200).
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index adf0e3b..bdf718c 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -268,7 +268,7 @@
 </histogram>
 
 <histogram name="Cryptohome.HomedirEncryptionType" enum="HomedirEncryptionType"
-    expires_after="2022-04-24">
+    expires_after="2023-04-24">
   <owner>dspaid@chromium.org</owner>
   <summary>
     The encryption type used for a user's cryptohome directory. This is logged
diff --git a/tools/metrics/histograms/metadata/language/histograms.xml b/tools/metrics/histograms/metadata/language/histograms.xml
index 2573c1a7..34f6d98 100644
--- a/tools/metrics/histograms/metadata/language/histograms.xml
+++ b/tools/metrics/histograms/metadata/language/histograms.xml
@@ -90,6 +90,19 @@
   </summary>
 </histogram>
 
+<histogram name="LanguageSettings.AppLanguagePrompt.HasTopULPMatch"
+    enum="BooleanYesNo" expires_after="2022-09-11">
+  <owner>perrier@chromium.org</owner>
+  <owner>chrome-language@google.com</owner>
+  <summary>
+    Whether or not the Chrome UI language matches the top ULP language when the
+    AppLanguagePrompt should be shown. Logged when checking if the prompt should
+    be shown and it has not already been shown.
+
+    Only the base languages are compared so pt-PT is considered equal to pt-BR.
+  </summary>
+</histogram>
+
 <histogram name="LanguageSettings.AppLanguagePrompt.IsOnline"
     enum="BooleanYesNo" expires_after="2022-09-11">
   <owner>perrier@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 8d2ef3a..0af78f4 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -2550,7 +2550,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.WebView.AppOptIn" enum="SafeBrowsingAppOptIn"
-    expires_after="2022-06-01">
+    expires_after="2023-04-19">
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 345ca0b..88de9bf 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -73,6 +73,9 @@
 
 <histogram name="SBClientDownload.DmgAnalysisTimedOut" enum="BooleanTimedOut"
     expires_after="2022-09-30">
+  <obsolete>
+    Replaced with SBClientDownload.DmgArchiveAnalysisResult in 04-2022
+  </obsolete>
   <owner>drubery@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -427,6 +430,21 @@
   </token>
 </histogram>
 
+<histogram name="SBClientDownload.{ArchiveType}ArchiveAnalysisResult"
+    enum="ArchiveAnalysisResult" expires_after="2022-10-26">
+  <owner>drubery@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    Records a more granular reason why we failed to unpack a {ArchiveType} file.
+    This is recorded on every download of an {ArchiveType} file.
+  </summary>
+  <token key="ArchiveType">
+    <variant name="Dmg"/>
+    <variant name="Rar"/>
+    <variant name="Zip"/>
+  </token>
+</histogram>
+
 <histogram name="SBClientPhishing.ApplyTfliteTime.{Operation}" units="ms"
     expires_after="2023-03-18">
   <owner>drubery@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index 76edabd..19692e6 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -312,9 +312,9 @@
 </histogram>
 
 <histogram name="Search.ContextualSearch.TranslationNeeded"
-    enum="ContextualSearchGestureIsTap" expires_after="2022-06-05">
+    enum="ContextualSearchGestureIsTap" expires_after="2022-08-05">
   <owner>donnd@chromium.org</owner>
-  <owner>twellington@chromium.org</owner>
+  <owner>contextual-search-eng@google.com</owner>
   <summary>
     Records that a translation is needed for the current Contextual Search, and
     whether the selection was originally created by a Tap gesture. This is
diff --git a/tools/perf/core/bootstrap.py b/tools/perf/core/bootstrap.py
index 53dee9e..2999ccd 100644
--- a/tools/perf/core/bootstrap.py
+++ b/tools/perf/core/bootstrap.py
@@ -20,14 +20,14 @@
     given deps file, and all of its sub-dependencies. This amounts to
     the keys of the 'deps' dictionary.
   """
-  deps = {}
-  deps_includes = {}
-
   chrome_root = os.path.dirname(__file__)
   while os.path.basename(chrome_root) != 'src':
     chrome_root = os.path.abspath(os.path.join(chrome_root, '..'))
 
-  exec (open(deps_file).read())  # pylint: disable=exec-used
+  loaded = {}
+  exec(open(deps_file).read(), globals(), loaded)  # pylint: disable=exec-used
+  deps = loaded.get('deps', {})
+  deps_includes = loaded.get('deps_includes', {})
 
   deps_paths = list(deps.keys())
 
diff --git a/tools/typescript/ts_definitions.py b/tools/typescript/ts_definitions.py
index 9ebba67ff..f9ffa925 100644
--- a/tools/typescript/ts_definitions.py
+++ b/tools/typescript/ts_definitions.py
@@ -49,6 +49,10 @@
   tsconfig['files'] = [os.path.join(root_dir, f) for f in args.js_files]
   tsconfig['compilerOptions']['rootDir'] = root_dir
   tsconfig['compilerOptions']['outDir'] = out_dir
+  if tsconfig['compilerOptions']['typeRoots'] is not None:
+    tsconfig['compilerOptions']['typeRoots'] = \
+        [os.path.relpath(os.path.join(_HERE_DIR, f), args.gen_dir) for f \
+             in tsconfig['compilerOptions']['typeRoots']]
 
   # Handle custom path mappings, for example chrome://resources/ URLs.
   if args.path_mappings is not None:
diff --git a/tools/typescript/tsconfig_definitions_base.json b/tools/typescript/tsconfig_definitions_base.json
index 5787be4..d366097 100644
--- a/tools/typescript/tsconfig_definitions_base.json
+++ b/tools/typescript/tsconfig_definitions_base.json
@@ -9,6 +9,12 @@
     "emitDeclarationOnly": true,
     "pretty": true,
     "removeComments": true,
-    "listEmittedFiles": true
+    "listEmittedFiles": true,
+    "typeRoots": [
+      "../../third_party/node/node_modules/@types"
+    ],
+    "types": [
+      "trusted-types"
+    ]
   }
 }
diff --git a/ui/color/color_id.h b/ui/color/color_id.h
index 13c1a8c..14d91a0 100644
--- a/ui/color/color_id.h
+++ b/ui/color/color_id.h
@@ -184,6 +184,17 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 #define PLATFORM_SPECIFIC_COLOR_IDS \
+  E_CPONLY(kColorAshSystemUIBorderColor1) \
+  E_CPONLY(kColorAshSystemUIBorderColor2) \
+  E_CPONLY(kColorAshSystemUIHighlightColor1) \
+  E_CPONLY(kColorAshSystemUIHighlightColor2) \
+  \
+  /* TODO(crbug/1319917): Remove these when dark light mode is launched. */ \
+  E_CPONLY(kColorAshSystemUILightBorderColor1) \
+  E_CPONLY(kColorAshSystemUILightBorderColor2) \
+  E_CPONLY(kColorAshSystemUILightHighlightColor1) \
+  E_CPONLY(kColorAshSystemUILightHighlightColor2) \
+  \
   E_CPONLY(kColorAshSystemUIMenuBackground) \
   E_CPONLY(kColorAshSystemUIMenuIcon) \
   E_CPONLY(kColorAshSystemUIMenuItemBackgroundSelected) \
diff --git a/ui/ozone/platform/x11/vulkan_surface_x11.cc b/ui/ozone/platform/x11/vulkan_surface_x11.cc
index 7f7a6ca..b79baa23 100644
--- a/ui/ozone/platform/x11/vulkan_surface_x11.cc
+++ b/ui/ozone/platform/x11/vulkan_surface_x11.cc
@@ -41,6 +41,7 @@
     return nullptr;
   }
 
+  // TODO(penghuang): using the same xcb connection for VulkanSurface.
   VkSurfaceKHR vk_surface;
   const VkXcbSurfaceCreateInfoKHR surface_create_info = {
       .sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
@@ -104,10 +105,13 @@
                                gfx::OverlayTransform pre_transform) {
   DCHECK_EQ(pre_transform, gfx::OVERLAY_TRANSFORM_NONE);
 
-  auto* connection = x11::Connection::Get();
-  connection->ConfigureWindow(x11::ConfigureWindowRequest{
-      .window = window_, .width = size.width(), .height = size.height()});
-  connection->Flush();
+  // Vulkan WSI uses a separate xcb connection, so we need to synchronize
+  // ConfigureWindow call.
+  x11::Connection::Get()
+      ->ConfigureWindow(x11::ConfigureWindowRequest{
+          .window = window_, .width = size.width(), .height = size.height()})
+      .Sync();
+
   return VulkanSurface::Reshape(size, pre_transform);
 }
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 5e068e9a..45adb61 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -553,7 +553,12 @@
   ]
 
   if (is_chromeos_ash) {
-    deps += [ "//ui/base/ime/ash" ]
+    public += [ "highlight_border.h" ]
+    sources += [ "highlight_border.cc" ]
+    deps += [
+      "//ash/constants",
+      "//ui/base/ime/ash",
+    ]
   }
 
   if (is_linux || is_chromeos || is_fuchsia) {
diff --git a/ui/views/DEPS b/ui/views/DEPS
index 7157b07..e751e98 100644
--- a/ui/views/DEPS
+++ b/ui/views/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/constants",
   "+cc/paint",
   "+components/crash/core/common/crash_key.h",
   "+components/remote_cocoa",
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc
index b78729cd..1ffec70 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -33,11 +33,16 @@
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/submenu_view.h"
+#include "ui/views/highlight_border.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/round_rect_painter.h"
 #include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/constants/ash_features.h"
+#endif
+
 namespace views {
 
 namespace {
@@ -440,12 +445,20 @@
 
   corner_radius_ = bubble_border->corner_radius();
   // If the menu uses Ash system UI layout, use `background_view` to build a
-  // blurry background. Otherwise, use default BubbleBackground.
+  // blurry background with highlight border. Otherwise, use default
+  // BubbleBackground.
   if (use_ash_system_ui_layout_) {
     background_view_->layer()->SetRoundedCornerRadius(
         gfx::RoundedCornersF(corner_radius_));
     background_view_->SetBackground(
         CreateThemedRoundedRectBackground(id, corner_radius_));
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+    if (ash::features::IsDarkLightModeEnabled()) {
+      background_view_->SetBorder(std::make_unique<HighlightBorder>(
+          corner_radius_, HighlightBorder::Type::kHighlightBorder1,
+          /*use_light_colors=*/false));
+    }
+#endif
   } else {
     SetBackground(std::make_unique<BubbleBackground>(bubble_border.get()));
   }
diff --git a/ash/style/highlight_border.cc b/ui/views/highlight_border.cc
similarity index 63%
rename from ash/style/highlight_border.cc
rename to ui/views/highlight_border.cc
index 90362ae..94243c2 100644
--- a/ash/style/highlight_border.cc
+++ b/ui/views/highlight_border.cc
@@ -1,12 +1,12 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/style/highlight_border.h"
+#include "ui/views/highlight_border.h"
 
 #include "ash/constants/ash_features.h"
-#include "ash/public/cpp/style/scoped_light_mode_as_default.h"
-#include "ash/style/ash_color_provider.h"
+#include "ui/color/color_id.h"
+#include "ui/color/color_provider.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/rounded_corners_f.h"
@@ -14,37 +14,47 @@
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/views/view.h"
 
-namespace ash {
+namespace views {
 
 constexpr int kHighlightBorderThickness = 1;
 
 // static
 void HighlightBorder::PaintBorderToCanvas(
     gfx::Canvas* canvas,
+    const views::View& view,
     const gfx::Rect& bounds,
     const gfx::RoundedCornersF& corner_radii,
     Type type,
     bool use_light_colors) {
-  AshColorProvider* color_provider = AshColorProvider::Get();
-  const AshColorProvider::ControlsLayerType highlight_color_type =
-      type == HighlightBorder::Type::kHighlightBorder1
-          ? AshColorProvider::ControlsLayerType::kHighlightColor1
-          : AshColorProvider::ControlsLayerType::kHighlightColor2;
-  const AshColorProvider::ControlsLayerType border_color_type =
-      type == HighlightBorder::Type::kHighlightBorder1
-          ? AshColorProvider::ControlsLayerType::kBorderColor1
-          : AshColorProvider::ControlsLayerType::kBorderColor2;
-  SkColor inner_color =
-      color_provider->GetControlsLayerColor(highlight_color_type);
-  SkColor outer_color =
-      color_provider->GetControlsLayerColor(border_color_type);
-
-  if (use_light_colors && !features::IsDarkLightModeEnabled()) {
-    ScopedLightModeAsDefault scoped_light_mode_as_default;
-    inner_color = color_provider->GetControlsLayerColor(highlight_color_type);
-    outer_color = color_provider->GetControlsLayerColor(border_color_type);
+  ui::ColorId highlight_color_id;
+  ui::ColorId border_color_id;
+  if (use_light_colors) {
+    // TODO(crbug/1319917): These light color values are used here since we want
+    // to use light colors when dark/light mode feature is not enabled. This
+    // should be removed after dark light mode is launched.
+    DCHECK(!ash::features::IsDarkLightModeEnabled());
+    highlight_color_id = type == HighlightBorder::Type::kHighlightBorder1
+                             ? ui::kColorAshSystemUILightHighlightColor1
+                             : ui::kColorAshSystemUILightHighlightColor2;
+    border_color_id = type == HighlightBorder::Type::kHighlightBorder1
+                          ? ui::kColorAshSystemUILightBorderColor1
+                          : ui::kColorAshSystemUILightBorderColor2;
+  } else {
+    highlight_color_id = type == HighlightBorder::Type::kHighlightBorder1
+                             ? ui::kColorAshSystemUIHighlightColor1
+                             : ui::kColorAshSystemUIHighlightColor2;
+    border_color_id = type == HighlightBorder::Type::kHighlightBorder1
+                          ? ui::kColorAshSystemUIBorderColor1
+                          : ui::kColorAshSystemUIBorderColor2;
   }
 
+  // `view` should be embedded in a Widget to use color provider.
+  DCHECK(view.GetWidget());
+  const SkColor inner_color =
+      view.GetColorProvider()->GetColor(highlight_color_id);
+  const SkColor outer_color =
+      view.GetColorProvider()->GetColor(border_color_id);
+
   cc::PaintFlags flags;
   flags.setStrokeWidth(kHighlightBorderThickness);
   flags.setColor(outer_color);
@@ -90,7 +100,7 @@
       insets_type_(insets_type) {}
 
 void HighlightBorder::Paint(const views::View& view, gfx::Canvas* canvas) {
-  PaintBorderToCanvas(canvas, view.GetLocalBounds(),
+  PaintBorderToCanvas(canvas, view, view.GetLocalBounds(),
                       gfx::RoundedCornersF(corner_radius_), type_,
                       use_light_colors_);
 }
@@ -111,4 +121,4 @@
                    kHighlightBorderThickness * 4);
 }
 
-}  // namespace ash
+}  // namespace views
diff --git a/ash/style/highlight_border.h b/ui/views/highlight_border.h
similarity index 76%
rename from ash/style/highlight_border.h
rename to ui/views/highlight_border.h
index 33499135..784e050 100644
--- a/ash/style/highlight_border.h
+++ b/ui/views/highlight_border.h
@@ -1,24 +1,25 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
+// Copyright 2022 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_STYLE_HIGHLIGHT_BORDER_H_
-#define ASH_STYLE_HIGHLIGHT_BORDER_H_
+#ifndef UI_VIEWS_HIGHLIGHT_BORDER_H_
+#define UI_VIEWS_HIGHLIGHT_BORDER_H_
 
-#include "ash/ash_export.h"
 #include "ui/views/border.h"
+#include "ui/views/views_export.h"
 
 namespace gfx {
 class Rect;
 class RoundedCornersF;
 }  // namespace gfx
 
-namespace ash {
+namespace views {
 
 // A rounded rectangle border that has inner (highlight) and outer color.
 // Useful when go/cros-launcher-spec mentions "BorderHighlight".
-class ASH_EXPORT HighlightBorder : public views::Border {
+class VIEWS_EXPORT HighlightBorder : public views::Border {
  public:
+  // TODO(crbug/1319944): Change these type names to something more descriptive.
   enum class Type {
     // A higher contrast highlight border than the `kHighlightBorder2` used
     // for floating components that do not have a shield below.
@@ -44,10 +45,12 @@
     kFullInsets,
   };
 
-  // Paints the highlight border onto `canvas`. Note that directly using this
-  // function won't set the insets on any view so it acts like setting kNoInsets
-  // when using HighlightBorder class.
+  // Paints the highlight border onto `canvas` for the specified `view`. The
+  // color of the border will be determined using `view`'s color provider. Note
+  // that directly using this function won't set the insets on any view so it
+  // acts like setting kNoInsets when using HighlightBorder class.
   static void PaintBorderToCanvas(gfx::Canvas* canvas,
+                                  const views::View& view,
                                   const gfx::Rect& bounds,
                                   const gfx::RoundedCornersF& corner_radii,
                                   Type type,
@@ -80,6 +83,6 @@
   const InsetsType insets_type_;
 };
 
-}  // namespace ash
+}  // namespace views
 
-#endif  // ASH_STYLE_HIGHLIGHT_BORDER_H_
+#endif  // UI_VIEWS_HIGHLIGHT_BORDER_H_
diff --git a/ui/webui/resources/BUILD.gn b/ui/webui/resources/BUILD.gn
index 2679264..4c303a1 100644
--- a/ui/webui/resources/BUILD.gn
+++ b/ui/webui/resources/BUILD.gn
@@ -193,6 +193,7 @@
   "js/list_property_update_behavior.m.d.ts",
   "js/parse_html_subset.m.d.ts",
   "js/promise_resolver.m.d.ts",
+  "js/static_types.d.ts",
   "js/web_ui_listener_behavior.m.d.ts",
 ]
 
@@ -296,7 +297,6 @@
       "js/cr/ui/splitter.js",
       "js/cr/ui/store_client.js",
       "js/cr/ui/tree.js",
-      "js/static_types.js",
     ]
   }
 }
diff --git a/ui/webui/resources/cr_elements/cr_tab_box/cr_tab_box.ts b/ui/webui/resources/cr_elements/cr_tab_box/cr_tab_box.ts
index 3f3236e..4b5bc59 100644
--- a/ui/webui/resources/cr_elements/cr_tab_box/cr_tab_box.ts
+++ b/ui/webui/resources/cr_elements/cr_tab_box/cr_tab_box.ts
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert_ts.js';
-import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
-import {CustomElement} from 'chrome://resources/js/custom_element.js';
+import {assert} from '../../js/assert_ts.js';
+import {FocusOutlineManager} from '../../js/cr/ui/focus_outline_manager.m.js';
+import {CustomElement} from '../../js/custom_element.js';
+import {getTrustedHTML} from '../../js/static_types.js';
 
 export class CrTabBoxElement extends CustomElement {
   static override get template() {
-    return `{__html_template__}`;
+    return getTrustedHTML`{__html_template__}`;
   }
 
   private selectedIndex_: number = -1;
diff --git a/ui/webui/resources/js/custom_element.ts b/ui/webui/resources/js/custom_element.ts
index b2f0f1a2..a83ae55a 100644
--- a/ui/webui/resources/js/custom_element.ts
+++ b/ui/webui/resources/js/custom_element.ts
@@ -2,15 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+
 /**
  * @fileoverview Base class for Web Components that don't use Polymer.
  * See the following file for usage:
  * chrome/test/data/webui/js/custom_element_test.js
  */
 
+function emptyHTML(): string|TrustedHTML {
+  return window.trustedTypes ? window.trustedTypes.emptyHTML : '';
+}
+
 export class CustomElement extends HTMLElement {
-  static get template(): string {
-    return '';
+  static get template() {
+    return emptyHTML();
   }
 
   constructor() {
@@ -18,8 +23,11 @@
 
     this.attachShadow({mode: 'open'});
     const template = document.createElement('template');
-    template.innerHTML =
-        (this.constructor as typeof CustomElement).template || '';
+    const html =
+        (this.constructor as typeof CustomElement).template || emptyHTML();
+    // This is a workaround for the fact that the innerHTML setter only accepts
+    // a string and not TrustedHTML.
+    template.innerHTML = html as unknown as string;
     this.shadowRoot!.appendChild(template.content.cloneNode(true));
   }
 
diff --git a/ui/webui/resources/js/static_types.d.ts b/ui/webui/resources/js/static_types.d.ts
new file mode 100644
index 0000000..3e605bd
--- /dev/null
+++ b/ui/webui/resources/js/static_types.d.ts
@@ -0,0 +1,8 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+export function getTrustedHTML(literal: string[]|
+                               TemplateStringsArray): TrustedHTML|string;
+export function getTrustedScript(literal: string[]): TrustedScript|string;
+export function getTrustedScriptURL(literal: string[]): TrustedScriptURL|string;
diff --git a/ui/wm/core/compound_event_filter.cc b/ui/wm/core/compound_event_filter.cc
index 61d5794e..a5ec2ebe 100644
--- a/ui/wm/core/compound_event_filter.cc
+++ b/ui/wm/core/compound_event_filter.cc
@@ -28,7 +28,7 @@
 // Returns true if the cursor should be hidden on touch events.
 // TODO(tdanderson|rsadam): Move this function into CursorClient.
 bool ShouldHideCursorOnTouch(const ui::TouchEvent& event) {
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
   return true;
 #else
   // Linux Aura does not hide the cursor on touch by default.
diff --git a/weblayer/browser/no_state_prefetch/prerender_tab_helper.cc b/weblayer/browser/no_state_prefetch/prerender_tab_helper.cc
index 944e081..65257ab 100644
--- a/weblayer/browser/no_state_prefetch/prerender_tab_helper.cc
+++ b/weblayer/browser/no_state_prefetch/prerender_tab_helper.cc
@@ -17,15 +17,9 @@
 
 PrerenderTabHelper::~PrerenderTabHelper() = default;
 
-void PrerenderTabHelper::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // TODO(https://crbug.com/1218946): With MPArch there may be multiple main
-  // frames. This caller was converted automatically to the primary main frame
-  // to preserve its semantics. Follow up to confirm correctness.
-  if (!navigation_handle->IsInPrimaryMainFrame() ||
-      !navigation_handle->HasCommitted() || navigation_handle->IsErrorPage()) {
+void PrerenderTabHelper::PrimaryPageChanged(content::Page& page) {
+  if (page.GetMainDocument().IsErrorDocument())
     return;
-  }
 
   prerender::NoStatePrefetchManager* no_state_prefetch_manager =
       NoStatePrefetchManagerFactory::GetForBrowserContext(
@@ -33,7 +27,8 @@
 
   if (no_state_prefetch_manager &&
       !no_state_prefetch_manager->IsWebContentsPrefetching(web_contents()))
-    no_state_prefetch_manager->RecordNavigation(navigation_handle->GetURL());
+    no_state_prefetch_manager->RecordNavigation(
+        page.GetMainDocument().GetLastCommittedURL());
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(PrerenderTabHelper);
diff --git a/weblayer/browser/no_state_prefetch/prerender_tab_helper.h b/weblayer/browser/no_state_prefetch/prerender_tab_helper.h
index 9ca3a4f..5d4f344f 100644
--- a/weblayer/browser/no_state_prefetch/prerender_tab_helper.h
+++ b/weblayer/browser/no_state_prefetch/prerender_tab_helper.h
@@ -29,8 +29,7 @@
   PrerenderTabHelper& operator=(const PrerenderTabHelper&) = delete;
 
   // content::WebContentsObserver implementation.
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
+  void PrimaryPageChanged(content::Page& page) override;
 
  private:
   explicit PrerenderTabHelper(content::WebContents* web_contents);
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index e014db1..c8a04f38 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -1407,16 +1407,13 @@
 
   AutofillClientImpl::CreateForWebContents(web_contents);
 
-  autofill::AutofillManager::AutofillDownloadManagerState
-      enable_autofill_download_manager =
-          autofill::AutofillProvider::is_download_manager_disabled_for_testing()
-              ? autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER
-              : autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER;
-
   autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       web_contents, AutofillClientImpl::FromWebContents(web_contents),
-      i18n::GetApplicationLocale(), enable_autofill_download_manager,
-      base::BindRepeating(&autofill::AndroidAutofillManager::Create));
+      base::BindRepeating(&autofill::AndroidDriverInitHook,
+                          AutofillClientImpl::FromWebContents(web_contents),
+                          autofill::AutofillManager::EnableDownloadManager(
+                              !autofill::AutofillProvider::
+                                  is_download_manager_disabled_for_testing())));
 }
 
 #endif  // BUILDFLAG(IS_ANDROID)