diff --git a/DEPS b/DEPS
index 13a3814..4c6a8b2 100644
--- a/DEPS
+++ b/DEPS
@@ -253,11 +253,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '72a425bab711d109a9b663966eaaa1197025b0a8',
+  'skia_revision': 'ede5dae4db59840ef7d6e76496f92ec8d10728ef',
   # 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': '4e1875f07b4c8f59714d190c6e7999115c8af430',
+  'v8_revision': '53958a11c7583d9e0e2eaaeb000ea418ae67efdd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -265,7 +265,7 @@
   # 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': '89c498edff683c68df6be0dd224848f4b4af5ae7',
+  'swiftshader_revision': '79d4c6cae485c50d2d49aa1b3defe601dc23f145',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -320,7 +320,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '74c0bf3d684ebb866442b4c5cd7f4ba3230d67d0',
+  'catapult_revision': '46b602cdaf2c2983e0b5cdbb2eca3c711d1417e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -364,7 +364,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': '1d882f55b7f47dd06fe57efdf286100bf2a3ad6b',
+  'dawn_revision': '8e2a1f9ab93e38e1e22946a94d55b997c2d9600e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1106,7 +1106,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '7feb245cf57e743cb5d0acccc887bad15c73ff2d',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '87172073a6c87178dca378501fd56c52f6f76a4a',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1713,10 +1713,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'cf04aebdf9b53bb2853f22a81465688daf879ec6',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'd54181c886109c3e1e34930fbeb03ece2c1e56e4',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '9007653926414cb1afb3fe7e882a31ac296163c8',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b767350e486cb850a4e39f57b461f0cbaf25bb51',
+    Var('webrtc_git') + '/src.git' + '@' + '82caafd607f3f273d02476378a71b1ddafbeda0d',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1786,7 +1786,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@82e17f7a45a28363a30d0bbc9fe23ada6f44ee2e',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@df8bee5c5fa5f495230fc431738f9058a19e9170',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 827669ae..476d4bb 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -14,7 +14,11 @@
 import("//chrome/android/chrome_common_shared_library.gni")
 import("//chrome/android/chrome_public_apk_tmpl.gni")
 import("//chrome/android/trichrome.gni")
+import("//components/safe_browsing/buildflags.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
+import("//device/vr/buildflags/buildflags.gni")
+import("//pdf/features.gni")
+import("//printing/buildflags/buildflags.gni")
 import("//tools/grit/repack.gni")
 import("//tools/resources/generate_resource_allowlist.gni")
 import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
@@ -1113,6 +1117,11 @@
 
   use_brotli = true
 
+  defines = [
+    "enable_basic_printing=$enable_basic_printing",
+    "safe_browsing_mode=$safe_browsing_mode",
+  ]
+
   # See :generate_webui_resources for an explanation of the allowlist
   _allowlist = rebase_path("$target_gen_dir/grit_resources_allowlist.txt",
                            root_build_dir)
@@ -1135,6 +1144,12 @@
     "//components/resources:about_credits",
     "//components/resources/ssl/ssl_error_assistant:make_ssl_error_assistant_protobuf",
   ]
+
+  if (safe_browsing_mode != 0) {
+    deps += [
+      "//components/safe_browsing/content/resources:make_file_types_protobuf",
+    ]
+  }
 }
 
 grit("generate_components_scaled_resources") {
@@ -1163,6 +1178,12 @@
 grit("generate_components_strings") {
   source = "../components/components_strings.grd"
 
+  defines = [
+    "enable_pdf=$enable_pdf",
+    "enable_print_preview=$enable_print_preview",
+    "enable_vr=$enable_vr",
+  ]
+
   # components_strings contains strings from all components. WebView
   # will never display most of them, so we try to limit the included
   # strings. This allowlist trims about 50% more than the compile-based
diff --git a/android_webview/tools/system_webview_shell/BUILD.gn b/android_webview/tools/system_webview_shell/BUILD.gn
index a7ad137..ea387c6 100644
--- a/android_webview/tools/system_webview_shell/BUILD.gn
+++ b/android_webview/tools/system_webview_shell/BUILD.gn
@@ -62,10 +62,10 @@
   deps = [
     ":system_webview_shell_apk_resources",
     "//base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:guava_android_java",
     "//third_party/androidx:androidx_activity_activity_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_savedstate_savedstate_java",
     "//third_party/androidx:androidx_webkit_webkit_java",
   ]
@@ -109,7 +109,6 @@
     "apk/res/values/styles.xml",
     "apk/res/xml/network_security_config.xml",
   ]
-  deps = [ "//third_party/android_deps:android_support_v7_appcompat_java" ]
 }
 
 instrumentation_test_apk("system_webview_shell_page_cycler_apk") {
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 9c3e0be7..26b381c 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1742,8 +1742,6 @@
     "utility/rounded_window_targeter.h",
     "utility/transformer_util.cc",
     "utility/transformer_util.h",
-    "wallpaper/online_wallpaper_variant_info_fetcher.cc",
-    "wallpaper/online_wallpaper_variant_info_fetcher.h",
     "wallpaper/wallpaper_base_view.cc",
     "wallpaper/wallpaper_base_view.h",
     "wallpaper/wallpaper_constants.h",
@@ -2829,7 +2827,6 @@
     "utility/lottie_util_unittest.cc",
     "utility/occlusion_tracker_pauser_unittest.cc",
     "utility/rounded_window_targeter_unittest.cc",
-    "wallpaper/online_wallpaper_variant_info_fetcher_unittest.cc",
     "wallpaper/wallpaper_controller_unittest.cc",
     "wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc",
     "wallpaper/wallpaper_utils/wallpaper_resizer_unittest.cc",
diff --git a/ash/app_list/views/continue_task_container_view.cc b/ash/app_list/views/continue_task_container_view.cc
index 5ae9ec1..9f33b40 100644
--- a/ash/app_list/views/continue_task_container_view.cc
+++ b/ash/app_list/views/continue_task_container_view.cc
@@ -174,6 +174,15 @@
   } else {
     animations_timer_.Start(FROM_HERE, base::Seconds(2), base::DoNothing());
   }
+
+  auto* notifier = view_delegate_->GetNotifier();
+  if (notifier) {
+    // NOTE: Use `IsDrawn()` instead of `is_visible` to account for parent
+    // container visibility - `IsDrawn()` will return false if this view is
+    // visible but its parent is not.
+    notifier->NotifyContinueSectionVisibilityChanged(
+        SearchResultDisplayType::kContinue, IsDrawn());
+  }
 }
 
 bool ContinueTaskContainerView::OnKeyPressed(const ui::KeyEvent &event) {
diff --git a/ash/app_list/views/recent_apps_view.cc b/ash/app_list/views/recent_apps_view.cc
index 8bec8f08..83bfc572 100644
--- a/ash/app_list/views/recent_apps_view.cc
+++ b/ash/app_list/views/recent_apps_view.cc
@@ -212,8 +212,12 @@
       items.push_back(item);
   }
 
-  if (items.size() < kMinRecommendedApps)
+  if (items.size() < kMinRecommendedApps) {
+    if (auto* notifier = view_delegate_->GetNotifier()) {
+      notifier->NotifyResultsUpdated(SearchResultDisplayType::kRecentApps, {});
+    }
     return;
+  }
 
   if (auto* notifier = view_delegate_->GetNotifier()) {
     std::vector<AppListNotifier::Result> notifier_results;
@@ -253,7 +257,12 @@
 void RecentAppsView::UpdateVisibility() {
   const bool has_enough_apps = item_views_.size() >= kMinRecommendedApps;
   const bool hidden_by_user = view_delegate_->ShouldHideContinueSection();
-  SetVisible(has_enough_apps && !hidden_by_user);
+  const bool visible = has_enough_apps && !hidden_by_user;
+  SetVisible(visible);
+  if (auto* notifier = view_delegate_->GetNotifier()) {
+    notifier->NotifyContinueSectionVisibilityChanged(
+        SearchResultDisplayType::kRecentApps, visible);
+  }
 }
 
 int RecentAppsView::GetItemViewCount() const {
diff --git a/ash/capture_mode/capture_mode_camera_preview_view.cc b/ash/capture_mode/capture_mode_camera_preview_view.cc
index 0a2e81e..47cacd9 100644
--- a/ash/capture_mode/capture_mode_camera_preview_view.cc
+++ b/ash/capture_mode/capture_mode_camera_preview_view.cc
@@ -7,6 +7,7 @@
 #include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_util.h"
 #include "ash/resources/vector_icons/vector_icons.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/bind.h"
@@ -109,13 +110,8 @@
           AshColorProvider::BaseLayerType::kTransparent80),
       resize_button_->GetPreferredSize().height() / 2.f));
 
-  // Ensure that when `FadeInResizeButton` was called first time, it animates
-  // from 0 to 1.
-  resize_button_->layer()->SetOpacity(0);
-
-  // The resize button should be hidden by default so that it doesn't handle
-  // events.
-  resize_button_->SetVisible(false);
+  accessibility_observation_.Observe(Shell::Get()->accessibility_controller());
+  RefreshResizeButtonVisibility();
   UpdateResizeButtonTooltip();
 }
 
@@ -283,6 +279,14 @@
       gfx::Insets(views::FocusRing::kDefaultHaloThickness / 2));
 }
 
+void CameraPreviewView::OnAccessibilityStatusChanged() {
+  RefreshResizeButtonVisibility();
+}
+
+void CameraPreviewView::OnAccessibilityControllerShutdown() {
+  accessibility_observation_.Reset();
+}
+
 void CameraPreviewView::OnResizeButtonPressed() {
   camera_controller_->ToggleCameraPreviewSize();
   UpdateResizeButton();
@@ -357,7 +361,8 @@
   if (!is_collapsible_ || camera_controller_->is_drag_in_progress())
     return 0.f;
 
-  if (IsMouseHovered() || resize_button_->IsMouseHovered() ||
+  if (Shell::Get()->accessibility_controller()->IsSwitchAccessRunning() ||
+      IsMouseHovered() || resize_button_->IsMouseHovered() ||
       resize_button_->has_focus() || has_been_tapped_) {
     return 1.f;
   }
diff --git a/ash/capture_mode/capture_mode_camera_preview_view.h b/ash/capture_mode/capture_mode_camera_preview_view.h
index b8bda49..047d9b8 100644
--- a/ash/capture_mode/capture_mode_camera_preview_view.h
+++ b/ash/capture_mode/capture_mode_camera_preview_view.h
@@ -5,11 +5,14 @@
 #ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_PREVIEW_VIEW_H_
 #define ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_PREVIEW_VIEW_H_
 
+#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/accessibility_observer.h"
 #include "ash/capture_mode/camera_video_frame_renderer.h"
 #include "ash/capture_mode/capture_mode_button.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
 #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/geometry/size.h"
@@ -56,7 +59,8 @@
 // be responsible for painting the latest camera video frame inside its bounds.
 class CameraPreviewView
     : public views::View,
-      public CaptureModeSessionFocusCycler::HighlightableView {
+      public CaptureModeSessionFocusCycler::HighlightableView,
+      public AccessibilityObserver {
  public:
   METADATA_HEADER(CameraPreviewView);
 
@@ -104,6 +108,10 @@
   views::View* GetView() override;
   std::unique_ptr<views::HighlightPathGenerator> CreatePathGenerator() override;
 
+  // AccessibilityObserver:
+  void OnAccessibilityStatusChanged() override;
+  void OnAccessibilityControllerShutdown() override;
+
   base::OneShotTimer* resize_button_hide_timer_for_test() {
     return &resize_button_hide_timer_;
   }
@@ -169,6 +177,9 @@
   // True only while handling a gesture tap event on this view.
   bool has_been_tapped_ = false;
 
+  base::ScopedObservation<AccessibilityControllerImpl, AccessibilityObserver>
+      accessibility_observation_{this};
+
   base::WeakPtrFactory<CameraPreviewView> weak_ptr_factory_{this};
 };
 
diff --git a/ash/capture_mode/capture_mode_camera_unittests.cc b/ash/capture_mode/capture_mode_camera_unittests.cc
index 2baac7c..321a367 100644
--- a/ash/capture_mode/capture_mode_camera_unittests.cc
+++ b/ash/capture_mode/capture_mode_camera_unittests.cc
@@ -3398,6 +3398,114 @@
   }
 }
 
+// Tests that the resize button will stay visible after mouse exiting the
+// preview and time exceeding the predefined duration on mouse event when switch
+// access is enabled. And the resize button will behave in a default way if
+// switch access is not enabled.
+TEST_P(CaptureModeCameraPreviewTest,
+       ResizeButtonSwitchAccessVisibilityTestOnMouseEvent) {
+  UpdateDisplay("1366x768");
+
+  CaptureModeCameraController* camera_controller = GetCameraController();
+  AddDefaultCamera();
+  camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1));
+  auto* event_generator = GetEventGenerator();
+
+  for (const bool switch_access_enabled : {false, true}) {
+    AccessibilityControllerImpl* a11y_controller =
+        Shell::Get()->accessibility_controller();
+    a11y_controller->switch_access().SetEnabled(switch_access_enabled);
+    EXPECT_EQ(switch_access_enabled, a11y_controller->IsSwitchAccessRunning());
+
+    StartCaptureSessionWithParam();
+    views::Widget* preview_widget = camera_controller->camera_preview_widget();
+    DCHECK(preview_widget);
+    gfx::Rect preview_bounds = preview_widget->GetWindowBoundsInScreen();
+    CaptureModeButton* resize_button = GetPreviewResizeButton();
+
+    // Tests the default visibility of the resize button based on whether switch
+    // access is enabled or not.
+    EXPECT_EQ(resize_button->GetVisible(),
+              switch_access_enabled ? true : false);
+
+    event_generator->MoveMouseTo(preview_bounds.CenterPoint());
+    EXPECT_TRUE(resize_button->GetVisible());
+
+    auto outside_point = preview_bounds.origin();
+    outside_point.Offset(-1, -1);
+    event_generator->MoveMouseTo(outside_point);
+    base::OneShotTimer* timer = camera_controller->camera_preview_view()
+                                    ->resize_button_hide_timer_for_test();
+    timer->FireNow();
+    EXPECT_EQ(resize_button->GetVisible(),
+              switch_access_enabled ? true : false);
+
+    // Tests that the resize button will be hidden when start dragging the
+    // camera preview regardless of whether the switch access is enabled or not.
+    event_generator->MoveMouseTo(preview_bounds.CenterPoint());
+    EXPECT_TRUE(resize_button->GetVisible());
+    event_generator->PressLeftButton();
+    EXPECT_FALSE(resize_button->GetVisible());
+    event_generator->MoveMouseBy(-100, -100);
+    EXPECT_FALSE(resize_button->GetVisible());
+
+    // Tests that the resize button will be visible if the switch access is
+    // enabled after releasing the drag and not visible otherwise.
+    event_generator->ReleaseLeftButton();
+    EXPECT_EQ(resize_button->GetVisible(),
+              switch_access_enabled ? true : false);
+
+    CaptureModeController::Get()->Stop();
+  }
+}
+
+// Tests that the resize button will stay visible after tapping on the preview
+// and time exceeding the predefined duration on tap event when switch access is
+// enabled. And the resize button will behave in a default way if switch
+// access is not enabled.
+TEST_P(CaptureModeCameraPreviewTest,
+       ResizeButtonSwitchAccessVisibilityTestOnTapEvent) {
+  UpdateDisplay("1366x768");
+
+  SwitchToTabletMode();
+  EXPECT_TRUE(Shell::Get()->IsInTabletMode());
+
+  CaptureModeCameraController* camera_controller = GetCameraController();
+  AddDefaultCamera();
+  camera_controller->SetSelectedCamera(CameraId(kDefaultCameraModelId, 1));
+  auto* event_generator = GetEventGenerator();
+
+  for (const bool switch_access_enabled : {false, true}) {
+    AccessibilityControllerImpl* a11y_controller =
+        Shell::Get()->accessibility_controller();
+    a11y_controller->switch_access().SetEnabled(switch_access_enabled);
+    EXPECT_EQ(switch_access_enabled, a11y_controller->IsSwitchAccessRunning());
+
+    StartCaptureSessionWithParam();
+    views::Widget* preview_widget = camera_controller->camera_preview_widget();
+    DCHECK(preview_widget);
+    gfx::Rect preview_bounds = preview_widget->GetWindowBoundsInScreen();
+    CaptureModeButton* resize_button = GetPreviewResizeButton();
+
+    // Tests the default visibility of the resize button based on whether switch
+    // access is enabled or not.
+    EXPECT_EQ(resize_button->GetVisible(),
+              switch_access_enabled ? true : false);
+
+    event_generator->GestureTapAt(preview_bounds.CenterPoint());
+    EXPECT_TRUE(resize_button->GetVisible());
+
+    base::OneShotTimer* timer = camera_controller->camera_preview_view()
+                                    ->resize_button_hide_timer_for_test();
+    if (timer->IsRunning())
+      timer->FireNow();
+
+    EXPECT_EQ(resize_button->GetVisible(),
+              switch_access_enabled ? true : false);
+    CaptureModeController::Get()->Stop();
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          CaptureModeCameraPreviewTest,
                          testing::Values(CaptureModeSource::kFullscreen,
diff --git a/ash/components/arc/mojom/net.mojom b/ash/components/arc/mojom/net.mojom
index 2465a8b..463dca9 100644
--- a/ash/components/arc/mojom/net.mojom
+++ b/ash/components/arc/mojom/net.mojom
@@ -430,6 +430,31 @@
   // True if the network has been autodetected by the platform as a metered
   // network or if the user explicitly marked the network as metered in the UI.
   [MinVersion=14] bool is_metered;
+
+  // Routes of destinations for the host network in CIDR format. If null/empty,
+  // all (both IPv4 and IPv6) destinations are assumed to go through the host
+  // network. As long as there is at least one destination in the list,
+  // regardless of IPv4 or IPv6 family, then no default destinations for any
+  // IPv* family are assumed anymore.
+  //
+  // For example:
+  //   [ ] => Implies 0.0.0.0/0 and ::/0
+  //   [ 10.8.0.5/32 ] => 10.8.0.5/32 for IPv4, no IPv6
+  //   [ 2001:DB8::/32 ] => 2001:DB8::/32 for IPv6, no IPv4
+  //   [ 10.8.0.5/32, ::/0 ] => 10.8.0.5/32 for IPv4, all IPv6
+  //   [ 2001:DB88::/32, 0.0.0.0/0 ] => 2001::DB8::/32 for IPv6, all IPv4
+  //
+  // If the same destination is present in include_routes and exclude_routes,
+  // then exclude_routes will take precedence and the destination will be
+  // excluded from the host network.
+  [MinVersion=15] array<string>? include_routes;
+
+  // Routes of destinations to exclude for the host network in CIDR format. If
+  // null/empty, it is assumed no destinations are excluded. If the same
+  // destination is present in include_routes and exclude_routes, then
+  // exclude_routes will take precedence and the destination will be excluded
+  // from the host network.
+  [MinVersion=15] array<string>? exclude_routes;
 };
 
 // Describes a Wifi network configuration that ARC has requested the host to
diff --git a/ash/components/arc/net/arc_net_host_impl.cc b/ash/components/arc/net/arc_net_host_impl.cc
index 43b65f1..37a4201 100644
--- a/ash/components/arc/net/arc_net_host_impl.cc
+++ b/ash/components/arc/net/arc_net_host_impl.cc
@@ -276,6 +276,28 @@
   const int mtu = shill_ipconfig->FindIntPath(shill::kMtuProperty).value_or(0);
   if (mtu > 0)
     network->host_mtu = mtu;
+
+  if (const auto* include_routes_list =
+          shill_ipconfig->FindListKey(shill::kIncludedRoutesProperty)) {
+    for (const auto& include_routes_value :
+         include_routes_list->GetListDeprecated()) {
+      const std::string& include_route = include_routes_value.GetString();
+      if (!include_route.empty()) {
+        network->include_routes->push_back(include_route);
+      }
+    }
+  }
+
+  if (const auto* exclude_routes_list =
+          shill_ipconfig->FindListKey(shill::kExcludedRoutesProperty)) {
+    for (const auto& exclude_routes_value :
+         exclude_routes_list->GetListDeprecated()) {
+      const std::string& exclude_route = exclude_routes_value.GetString();
+      if (!exclude_route.empty()) {
+        network->exclude_routes->push_back(exclude_route);
+      }
+    }
+  }
 }
 
 arc::mojom::NetworkConfigurationPtr TranslateNetworkProperties(
@@ -286,6 +308,8 @@
   mojo->host_ipv6_global_addresses = std::vector<std::string>();
   mojo->host_search_domains = std::vector<std::string>();
   mojo->host_dns_addresses = std::vector<std::string>();
+  mojo->include_routes = std::vector<std::string>();
+  mojo->exclude_routes = std::vector<std::string>();
   mojo->connection_state =
       TranslateConnectionState(network_state->connection_state());
   mojo->guid = network_state->guid();
diff --git a/ash/public/cpp/app_list/app_list_notifier.h b/ash/public/cpp/app_list/app_list_notifier.h
index 7cd9840..967eb24 100644
--- a/ash/public/cpp/app_list/app_list_notifier.h
+++ b/ash/public/cpp/app_list/app_list_notifier.h
@@ -75,6 +75,11 @@
   virtual void AddObserver(Observer* observer) = 0;
   virtual void RemoveObserver(Observer* observer) = 0;
 
+  // Called when visibility of a container within the launcher continue section
+  // (continue task suggestions, or recent apps) changes.
+  virtual void NotifyContinueSectionVisibilityChanged(Location location,
+                                                      bool visible) = 0;
+
   // Called to indicate a search |result| has been launched at the UI surface
   // |location|.
   virtual void NotifyLaunched(Location location, const Result& result) = 0;
diff --git a/ash/services/ime/BUILD.gn b/ash/services/ime/BUILD.gn
index 3c16ddb..d2fe81d 100644
--- a/ash/services/ime/BUILD.gn
+++ b/ash/services/ime/BUILD.gn
@@ -19,10 +19,10 @@
   ]
 }
 
-source_set("shared_lib") {
+source_set("decoder") {
   sources = [
-    "ime_shared_lib.cc",
-    "ime_shared_lib.h",
+    "ime_decoder.cc",
+    "ime_decoder.h",
   ]
 
   deps = [
@@ -49,7 +49,7 @@
 
   deps = [
     ":constants",
-    ":shared_lib",
+    ":decoder",
     "//ash/constants",
     "//ash/services/ime/public/cpp:rulebased",
     "//ash/services/ime/public/cpp/shared_lib:interfaces",
@@ -66,7 +66,7 @@
 
   deps = [
     ":constants",
-    ":shared_lib",
+    ":decoder",
     "//base",
     "//sandbox/linux:sandbox_services",
     "//sandbox/policy",
@@ -84,7 +84,7 @@
     ":lib",
     ":test_support",
     "//ash/constants",
-    "//ash/services/ime:shared_lib",
+    "//ash/services/ime:decoder",
     "//ash/services/ime/public/mojom",
     "//base",
     "//base/test:test_support",
diff --git a/ash/services/ime/decoder/decoder_engine.cc b/ash/services/ime/decoder/decoder_engine.cc
index 9385d5d..66a2ca53 100644
--- a/ash/services/ime/decoder/decoder_engine.cc
+++ b/ash/services/ime/decoder/decoder_engine.cc
@@ -57,7 +57,7 @@
 
 DecoderEngine::DecoderEngine(
     ImeCrosPlatform* platform,
-    absl::optional<ImeSharedLib::EntryPoints> entry_points) {
+    absl::optional<ImeDecoder::EntryPoints> entry_points) {
   if (!entry_points) {
     LOG(WARNING) << "DecoderEngine INIT INCOMPLETE.";
     return;
diff --git a/ash/services/ime/decoder/decoder_engine.h b/ash/services/ime/decoder/decoder_engine.h
index ba23467f..0828e411 100644
--- a/ash/services/ime/decoder/decoder_engine.h
+++ b/ash/services/ime/decoder/decoder_engine.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SERVICES_IME_DECODER_DECODER_ENGINE_H_
 #define ASH_SERVICES_IME_DECODER_DECODER_ENGINE_H_
 
-#include "ash/services/ime/ime_shared_lib.h"
+#include "ash/services/ime/ime_decoder.h"
 #include "ash/services/ime/public/cpp/shared_lib/interfaces.h"
 #include "ash/services/ime/public/mojom/input_engine.mojom.h"
 #include "ash/services/ime/public/mojom/input_method.mojom.h"
@@ -29,9 +29,8 @@
 // shared lib, to facilitate accessing an IME engine therein via ProtoMode.
 class DecoderEngine : public mojom::InputChannel {
  public:
-  explicit DecoderEngine(
-      ImeCrosPlatform* platform,
-      absl::optional<ImeSharedLib::EntryPoints> entry_points);
+  explicit DecoderEngine(ImeCrosPlatform* platform,
+                         absl::optional<ImeDecoder::EntryPoints> entry_points);
 
   DecoderEngine(const DecoderEngine&) = delete;
   DecoderEngine& operator=(const DecoderEngine&) = delete;
@@ -50,7 +49,7 @@
                       ProcessMessageCallback callback) override;
 
  private:
-  absl::optional<ImeSharedLib::EntryPoints> decoder_entry_points_;
+  absl::optional<ImeDecoder::EntryPoints> decoder_entry_points_;
   mojo::ReceiverSet<mojom::InputChannel> decoder_channel_receivers_;
 };
 
diff --git a/ash/services/ime/decoder/system_engine.cc b/ash/services/ime/decoder/system_engine.cc
index 5aacddc..73fb6f0 100644
--- a/ash/services/ime/decoder/system_engine.cc
+++ b/ash/services/ime/decoder/system_engine.cc
@@ -15,7 +15,7 @@
 
 SystemEngine::SystemEngine(
     ImeCrosPlatform* platform,
-    absl::optional<ImeSharedLib::EntryPoints> entry_points) {
+    absl::optional<ImeDecoder::EntryPoints> entry_points) {
   if (!entry_points) {
     LOG(WARNING) << "SystemEngine INIT INCOMPLETE.";
     return;
diff --git a/ash/services/ime/decoder/system_engine.h b/ash/services/ime/decoder/system_engine.h
index 40aa93eb..cdda1cc 100644
--- a/ash/services/ime/decoder/system_engine.h
+++ b/ash/services/ime/decoder/system_engine.h
@@ -5,7 +5,7 @@
 #ifndef ASH_SERVICES_IME_DECODER_SYSTEM_ENGINE_H_
 #define ASH_SERVICES_IME_DECODER_SYSTEM_ENGINE_H_
 
-#include "ash/services/ime/ime_shared_lib.h"
+#include "ash/services/ime/ime_decoder.h"
 #include "ash/services/ime/public/cpp/shared_lib/interfaces.h"
 #include "ash/services/ime/public/mojom/connection_factory.mojom.h"
 #include "ash/services/ime/public/mojom/input_engine.mojom.h"
@@ -30,7 +30,7 @@
 class SystemEngine {
  public:
   explicit SystemEngine(ImeCrosPlatform* platform,
-                        absl::optional<ImeSharedLib::EntryPoints> entry_points);
+                        absl::optional<ImeDecoder::EntryPoints> entry_points);
 
   SystemEngine(const SystemEngine&) = delete;
   SystemEngine& operator=(const SystemEngine&) = delete;
@@ -49,7 +49,7 @@
   bool IsConnected();
 
  private:
-  absl::optional<ImeSharedLib::EntryPoints> decoder_entry_points_;
+  absl::optional<ImeDecoder::EntryPoints> decoder_entry_points_;
 };
 
 }  // namespace ime
diff --git a/ash/services/ime/decoder/system_engine_unittest.cc b/ash/services/ime/decoder/system_engine_unittest.cc
index f1d3f9b..fecf2c38 100644
--- a/ash/services/ime/decoder/system_engine_unittest.cc
+++ b/ash/services/ime/decoder/system_engine_unittest.cc
@@ -21,7 +21,7 @@
 class TestDecoderState;
 
 // The fake decoder state has to be available globally because
-// ImeSharedLib::EntryPoints is a list of stateless C functions, so the only way
+// ImeDecoder::EntryPoints is a list of stateless C functions, so the only way
 // to have a stateful fake is to have a global reference to it.
 TestDecoderState* g_test_decoder_state = nullptr;
 
@@ -78,10 +78,10 @@
   mojo::Remote<mojom::InputMethodHost> input_method_host;
 };
 
-ImeSharedLib::EntryPoints CreateDecoderEntryPoints(TestDecoderState* state) {
+ImeDecoder::EntryPoints CreateDecoderEntryPoints(TestDecoderState* state) {
   g_test_decoder_state = state;
 
-  ImeSharedLib::EntryPoints entry_points = {
+  ImeDecoder::EntryPoints entry_points = {
       .init_proto_mode = [](ImeCrosPlatform* platform) {},
       .close_proto_mode = []() {},
       .supports = [](const char* ime_spec) { return true; },
@@ -168,7 +168,7 @@
 
 TEST_F(SystemEngineTest, BindRequestConnectsInputMethod) {
   TestDecoderState state;
-  ImeSharedLib::EntryPoints entry_points = CreateDecoderEntryPoints(&state);
+  ImeDecoder::EntryPoints entry_points = CreateDecoderEntryPoints(&state);
   SystemEngine engine(/*platform=*/nullptr, entry_points);
 
   mojo::Remote<mojom::InputMethod> input_method;
@@ -184,7 +184,7 @@
 
 TEST_F(SystemEngineTest, CanSendMessagesAfterBinding) {
   TestDecoderState state;
-  ImeSharedLib::EntryPoints entry_points = CreateDecoderEntryPoints(&state);
+  ImeDecoder::EntryPoints entry_points = CreateDecoderEntryPoints(&state);
   SystemEngine engine(/*platform=*/nullptr, entry_points);
 
   mojo::Remote<mojom::InputMethod> input_method;
@@ -203,7 +203,7 @@
 
 TEST_F(SystemEngineTest, CanReceiveMessagesAfterBinding) {
   TestDecoderState state;
-  ImeSharedLib::EntryPoints entry_points = CreateDecoderEntryPoints(&state);
+  ImeDecoder::EntryPoints entry_points = CreateDecoderEntryPoints(&state);
   SystemEngine engine(/*platform=*/nullptr, entry_points);
 
   mojo::Remote<mojom::InputMethod> input_method;
diff --git a/ash/services/ime/ime_shared_lib.cc b/ash/services/ime/ime_decoder.cc
similarity index 78%
rename from ash/services/ime/ime_shared_lib.cc
rename to ash/services/ime/ime_decoder.cc
index 7e5f353..73118873 100644
--- a/ash/services/ime/ime_shared_lib.cc
+++ b/ash/services/ime/ime_decoder.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/services/ime/ime_shared_lib.h"
+#include "ash/services/ime/ime_decoder.h"
 
 #include "ash/constants/ash_features.h"
 #include "ash/services/ime/constants.h"
@@ -16,18 +16,18 @@
 
 namespace {
 
-const char kCrosImeSharedLib[] = "libimedecoder.so";
+const char kCrosImeDecoderLib[] = "libimedecoder.so";
 
 // TODO(b/161491092): Add test image path based on value of
 // "CHROMEOS_RELEASE_TRACK" from `base::SysInfo::GetLsbReleaseValue`.
-// Returns ImeSharedLib path based on the run time env.
-base::FilePath GetImeSharedLibPath() {
+// Returns ImeDecoderLib path based on the run time env.
+base::FilePath GetImeDecoderLibPath() {
 #if defined(__x86_64__) || defined(__aarch64__)
   base::FilePath lib_path("/usr/lib64");
 #else
   base::FilePath lib_path("/usr/lib");
 #endif
-  lib_path = lib_path.Append(kCrosImeSharedLib);
+  lib_path = lib_path.Append(kCrosImeDecoderLib);
   return lib_path;
 }
 
@@ -54,15 +54,15 @@
 
 }  // namespace
 
-ImeSharedLibImpl::ImeSharedLibImpl() = default;
+ImeDecoderImpl::ImeDecoderImpl() = default;
 
-absl::optional<ImeSharedLib::EntryPoints>
-ImeSharedLibImpl::MaybeLoadThenReturnEntryPoints() {
+absl::optional<ImeDecoder::EntryPoints>
+ImeDecoderImpl::MaybeLoadThenReturnEntryPoints() {
   if (entry_points_) {
     return entry_points_;
   }
 
-  base::FilePath path = GetImeSharedLibPath();
+  base::FilePath path = GetImeDecoderLibPath();
 
   // Add dlopen flags (RTLD_LAZY | RTLD_NODELETE) later.
   base::ScopedNativeLibrary library = base::ScopedNativeLibrary(path);
@@ -77,12 +77,12 @@
           library.GetFunctionPointer(kInitProtoModeFnName)),
       .close_proto_mode = reinterpret_cast<CloseProtoModeFn>(
           library.GetFunctionPointer(kCloseProtoModeFnName)),
-      .supports = reinterpret_cast<ImeSharedLibSupportsFn>(
-          library.GetFunctionPointer(kImeSharedLibSupportsFnName)),
-      .activate_ime = reinterpret_cast<ImeSharedLibActivateImeFn>(
-          library.GetFunctionPointer(kImeSharedLibActivateImeFnName)),
-      .process = reinterpret_cast<ImeSharedLibProcessFn>(
-          library.GetFunctionPointer(kImeSharedLibProcessFnName)),
+      .supports = reinterpret_cast<ImeDecoderSupportsFn>(
+          library.GetFunctionPointer(kImeDecoderSupportsFnName)),
+      .activate_ime = reinterpret_cast<ImeDecoderActivateImeFn>(
+          library.GetFunctionPointer(kImeDecoderActivateImeFnName)),
+      .process = reinterpret_cast<ImeDecoderProcessFn>(
+          library.GetFunctionPointer(kImeDecoderProcessFnName)),
       .init_mojo_mode = reinterpret_cast<InitMojoModeFn>(
           library.GetFunctionPointer(kInitMojoModeFnName)),
       .close_mojo_mode = reinterpret_cast<CloseMojoModeFn>(
@@ -120,10 +120,10 @@
   return entry_points_;
 }
 
-ImeSharedLibImpl::~ImeSharedLibImpl() = default;
+ImeDecoderImpl::~ImeDecoderImpl() = default;
 
-ImeSharedLibImpl* ImeSharedLibImpl::GetInstance() {
-  static base::NoDestructor<ImeSharedLibImpl> instance;
+ImeDecoderImpl* ImeDecoderImpl::GetInstance() {
+  static base::NoDestructor<ImeDecoderImpl> instance;
   return instance.get();
 }
 
diff --git a/ash/services/ime/ime_shared_lib.h b/ash/services/ime/ime_decoder.h
similarity index 68%
rename from ash/services/ime/ime_shared_lib.h
rename to ash/services/ime/ime_decoder.h
index e7fb14ec..ead5ea9 100644
--- a/ash/services/ime/ime_shared_lib.h
+++ b/ash/services/ime/ime_decoder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef ASH_SERVICES_IME_IME_SHARED_LIB_H_
-#define ASH_SERVICES_IME_IME_SHARED_LIB_H_
+#ifndef ASH_SERVICES_IME_IME_DECODER_H_
+#define ASH_SERVICES_IME_IME_DECODER_H_
 
 #include "ash/services/ime/public/cpp/shared_lib/interfaces.h"
 
@@ -26,16 +26,15 @@
 inline constexpr char kCloseProtoModeFnName[] = "CloseProtoMode";
 typedef void (*CloseProtoModeFn)();
 
-inline constexpr char kImeSharedLibSupportsFnName[] = "ImeSharedLibSupports";
-typedef bool (*ImeSharedLibSupportsFn)(const char* ime_spec);
+inline constexpr char kImeDecoderSupportsFnName[] = "ImeDecoderSupports";
+typedef bool (*ImeDecoderSupportsFn)(const char* ime_spec);
 
-inline constexpr char kImeSharedLibActivateImeFnName[] =
-    "ImeSharedLibActivateIme";
-typedef bool (*ImeSharedLibActivateImeFn)(const char* ime_spec,
-                                          ImeClientDelegate* delegate);
+inline constexpr char kImeDecoderActivateImeFnName[] = "ImeDecoderActivateIme";
+typedef bool (*ImeDecoderActivateImeFn)(const char* ime_spec,
+                                        ImeClientDelegate* delegate);
 
-inline constexpr char kImeSharedLibProcessFnName[] = "ImeSharedLibProcess";
-typedef void (*ImeSharedLibProcessFn)(const uint8_t* data, size_t size);
+inline constexpr char kImeDecoderProcessFnName[] = "ImeDecoderProcess";
+typedef void (*ImeDecoderProcessFn)(const uint8_t* data, size_t size);
 
 inline constexpr char kInitMojoModeFnName[] = "InitMojoMode";
 typedef void (*InitMojoModeFn)(ImeCrosPlatform* platform);
@@ -61,11 +60,12 @@
 
 // END: Signatures of "C" API entry points of CrOS 1P IME shared lib.
 
-// This class manages the dynamic loading of CrOS 1P IME shared lib.so
-// and facilitates access to it's "C" API entry points.
-class ImeSharedLib {
+// TODO(b/214153032): Rename to ImeSharedLib to better reflect what this
+// represents. This class manages the dynamic loading of CrOS 1P IME shared lib
+// .so, and facilitates access to its "C" API entry points.
+class ImeDecoder {
  public:
-  virtual ~ImeSharedLib() = default;
+  virtual ~ImeDecoder() = default;
 
   // Function pointers to "C" API entry points of the loaded IME shared library.
   // See ash/services/ime/public/cpp/shared_lib/interfaces.h for API specs.
@@ -77,9 +77,9 @@
     // indicate they only pertain to the IME shared lib's ProtoMode. While it's
     // "hard" to rename corresponding "C" API functions due to cross-repo
     // backward compat requirements, these are local and rename is feasible.
-    ImeSharedLibSupportsFn supports;
-    ImeSharedLibActivateImeFn activate_ime;
-    ImeSharedLibProcessFn process;
+    ImeDecoderSupportsFn supports;
+    ImeDecoderActivateImeFn activate_ime;
+    ImeDecoderProcessFn process;
 
     InitMojoModeFn init_mojo_mode;
     CloseMojoModeFn close_mojo_mode;
@@ -99,23 +99,26 @@
   virtual absl::optional<EntryPoints> MaybeLoadThenReturnEntryPoints() = 0;
 };
 
-// ImeSharedLib is implemented as a singleton and is initialized before 'ime'
+// A proxy class for the IME decoder.
+// ImeDecoder is implemented as a singleton and is initialized before 'ime'
 // sandbox is engaged.
-class ImeSharedLibImpl : public ImeSharedLib {
+// TODO(b/214153032): Rename to ImeSharedLibImpl, as soon as ImeDecoder is
+// renamed to ImeSharedLib, to better reflect what this represents.
+class ImeDecoderImpl : public ImeDecoder {
  public:
-  // Gets the singleton ImeSharedLibImpl.
-  static ImeSharedLibImpl* GetInstance();
+  // Gets the singleton ImeDecoderImpl.
+  static ImeDecoderImpl* GetInstance();
 
-  ImeSharedLibImpl(const ImeSharedLibImpl&) = delete;
-  ImeSharedLibImpl& operator=(const ImeSharedLibImpl&) = delete;
+  ImeDecoderImpl(const ImeDecoderImpl&) = delete;
+  ImeDecoderImpl& operator=(const ImeDecoderImpl&) = delete;
 
   absl::optional<EntryPoints> MaybeLoadThenReturnEntryPoints() override;
 
  private:
-  friend class base::NoDestructor<ImeSharedLibImpl>;
+  friend class base::NoDestructor<ImeDecoderImpl>;
 
-  explicit ImeSharedLibImpl();
-  ~ImeSharedLibImpl() override;
+  explicit ImeDecoderImpl();
+  ~ImeDecoderImpl() override;
 
   // Result of IME decoder DSO initialization.
   absl::optional<base::ScopedNativeLibrary> library_;
@@ -126,4 +129,4 @@
 }  // namespace ime
 }  // namespace ash
 
-#endif  // ASH_SERVICES_IME_IME_SHARED_LIB_H_
+#endif  // ASH_SERVICES_IME_IME_DECODER_H_
diff --git a/ash/services/ime/ime_sandbox_hook.cc b/ash/services/ime/ime_sandbox_hook.cc
index 23eb123..aa77e1c 100644
--- a/ash/services/ime/ime_sandbox_hook.cc
+++ b/ash/services/ime/ime_sandbox_hook.cc
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "ash/services/ime/constants.h"
-#include "ash/services/ime/ime_shared_lib.h"
+#include "ash/services/ime/ime_decoder.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -77,7 +77,7 @@
   // TODO(crbug.com/1217513): This is not ideal, as it means rule-based
   // input methods will unnecessarily load the IME decoder shared library.
   // Either remove this line, or use a separate sandbox for rule-based.
-  ImeSharedLibImpl::GetInstance()->MaybeLoadThenReturnEntryPoints();
+  ImeDecoderImpl::GetInstance()->MaybeLoadThenReturnEntryPoints();
   instance->EngageNamespaceSandboxIfPossible();
   return true;
 }
diff --git a/ash/services/ime/ime_service.cc b/ash/services/ime/ime_service.cc
index 39534aa..711ea002 100644
--- a/ash/services/ime/ime_service.cc
+++ b/ash/services/ime/ime_service.cc
@@ -58,11 +58,11 @@
 
 ImeService::ImeService(
     mojo::PendingReceiver<mojom::ImeService> receiver,
-    ImeSharedLib* ime_shared_lib,
+    ImeDecoder* ime_decoder,
     std::unique_ptr<FieldTrialParamsRetriever> field_trial_params_retriever)
     : receiver_(this, std::move(receiver)),
       main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      ime_shared_lib_(ime_shared_lib),
+      ime_decoder_(ime_decoder),
       field_trial_params_retriever_(std::move(field_trial_params_retriever)) {}
 
 ImeService::~ImeService() = default;
@@ -107,7 +107,7 @@
   ResetAllBackendConnections();
 
   decoder_engine_ = std::make_unique<DecoderEngine>(
-      this, ime_shared_lib_->MaybeLoadThenReturnEntryPoints());
+      this, ime_decoder_->MaybeLoadThenReturnEntryPoints());
   bool bound = decoder_engine_->BindRequest(
       ime_spec, std::move(to_engine_request), std::move(from_engine), extra);
   std::move(callback).Run(bound);
@@ -138,7 +138,7 @@
     }
     case mojom::ConnectionTarget::kDecoder: {
       system_engine_ = std::make_unique<SystemEngine>(
-          this, ime_shared_lib_->MaybeLoadThenReturnEntryPoints());
+          this, ime_decoder_->MaybeLoadThenReturnEntryPoints());
       bool bound =
           system_engine_->BindConnectionFactory(std::move(connection_factory));
       std::move(callback).Run(bound);
diff --git a/ash/services/ime/ime_service.h b/ash/services/ime/ime_service.h
index b291b9d..c07f0c2 100644
--- a/ash/services/ime/ime_service.h
+++ b/ash/services/ime/ime_service.h
@@ -55,7 +55,7 @@
  public:
   explicit ImeService(
       mojo::PendingReceiver<mojom::ImeService> receiver,
-      ImeSharedLib* ime_shared_lib,
+      ImeDecoder* ime_decoder,
       std::unique_ptr<FieldTrialParamsRetriever> field_trial_params_retriever);
 
   ImeService(const ImeService&) = delete;
@@ -133,7 +133,9 @@
   mojo::Remote<mojom::PlatformAccessProvider> platform_access_;
   mojo::ReceiverSet<mojom::InputEngineManager> manager_receivers_;
 
-  ImeSharedLib* ime_shared_lib_ = nullptr;
+  // TODO(b/214153032): Rename to better reflect what this represents:
+  //     ime_decoder_ --> ime_shared_lib_
+  ImeDecoder* ime_decoder_ = nullptr;
 
   std::unique_ptr<FieldTrialParamsRetriever> field_trial_params_retriever_;
 };
diff --git a/ash/services/ime/ime_service_unittest.cc b/ash/services/ime/ime_service_unittest.cc
index 360b651..3918ad1 100644
--- a/ash/services/ime/ime_service_unittest.cc
+++ b/ash/services/ime/ime_service_unittest.cc
@@ -5,7 +5,7 @@
 #include "ash/services/ime/ime_service.h"
 
 #include "ash/constants/ash_features.h"
-#include "ash/services/ime/ime_shared_lib.h"
+#include "ash/services/ime/ime_decoder.h"
 #include "ash/services/ime/mock_input_channel.h"
 #include "ash/services/ime/public/mojom/input_engine.mojom.h"
 #include "ash/services/ime/public/mojom/input_method.mojom.h"
@@ -41,7 +41,7 @@
 class TestDecoderState;
 
 // The fake decoder state has to be available globally because
-// ImeSharedLib::EntryPoints is a list of stateless C functions, so the only way
+// ImeDecoder::EntryPoints is a list of stateless C functions, so the only way
 // to have a stateful fake is to have a global reference to it.
 TestDecoderState* g_test_decoder_state = nullptr;
 
@@ -77,14 +77,14 @@
   mojo::Receiver<ime::mojom::ConnectionFactory> connection_factory_{this};
 };
 
-class TestImeSharedLib : public ImeSharedLib {
+class TestImeDecoder : public ImeDecoder {
  public:
-  static TestImeSharedLib* GetInstance() {
-    static base::NoDestructor<TestImeSharedLib> instance;
+  static TestImeDecoder* GetInstance() {
+    static base::NoDestructor<TestImeDecoder> instance;
     return instance.get();
   }
 
-  absl::optional<ImeSharedLib::EntryPoints> MaybeLoadThenReturnEntryPoints()
+  absl::optional<ImeDecoder::EntryPoints> MaybeLoadThenReturnEntryPoints()
       override {
     return entry_points_;
   }
@@ -120,13 +120,13 @@
   }
 
  private:
-  friend class base::NoDestructor<TestImeSharedLib>;
+  friend class base::NoDestructor<TestImeDecoder>;
 
-  explicit TestImeSharedLib() { ResetState(); }
+  explicit TestImeDecoder() { ResetState(); }
 
-  ~TestImeSharedLib() override = default;
+  ~TestImeDecoder() override = default;
 
-  absl::optional<ImeSharedLib::EntryPoints> entry_points_;
+  absl::optional<ImeDecoder::EntryPoints> entry_points_;
 };
 
 struct MockInputMethodHost : public mojom::InputMethodHost {
@@ -212,7 +212,7 @@
   void SetUp() override {
     service_ = std::make_unique<ImeService>(
         remote_service_.BindNewPipeAndPassReceiver(),
-        TestImeSharedLib::GetInstance(),
+        TestImeDecoder::GetInstance(),
         std::make_unique<TestFieldTrialParamsRetriever>());
     remote_service_->BindInputEngineManager(
         remote_manager_.BindNewPipeAndPassReceiver());
@@ -220,7 +220,7 @@
 
   void TearDown() override {
     service_.reset();
-    TestImeSharedLib::GetInstance()->ResetState();
+    TestImeDecoder::GetInstance()->ResetState();
   }
 
   mojo::Remote<mojom::ImeService> remote_service_;
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index 3ea3602ad..13c1534 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -443,13 +443,18 @@
             views::InkDrop::Get(this)->GetInkDrop()->GetTargetInkDropState());
   views::InkDrop::Get(this)->GetInkDrop()->AnimateToState(
       views::InkDropState::DEACTIVATED);
+  context_menu_target_visibility_ = false;
 }
 
 void ShelfAppButton::ShowContextMenu(const gfx::Point& p,
                                      ui::MenuSourceType source_type) {
-  if (!context_menu_controller())
+  // Return early if:
+  // 1. the context menu controller is not set; or
+  // 2. `context_menu_target_visibility_` is already true.
+  if (!context_menu_controller() || context_menu_target_visibility_)
     return;
 
+  context_menu_target_visibility_ = true;
   auto weak_this = weak_factory_.GetWeakPtr();
 
   ShelfButton::ShowContextMenu(p, source_type);
@@ -538,6 +543,12 @@
   return icon_width == shelf_view_->GetButtonIconSize();
 }
 
+void ShelfAppButton::OnContextMenuModelRequestCanceled() {
+  // The request for the context menu model gets canceled so reset the context
+  // menu target visibility.
+  context_menu_target_visibility_ = false;
+}
+
 bool ShelfAppButton::FireDragTimerForTest() {
   if (!drag_timer_.IsRunning())
     return false;
@@ -764,15 +775,7 @@
         // on a shelf app button, the button's inkdrop could be in the pending
         // state while the button's context menu is hidden. In this case, we
         // have to hide the inkdrop explicitly.
-
-        // Note that the ET_GESTURE_END event may be received during the
-        // building of the context menu by triggering the synthesized gesture
-        // end event. Therefore we have to wait until the context menu is
-        // completely built.
-        base::SequencedTaskRunnerHandle::Get()->PostTask(
-            FROM_HERE,
-            base::BindOnce(&ShelfAppButton::MaybeHideInkDropWhenGestureEnds,
-                           weak_factory_.GetWeakPtr()));
+        MaybeHideInkDropWhenGestureEnds();
       }
 
       ClearDragStateOnGestureEnd();
@@ -956,11 +959,11 @@
 }
 
 void ShelfAppButton::MaybeHideInkDropWhenGestureEnds() {
-  if (shelf_view_->IsShowingMenuForView(this) ||
+  if (context_menu_target_visibility_ ||
       views::InkDrop::Get(this)->GetInkDrop()->GetTargetInkDropState() ==
           views::InkDropState::HIDDEN) {
-    // Return early if the shelf app button's context menu is showing or
-    // the button's inkdrop has been hidden.
+    // Return early if the shelf app button's context menu should show or
+    // the button's inkdrop has already been hidden.
     return;
   }
 
diff --git a/ash/shelf/shelf_app_button.h b/ash/shelf/shelf_app_button.h
index 89405db..f7e0640 100644
--- a/ash/shelf/shelf_app_button.h
+++ b/ash/shelf/shelf_app_button.h
@@ -19,7 +19,7 @@
 namespace views {
 class DotIndicator;
 class ImageView;
-}
+}  // namespace views
 
 namespace ash {
 struct ShelfItem;
@@ -121,6 +121,9 @@
   // Returns whether the icon size is up to date.
   bool IsIconSizeCurrent();
 
+  // Called when the request for the context menu model is canceled.
+  void OnContextMenuModelRequestCanceled();
+
   bool FireDragTimerForTest();
   void FireRippleActivationTimerForTest();
 
@@ -218,6 +221,11 @@
   // A timer to activate the ink drop ripple during a long press.
   base::OneShotTimer ripple_activation_timer_;
 
+  // The target visibility of the shelf app's context menu.
+  // NOTE: when `context_menu_target_visibility_` is true, the context menu may
+  // not show yet due to the async request for the menu model.
+  bool context_menu_target_visibility_ = false;
+
   std::unique_ptr<ShelfButtonDelegate::ScopedActiveInkDropCount>
       ink_drop_count_;
 
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 9d3825d..b18b5b3 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1499,6 +1499,8 @@
   // a context menu just after drag starts.
   if (!context_menu_callback_.IsCancelled()) {
     context_menu_callback_.Cancel();
+    GetShelfAppButton(item_awaiting_response_)
+        ->OnContextMenuModelRequestCanceled();
     item_awaiting_response_ = ShelfID();
   }
 
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 5576061..50a4277 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -3432,6 +3432,57 @@
   EXPECT_EQ(views::InkDropState::HIDDEN, GetInkDropStateOfAppIcon1());
 }
 
+TEST_F(ShelfViewGestureTapTest,
+       PressEscapeKeyBeforeReleaseLongPressOnAppButton) {
+  const ShelfID id = AddAppShortcut();
+  auto item_delegate_owned =
+      std::make_unique<AsyncContextMenuShelfItemDelegate>();
+  AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
+  model_->ReplaceShelfItemDelegate(id, std::move(item_delegate_owned));
+
+  ShelfAppButton* app_button = GetButtonByID(id);
+  GetEventGenerator()->MoveTouch(app_button->GetBoundsInScreen().CenterPoint());
+  GetEventGenerator()->PressTouch();
+
+  // Fast forward to generate the ET_GESTURE_SHOW_PRESS event.
+  task_environment()->FastForwardBy(base::Milliseconds(200));
+
+  // Fast forward to generate the ET_GESTURE_LONG_PRESS event to show the
+  // context menu.
+  task_environment()->FastForwardBy(base::Milliseconds(1000));
+  EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
+
+  // Build a dummy context menu and show it.
+  {
+    auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
+    menu_model->AddItem(203, u"item");
+    item_delegate->RunPendingContextMenuCallback(std::move(menu_model));
+    EXPECT_TRUE(shelf_view_->IsShowingMenuForView(app_button));
+  }
+
+  // Press Escape. The context menu should be closed.
+  GetEventGenerator()->PressAndReleaseKey(ui::VKEY_ESCAPE);
+  EXPECT_FALSE(shelf_view_->IsShowingMenu());
+  EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
+
+  // Release the gesture press. The context menu should show again.
+  GetEventGenerator()->ReleaseTouch();
+  task_environment()->FastForwardBy(base::Milliseconds(1000));
+  EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
+  {
+    auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
+    menu_model->AddItem(203, u"item");
+    item_delegate->RunPendingContextMenuCallback(std::move(menu_model));
+    EXPECT_TRUE(shelf_view_->IsShowingMenuForView(app_button));
+  }
+
+  // Verify that the ink drop of the app button for which the context menu shows
+  // for is activated.
+  EXPECT_EQ(
+      views::InkDropState::ACTIVATED,
+      views::InkDrop::Get(app_button)->GetInkDrop()->GetTargetInkDropState());
+}
+
 // Verifies the shelf app button's inkdrop behavior when the mouse click
 // occurs before gesture long press.
 TEST_F(ShelfViewGestureTapTest, MouseClickInterruptionBeforeGestureLongPress) {
diff --git a/ash/system/machine_learning/user_settings_event_logger_unittest.cc b/ash/system/machine_learning/user_settings_event_logger_unittest.cc
index 14dbf63f..26e0c5e 100644
--- a/ash/system/machine_learning/user_settings_event_logger_unittest.cc
+++ b/ash/system/machine_learning/user_settings_event_logger_unittest.cc
@@ -50,8 +50,7 @@
   wifi->signal_strength = signal_strength;
   wifi->security = security_type;
   network->type = NetworkType::kWiFi;
-  network->type_state = NetworkTypeStateProperties::New();
-  network->type_state->set_wifi(std::move(wifi));
+  network->type_state = NetworkTypeStateProperties::NewWifi(std::move(wifi));
 
   return network;
 }
@@ -62,8 +61,8 @@
   CellularStatePropertiesPtr cellular = CellularStateProperties::New();
   cellular->signal_strength = signal_strength;
   network->type = NetworkType::kCellular;
-  network->type_state = NetworkTypeStateProperties::New();
-  network->type_state->set_cellular(std::move(cellular));
+  network->type_state =
+      NetworkTypeStateProperties::NewCellular(std::move(cellular));
 
   return network;
 }
diff --git a/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc b/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc
deleted file mode 100644
index 2c25711b..0000000
--- a/ash/wallpaper/online_wallpaper_variant_info_fetcher.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-// 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/wallpaper/online_wallpaper_variant_info_fetcher.h"
-
-#include <algorithm>
-
-#include "ash/public/cpp/wallpaper/online_wallpaper_params.h"
-#include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
-#include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
-#include "ash/public/cpp/wallpaper/wallpaper_info.h"
-#include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "base/strings/string_piece.h"
-#include "base/task/post_task.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-
-namespace ash {
-namespace {
-
-// Convenience alias for ColorMode enum.
-using ColorMode = OnlineWallpaperVariantInfoFetcher::ColorMode;
-
-// Checks if the given |variant| is suitable for the current system's color
-// mode. Image with type |Image_ImageType_IMAGE_TYPE_UNKNOWN| is not D/L aware
-// and should be used regardless of color mode.
-bool IsSuitableOnlineWallpaperVariant(const OnlineWallpaperVariant& variant,
-                                      ColorMode mode) {
-  switch (variant.type) {
-    case backdrop::Image_ImageType_IMAGE_TYPE_UNKNOWN:
-      return true;
-    case backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE:
-      return mode == ColorMode::kLightMode;
-    case backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE:
-      return mode == ColorMode::kDarkMode;
-  }
-}
-
-// Returns a pointer to the first matching variant in |variants| if one
-// exists.
-const OnlineWallpaperVariant* FirstValidVariant(
-    const std::vector<OnlineWallpaperVariant>& variants,
-    ColorMode mode) {
-  const auto iter = std::find_if(
-      variants.begin(), variants.end(), [mode](const auto& variant) {
-        return IsSuitableOnlineWallpaperVariant(variant, mode);
-      });
-  if (iter != variants.end())
-    return &(*iter);
-
-  return nullptr;
-}
-
-// The filtered results from a set of backdrop::Images for a given |asset_id|
-// and |mode| value.
-class VariantMatches {
- public:
-  VariantMatches(VariantMatches&&) = default;
-
-  VariantMatches(const VariantMatches&) = delete;
-  VariantMatches& operator=(const VariantMatches&) = delete;
-
-  ~VariantMatches() = default;
-
-  // Filters |images| to only the entries that match |asset_id| and
-  // |mode|.
-  static absl::optional<VariantMatches> FromImages(
-      uint64_t asset_id,
-      ColorMode mode,
-      const std::vector<backdrop::Image>& images) {
-    // Find the exact image in the |images| collection.
-    auto image_iter = std::find_if(images.begin(), images.end(),
-                                   [asset_id](const backdrop::Image& image) {
-                                     return asset_id == image.asset_id();
-                                   });
-
-    if (image_iter == images.end())
-      return absl::nullopt;
-
-    uint64_t unit_id = image_iter->unit_id();
-    std::vector<OnlineWallpaperVariant> variants;
-    for (const auto& image : images) {
-      if (image.unit_id() == unit_id) {
-        variants.emplace_back(image.asset_id(), GURL(image.image_url()),
-                              image.has_image_type()
-                                  ? image.image_type()
-                                  : backdrop::Image::IMAGE_TYPE_UNKNOWN);
-      }
-    }
-
-    const OnlineWallpaperVariant* variant = FirstValidVariant(variants, mode);
-    if (!variant) {
-      // At least one usable variant must be found to use this set of images.
-      return absl::nullopt;
-    }
-
-    return VariantMatches(unit_id, std::move(variants), *variant);
-  }
-
-  // The unit id of the Variant that matched |asset_id| and |mode|.
-  const uint64_t unit_id;
-
-  // The set of images that are appropriate for |asset_id| and
-  // |mode|.
-  const std::vector<OnlineWallpaperVariant> variants;
-
-  // The first instance from |variants| that matches |asset_id| and
-  // |mode|.
-  const OnlineWallpaperVariant first_match;
-
- private:
-  VariantMatches(uint64_t unit_id_in,
-                 std::vector<OnlineWallpaperVariant>&& variants_in,
-                 const OnlineWallpaperVariant& first_match_in)
-      : unit_id(unit_id_in),
-        variants(variants_in),
-        first_match(first_match_in) {
-    DCHECK_EQ(std::count(variants.begin(), variants.end(), first_match), 1);
-  }
-};
-
-bool IsDaily(const WallpaperInfo& info) {
-  return info.type == WallpaperType::kDaily;
-}
-
-}  // namespace
-
-OnlineWallpaperVariantInfoFetcher::OnlineWallpaperRequest::
-    OnlineWallpaperRequest(const AccountId& account_id_in,
-                           const std::string& collection_id_in,
-                           WallpaperLayout layout_in,
-                           bool daily_refresh_enabled_in,
-                           ColorMode mode_in)
-    : account_id(account_id_in),
-      collection_id(collection_id_in),
-      layout(layout_in),
-      daily_refresh_enabled(daily_refresh_enabled_in),
-      mode(mode_in) {}
-
-OnlineWallpaperVariantInfoFetcher::OnlineWallpaperRequest::
-    ~OnlineWallpaperRequest() = default;
-
-OnlineWallpaperVariantInfoFetcher::OnlineWallpaperVariantInfoFetcher() =
-    default;
-OnlineWallpaperVariantInfoFetcher::~OnlineWallpaperVariantInfoFetcher() =
-    default;
-
-void OnlineWallpaperVariantInfoFetcher::SetClient(
-    WallpaperControllerClient* client) {
-  wallpaper_controller_client_ = client;
-}
-
-void OnlineWallpaperVariantInfoFetcher::FetchOnlineWallpaper(
-    const AccountId& account_id,
-    const WallpaperInfo& info,
-    ColorMode mode,
-    FetchParamsCallback callback) {
-  DCHECK(info.type == WallpaperType::kDaily ||
-         info.type == WallpaperType::kOnline);
-
-  DCHECK(wallpaper_controller_client_);
-
-  if (info.unit_id.has_value() && !info.variants.empty()) {
-    // |info| already has all of the data we need.
-    const OnlineWallpaperVariant* variant =
-        FirstValidVariant(info.variants, mode);
-    if (!variant) {
-      NOTREACHED() << "No suitable wallpaper for "
-                   << (mode == ColorMode::kDarkMode ? "dark" : "lite")
-                   << " mode in collection";
-      base::SequencedTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
-      return;
-    }
-
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            std::move(callback),
-            OnlineWallpaperParams{account_id, variant->asset_id,
-                                  GURL(variant->raw_url), info.collection_id,
-                                  info.layout, /*preview_mode=*/false,
-                                  /*from_user=*/false, IsDaily(info),
-                                  info.unit_id, info.variants}));
-    return;
-  }
-
-  // For requests from existing WallpaperInfo, asset_id is always populated.
-  DCHECK(info.asset_id.has_value());
-
-  bool daily = IsDaily(info);
-  auto request = std::make_unique<OnlineWallpaperRequest>(
-      account_id, info.collection_id, info.layout, daily, mode);
-
-  wallpaper_controller_client_->FetchImagesForCollection(
-      request->collection_id,
-      base::BindOnce(
-          &OnlineWallpaperVariantInfoFetcher::FindAndSetOnlineWallpaperVariants,
-          weak_factory_.GetWeakPtr(), std::move(request), *info.asset_id,
-          std::move(callback)));
-}
-
-bool OnlineWallpaperVariantInfoFetcher::FetchDailyWallpaper(
-    const AccountId& account_id,
-    const WallpaperInfo& info,
-    ColorMode mode,
-    FetchParamsCallback callback) {
-  DCHECK(IsDaily(info));
-
-  // We might not have a client yet.
-  if (!wallpaper_controller_client_)
-    return false;
-
-  bool daily = true;  // This is always a a daily wallpaper.
-  auto request = std::make_unique<OnlineWallpaperRequest>(
-      account_id, info.collection_id, info.layout, daily, mode);
-  wallpaper_controller_client_->FetchDailyRefreshWallpaper(
-      info.collection_id,
-      base::BindOnce(&OnlineWallpaperVariantInfoFetcher::OnSingleFetch,
-                     weak_factory_.GetWeakPtr(), std::move(request),
-                     std::move(callback)));
-  return true;
-}
-
-void OnlineWallpaperVariantInfoFetcher::OnSingleFetch(
-    std::unique_ptr<OnlineWallpaperRequest> request,
-    FetchParamsCallback callback,
-    bool success,
-    const backdrop::Image& image) {
-  if (!success) {
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-
-  // If wallpaper_controller_client_ is null, we're shutting down.
-  if (!wallpaper_controller_client_)
-    return;
-
-  wallpaper_controller_client_->FetchImagesForCollection(
-      request->collection_id,
-      base::BindOnce(
-          &OnlineWallpaperVariantInfoFetcher::FindAndSetOnlineWallpaperVariants,
-          weak_factory_.GetWeakPtr(), std::move(request), image.asset_id(),
-          std::move(callback)));
-}
-
-void OnlineWallpaperVariantInfoFetcher::FindAndSetOnlineWallpaperVariants(
-    std::unique_ptr<OnlineWallpaperRequest> request,
-    uint64_t asset_id,
-    FetchParamsCallback callback,
-    bool success,
-    const std::vector<backdrop::Image>& images) {
-  if (!success) {
-    LOG(WARNING) << "Failed to fetch online wallpapers";
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-
-  absl::optional<VariantMatches> matches =
-      VariantMatches::FromImages(asset_id, request->mode, images);
-  if (!matches) {
-    LOG(ERROR) << "No valid variants";
-    std::move(callback).Run(absl::nullopt);
-    return;
-  }
-
-  const OnlineWallpaperVariant& first_image = matches->first_match;
-  DCHECK(IsSuitableOnlineWallpaperVariant(first_image, request->mode));
-
-  std::move(callback).Run(ash::OnlineWallpaperParams{
-      request->account_id, first_image.asset_id, first_image.raw_url,
-      request->collection_id, request->layout, /*preview_mode=*/false,
-      /*from_user=*/false, request->daily_refresh_enabled, matches->unit_id,
-      matches->variants});
-}
-
-}  // namespace ash
diff --git a/ash/wallpaper/online_wallpaper_variant_info_fetcher.h b/ash/wallpaper/online_wallpaper_variant_info_fetcher.h
deleted file mode 100644
index ca27f54..0000000
--- a/ash/wallpaper/online_wallpaper_variant_info_fetcher.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// 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_WALLPAPER_ONLINE_WALLPAPER_VARIANT_INFO_FETCHER_H_
-#define ASH_WALLPAPER_ONLINE_WALLPAPER_VARIANT_INFO_FETCHER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "ash/ash_export.h"
-#include "ash/public/cpp/wallpaper/online_wallpaper_params.h"
-#include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
-#include "ash/public/cpp/wallpaper/wallpaper_info.h"
-#include "base/callback_forward.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "components/account_id/account_id.h"
-
-namespace backdrop {
-class Image;
-}
-
-namespace ash {
-
-class WallpaperControllerClient;
-
-// Resolves wallpaper variants from WallpaperInfo for WallpaperController.
-class ASH_EXPORT OnlineWallpaperVariantInfoFetcher {
- public:
-  enum class ColorMode {
-    kDarkMode = 0,
-    kLightMode,
-  };
-
-  OnlineWallpaperVariantInfoFetcher();
-
-  OnlineWallpaperVariantInfoFetcher(const OnlineWallpaperVariantInfoFetcher&) =
-      delete;
-  OnlineWallpaperVariantInfoFetcher& operator=(
-      const OnlineWallpaperVariantInfoFetcher&) = delete;
-
-  ~OnlineWallpaperVariantInfoFetcher();
-
-  void SetClient(WallpaperControllerClient* client);
-
-  // Callback for Fetch* methods which populates the |unit_id| and |variants|
-  // fields in OnlineWallpaperParams.
-  using FetchParamsCallback =
-      base::OnceCallback<void(absl::optional<OnlineWallpaperParams>)>;
-
-  // Fetches the wallpaper variants for |info| to produce a fully populated
-  // OnlineWallpaperParams in |callback|. The selected wallpaper will is
-  // designated by asset_id in |info|.
-  void FetchOnlineWallpaper(const AccountId& account_id,
-                            const WallpaperInfo& info,
-                            OnlineWallpaperVariantInfoFetcher::ColorMode mode,
-                            FetchParamsCallback callback);
-
-  // Always fetches a new daily refresh wallpaper and calls |callback| with a
-  // fully populated OnlineWallpaperParams.
-  bool FetchDailyWallpaper(const AccountId& account_id,
-                           const WallpaperInfo& info,
-                           OnlineWallpaperVariantInfoFetcher::ColorMode mode,
-                           FetchParamsCallback callback);
-
- private:
-  // An internal representation of the partial information required to construct
-  // a complete OnlineWallpaperParams object as provided by the caller of
-  // Fetch*.
-  class OnlineWallpaperRequest {
-   public:
-    OnlineWallpaperRequest(const AccountId& account_id,
-                           const std::string& collection_id,
-                           WallpaperLayout layout,
-                           bool daily_refresh_enabled,
-                           OnlineWallpaperVariantInfoFetcher::ColorMode mode);
-    OnlineWallpaperRequest(const OnlineWallpaperRequest&) = delete;
-    OnlineWallpaperRequest& operator=(const OnlineWallpaperRequest&) = delete;
-    ~OnlineWallpaperRequest();
-
-    AccountId account_id;
-    std::string collection_id;
-    WallpaperLayout layout;
-    bool daily_refresh_enabled;
-    OnlineWallpaperVariantInfoFetcher::ColorMode mode;
-  };
-
-  // Handles the response for a single random image in a collection and proceeds
-  // to fetch the rest of the collection.
-  void OnSingleFetch(std::unique_ptr<OnlineWallpaperRequest> request,
-                     FetchParamsCallback callback,
-                     bool success,
-                     const backdrop::Image& image);
-
-  // Finishes variants fetch by populating the remaining fields for
-  // OnlineWallpaperParams in |callback|. Combines data from |request| with
-  // |images| and the matching variant in |images| for |asset_id|.
-  void FindAndSetOnlineWallpaperVariants(
-      std::unique_ptr<OnlineWallpaperRequest> request,
-      uint64_t asset_id,
-      FetchParamsCallback callback,
-      bool success,
-      const std::vector<backdrop::Image>& images);
-
-  raw_ptr<WallpaperControllerClient> wallpaper_controller_client_ =
-      nullptr;  // not owned
-
-  base::WeakPtrFactory<OnlineWallpaperVariantInfoFetcher> weak_factory_{this};
-};
-
-}  // namespace ash
-
-#endif  //  ASH_WALLPAPER_ONLINE_WALLPAPER_VARIANT_INFO_FETCHER_H_
diff --git a/ash/wallpaper/online_wallpaper_variant_info_fetcher_unittest.cc b/ash/wallpaper/online_wallpaper_variant_info_fetcher_unittest.cc
deleted file mode 100644
index 310abfb..0000000
--- a/ash/wallpaper/online_wallpaper_variant_info_fetcher_unittest.cc
+++ /dev/null
@@ -1,251 +0,0 @@
-// 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/wallpaper/online_wallpaper_variant_info_fetcher.h"
-
-#include "ash/public/cpp/wallpaper/wallpaper_info.h"
-#include "ash/wallpaper/test_wallpaper_controller_client.h"
-#include "base/callback_forward.h"
-#include "base/run_loop.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/gtest_util.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_future.h"
-#include "components/account_id/account_id.h"
-#include "testing/gmock/include/gmock/gmock-matchers.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash {
-namespace {
-
-// Convenience aliases for light and dark mode names.
-using ColorMode = OnlineWallpaperVariantInfoFetcher::ColorMode;
-
-constexpr char kUser1[] = "user1@test.com";
-const AccountId kAccount1 = AccountId::FromUserEmailGaiaId(kUser1, kUser1);
-constexpr char kDummyCollectionId[] = "testCollectionId";
-
-// Returns a set of images with the given |type|.
-std::vector<backdrop::Image> ImageSet(backdrop::Image_ImageType type,
-                                      size_t size) {
-  std::vector<backdrop::Image> images;
-  for (size_t i = 0; i < size; i++) {
-    images.emplace_back();
-    images.back().set_image_url(
-        base::StringPrintf("https://test_wallpaper/%zu", i));
-    // A "unique" asset id.
-    images.back().set_asset_id(42 + i);
-    // Images should all have a different unit id except in a light/dark pair.
-    images.back().set_unit_id(13 + i);
-    images.back().set_image_type(type);
-  }
-  return images;
-}
-
-class OnlineWallpaperVariantInfoFetcherTest : public testing::Test {
- public:
-  OnlineWallpaperVariantInfoFetcherTest()
-      : task_environment_(base::test::TaskEnvironment::MainThreadType::UI) {}
-
-  void SetUp() override {
-    wallpaper_fetcher_ = std::make_unique<OnlineWallpaperVariantInfoFetcher>();
-    wallpaper_fetcher_->SetClient(&client_);
-  }
-
-  void TearDown() override {}
-
- protected:
-  base::test::SingleThreadTaskEnvironment task_environment_;
-
-  TestWallpaperControllerClient client_;
-  std::unique_ptr<OnlineWallpaperVariantInfoFetcher> wallpaper_fetcher_;
-};
-
-// Verify that variants in params is populated.
-TEST_F(OnlineWallpaperVariantInfoFetcherTest,
-       FetchDailyWallpaper_VariantsPopulated) {
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-  WallpaperInfo info("", WallpaperLayout::WALLPAPER_LAYOUT_CENTER,
-                     WallpaperType::kDaily, base::Time::Now());
-  info.collection_id = kDummyCollectionId;
-
-  wallpaper_fetcher_->FetchDailyWallpaper(
-      kAccount1, info, ColorMode::kLightMode, test_future.GetCallback());
-
-  ASSERT_TRUE(test_future.Wait()) << "Fetch Daily never ran callback";
-  auto result = test_future.Get();
-  ASSERT_TRUE(result);
-  EXPECT_FALSE(result->variants.empty());
-}
-
-// Verify that repeated requests for daily wallpaper changes the url.
-TEST_F(OnlineWallpaperVariantInfoFetcherTest,
-       FetchDailyWallpaper_EveryRequestDifferent) {
-  // Add some images for a new collection id.
-  const std::string kCollectionId = "FetchDaily";
-  client_.AddCollection(kCollectionId,
-                        ImageSet(backdrop::Image::IMAGE_TYPE_UNKNOWN, 6u));
-
-  // First fetch
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-  WallpaperInfo info("", WallpaperLayout::WALLPAPER_LAYOUT_CENTER,
-                     WallpaperType::kDaily, base::Time::Now());
-  info.collection_id = kCollectionId;
-
-  wallpaper_fetcher_->FetchDailyWallpaper(
-      kAccount1, info, ColorMode::kLightMode, test_future.GetCallback());
-  auto first_result = test_future.Get();
-  EXPECT_TRUE(first_result);
-
-  // Calling FetchDaily with the same arguments should yield a different params
-  // object if there is more than one wallpaper in the collection.
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future2;
-  wallpaper_fetcher_->FetchDailyWallpaper(
-      kAccount1, info, ColorMode::kLightMode, test_future2.GetCallback());
-
-  auto second_result = test_future2.Get();
-  EXPECT_TRUE(second_result);
-
-  EXPECT_NE(first_result->url, second_result->url)
-      << "Urls for the two calls should be different";
-}
-
-// Verify that variants with matching unit id are selected and the asset of the
-// appropriate type (dark/light).
-TEST_F(OnlineWallpaperVariantInfoFetcherTest,
-       FetchOnlineWallpaper_DarkLightVariants) {
-  // Add some images for a new collection id.
-  const std::string kCollectionId = "FetchOnline";
-  const uint64_t kLightAssetId = 99;
-  const std::string kLightUrl = "https://preferred_wallpaper/images/99";
-  const uint64_t kDarkAssetId = 103;
-  const std::string kDarkUrl = "https://preferred_wallpaper/images/103";
-  const uint64_t kUnitId = 432;
-
-  // Initially populate the collection with images we won't use.
-  std::vector<backdrop::Image> images =
-      ImageSet(backdrop::Image::IMAGE_TYPE_UNKNOWN, 6u);
-
-  // Push a dark and light asset that share a unit id.
-  backdrop::Image light_image;
-  light_image.set_asset_id(kLightAssetId);
-  light_image.set_unit_id(kUnitId);
-  light_image.set_image_type(backdrop::Image::IMAGE_TYPE_LIGHT_MODE);
-  light_image.set_image_url(kLightUrl);
-  images.push_back(light_image);
-
-  backdrop::Image dark_image;
-  dark_image.set_asset_id(kDarkAssetId);
-  dark_image.set_unit_id(kUnitId);
-  dark_image.set_image_type(backdrop::Image::IMAGE_TYPE_DARK_MODE);
-  dark_image.set_image_url(kDarkUrl);
-  images.push_back(dark_image);
-
-  client_.AddCollection(kCollectionId, images);
-
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-  WallpaperInfo info("", WallpaperLayout::WALLPAPER_LAYOUT_CENTER,
-                     WallpaperType::kOnline, base::Time::Now());
-  info.collection_id = kCollectionId;
-  info.asset_id = kLightAssetId;
-  {
-    // Checking light mode where asset id matches.
-    wallpaper_fetcher_->FetchOnlineWallpaper(
-        kAccount1, info, ColorMode::kLightMode, test_future.GetCallback());
-    auto result = test_future.Get();
-    EXPECT_TRUE(result);
-    EXPECT_EQ(2u, result->variants.size());
-    EXPECT_EQ(kLightUrl, result->url.spec());
-  }
-  {
-    base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-
-    // Verify that we get a dark mode asset when in dark mode but our asset_id
-    // is for a light asset.
-    wallpaper_fetcher_->FetchOnlineWallpaper(
-        kAccount1, info, ColorMode::kDarkMode, test_future.GetCallback());
-    auto result = test_future.Get();
-    EXPECT_TRUE(result);
-    EXPECT_EQ(2u, result->variants.size());
-    EXPECT_EQ(kDarkUrl, result->url.spec());
-  }
-}
-
-// Verify that the request fails if there are no matching variants for dark
-// mode.
-TEST_F(OnlineWallpaperVariantInfoFetcherTest, FetchOnlineWallpaper_NoDarkMode) {
-  const std::string kCollectionId = "OnlyLightModeWallpaper";
-  std::vector<backdrop::Image> images =
-      ImageSet(backdrop::Image::IMAGE_TYPE_LIGHT_MODE, 4u);
-  client_.AddCollection(kCollectionId, images);
-
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-  WallpaperInfo info("", WallpaperLayout::WALLPAPER_LAYOUT_CENTER,
-                     WallpaperType::kOnline, base::Time::Now());
-  info.collection_id = kCollectionId;
-  // Pick an arbiratry asset in the set.
-  info.asset_id = images.front().asset_id();
-
-  // Initial request will pass because variants are not populated in |info|.
-  // Requesting dark mode when there are only light assets.
-  wallpaper_fetcher_->FetchOnlineWallpaper(
-      kAccount1, info, ColorMode::kDarkMode, test_future.GetCallback());
-
-  // Result should be missing if no suitable variants are found.
-  auto result = test_future.Get();
-  EXPECT_FALSE(result);
-}
-
-// When variants are already populated, params are returned.
-TEST_F(OnlineWallpaperVariantInfoFetcherTest, FetchOnlineWallpaper_FromInfo) {
-  const uint64_t kAssetId = 14;
-  const GURL kUrl("https://populated_url/14");
-  const std::string kCollectionId = "PrePopulatedCollection";
-  const uint64_t kUnitId = 31;
-  const std::vector<OnlineWallpaperVariant> kVariants = {OnlineWallpaperVariant(
-      kAssetId, kUrl, backdrop::Image::IMAGE_TYPE_UNKNOWN)};
-  OnlineWallpaperParams params(
-      kAccount1, kAssetId, kUrl, kCollectionId,
-      WallpaperLayout::WALLPAPER_LAYOUT_CENTER, /*preview_mode=*/false,
-      /*from_user=*/true, /*daily_refresh_enabled=*/false, kUnitId, kVariants);
-  WallpaperInfo info(params);
-
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-  wallpaper_fetcher_->FetchOnlineWallpaper(
-      kAccount1, info, ColorMode::kDarkMode, test_future.GetCallback());
-
-  // Callback is called
-  auto result = test_future.Get();
-  EXPECT_TRUE(result);
-
-  // WallpaperControllerClient is not used if OnlineWallpaperParams is fully
-  // populated.
-  EXPECT_EQ(0u, client_.fetch_images_for_collection_count());
-}
-
-// When variants are already populated but types don't match, returns false.
-TEST_F(OnlineWallpaperVariantInfoFetcherTest,
-       FetchOnlineWallpaper_FromInfoLightDarkMismatch) {
-  const uint64_t kAssetId = 14;
-  const GURL kUrl("https://populated_url/14");
-  const std::string kCollectionId = "PrePopulatedCollection";
-  const uint64_t kUnitId = 31;
-  // Only one light mode wallpaper
-  const std::vector<OnlineWallpaperVariant> kVariants = {OnlineWallpaperVariant(
-      kAssetId, kUrl, backdrop::Image::IMAGE_TYPE_LIGHT_MODE)};
-
-  OnlineWallpaperParams params(
-      kAccount1, kAssetId, kUrl, kCollectionId,
-      WallpaperLayout::WALLPAPER_LAYOUT_CENTER, /*preview_mode=*/false,
-      /*from_user=*/true, /*daily_refresh_enabled=*/false, kUnitId, kVariants);
-  WallpaperInfo info(params);
-
-  // In dark mode, fetch will fail if the variant is missing.
-  base::test::TestFuture<absl::optional<OnlineWallpaperParams>> test_future;
-  EXPECT_DCHECK_DEATH(wallpaper_fetcher_->FetchOnlineWallpaper(
-      kAccount1, info, ColorMode::kDarkMode, test_future.GetCallback()));
-}
-
-}  // namespace
-}  // namespace ash
diff --git a/ash/wallpaper/test_wallpaper_controller_client.cc b/ash/wallpaper/test_wallpaper_controller_client.cc
index aebeb14..3be9167 100644
--- a/ash/wallpaper/test_wallpaper_controller_client.cc
+++ b/ash/wallpaper/test_wallpaper_controller_client.cc
@@ -37,12 +37,6 @@
 
 TestWallpaperControllerClient::~TestWallpaperControllerClient() = default;
 
-void TestWallpaperControllerClient::AddCollection(
-    const std::string& collection_id,
-    const std::vector<backdrop::Image>& images) {
-  variations_[collection_id] = images;
-}
-
 void TestWallpaperControllerClient::ResetCounts() {
   open_count_ = 0;
   close_preview_count_ = 0;
@@ -89,15 +83,13 @@
     return;
   }
 
-  image_index_ = ++image_index_ % iter->second.size();
-  backdrop::Image image(iter->second.at(image_index_));
+  backdrop::Image image(iter->second.back());
   std::move(callback).Run(/*success=*/true, std::move(image));
 }
 
 void TestWallpaperControllerClient::FetchImagesForCollection(
     const std::string& collection_id,
     FetchImagesForCollectionCallback callback) {
-  fetch_images_for_collection_count_++;
   auto iter = variations_.find(collection_id);
   if (fetch_images_for_collection_fails_ || iter == variations_.end()) {
     std::move(callback).Run(/*success=*/false, std::vector<backdrop::Image>());
diff --git a/ash/wallpaper/test_wallpaper_controller_client.h b/ash/wallpaper/test_wallpaper_controller_client.h
index 23e53bb5..ff7d38d 100644
--- a/ash/wallpaper/test_wallpaper_controller_client.h
+++ b/ash/wallpaper/test_wallpaper_controller_client.h
@@ -29,11 +29,6 @@
       const TestWallpaperControllerClient&) = delete;
   virtual ~TestWallpaperControllerClient();
 
-  // Add a set of |images| for |collection_id| that will be returned for
-  // FetchDailyRefreshWallpaper and FetchImagesForCollection.
-  void AddCollection(const std::string& collection_id,
-                     const std::vector<backdrop::Image>& images);
-
   size_t open_count() const { return open_count_; }
   size_t close_preview_count() const { return close_preview_count_; }
   size_t set_default_wallpaper_count() const {
@@ -42,9 +37,6 @@
   size_t migrate_collection_id_from_chrome_app_count() const {
     return migrate_collection_id_from_chrome_app_count_;
   }
-  size_t fetch_images_for_collection_count() const {
-    return fetch_images_for_collection_count_;
-  }
   std::string get_fetch_daily_refresh_wallpaper_param() const {
     return fetch_daily_refresh_wallpaper_param_;
   }
@@ -118,7 +110,6 @@
   size_t close_preview_count_ = 0;
   size_t set_default_wallpaper_count_ = 0;
   size_t migrate_collection_id_from_chrome_app_count_ = 0;
-  size_t fetch_images_for_collection_count_ = 0;
   std::string fetch_daily_refresh_wallpaper_param_;
   bool fetch_daily_refresh_info_fails_ = false;
   AccountId get_wallpaper_path_from_drive_fs_account_id_;
@@ -129,7 +120,6 @@
   bool fetch_google_photos_photo_fails_ = false;
   bool google_photo_has_been_deleted_ = false;
 
-  int image_index_ = 0;
   base::flat_map<std::string, std::vector<backdrop::Image>> variations_;
 };
 
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index c56284e..a315d721 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -36,6 +36,7 @@
 #include "ash/wallpaper/wallpaper_widget_controller.h"
 #include "ash/wallpaper/wallpaper_window_state_manager.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
+#include "ash/webui/personalization_app/proto/backdrop_wallpaper.pb.h"
 #include "ash/wm/overview/overview_constants.h"
 #include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -487,6 +488,22 @@
   return url_to_file_path_map;
 }
 
+// Checks if the given |variant| is suitable for the current system's color
+// mode. Image with type |Image_ImageType_IMAGE_TYPE_UNKNOWN| is not D/L aware
+// and should be used regardless of color mode.
+bool IsSuitableOnlineWallpaperVariant(const OnlineWallpaperVariant& variant) {
+  bool dark_mode_enabled =
+      Shell::Get()->ash_color_provider()->IsDarkModeEnabled();
+  switch (variant.type) {
+    case backdrop::Image_ImageType_IMAGE_TYPE_UNKNOWN:
+      return true;
+    case backdrop::Image_ImageType_IMAGE_TYPE_LIGHT_MODE:
+      return !dark_mode_enabled;
+    case backdrop::Image_ImageType_IMAGE_TYPE_DARK_MODE:
+      return dark_mode_enabled;
+  }
+}
+
 // Saves the online wallpaper with both large and small sizes to local file
 // system.
 void SaveOnlineWallpaper(const std::string& url,
@@ -768,13 +785,6 @@
                                  kLargeWallpaperMaxHeight));
 }
 
-// Returns an appropriate ColorMode value based on the Light/Dark mode state.
-OnlineWallpaperVariantInfoFetcher::ColorMode GetColorMode() {
-  return Shell::Get()->ash_color_provider()->IsDarkModeEnabled()
-             ? OnlineWallpaperVariantInfoFetcher::ColorMode::kDarkMode
-             : OnlineWallpaperVariantInfoFetcher::ColorMode::kLightMode;
-}
-
 }  // namespace
 
 const char WallpaperControllerImpl::kSmallWallpaperSubDir[] = "small";
@@ -798,17 +808,11 @@
 // static
 std::unique_ptr<WallpaperControllerImpl> WallpaperControllerImpl::Create(
     PrefService* local_state) {
-  auto online_wallpaper_variant_fetcher =
-      std::make_unique<OnlineWallpaperVariantInfoFetcher>();
-  return std::make_unique<WallpaperControllerImpl>(
-      local_state, std::move(online_wallpaper_variant_fetcher));
+  return std::make_unique<WallpaperControllerImpl>(local_state);
 }
 
-WallpaperControllerImpl::WallpaperControllerImpl(
-    PrefService* local_state,
-    std::unique_ptr<OnlineWallpaperVariantInfoFetcher> online_fetcher)
-    : variant_info_fetcher_(std::move(online_fetcher)),
-      color_profiles_(GetProminentColorProfiles()),
+WallpaperControllerImpl::WallpaperControllerImpl(PrefService* local_state)
+    : color_profiles_(GetProminentColorProfiles()),
       wallpaper_reload_delay_(kWallpaperReloadDelay),
       sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
@@ -1177,7 +1181,6 @@
 
 void WallpaperControllerImpl::SetClient(WallpaperControllerClient* client) {
   wallpaper_controller_client_ = client;
-  variant_info_fetcher_->SetClient(client);
 }
 
 void WallpaperControllerImpl::Init(
@@ -2233,29 +2236,6 @@
       url_to_file_path_map.at(params.url.spec()));
 }
 
-void WallpaperControllerImpl::OnWallpaperVariantsFetched(
-    WallpaperType type,
-    SetWallpaperCallback callback,
-    absl::optional<OnlineWallpaperParams> params) {
-  DCHECK(type == WallpaperType::kDaily || type == WallpaperType::kOnline);
-  if (params) {
-    SetOnlineWallpaper(*params, std::move(callback));
-
-    // The Daily Refresh timer depends on the value of the user WallpaperInfo.
-    // it after setting the wallpaper value.
-    if (type == WallpaperType::kDaily)
-      StartDailyRefreshTimer();
-    return;
-  }
-
-  // Report that setting the wallpaper failed.
-  std::move(callback).Run(false);
-
-  // Daily wallpaper should schedule retry.
-  if (type == WallpaperType::kDaily)
-    OnFetchDailyWallpaperFailed();
-}
-
 void WallpaperControllerImpl::OnOnlineWallpaperDecoded(
     const OnlineWallpaperParams& params,
     bool save_file,
@@ -3015,7 +2995,7 @@
       HandleDailyWallpaperInfoSyncedIn(account_id, info);
       break;
     case WallpaperType::kOnline:
-      HandleSettingOnlineWallpaperFromWallpaperInfo(account_id, info);
+      HandleOnlineWallpaperInfoSyncedIn(account_id, info);
       break;
     case WallpaperType::kGooglePhotos:
     case WallpaperType::kDailyGooglePhotos:
@@ -3078,7 +3058,6 @@
     size_t current_index,
     const gfx::ImageSkia& image) {
   if (image.isNull()) {
-    LOG(WARNING) << "Image download failed " << current_index;
     std::move(on_done).Run();
     return;
   }
@@ -3177,8 +3156,7 @@
   }
   if (synced_info == local_info)
     return;
-  if (synced_info.date >= local_info.date) {
-    // If synced is newer or the same age, it wins.
+  if (synced_info.date > local_info.date) {
     HandleWallpaperInfoSyncedIn(account_id, synced_info);
   } else if (local_info.type == WallpaperType::kCustomized) {
     // Generally, we handle setting synced_info when local_info is updated.
@@ -3229,19 +3207,13 @@
               set_wallpaper_weak_factory_.GetWeakPtr(), account_id,
               info.collection_id, std::move(callback)));
     } else {
-      DCHECK_EQ(info.type, WallpaperType::kDaily);
-      OnlineWallpaperVariantInfoFetcher::FetchParamsCallback fetch_callback =
-          base::BindOnce(&WallpaperControllerImpl::OnWallpaperVariantsFetched,
-                         set_wallpaper_weak_factory_.GetWeakPtr(), info.type,
-                         std::move(callback));
-      // Fetch can fail if wallpaper_controller_client has been cleared or
-      // |info| is malformed.
-      if (!variant_info_fetcher_->FetchDailyWallpaper(
-              account_id, info, GetColorMode(), std::move(fetch_callback))) {
-        // Could not start fetch of wallpaper variants. Likely because the
-        // chrome client isn't ready. Schedule for later.
-        NOTREACHED() << "Failed to initiate daily wallpaper fetch";
-      }
+      wallpaper_controller_client_->FetchDailyRefreshWallpaper(
+          GetDailyRefreshCollectionId(account_id),
+          base::BindOnce(&WallpaperControllerImpl::SetDailyWallpaper,
+                         set_wallpaper_weak_factory_.GetWeakPtr(), account_id,
+                         GetDailyRefreshCollectionId(account_id),
+                         ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED,
+                         std::move(callback)));
     }
   } else {
     StartDailyRefreshTimer();
@@ -3249,6 +3221,44 @@
   }
 }
 
+void WallpaperControllerImpl::SetDailyWallpaper(
+    const AccountId& account_id,
+    const std::string& collection_id,
+    WallpaperLayout layout,
+    RefreshWallpaperCallback callback,
+    bool success,
+    const backdrop::Image& image) {
+  if (success) {
+    wallpaper_controller_client_->FetchImagesForCollection(
+        collection_id,
+        base::BindOnce(
+            &WallpaperControllerImpl::FindAndSetOnlineWallpaperVariants,
+            weak_factory_.GetWeakPtr(),
+            OnlineWallpaperParams(
+                account_id, image.asset_id(), GURL(image.image_url()),
+                collection_id, layout, /*preview_mode=*/false,
+                /*from_user=*/false, /*daily_refresh_enabled=*/true,
+                /*unit_id=*/absl::nullopt,
+                /*variants=*/std::vector<OnlineWallpaperVariant>()),
+            base::BindOnce(&WallpaperControllerImpl::OnSetDailyWallpaper,
+                           weak_factory_.GetWeakPtr(), std::move(callback))));
+  } else {
+    OnFetchDailyWallpaperFailed();
+    std::move(callback).Run(false);
+  }
+}
+
+void WallpaperControllerImpl::OnSetDailyWallpaper(
+    RefreshWallpaperCallback callback,
+    bool success) {
+  std::move(callback).Run(success);
+  if (success) {
+    StartDailyRefreshTimer();
+  } else {
+    OnFetchDailyWallpaperFailed();
+  }
+}
+
 void WallpaperControllerImpl::StartDailyRefreshTimer() {
   base::TimeDelta timer_delay =
       FuzzTimeDelta(GetTimeToNextDailyRefreshUpdate());
@@ -3265,7 +3275,6 @@
 }
 
 void WallpaperControllerImpl::StartUpdateWallpaperTimer(base::TimeDelta delay) {
-  DCHECK(delay.is_positive());
   base::Time desired_run_time = base::Time::Now() + delay;
   update_wallpaper_timer_.Start(
       FROM_HERE, desired_run_time,
@@ -3278,9 +3287,8 @@
   WallpaperInfo info;
   if (!GetUserWallpaperInfo(GetActiveAccountId(), &info))
     return base::TimeDelta();
-  base::TimeDelta delta = (info.date + base::Days(1)) - base::Time::Now();
-  // Guarantee the delta is always 0 or positive.
-  return delta.is_positive() ? delta : base::TimeDelta();
+  return info.date.ToDeltaSinceWindowsEpoch() -
+         base::Time::Now().ToDeltaSinceWindowsEpoch() + base::Days(1);
 }
 
 void WallpaperControllerImpl::OnUpdateWallpaperTimerExpired() {
@@ -3417,17 +3425,21 @@
 void WallpaperControllerImpl::HandleDailyWallpaperInfoSyncedIn(
     const AccountId& account_id,
     const WallpaperInfo& info) {
-  DCHECK(info.type == WallpaperType::kDaily);
   std::string old_collection_id = GetDailyRefreshCollectionId(account_id);
   if (info.collection_id == old_collection_id)
     return;
-  OnlineWallpaperVariantInfoFetcher::FetchParamsCallback callback =
-      base::BindOnce(&WallpaperControllerImpl::OnWallpaperVariantsFetched,
-                     weak_factory_.GetWeakPtr(), info.type, base::DoNothing());
-  if (!variant_info_fetcher_->FetchDailyWallpaper(
-          account_id, info, GetColorMode(), std::move(callback))) {
-    NOTREACHED() << "Fetch of daily wallpaper info failed.";
-  }
+  wallpaper_controller_client_->FetchDailyRefreshWallpaper(
+      info.collection_id,
+      base::BindOnce(&WallpaperControllerImpl::SetDailyWallpaper,
+                     weak_factory_.GetWeakPtr(), account_id, info.collection_id,
+                     ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED,
+                     base::DoNothing()));
+}
+
+void WallpaperControllerImpl::HandleOnlineWallpaperInfoSyncedIn(
+    const AccountId& account_id,
+    const WallpaperInfo& info) {
+  HandleSettingOnlineWallpaperFromWallpaperInfo(account_id, info);
 }
 
 void WallpaperControllerImpl::HandleGooglePhotosWallpaperInfoSyncedIn(
@@ -3463,14 +3475,79 @@
 void WallpaperControllerImpl::HandleSettingOnlineWallpaperFromWallpaperInfo(
     const AccountId& account_id,
     const WallpaperInfo& info) {
-  DCHECK(info.type == WallpaperType::kDaily ||
-         info.type == WallpaperType::kOnline);
-  OnlineWallpaperVariantInfoFetcher::FetchParamsCallback callback =
-      base::BindOnce(&WallpaperControllerImpl::OnWallpaperVariantsFetched,
-                     weak_factory_.GetWeakPtr(), info.type, base::DoNothing());
+  bool daily_refresh_enabled = info.type == WallpaperType::kDaily;
+  if (info.unit_id.has_value() && !info.variants.empty()) {
+    const auto iter = std::find_if(
+        info.variants.begin(), info.variants.end(), [](const auto& variant) {
+          return IsSuitableOnlineWallpaperVariant(variant);
+        });
+    if (iter != info.variants.end()) {
+      SetOnlineWallpaper(
+          ash::OnlineWallpaperParams{account_id, iter->asset_id,
+                                     GURL(iter->raw_url), info.collection_id,
+                                     info.layout, /*preview_mode=*/false,
+                                     /*from_user=*/false, daily_refresh_enabled,
+                                     info.unit_id, info.variants},
+          base::DoNothing());
+    }
+  } else {
+    wallpaper_controller_client_->FetchImagesForCollection(
+        info.collection_id,
+        base::BindOnce(
+            &WallpaperControllerImpl::FindAndSetOnlineWallpaperVariants,
+            weak_factory_.GetWeakPtr(),
+            OnlineWallpaperParams(
+                account_id, info.asset_id, GURL(info.location),
+                info.collection_id, info.layout,
+                /*preview_mode=*/false,
+                /*from_user=*/false, daily_refresh_enabled,
+                /*unit_id=*/absl::nullopt,
+                /*variants=*/std::vector<OnlineWallpaperVariant>()),
+            base::DoNothing()));
+  }
+}
 
-  variant_info_fetcher_->FetchOnlineWallpaper(account_id, info, GetColorMode(),
-                                              std::move(callback));
+void WallpaperControllerImpl::FindAndSetOnlineWallpaperVariants(
+    const OnlineWallpaperParams& params,
+    base::OnceCallback<void(bool success)> callback,
+    bool success,
+    const std::vector<backdrop::Image>& images) {
+  if (!success) {
+    LOG(ERROR) << "Failed to fetch online wallpapers.";
+    std::move(callback).Run(success);
+    return;
+  }
+
+  absl::optional<uint64_t> unit_id;
+  std::vector<ash::OnlineWallpaperVariant> variants;
+  for (const auto& image : images) {
+    if (image.asset_id() == params.asset_id) {
+      unit_id = image.unit_id();
+    }
+  }
+  for (const auto& image : images) {
+    if (image.unit_id() == unit_id) {
+      variants.emplace_back(image.asset_id(), GURL(image.image_url()),
+                            image.has_image_type()
+                                ? image.image_type()
+                                : backdrop::Image::IMAGE_TYPE_UNKNOWN);
+    }
+  }
+
+  const auto iter =
+      std::find_if(variants.begin(), variants.end(), [](const auto& variant) {
+        return IsSuitableOnlineWallpaperVariant(variant);
+      });
+  if (iter == variants.end()) {
+    std::move(callback).Run(/*success=*/false);
+  } else {
+    SetOnlineWallpaper(
+        ash::OnlineWallpaperParams{
+            params.account_id, iter->asset_id, GURL(iter->raw_url),
+            params.collection_id, params.layout, params.preview_mode,
+            params.from_user, params.daily_refresh_enabled, unit_id, variants},
+        std::move(callback));
+  }
 }
 
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_controller_impl.h b/ash/wallpaper/wallpaper_controller_impl.h
index c4676bc..ec41be0 100644
--- a/ash/wallpaper/wallpaper_controller_impl.h
+++ b/ash/wallpaper/wallpaper_controller_impl.h
@@ -22,7 +22,6 @@
 #include "ash/public/cpp/wallpaper/wallpaper_info.h"
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
 #include "ash/shell_observer.h"
-#include "ash/wallpaper/online_wallpaper_variant_info_fetcher.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_observer.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer_observer.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom-forward.h"
@@ -117,9 +116,7 @@
 
   // Prefer to use to obtain an new instance unless injecting non-production
   // members i.e. in tests.
-  explicit WallpaperControllerImpl(
-      PrefService* local_state,
-      std::unique_ptr<OnlineWallpaperVariantInfoFetcher> fetcher);
+  explicit WallpaperControllerImpl(PrefService* local_state);
 
   WallpaperControllerImpl(const WallpaperControllerImpl&) = delete;
   WallpaperControllerImpl& operator=(const WallpaperControllerImpl&) = delete;
@@ -460,11 +457,6 @@
       const OnlineWallpaperParams& params,
       const base::flat_map<std::string, base::FilePath>& url_to_file_path_map);
 
-  // Handler to receive Fetch*Wallpaper variants callbacks.
-  void OnWallpaperVariantsFetched(WallpaperType type,
-                                  SetWallpaperCallback callback,
-                                  absl::optional<OnlineWallpaperParams> params);
-
   // Used as the callback of decoding wallpapers of type
   // `WallpaperType::kOnline`. Saves the image to local file if `save_file` is
   // true, and shows the wallpaper immediately if `params.account_id` is the
@@ -699,6 +691,20 @@
   // If the user has a Google Photos wallpaper set.
   bool IsGooglePhotosWallpaperSet() const;
 
+  // Callback from the client providing a url to a wallpaper from the user
+  // specified collection when daily refresh is enabled. If |success| is
+  // false, fetching the |image| failed, and should be tried again soon.
+  void SetDailyWallpaper(const AccountId& account_id,
+                         const std::string& collection_id,
+                         WallpaperLayout layout,
+                         RefreshWallpaperCallback callback,
+                         bool success,
+                         const backdrop::Image& image);
+
+  // Called after attempting to download and set a daily refresh wallpaper.
+  // On failure retry again in a while.
+  void OnSetDailyWallpaper(RefreshWallpaperCallback callback, bool success);
+
   // Starts a wall clock timer, to update the wallpaper 24 hours since the last
   // wallpaper was set.
   void StartDailyRefreshTimer();
@@ -747,6 +753,9 @@
   void HandleDailyWallpaperInfoSyncedIn(const AccountId& account_id,
                                         const WallpaperInfo& info);
 
+  void HandleOnlineWallpaperInfoSyncedIn(const AccountId& account_id,
+                                         const WallpaperInfo& info);
+
   void HandleGooglePhotosWallpaperInfoSyncedIn(const AccountId& account_id,
                                                const WallpaperInfo& info);
 
@@ -756,6 +765,15 @@
       const AccountId& account_id,
       const WallpaperInfo& info);
 
+  // Find the variants for the online wallpaper with given |params.asset_id| and
+  // |params.collection_id| and determine the right variant to set based on the
+  // system's color mode.
+  void FindAndSetOnlineWallpaperVariants(
+      const OnlineWallpaperParams& params,
+      base::OnceCallback<void(bool)> callback,
+      bool success,
+      const std::vector<backdrop::Image>& images);
+
   bool locked_ = false;
 
   WallpaperMode wallpaper_mode_ = WALLPAPER_NONE;
@@ -774,9 +792,6 @@
   // active.
   std::unique_ptr<WallpaperWindowStateManager> window_state_manager_;
 
-  // Delegate to resolve online wallpaper variants.
-  std::unique_ptr<OnlineWallpaperVariantInfoFetcher> variant_info_fetcher_;
-
   // The prominent colors extracted from the current wallpaper.
   // kInvalidWallpaperColor is used by default or if extracting colors fails.
   std::vector<SkColor> prominent_colors_;
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 08aee7eb..44fb316 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -46,7 +46,6 @@
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time_override.h"
@@ -340,15 +339,8 @@
 }
 
 WallpaperInfo InfoWithType(WallpaperType type) {
-  WallpaperInfo info(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED, type,
-                     base::Time::Now());
-  if (type == WallpaperType::kDaily || type == WallpaperType::kOnline) {
-    // Daily and Online types require asset id and collection id.
-    info.asset_id = 1234;
-    info.collection_id = "placeholder collection";
-    info.location = "https://example.com/example.jpeg";
-  }
-  return info;
+  return WallpaperInfo(std::string(), WALLPAPER_LAYOUT_CENTER_CROPPED, type,
+                       base::Time::Now());
 }
 
 base::Time DayBeforeYesterdayish() {
@@ -398,8 +390,7 @@
 
 class WallpaperControllerTestBase : public AshTestBase {
  public:
-  WallpaperControllerTestBase()
-      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  WallpaperControllerTestBase() = default;
 
   WallpaperControllerTestBase(const WallpaperControllerTestBase&) = delete;
   WallpaperControllerTestBase& operator=(const WallpaperControllerTestBase&) =
@@ -3103,7 +3094,24 @@
       base::BindLambdaForTesting(
           [&is_second_callback_run]() { is_second_callback_run = true; }),
       test_window.get());
-  RunAllTasksUntilIdle();
+  {
+    // The animation is quite short (0.01 seconds) which is problematic in
+    // debug builds if RunAllTasksUntilIdle is a bit slow to execute. That leads
+    // to test flakes. We work around that by temporarily freezing time, which
+    // prevents the animation from unexpectedly completing too soon.
+    // Ideally this test should use MockTime instead, which will become easier
+    // after https://crrev.com/c/1352260 lands.
+    base::subtle::ScopedTimeClockOverrides time_override(
+        nullptr,
+        []() {
+          static base::TimeTicks time_ticks =
+              base::subtle::TimeTicksNowIgnoringOverride();
+          return time_ticks;
+        },
+        nullptr);
+
+    RunAllTasksUntilIdle();
+  }
   // Neither callback is run because the animation of the first wallpaper
   // hasn't finished yet.
   EXPECT_FALSE(is_first_callback_run);
@@ -3111,7 +3119,6 @@
 
   // Force the animation to complete. The two callbacks are both run.
   RunDesktopControllerAnimation();
-  RunAllTasksUntilIdle();
   EXPECT_TRUE(is_first_callback_run);
   EXPECT_TRUE(is_second_callback_run);
 
@@ -3121,7 +3128,6 @@
       base::BindLambdaForTesting(
           [&is_third_callback_run]() { is_third_callback_run = true; }),
       test_window.get());
-  RunAllTasksUntilIdle();
   EXPECT_TRUE(is_third_callback_run);
 }
 
@@ -3776,9 +3782,9 @@
   ClearLogin();
   SimulateUserLogin(account_id_1);
 
-  // Info is set as over a day old so we expect one task to run in under an hour
-  // (due to fuzzing) then it will idle.
-  task_environment()->FastForwardBy(base::Hours(1));
+  // |daily_refresh_timer_| adds a task to the sequence, as opposed to execute
+  // within the task that it is called in.
+  RunAllTasksUntilIdle();
 
   EXPECT_EQ(TestWallpaperControllerClient::kDummyCollectionId,
             client_.get_fetch_daily_refresh_wallpaper_param());
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index 0cb4a0e..82f2b125 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -60,6 +60,7 @@
       {"personalizationTitle",
        IDS_PERSONALIZATION_APP_PERSONALIZATION_HUB_TITLE},
       {"wallpaperLabel", IDS_PERSONALIZATION_APP_WALLPAPER_LABEL},
+      {"defaultWallpaper", IDS_PERSONALIZATION_APP_DEFAULT_WALLPAPER},
       {"back", IDS_PERSONALIZATION_APP_BACK_BUTTON},
       {"currentlySet", IDS_PERSONALIZATION_APP_CURRENTLY_SET},
       {"myImagesLabel", IDS_PERSONALIZATION_APP_MY_IMAGES},
diff --git a/ash/webui/personalization_app/resources/trusted/user/avatar_list_element.ts b/ash/webui/personalization_app/resources/trusted/user/avatar_list_element.ts
index 3f5ecba..c0af1829 100644
--- a/ash/webui/personalization_app/resources/trusted/user/avatar_list_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/user/avatar_list_element.ts
@@ -298,11 +298,16 @@
     }
   }
 
+  private camelToKebab_(className: string): string {
+    return className.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
+  }
+
   private getOptionInnerContainerClass_(option: Option, image: UserImage|null):
       string {
+    const defaultClass = option ? option.class : 'image-container';
     return this.getAriaSelected_(option, image) === 'true' ?
-        `${option.class} selected-${option.id}` :
-        option.class;
+        `${defaultClass} selected-${this.camelToKebab_(option.id)}` :
+        defaultClass;
   }
 }
 
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts
index 5a59cbec..f7aa050 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_selected_element.ts
@@ -195,6 +195,9 @@
     if (!image) {
       return this.i18n('unknownImageAttribution');
     }
+    if (image.type === WallpaperType.kDefault) {
+      return this.i18n('defaultWallpaper');
+    }
     if (isNonEmptyArray(image.attribution)) {
       const title = image.attribution[0];
       return dailyRefreshCollectionId ?
@@ -336,6 +339,9 @@
       return this.i18n('currentlySet') + ' ' +
           this.i18n('unknownImageAttribution');
     }
+    if (image.type === WallpaperType.kDefault) {
+      return `${this.i18n('currentlySet')} ${this.i18n('defaultWallpaper')}`;
+    }
     if (isNonEmptyArray(image.attribution)) {
       return dailyRefreshCollectionId ?
           [
diff --git a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html
index 3a91f16..0c46c2c 100644
--- a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html
+++ b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.html
@@ -62,6 +62,7 @@
       <cr-input
           id="rsuCode"
           value="{{rsuCode_}}"
+          on-keydown="onKeyDown_"
           maxlength="[[rsuCodeExpectedLength_]]"
           invalid="[[rsuCodeInvalid_]]"
           disabled="[[allButtonsDisabled]]">
diff --git a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
index cb0e3f4..fd5c20c 100644
--- a/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
+++ b/ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.js
@@ -13,7 +13,7 @@
 
 import {getShimlessRmaService} from './mojo_interface_provider.js';
 import {QrCode, ShimlessRmaServiceInterface, StateResult} from './shimless_rma_types.js';
-import {enableNextButton} from './shimless_rma_util.js';
+import {dispatchNextButtonClick, enableNextButton} from './shimless_rma_util.js';
 
 // The size of each tile in pixels.
 const QR_CODE_TILE_SIZE = 5;
@@ -191,6 +191,16 @@
   }
 
   /**
+   * @param {!Event} event
+   * @protected
+   */
+  onKeyDown_(event) {
+    if (event.key === 'Enter') {
+      dispatchNextButtonClick(this);
+    }
+  }
+
+  /**
    * @return {!CanvasRenderingContext2D}
    * @private
    */
diff --git a/ash/webui/shimless_rma/resources/shimless_rma.html b/ash/webui/shimless_rma/resources/shimless_rma.html
index e6ac1c4..aaf8c9c 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma.html
+++ b/ash/webui/shimless_rma/resources/shimless_rma.html
@@ -50,6 +50,11 @@
     width: 20px;
   }
 
+  .button-icon {
+    height: var(--cr-icon-size);
+    width: var(--cr-icon-size);
+  }
+
   #cancelButtonSpinner,
   #nextButtonCaret,
   #nextButtonSpinner {
@@ -86,9 +91,9 @@
       <paper-spinner-lite id="backButtonSpinner" class="busy-icon"
           hidden$="[[!backButtonClicked_]]" active>
       </paper-spinner-lite>
-      <span id="backButtonCaret" hidden$="[[backButtonClicked_]]">
-        &#60;
-      </span>
+      <iron-icon id="backButtonCaret" icon="cr:chevron-left"
+          class="button-icon" hidden$="[[backButtonClicked_]]">
+      </iron-icon>
       <span id="backButtonLabel">
         [[i18n('backButtonLabel')]]
       </span>
@@ -117,9 +122,9 @@
       <paper-spinner-lite id="nextButtonSpinner" class="busy-icon"
           hidden$="[[!nextButtonClicked_]]" active>
       </paper-spinner-lite>
-      <span id="nextButtonCaret" hidden$="[[nextButtonClicked_]]">
-        &#62;
-      </span>
+      <iron-icon id="nextButtonCaret" icon="cr:chevron-right"
+          class="button-icon" hidden$="[[nextButtonClicked_]]">
+      </iron-icon>
     </cr-button>
   </div>
 </div>
diff --git a/ash/webui/shimless_rma/resources/shimless_rma.js b/ash/webui/shimless_rma/resources/shimless_rma.js
index 9649931..feab56d 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma.js
@@ -388,6 +388,15 @@
     };
 
     /**
+     * The nextButtonCallback_ callback is used by the landing page to simulate
+     * the next button being clicked.
+     * @private {?Function}
+     */
+    this.nextButtonCallback_ = (e) => {
+      this.onNextButtonClicked_();
+    };
+
+    /**
      * The setNextButtonLabelCallback callback is used by page elements to set
      * the text label for the 'Next' button.
      * @private {?Function}
@@ -411,6 +420,7 @@
     window.addEventListener(
         'enable-all-buttons', this.enableAllButtonsCallback_);
     window.addEventListener('click-cancel-button', this.cancelButtonCallback_);
+    window.addEventListener('click-next-button', this.nextButtonCallback_);
   }
 
   /** @override */
@@ -427,6 +437,7 @@
         'enable-all-buttons', this.enableAllButtonsCallback_);
     window.removeEventListener(
         'click-cancel-button', this.cancelButtonCallback_);
+    window.removeEventListener('click-next-button', this.nextButtonCallback_);
   }
 
   /** @override */
diff --git a/ash/webui/shimless_rma/resources/shimless_rma_util.js b/ash/webui/shimless_rma/resources/shimless_rma_util.js
index 31fbc9c0..62647bb 100644
--- a/ash/webui/shimless_rma/resources/shimless_rma_util.js
+++ b/ash/webui/shimless_rma/resources/shimless_rma_util.js
@@ -64,3 +64,14 @@
       {bubbles: true, composed: true, detail: fn},
       ));
 }
+
+/**
+ * Dispatches an event to click the next button.
+ * @param {!HTMLElement} element
+ */
+export function dispatchNextButtonClick(element) {
+  element.dispatchEvent(new CustomEvent('click-next-button', {
+    bubbles: true,
+    composed: true,
+  }));
+}
diff --git a/ash/wm/desks/templates/desks_templates_grid_view.cc b/ash/wm/desks/templates/desks_templates_grid_view.cc
index a199a7c..a4d41dc9 100644
--- a/ash/wm/desks/templates/desks_templates_grid_view.cc
+++ b/ash/wm/desks/templates/desks_templates_grid_view.cc
@@ -231,6 +231,7 @@
   // be the new template, while the rest will be sorted alphabetically.
   for (size_t i = 0; i < grid_items_.size(); i++)
     ReorderChildView(grid_items_[i], i);
+  NotifyAccessibilityEvent(ax::mojom::Event::kTreeChanged, true);
 
   if (bounds_animator_.IsAnimating())
     bounds_animator_.Cancel();
@@ -265,10 +266,8 @@
   // and sort the remaining templates after it.
   SortTemplateGridItems(last_saved_template_uuid);
 
-  if (!initializing_grid_view) {
+  if (!initializing_grid_view)
     AnimateGridItems(new_grid_items);
-    NotifyAccessibilityEvent(ax::mojom::Event::kTreeChanged, true);
-  }
 }
 
 void DesksTemplatesGridView::DeleteTemplates(
diff --git a/ash/wm/desks/templates/desks_templates_icon_container.cc b/ash/wm/desks/templates/desks_templates_icon_container.cc
index e231eebd..b137274 100644
--- a/ash/wm/desks/templates/desks_templates_icon_container.cc
+++ b/ash/wm/desks/templates/desks_templates_icon_container.cc
@@ -71,6 +71,7 @@
 // its values.
 void InsertIconIdentifierToIconInfo(
     const std::string& app_id,
+    const std::u16string& app_title,
     const std::string& identifier,
     int activation_index,
     std::map<std::string, DesksTemplatesIconContainer::IconInfo>*
@@ -78,8 +79,9 @@
   // A single app/site can have multiple windows so count their occurrences and
   // use the smallest activation index for sorting purposes.
   if (!base::Contains(*out_icon_identifier_to_icon_info, identifier)) {
-    (*out_icon_identifier_to_icon_info)[identifier] = {app_id, activation_index,
-                                                       /*count=*/1};
+    (*out_icon_identifier_to_icon_info)[identifier] = {
+        app_id, base::UTF16ToASCII(app_title), activation_index,
+        /*count=*/1};
   } else {
     ++(*out_icon_identifier_to_icon_info)[identifier].count;
     (*out_icon_identifier_to_icon_info)[identifier].activation_index = std::min(
@@ -113,13 +115,14 @@
     const int activation_index = restore_data.second->activation_index.value();
     const int active_tab_index =
         restore_data.second->active_tab_index.value_or(-1);
+    const std::u16string app_title = restore_data.second->title.value_or(u"");
     if (restore_data.second->urls.has_value() && is_browser) {
       const auto& urls = restore_data.second->urls.value();
       for (int i = 0; i < static_cast<int>(urls.size()); ++i) {
         // Strip extra information from the url so urls with the same host but
         // different queries are treated the same.
         InsertIconIdentifierToIconInfo(
-            app_id, urls[i].GetWithEmptyPath().spec(),
+            app_id, app_title, urls[i].GetWithEmptyPath().spec(),
             active_tab_index == i ? activation_index
                                   : kInactiveTabOffset + activation_index,
             out_icon_identifier_to_icon_info);
@@ -132,7 +135,8 @@
       if (IsBrowserAppId(app_id) && app_name.has_value())
         new_app_id = app_restore::GetAppIdFromAppName(app_name.value());
 
-      InsertIconIdentifierToIconInfo(app_id, new_app_id, activation_index,
+      InsertIconIdentifierToIconInfo(app_id, app_title, new_app_id,
+                                     activation_index,
                                      out_icon_identifier_to_icon_info);
     }
   }
@@ -196,8 +200,9 @@
 
     // Since there were no modifications to `app_id`, app id and icon identifier
     // are both `app_id`.
-    InsertIconIdentifierToIconInfo(/*app_id=*/app_id, /*identifier=*/app_id, i,
-                                   &icon_identifier_to_icon_info);
+    InsertIconIdentifierToIconInfo(
+        /*app_id=*/app_id, /*app_title=*/window->GetTitle(),
+        /*identifier=*/app_id, i, &icon_identifier_to_icon_info);
   }
 
   CreateIconViewsFromIconIdentifiers(
@@ -277,7 +282,8 @@
                   DesksTemplatesIconView::kIconSize / 2))
               .Build());
       icon_view->SetIconIdentifierAndCount(icon_identifier, icon_info.app_id,
-                                           icon_info.count, /*show_plus=*/true);
+                                           icon_info.app_title, icon_info.count,
+                                           /*show_plus=*/true);
     } else {
       num_hidden_icons += icon_info.count;
     }
@@ -298,10 +304,11 @@
                            DesksTemplatesIconView::kIconSize / 2))
                        .Build());
 
-  // Set both `icon_identifier` and `app_id` to be empty strings for overflow
-  // icon views, since only the count should matter.
+  // Set `icon_identifier`, `app_id` and `app_title` to be empty strings for
+  // overflow icon views, since only the count should matter.
   overflow_icon_view->SetIconIdentifierAndCount(
       /*icon_identifier=*/std::string(), /*app_id=*/std::string(),
+      /*app_title=*/std::string(),
       /*count=*/num_hidden_icons, show_plus);
 }
 
diff --git a/ash/wm/desks/templates/desks_templates_icon_container.h b/ash/wm/desks/templates/desks_templates_icon_container.h
index 318ed73a..fa792fa 100644
--- a/ash/wm/desks/templates/desks_templates_icon_container.h
+++ b/ash/wm/desks/templates/desks_templates_icon_container.h
@@ -44,6 +44,7 @@
   // icons/favicons to display.
   struct IconInfo {
     std::string app_id;
+    std::string app_title;
     int activation_index;
     int count;
   };
diff --git a/ash/wm/desks/templates/desks_templates_icon_view.cc b/ash/wm/desks/templates/desks_templates_icon_view.cc
index 47e9622..2f12db5 100644
--- a/ash/wm/desks/templates/desks_templates_icon_view.cc
+++ b/ash/wm/desks/templates/desks_templates_icon_view.cc
@@ -65,6 +65,7 @@
 void DesksTemplatesIconView::SetIconIdentifierAndCount(
     const std::string& icon_identifier,
     const std::string& app_id,
+    const std::string& app_title,
     int count,
     bool show_plus) {
   icon_identifier_ = icon_identifier;
@@ -98,11 +99,14 @@
   if (icon_identifier_.empty())
     return;
 
+  // Add the icon to the front so that it gets read out before `count_label_` by
+  // spoken feedback.
   DCHECK(!icon_view_);
   icon_view_ =
-      AddChildView(views::Builder<RoundedImageView>()
-                       .SetCornerRadius(DesksTemplatesIconView::kIconSize / 2)
-                       .Build());
+      AddChildViewAt(views::Builder<RoundedImageView>()
+                         .SetCornerRadius(DesksTemplatesIconView::kIconSize / 2)
+                         .Build(),
+                     0);
 
   // First check if the `icon_identifier_` is a special value, i.e. NTP url or
   // incognito window. If it is, use the corresponding icon for the special
@@ -114,6 +118,7 @@
                                 ->incognito_window_color_provider());
 
   icon_view_->GetViewAccessibility().OverrideRole(ax::mojom::Role::kImage);
+  icon_view_->GetViewAccessibility().OverrideName(app_title);
 
   // PWAs (e.g. Messages) should use icon identifier as they share the same app
   // id as Chrome and would return short name for app id as "Chromium" (see
@@ -122,12 +127,8 @@
   // duplicate favicons (see https://crbug.com/1281391).
   if (chrome_icon.has_value()) {
     icon_view_->SetImage(CreateResizedImageToIconSize(chrome_icon.value()));
-    icon_view_->GetViewAccessibility().OverrideName(
-        delegate->GetAppShortName(app_id));
     return;
   }
-  icon_view_->GetViewAccessibility().OverrideName(
-      delegate->GetAppShortName(icon_identifier_));
 
   // It's not a special value so `icon_identifier_` is either a favicon or an
   // app id. If `icon_identifier_` is not a valid url then it's an app id.
@@ -194,13 +195,15 @@
                                        .AsImageSkia()));
 
   // Move `this` to the back of the visible icons, i.e. before any invisible
-  // siblings and before the overflow counter,
+  // siblings and before the overflow counter. Notify the a11y API so that the
+  // spoken feedback order matches the view order.
   auto siblings = parent()->children();
   if (siblings.size() >= 2) {
     size_t i = 0;
     while (i < siblings.size() - 2 && siblings[i]->GetVisible())
       ++i;
     parent()->ReorderChildView(this, i);
+    parent()->NotifyAccessibilityEvent(ax::mojom::Event::kTreeChanged, true);
   }
 }
 
diff --git a/ash/wm/desks/templates/desks_templates_icon_view.h b/ash/wm/desks/templates/desks_templates_icon_view.h
index a4614f6..9a0d720 100644
--- a/ash/wm/desks/templates/desks_templates_icon_view.h
+++ b/ash/wm/desks/templates/desks_templates_icon_view.h
@@ -50,6 +50,7 @@
   // all unavailable icons.
   void SetIconIdentifierAndCount(const std::string& icon_identifier,
                                  const std::string& app_id,
+                                 const std::string& app_title,
                                  int count,
                                  bool show_plus);
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 4740e8e..f9f3ac6 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -398,7 +398,6 @@
     "memory/page_size.h",
     "memory/platform_shared_memory_handle.cc",
     "memory/platform_shared_memory_handle.h",
-    "memory/platform_shared_memory_mapper.cc",
     "memory/platform_shared_memory_mapper.h",
     "memory/platform_shared_memory_region.cc",
     "memory/platform_shared_memory_region.h",
@@ -417,6 +416,8 @@
     "memory/scoped_policy.h",
     "memory/scoped_refptr.h",
     "memory/shared_memory_hooks.h",
+    "memory/shared_memory_mapper.cc",
+    "memory/shared_memory_mapper.h",
     "memory/shared_memory_mapping.cc",
     "memory/shared_memory_mapping.h",
     "memory/shared_memory_security_policy.cc",
diff --git a/base/fuchsia/fuchsia_logging_unittest.cc b/base/fuchsia/fuchsia_logging_unittest.cc
index f526b4f..51e2013 100644
--- a/base/fuchsia/fuchsia_logging_unittest.cc
+++ b/base/fuchsia/fuchsia_logging_unittest.cc
@@ -34,6 +34,7 @@
       std::make_unique<fuchsia::logger::LogFilterOptions>();
   options->filter_by_pid = true;
   options->pid = Process::Current().Pid();
+  options->min_severity = fuchsia::logger::LogLevelFilter::INFO;
   fuchsia::logger::LogPtr log =
       ComponentContextForProcess()->svc()->Connect<fuchsia::logger::Log>();
   listener.ListenToLog(log.get(), std::move(options));
diff --git a/base/memory/platform_shared_memory_mapper.cc b/base/memory/platform_shared_memory_mapper.cc
deleted file mode 100644
index 7e225703..0000000
--- a/base/memory/platform_shared_memory_mapper.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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 "base/memory/platform_shared_memory_mapper.h"
-
-#include "base/check_op.h"
-
-namespace base {
-
-// static
-absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::Map(
-    subtle::PlatformSharedMemoryHandle handle,
-    bool write_allowed,
-    uint64_t offset,
-    size_t size) {
-  return MapInternal(std::move(handle), write_allowed, offset, size);
-}
-
-// static
-void PlatformSharedMemoryMapper::Unmap(span<uint8_t> mapping) {
-  return UnmapInternal(mapping);
-}
-
-}  // namespace base
diff --git a/base/memory/platform_shared_memory_mapper.h b/base/memory/platform_shared_memory_mapper.h
index c8dd5a89..c4b6c4b 100644
--- a/base/memory/platform_shared_memory_mapper.h
+++ b/base/memory/platform_shared_memory_mapper.h
@@ -6,36 +6,21 @@
 #define BASE_MEMORY_PLATFORM_SHARED_MEMORY_MAPPER_H_
 
 #include "base/base_export.h"
-#include "base/containers/span.h"
-#include "base/memory/platform_shared_memory_handle.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-#include <stdint.h>
+#include "base/memory/shared_memory_mapper.h"
 
 namespace base {
 
-// Static class responsible for the platform-specific logic to map a shared
-// memory region into the virtual address space of the process.
-class BASE_EXPORT PlatformSharedMemoryMapper {
+// Default implementation of the SharedMemoryMapper interface. Implements the
+// platform-specific logic for mapping shared memory regions into the virtual
+// address space of the process.
+class PlatformSharedMemoryMapper : public SharedMemoryMapper {
  public:
-  PlatformSharedMemoryMapper() = delete;
+  absl::optional<span<uint8_t>> Map(subtle::PlatformSharedMemoryHandle handle,
+                                    bool write_allowed,
+                                    uint64_t offset,
+                                    size_t size) override;
 
-  static absl::optional<span<uint8_t>> Map(
-      subtle::PlatformSharedMemoryHandle handle,
-      bool write_allowed,
-      uint64_t offset,
-      size_t size);
-
-  static void Unmap(span<uint8_t> mapping);
-
- private:
-  static absl::optional<span<uint8_t>> MapInternal(
-      subtle::PlatformSharedMemoryHandle handle,
-      bool write_allowed,
-      uint64_t offset,
-      size_t size);
-
-  static void UnmapInternal(span<uint8_t> mapping);
+  void Unmap(span<uint8_t> mapping) override;
 };
 
 }  // namespace base
diff --git a/base/memory/platform_shared_memory_mapper_android.cc b/base/memory/platform_shared_memory_mapper_android.cc
index 544e5fa6..8921f17 100644
--- a/base/memory/platform_shared_memory_mapper_android.cc
+++ b/base/memory/platform_shared_memory_mapper_android.cc
@@ -11,8 +11,7 @@
 
 namespace base {
 
-// static
-absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::MapInternal(
+absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::Map(
     subtle::PlatformSharedMemoryHandle handle,
     bool write_allowed,
     uint64_t offset,
@@ -32,8 +31,7 @@
   return make_span(reinterpret_cast<uint8_t*>(address), size);
 }
 
-// static
-void PlatformSharedMemoryMapper::UnmapInternal(span<uint8_t> mapping) {
+void PlatformSharedMemoryMapper::Unmap(span<uint8_t> mapping) {
   if (munmap(mapping.data(), mapping.size()) < 0)
     DPLOG(ERROR) << "munmap";
 }
diff --git a/base/memory/platform_shared_memory_mapper_fuchsia.cc b/base/memory/platform_shared_memory_mapper_fuchsia.cc
index 5278cd6..791130ff 100644
--- a/base/memory/platform_shared_memory_mapper_fuchsia.cc
+++ b/base/memory/platform_shared_memory_mapper_fuchsia.cc
@@ -11,8 +11,7 @@
 
 namespace base {
 
-// static
-absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::MapInternal(
+absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::Map(
     subtle::PlatformSharedMemoryHandle handle,
     bool write_allowed,
     uint64_t offset,
@@ -31,8 +30,7 @@
   return make_span(reinterpret_cast<uint8_t*>(addr), size);
 }
 
-// static
-void PlatformSharedMemoryMapper::UnmapInternal(span<uint8_t> mapping) {
+void PlatformSharedMemoryMapper::Unmap(span<uint8_t> mapping) {
   uintptr_t addr = reinterpret_cast<uintptr_t>(mapping.data());
   zx_status_t status = zx::vmar::root_self()->unmap(addr, mapping.size());
   if (status != ZX_OK)
diff --git a/base/memory/platform_shared_memory_mapper_mac.cc b/base/memory/platform_shared_memory_mapper_mac.cc
index 721d73f2..bc13008 100644
--- a/base/memory/platform_shared_memory_mapper_mac.cc
+++ b/base/memory/platform_shared_memory_mapper_mac.cc
@@ -11,8 +11,7 @@
 
 namespace base {
 
-// static
-absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::MapInternal(
+absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::Map(
     subtle::PlatformSharedMemoryHandle handle,
     bool write_allowed,
     uint64_t offset,
@@ -37,8 +36,7 @@
   return make_span(reinterpret_cast<uint8_t*>(address), size);
 }
 
-// static
-void PlatformSharedMemoryMapper::UnmapInternal(span<uint8_t> mapping) {
+void PlatformSharedMemoryMapper::Unmap(span<uint8_t> mapping) {
   kern_return_t kr = mach_vm_deallocate(
       mach_task_self(), reinterpret_cast<mach_vm_address_t>(mapping.data()),
       mapping.size());
diff --git a/base/memory/platform_shared_memory_mapper_posix.cc b/base/memory/platform_shared_memory_mapper_posix.cc
index dcaab5d..10ee476 100644
--- a/base/memory/platform_shared_memory_mapper_posix.cc
+++ b/base/memory/platform_shared_memory_mapper_posix.cc
@@ -11,8 +11,7 @@
 
 namespace base {
 
-// static
-absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::MapInternal(
+absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::Map(
     subtle::PlatformSharedMemoryHandle handle,
     bool write_allowed,
     uint64_t offset,
@@ -29,8 +28,7 @@
   return make_span(reinterpret_cast<uint8_t*>(address), size);
 }
 
-// static
-void PlatformSharedMemoryMapper::UnmapInternal(span<uint8_t> mapping) {
+void PlatformSharedMemoryMapper::Unmap(span<uint8_t> mapping) {
   if (munmap(mapping.data(), mapping.size()) < 0)
     DPLOG(ERROR) << "munmap";
 }
diff --git a/base/memory/platform_shared_memory_mapper_win.cc b/base/memory/platform_shared_memory_mapper_win.cc
index fdede77..6f4a4b3 100644
--- a/base/memory/platform_shared_memory_mapper_win.cc
+++ b/base/memory/platform_shared_memory_mapper_win.cc
@@ -23,8 +23,7 @@
 }
 }  // namespace
 
-// static
-absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::MapInternal(
+absl::optional<span<uint8_t>> PlatformSharedMemoryMapper::Map(
     subtle::PlatformSharedMemoryHandle handle,
     bool write_allowed,
     uint64_t offset,
@@ -49,8 +48,7 @@
                    GetMemorySectionSize(address));
 }
 
-// static
-void PlatformSharedMemoryMapper::UnmapInternal(span<uint8_t> mapping) {
+void PlatformSharedMemoryMapper::Unmap(span<uint8_t> mapping) {
   if (!UnmapViewOfFile(mapping.data()))
     DPLOG(ERROR) << "UnmapViewOfFile";
 }
diff --git a/base/memory/platform_shared_memory_region.cc b/base/memory/platform_shared_memory_region.cc
index 1214379..9277273 100644
--- a/base/memory/platform_shared_memory_region.cc
+++ b/base/memory/platform_shared_memory_region.cc
@@ -47,7 +47,8 @@
 
 absl::optional<span<uint8_t>> PlatformSharedMemoryRegion::MapAt(
     uint64_t offset,
-    size_t size) const {
+    size_t size,
+    SharedMemoryMapper* mapper) const {
   if (!IsValid())
     return absl::nullopt;
 
@@ -66,9 +67,11 @@
 
   RecordMappingWasBlockedHistogram(/*blocked=*/false);
 
+  if (!mapper)
+    mapper = SharedMemoryMapper::GetDefaultInstance();
+
   bool write_allowed = mode_ != Mode::kReadOnly;
-  auto result = PlatformSharedMemoryMapper::Map(GetPlatformHandle(),
-                                                write_allowed, offset, size);
+  auto result = mapper->Map(GetPlatformHandle(), write_allowed, offset, size);
 
   if (result.has_value()) {
     DCHECK(IsAligned(result.value().data(), kMapMinimumAlignment));
diff --git a/base/memory/platform_shared_memory_region.h b/base/memory/platform_shared_memory_region.h
index ed9607e..74d1eff 100644
--- a/base/memory/platform_shared_memory_region.h
+++ b/base/memory/platform_shared_memory_region.h
@@ -9,7 +9,7 @@
 #include "base/containers/span.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/platform_shared_memory_handle.h"
-#include "base/memory/platform_shared_memory_mapper.h"
+#include "base/memory/shared_memory_mapper.h"
 #include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -188,14 +188,15 @@
   bool ConvertToUnsafe();
 
   // Maps |size| bytes of the shared memory region starting with the given
-  // |offset| into the caller's address space. |offset| must be aligned to value
-  // of |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out
-  // of the region limits.
-  // Returns true and sets |memory| and |mapped_size| on success, returns false
-  // and leaves output parameters in unspecified state otherwise. The mapped
-  // address is guaranteed to have an alignment of at least
-  // |kMapMinimumAlignment|.
-  absl::optional<span<uint8_t>> MapAt(uint64_t offset, size_t size) const;
+  // |offset| into the caller's address space using the provided
+  // |SharedMemoryMapper|. |offset| must be aligned to value of
+  // |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out of
+  // the region limits. Returns the mapping as span on success, or absl::nullopt
+  // on failure. The mapped address is guaranteed to have an alignment of at
+  // least |kMapMinimumAlignment|.
+  absl::optional<span<uint8_t>> MapAt(uint64_t offset,
+                                      size_t size,
+                                      SharedMemoryMapper* mapper) const;
 
   const UnguessableToken& GetGUID() const { return guid_; }
 
diff --git a/base/memory/read_only_shared_memory_region.cc b/base/memory/read_only_shared_memory_region.cc
index 0950a57..70712b1 100644
--- a/base/memory/read_only_shared_memory_region.cc
+++ b/base/memory/read_only_shared_memory_region.cc
@@ -14,20 +14,23 @@
     ReadOnlySharedMemoryRegion::create_hook_ = nullptr;
 
 // static
-MappedReadOnlyRegion ReadOnlySharedMemoryRegion::Create(size_t size) {
+MappedReadOnlyRegion ReadOnlySharedMemoryRegion::Create(
+    size_t size,
+    SharedMemoryMapper* mapper) {
   if (create_hook_)
-    return create_hook_(size);
+    return create_hook_(size, mapper);
 
   subtle::PlatformSharedMemoryRegion handle =
       subtle::PlatformSharedMemoryRegion::CreateWritable(size);
   if (!handle.IsValid())
     return {};
 
-  auto result = handle.MapAt(0, handle.GetSize());
+  auto result = handle.MapAt(0, handle.GetSize(), mapper);
   if (!result.has_value())
     return {};
 
-  WritableSharedMemoryMapping mapping(result.value(), size, handle.GetGUID());
+  WritableSharedMemoryMapping mapping(result.value(), size, handle.GetGUID(),
+                                      mapper);
 #if BUILDFLAG(IS_MAC)
   handle.ConvertToReadOnly(mapping.memory());
 #else
@@ -65,21 +68,24 @@
   return ReadOnlySharedMemoryRegion(handle_.Duplicate());
 }
 
-ReadOnlySharedMemoryMapping ReadOnlySharedMemoryRegion::Map() const {
-  return MapAt(0, handle_.GetSize());
+ReadOnlySharedMemoryMapping ReadOnlySharedMemoryRegion::Map(
+    SharedMemoryMapper* mapper) const {
+  return MapAt(0, handle_.GetSize(), mapper);
 }
 
 ReadOnlySharedMemoryMapping ReadOnlySharedMemoryRegion::MapAt(
     uint64_t offset,
-    size_t size) const {
+    size_t size,
+    SharedMemoryMapper* mapper) const {
   if (!IsValid())
     return {};
 
-  auto result = handle_.MapAt(offset, size);
+  auto result = handle_.MapAt(offset, size, mapper);
   if (!result.has_value())
     return {};
 
-  return ReadOnlySharedMemoryMapping(result.value(), size, handle_.GetGUID());
+  return ReadOnlySharedMemoryMapping(result.value(), size, handle_.GetGUID(),
+                                     mapper);
 }
 
 bool ReadOnlySharedMemoryRegion::IsValid() const {
diff --git a/base/memory/read_only_shared_memory_region.h b/base/memory/read_only_shared_memory_region.h
index c9ec0dd..cbabdcd1 100644
--- a/base/memory/read_only_shared_memory_region.h
+++ b/base/memory/read_only_shared_memory_region.h
@@ -34,7 +34,8 @@
   // This means that the caller's process is the only process that can modify
   // the region content. If you need to pass write access to another process,
   // consider using WritableSharedMemoryRegion or UnsafeSharedMemoryRegion.
-  static MappedReadOnlyRegion Create(size_t size);
+  static MappedReadOnlyRegion Create(size_t size,
+                                     SharedMemoryMapper* mapper = nullptr);
   using CreateFunction = decltype(Create);
 
   // Returns a ReadOnlySharedMemoryRegion built from a platform-specific handle
@@ -78,14 +79,17 @@
   // read-only access. The mapped address is guaranteed to have an alignment of
   // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|.
   // Returns a valid ReadOnlySharedMemoryMapping instance on success, invalid
-  // otherwise.
-  ReadOnlySharedMemoryMapping Map() const;
+  // otherwise. A custom |SharedMemoryMapper| for mapping (and later unmapping)
+  // the region can be provided using the optional |mapper| parameter.
+  ReadOnlySharedMemoryMapping Map(SharedMemoryMapper* mapper = nullptr) const;
 
   // Same as above, but maps only |size| bytes of the shared memory region
   // starting with the given |offset|. |offset| must be aligned to value of
   // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if
   // requested bytes are out of the region limits.
-  ReadOnlySharedMemoryMapping MapAt(uint64_t offset, size_t size) const;
+  ReadOnlySharedMemoryMapping MapAt(uint64_t offset,
+                                    size_t size,
+                                    SharedMemoryMapper* mapper = nullptr) const;
 
   // Whether the underlying platform handle is valid.
   bool IsValid() const;
diff --git a/base/memory/ref_counted_unittest.nc b/base/memory/ref_counted_unittest.nc
index 0830ce2b..d25970d 100644
--- a/base/memory/ref_counted_unittest.nc
+++ b/base/memory/ref_counted_unittest.nc
@@ -22,4 +22,45 @@
 
 #endif
 
+#if defined(NCTEST_WRONG_REFCOUNT_BASE_CLASS)  // [r"fatal error: static_assert failed due to requirement 'std::is_base_of_v<base::Foo, base::Bar>' \"T implements RefCounted<U>, but U is not a base of T\.\""]
+
+class Foo : public base::RefCounted<Foo> {
+ private:
+  friend class base::RefCounted<Foo>;
+  ~Foo() {}
+};
+
+class Bar : public base::RefCounted<Foo> {
+ private:
+  friend class base::RefCounted<Bar>;
+  ~Bar() {}
+};
+
+void WontCompile() {
+  scoped_refptr<Bar> ptr;
+}
+
+#endif
+
+#if defined(NCTEST_WRONG_REFCOUNT_THREADSAFE_BASE_CLASS)  // [r"fatal error: static_assert failed due to requirement 'std::is_base_of_v<base::Foo, base::Bar>' \"T implements RefCountedThreadSafe<U>, but U is not a base of T\.\""]
+
+class Foo : public base::RefCountedThreadSafe<Foo> {
+ private:
+  friend class base::RefCountedThreadSafe<Foo>;
+  ~Foo() {}
+};
+
+class Bar : public base::RefCountedThreadSafe<Foo> {
+ private:
+  friend class base::RefCountedThreadSafe<Bar>;
+  ~Bar() {}
+};
+
+void WontCompile() {
+  scoped_refptr<Bar> ptr;
+}
+
+#endif
+
+
 }  // namespace base
diff --git a/base/memory/scoped_refptr.h b/base/memory/scoped_refptr.h
index ed70d55..45d32d45 100644
--- a/base/memory/scoped_refptr.h
+++ b/base/memory/scoped_refptr.h
@@ -23,6 +23,8 @@
 class RefCounted;
 template <class, typename>
 class RefCountedThreadSafe;
+template <class>
+class RefCountedDeleteOnSequence;
 class SequencedTaskRunner;
 class WrappedPromise;
 
@@ -41,6 +43,10 @@
 enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag };
 enum StartRefCountFromOneTag { kStartRefCountFromOneTag };
 
+// scoped_refptr<T> is typically used with one of several RefCounted<T> base
+// classes or with custom AddRef and Release methods. These overloads dispatch
+// on which was used.
+
 template <typename T, typename U, typename V>
 constexpr bool IsRefCountPreferenceOverridden(const T*,
                                               const RefCounted<U, V>*) {
@@ -56,10 +62,42 @@
                        std::decay_t<decltype(U::kRefCountPreference)>>::value;
 }
 
+template <typename T, typename U>
+constexpr bool IsRefCountPreferenceOverridden(
+    const T*,
+    const RefCountedDeleteOnSequence<U*>) {
+  return !std::is_same<std::decay_t<decltype(T::kRefCountPreference)>,
+                       std::decay_t<decltype(U::kRefCountPreference)>>::value;
+}
+
 constexpr bool IsRefCountPreferenceOverridden(...) {
   return false;
 }
 
+template <typename T, typename U, typename V>
+constexpr void AssertRefCountBaseMatches(const T*, const RefCounted<U, V>*) {
+  static_assert(std::is_base_of_v<U, T>,
+                "T implements RefCounted<U>, but U is not a base of T.");
+}
+
+template <typename T, typename U, typename V>
+constexpr void AssertRefCountBaseMatches(const T*,
+                                         const RefCountedThreadSafe<U, V>*) {
+  static_assert(
+      std::is_base_of_v<U, T>,
+      "T implements RefCountedThreadSafe<U>, but U is not a base of T.");
+}
+
+template <typename T, typename U>
+constexpr void AssertRefCountBaseMatches(const T*,
+                                         const RefCountedDeleteOnSequence<U>*) {
+  static_assert(
+      std::is_base_of_v<U, T>,
+      "T implements RefCountedDeleteOnSequence<U>, but U is not a base of T.");
+}
+
+constexpr void AssertRefCountBaseMatches(...) {}
+
 }  // namespace subtle
 
 // Creates a scoped_refptr from a raw pointer without incrementing the reference
@@ -312,12 +350,14 @@
 // static
 template <typename T>
 void scoped_refptr<T>::AddRef(T* ptr) {
+  base::subtle::AssertRefCountBaseMatches(ptr, ptr);
   ptr->AddRef();
 }
 
 // static
 template <typename T>
 void scoped_refptr<T>::Release(T* ptr) {
+  base::subtle::AssertRefCountBaseMatches(ptr, ptr);
   ptr->Release();
 }
 
diff --git a/base/memory/shared_memory_hooks_unittest.cc b/base/memory/shared_memory_hooks_unittest.cc
index 8538acc..72e8f3cd 100644
--- a/base/memory/shared_memory_hooks_unittest.cc
+++ b/base/memory/shared_memory_hooks_unittest.cc
@@ -26,7 +26,7 @@
 absl::optional<size_t> requested_unsafe_shmem_size;
 absl::optional<size_t> requested_writable_shmem_size;
 
-MappedReadOnlyRegion ReadOnlyShmemCreateHook(size_t size) {
+MappedReadOnlyRegion ReadOnlyShmemCreateHook(size_t size, SharedMemoryMapper* mapper) {
   requested_read_only_shmem_size = size;
   return {};
 }
diff --git a/base/memory/shared_memory_mapper.cc b/base/memory/shared_memory_mapper.cc
new file mode 100644
index 0000000..0538aae
--- /dev/null
+++ b/base/memory/shared_memory_mapper.cc
@@ -0,0 +1,17 @@
+// 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 "base/memory/shared_memory_mapper.h"
+
+#include "base/memory/platform_shared_memory_mapper.h"
+
+namespace base {
+
+// static
+SharedMemoryMapper* SharedMemoryMapper::GetDefaultInstance() {
+  static PlatformSharedMemoryMapper instance;
+  return &instance;
+}
+
+}  // namespace base
diff --git a/base/memory/shared_memory_mapper.h b/base/memory/shared_memory_mapper.h
new file mode 100644
index 0000000..3cdff54
--- /dev/null
+++ b/base/memory/shared_memory_mapper.h
@@ -0,0 +1,44 @@
+// 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 BASE_MEMORY_SHARED_MEMORY_MAPPER_H_
+#define BASE_MEMORY_SHARED_MEMORY_MAPPER_H_
+
+#include "base/base_export.h"
+#include "base/containers/span.h"
+#include "base/memory/platform_shared_memory_handle.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#include <stdint.h>
+
+namespace base {
+
+// Interface to implement mapping and unmapping of shared memory regions into
+// the virtual address space. The default implementation,
+// |PlatformSharedMemoryMapper| uses the platform-specific APIs to map the
+// region anywhere in the address space. Other implementations can be used for
+// example to always map the regions into an existing address space reservation.
+// Implementations of this interface should generally be statically allocated
+// as SharedMemoryMappings keep a reference to their mapper.
+class BASE_EXPORT SharedMemoryMapper {
+ public:
+  // Returns the default shared memory mapper.
+  static SharedMemoryMapper* GetDefaultInstance();
+
+  // Maps the shared memory region identified through the provided platform
+  // handle into the caller's address space.
+  virtual absl::optional<span<uint8_t>> Map(
+      subtle::PlatformSharedMemoryHandle handle,
+      bool write_allowed,
+      uint64_t offset,
+      size_t size) = 0;
+
+  // Unmaps the specified region of shared memory from the caller's address
+  // space.
+  virtual void Unmap(span<uint8_t> mapping) = 0;
+};
+
+}  // namespace base
+
+#endif  // BASE_MEMORY_SHARED_MEMORY_MAPPER_H_
diff --git a/base/memory/shared_memory_mapping.cc b/base/memory/shared_memory_mapping.cc
index 02b0d52..bfd9fe6 100644
--- a/base/memory/shared_memory_mapping.cc
+++ b/base/memory/shared_memory_mapping.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/logging.h"
-#include "base/memory/platform_shared_memory_mapper.h"
 #include "base/memory/shared_memory_security_policy.h"
 #include "base/memory/shared_memory_tracker.h"
 #include "base/unguessable_token.h"
@@ -20,7 +19,8 @@
 SharedMemoryMapping::SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept
     : mapped_span_(std::exchange(mapping.mapped_span_, span<uint8_t>())),
       size_(mapping.size_),
-      guid_(mapping.guid_) {}
+      guid_(mapping.guid_),
+      mapper_(mapping.mapper_) {}
 
 SharedMemoryMapping& SharedMemoryMapping::operator=(
     SharedMemoryMapping&& mapping) noexcept {
@@ -28,6 +28,7 @@
   mapped_span_ = std::exchange(mapping.mapped_span_, span<uint8_t>());
   size_ = mapping.size_;
   guid_ = mapping.guid_;
+  mapper_ = mapping.mapper_;
   return *this;
 }
 
@@ -37,8 +38,9 @@
 
 SharedMemoryMapping::SharedMemoryMapping(span<uint8_t> mapped_span,
                                          size_t size,
-                                         const UnguessableToken& guid)
-    : mapped_span_(mapped_span), size_(size), guid_(guid) {
+                                         const UnguessableToken& guid,
+                                         SharedMemoryMapper* mapper)
+    : mapped_span_(mapped_span), size_(size), guid_(guid), mapper_(mapper) {
   SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
 }
 
@@ -46,10 +48,12 @@
   if (!IsValid())
     return;
 
-  SharedMemorySecurityPolicy::ReleaseReservationForMapping(size_);
   SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
 
-  PlatformSharedMemoryMapper::Unmap(mapped_span_);
+  SharedMemoryMapper* mapper = mapper_;
+  if (!mapper)
+    mapper = SharedMemoryMapper::GetDefaultInstance();
+  mapper->Unmap(mapped_span_);
 }
 
 ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping() = default;
@@ -60,8 +64,9 @@
 ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
     span<uint8_t> mapped_span,
     size_t size,
-    const UnguessableToken& guid)
-    : SharedMemoryMapping(mapped_span, size, guid) {}
+    const UnguessableToken& guid,
+    SharedMemoryMapper* mapper)
+    : SharedMemoryMapping(mapped_span, size, guid, mapper) {}
 
 WritableSharedMemoryMapping::WritableSharedMemoryMapping() = default;
 WritableSharedMemoryMapping::WritableSharedMemoryMapping(
@@ -71,7 +76,8 @@
 WritableSharedMemoryMapping::WritableSharedMemoryMapping(
     span<uint8_t> mapped_span,
     size_t size,
-    const UnguessableToken& guid)
-    : SharedMemoryMapping(mapped_span, size, guid) {}
+    const UnguessableToken& guid,
+    SharedMemoryMapper* mapper)
+    : SharedMemoryMapping(mapped_span, size, guid, mapper) {}
 
 }  // namespace base
diff --git a/base/memory/shared_memory_mapping.h b/base/memory/shared_memory_mapping.h
index 82bfdebc..ca25a01 100644
--- a/base/memory/shared_memory_mapping.h
+++ b/base/memory/shared_memory_mapping.h
@@ -11,6 +11,7 @@
 #include "base/base_export.h"
 #include "base/check.h"
 #include "base/containers/span.h"
+#include "base/memory/shared_memory_mapper.h"
 #include "base/unguessable_token.h"
 
 namespace base {
@@ -70,7 +71,8 @@
  protected:
   SharedMemoryMapping(span<uint8_t> mapped_span,
                       size_t size,
-                      const UnguessableToken& guid);
+                      const UnguessableToken& guid,
+                      SharedMemoryMapper* mapper);
   void* raw_memory_ptr() const {
     return reinterpret_cast<void*>(mapped_span_.data());
   }
@@ -83,6 +85,7 @@
   span<uint8_t> mapped_span_;
   size_t size_ = 0;
   UnguessableToken guid_;
+  SharedMemoryMapper* mapper_ = nullptr;
 };
 
 // Class modeling a read-only mapping of a shared memory region into the
@@ -156,7 +159,8 @@
   friend class ReadOnlySharedMemoryRegion;
   ReadOnlySharedMemoryMapping(span<uint8_t> mapped_span,
                               size_t size,
-                              const UnguessableToken& guid);
+                              const UnguessableToken& guid,
+                              SharedMemoryMapper* mapper);
 };
 
 // Class modeling a writable mapping of a shared memory region into the
@@ -235,7 +239,8 @@
   friend class UnsafeSharedMemoryRegion;
   WritableSharedMemoryMapping(span<uint8_t> mapped_span,
                               size_t size,
-                              const UnguessableToken& guid);
+                              const UnguessableToken& guid,
+                              SharedMemoryMapper* mapper);
 };
 
 }  // namespace base
diff --git a/base/memory/unsafe_shared_memory_region.cc b/base/memory/unsafe_shared_memory_region.cc
index b1a3917..00d4f848 100644
--- a/base/memory/unsafe_shared_memory_region.cc
+++ b/base/memory/unsafe_shared_memory_region.cc
@@ -48,20 +48,24 @@
   return UnsafeSharedMemoryRegion(handle_.Duplicate());
 }
 
-WritableSharedMemoryMapping UnsafeSharedMemoryRegion::Map() const {
-  return MapAt(0, handle_.GetSize());
+WritableSharedMemoryMapping UnsafeSharedMemoryRegion::Map(
+    SharedMemoryMapper* mapper) const {
+  return MapAt(0, handle_.GetSize(), mapper);
 }
 
-WritableSharedMemoryMapping UnsafeSharedMemoryRegion::MapAt(uint64_t offset,
-                                                            size_t size) const {
+WritableSharedMemoryMapping UnsafeSharedMemoryRegion::MapAt(
+    uint64_t offset,
+    size_t size,
+    SharedMemoryMapper* mapper) const {
   if (!IsValid())
     return {};
 
-  auto result = handle_.MapAt(offset, size);
+  auto result = handle_.MapAt(offset, size, mapper);
   if (!result.has_value())
     return {};
 
-  return WritableSharedMemoryMapping(result.value(), size, handle_.GetGUID());
+  return WritableSharedMemoryMapping(result.value(), size, handle_.GetGUID(),
+                                     mapper);
 }
 
 bool UnsafeSharedMemoryRegion::IsValid() const {
diff --git a/base/memory/unsafe_shared_memory_region.h b/base/memory/unsafe_shared_memory_region.h
index ebfe0bc..1f0458f4 100644
--- a/base/memory/unsafe_shared_memory_region.h
+++ b/base/memory/unsafe_shared_memory_region.h
@@ -76,14 +76,17 @@
   // access. The mapped address is guaranteed to have an alignment of
   // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|.
   // Returns a valid WritableSharedMemoryMapping instance on success, invalid
-  // otherwise.
-  WritableSharedMemoryMapping Map() const;
+  // otherwise. A custom |SharedMemoryMapper| for mapping (and later unmapping)
+  // the region can be provided using the optional |mapper| parameter.
+  WritableSharedMemoryMapping Map(SharedMemoryMapper* mapper = nullptr) const;
 
   // Same as above, but maps only |size| bytes of the shared memory region
   // starting with the given |offset|. |offset| must be aligned to value of
   // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if
   // requested bytes are out of the region limits.
-  WritableSharedMemoryMapping MapAt(uint64_t offset, size_t size) const;
+  WritableSharedMemoryMapping MapAt(uint64_t offset,
+                                    size_t size,
+                                    SharedMemoryMapper* mapper = nullptr) const;
 
   // Whether the underlying platform handle is valid.
   bool IsValid() const;
diff --git a/base/memory/writable_shared_memory_region.cc b/base/memory/writable_shared_memory_region.cc
index 20c18d9..03b45599 100644
--- a/base/memory/writable_shared_memory_region.cc
+++ b/base/memory/writable_shared_memory_region.cc
@@ -63,21 +63,24 @@
     WritableSharedMemoryRegion&& region) = default;
 WritableSharedMemoryRegion::~WritableSharedMemoryRegion() = default;
 
-WritableSharedMemoryMapping WritableSharedMemoryRegion::Map() const {
-  return MapAt(0, handle_.GetSize());
+WritableSharedMemoryMapping WritableSharedMemoryRegion::Map(
+    SharedMemoryMapper* mapper) const {
+  return MapAt(0, handle_.GetSize(), mapper);
 }
 
 WritableSharedMemoryMapping WritableSharedMemoryRegion::MapAt(
     uint64_t offset,
-    size_t size) const {
+    size_t size,
+    SharedMemoryMapper* mapper) const {
   if (!IsValid())
     return {};
 
-  auto result = handle_.MapAt(offset, size);
+  auto result = handle_.MapAt(offset, size, mapper);
   if (!result.has_value())
     return {};
 
-  return WritableSharedMemoryMapping(result.value(), size, handle_.GetGUID());
+  return WritableSharedMemoryMapping(result.value(), size, handle_.GetGUID(),
+                                     mapper);
 }
 
 bool WritableSharedMemoryRegion::IsValid() const {
diff --git a/base/memory/writable_shared_memory_region.h b/base/memory/writable_shared_memory_region.h
index ed469a8..02f56e3e 100644
--- a/base/memory/writable_shared_memory_region.h
+++ b/base/memory/writable_shared_memory_region.h
@@ -84,14 +84,17 @@
   // access. The mapped address is guaranteed to have an alignment of
   // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|.
   // Returns a valid WritableSharedMemoryMapping instance on success, invalid
-  // otherwise.
-  WritableSharedMemoryMapping Map() const;
+  // otherwise. A custom |SharedMemoryMapper| for mapping (and later unmapping)
+  // the region can be provided using the optional |mapper| parameter.
+  WritableSharedMemoryMapping Map(SharedMemoryMapper* mapper = nullptr) const;
 
   // Same as above, but maps only |size| bytes of the shared memory block
   // starting with the given |offset|. |offset| must be aligned to value of
   // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if
   // requested bytes are out of the region limits.
-  WritableSharedMemoryMapping MapAt(uint64_t offset, size_t size) const;
+  WritableSharedMemoryMapping MapAt(uint64_t offset,
+                                    size_t size,
+                                    SharedMemoryMapper* mapper = nullptr) const;
 
   // Whether underlying platform handles are valid.
   bool IsValid() const;
diff --git a/base/profiler/stack_sampler_impl_unittest.cc b/base/profiler/stack_sampler_impl_unittest.cc
index ad8bae3..4c402bfc 100644
--- a/base/profiler/stack_sampler_impl_unittest.cc
+++ b/base/profiler/stack_sampler_impl_unittest.cc
@@ -272,13 +272,7 @@
 
 }  // namespace
 
-// TODO(crbug.com/1001923): Fails on Linux MSan.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-#define MAYBE_CopyStack DISABLED_MAYBE_CopyStack
-#else
-#define MAYBE_CopyStack CopyStack
-#endif
-TEST(StackSamplerImplTest, MAYBE_CopyStack) {
+TEST(StackSamplerImplTest, CopyStack) {
   ModuleCache module_cache;
   const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
   InjectModuleForContextInstructionPointer(stack, &module_cache);
diff --git a/base/strings/stringprintf.cc b/base/strings/stringprintf.cc
index 1a404853..bcace27 100644
--- a/base/strings/stringprintf.cc
+++ b/base/strings/stringprintf.cc
@@ -12,7 +12,6 @@
 #include "base/logging.h"
 #include "base/scoped_clear_last_error.h"
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 
 namespace base {
diff --git a/base/test/test_shared_memory_util.cc b/base/test/test_shared_memory_util.cc
index 9e65f327..8250aa4 100644
--- a/base/test/test_shared_memory_util.cc
+++ b/base/test/test_shared_memory_util.cc
@@ -150,11 +150,13 @@
     subtle::PlatformSharedMemoryRegion* region,
     uint64_t offset,
     size_t size) {
-  auto result = region->MapAt(offset, size);
+  SharedMemoryMapper* mapper = SharedMemoryMapper::GetDefaultInstance();
+  auto result = region->MapAt(offset, size, mapper);
   if (!result.has_value())
     return {};
 
-  return WritableSharedMemoryMapping(result.value(), size, region->GetGUID());
+  return WritableSharedMemoryMapping(result.value(), size, region->GetGUID(),
+                                     mapper);
 }
 
 template <>
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index e0b6e68a..8ece2d5 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220426.0.1
+8.20220426.2.1
diff --git a/cc/metrics/frame_sequence_metrics.cc b/cc/metrics/frame_sequence_metrics.cc
index 1f4c2a5..6b4d3b0 100644
--- a/cc/metrics/frame_sequence_metrics.cc
+++ b/cc/metrics/frame_sequence_metrics.cc
@@ -198,12 +198,13 @@
 
 bool FrameSequenceMetrics::HasEnoughDataForReporting() const {
   return impl_throughput_.frames_expected >= kMinFramesForThroughputMetric ||
-         main_throughput_.frames_expected >= kMinFramesForThroughputMetric;
+         main_throughput_.frames_expected >= kMinFramesForThroughputMetric ||
+         v2_.frames_expected >= kMinFramesForThroughputMetric;
 }
 
 bool FrameSequenceMetrics::HasDataLeftForReporting() const {
   return impl_throughput_.frames_expected > 0 ||
-         main_throughput_.frames_expected > 0;
+         main_throughput_.frames_expected > 0 || v2_.frames_expected > 0;
 }
 
 void FrameSequenceMetrics::AdoptTrace(FrameSequenceMetrics* adopt_from) {
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 2c170b2..cbf4bf3 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -265,7 +265,6 @@
     "//components/translate/content/android:java_resources",
     "//components/webapps/browser/android:java_resources",
     "//content/public/android:content_java_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_gridlayout_gridlayout_java",
     "//third_party/androidx:androidx_preference_preference_java",
@@ -608,7 +607,6 @@
     "//services/shape_detection:shape_detection_java",
     "//services/shape_detection/public/mojom:mojom_java",
     "//skia/public/mojom:mojom_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:chromium_play_services_availability_java",
     "//third_party/android_deps:com_google_android_play_core_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
@@ -622,7 +620,6 @@
     "//third_party/android_swipe_refresh:android_swipe_refresh_java",
     "//third_party/androidx:androidx_activity_activity_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_browser_browser_java",
     "//third_party/androidx:androidx_collection_collection_java",
     "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
@@ -1032,6 +1029,7 @@
     "//chrome/browser/ui/android/appmenu:java",
     "//chrome/browser/ui/android/appmenu/internal:junit",
     "//chrome/browser/ui/android/autofill/internal:junit",
+    "//chrome/browser/ui/android/autofill/test:test_support_java",
     "//chrome/browser/ui/android/default_browser_promo:java",
     "//chrome/browser/ui/android/default_browser_promo:junit",
     "//chrome/browser/ui/android/favicon:java",
@@ -1139,7 +1137,6 @@
     "//services/media_session/public/cpp/android:media_session_java",
     "//services/media_session/public/mojom:mojom_java",
     "//services/service_manager/public/java:service_manager_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:chromium_play_services_availability_shadows_java",
     "//third_party/android_deps:com_google_guava_listenablefuture_java",
     "//third_party/android_deps:com_googlecode_java_diff_utils_diffutils_java",
@@ -1594,7 +1591,6 @@
     "//services/network/public/mojom:mojom_proxy_config_java",
     "//services/network/public/mojom:url_loader_base_java",
     "//services/service_manager/public/java:service_manager_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/android_deps:com_google_flatbuffers_flatbuffers_java_java",
     "//third_party/android_deps:espresso_java",
@@ -1607,7 +1603,6 @@
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_browser_browser_java",
     "//third_party/androidx:androidx_collection_collection_java",
     "//third_party/androidx:androidx_preference_preference_java",
@@ -1726,13 +1721,10 @@
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_browser_browser_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
-    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/junit",
     "//third_party/ub-uiautomator:ub_uiautomator_java",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 10e3542..b45c38f 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -160,6 +160,7 @@
   "java/res/drawable-mdpi/tabswitcher_border_frame_inner_shadow.9.png",
   "java/res/drawable-mdpi/tabswitcher_border_frame_shadow.9.png",
   "java/res/drawable-mdpi/verify_checkmark.png",
+  "java/res/drawable-night/virtual_card_enrollment_illustration.xml",
   "java/res/drawable-nodpi/bookmark_widget_preview.png",
   "java/res/drawable-nodpi/widget_preview.png",
   "java/res/drawable-sw600dp/window_background.xml",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 2bc4ee225..4718359 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -22,7 +22,6 @@
   "junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java",
-  "junit/src/org/chromium/chrome/browser/autofill/settings/FakeModalDialogManager.java",
   "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java",
   "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskTest.java",
   "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncGooglePlayServicesCheckerTest.java",
diff --git a/chrome/android/features/cablev2_authenticator/BUILD.gn b/chrome/android/features/cablev2_authenticator/BUILD.gn
index c610598..8465a27 100644
--- a/chrome/android/features/cablev2_authenticator/BUILD.gn
+++ b/chrome/android/features/cablev2_authenticator/BUILD.gn
@@ -23,8 +23,9 @@
     "//components/webauthn/android:java",
     "//content/public/android:content_java",
     "//mojo/public/mojom/base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//ui/android:ui_full_java",
   ]
diff --git a/chrome/android/features/keyboard_accessory/internal/BUILD.gn b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
index e0686b1..ba5dac7 100644
--- a/chrome/android/features/keyboard_accessory/internal/BUILD.gn
+++ b/chrome/android/features/keyboard_accessory/internal/BUILD.gn
@@ -35,10 +35,11 @@
     "//components/image_fetcher:java",
     "//components/url_formatter/android:url_formatter_java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_viewpager_viewpager_java",
     "//ui/android:ui_java",
diff --git a/chrome/android/features/start_surface/internal/BUILD.gn b/chrome/android/features/start_surface/internal/BUILD.gn
index b436f2f..98e7a972 100644
--- a/chrome/android/features/start_surface/internal/BUILD.gn
+++ b/chrome/android/features/start_surface/internal/BUILD.gn
@@ -93,10 +93,12 @@
     "//components/user_prefs/android:java",
     "//components/version_info/android:version_constants_java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_browser_browser_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_utils_java",
     "//ui/base/mojom:mojom_java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 614df94d..4cf2459 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -235,16 +235,15 @@
     "//content/public/android:content_java",
     "//content/public/android:content_java_resources",
     "//net/android:net_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
     "//third_party/androidx:androidx_core_core_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
     "//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
     "//ui/android:ui_java",
     "//ui/base/mojom:mojom_java",
     "//url:gurl_java",
diff --git a/chrome/android/features/vr/BUILD.gn b/chrome/android/features/vr/BUILD.gn
index 8961852f..3523f9e 100644
--- a/chrome/android/features/vr/BUILD.gn
+++ b/chrome/android/features/vr/BUILD.gn
@@ -94,11 +94,8 @@
     "//components/policy/android:policy_java",
     "//content/public/android:content_java",
     "//device/vr:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
     "//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_java",
     "//third_party/gvr-android-keyboard:kb_java",
     "//third_party/gvr-android-sdk:gvr_common_java",
     "//ui/android:ui_full_java",
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni
index 290960c4..e0e665d 100644
--- a/chrome/android/feed/feed_java_sources.gni
+++ b/chrome/android/feed/feed_java_sources.gni
@@ -7,16 +7,21 @@
 feed_deps = [
   "//base:base_java",
   "//chrome/android/feed:chrome_feed_java_resources",
-  "//third_party/android_deps:android_support_v7_appcompat_java",
   "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
   "//third_party/android_deps:javax_inject_javax_inject_java",
   "//third_party/android_deps:protobuf_lite_runtime_java",
   "//third_party/androidx:androidx_annotation_annotation_java",
+  "//third_party/androidx:androidx_appcompat_appcompat_java",
+  "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
   "//third_party/androidx:androidx_cardview_cardview_java",
   "//third_party/androidx:androidx_collection_collection_java",
-  "//third_party/androidx:androidx_interpolator_interpolator_java",
+  "//third_party/androidx:androidx_core_core_java",
+  "//third_party/androidx:androidx_fragment_fragment_java",
+  "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
+  "//third_party/androidx:androidx_media_media_java",
   "//third_party/androidx:androidx_recyclerview_recyclerview_java",
   "//third_party/androidx:androidx_swiperefreshlayout_swiperefreshlayout_java",
+  "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
 ]
 
 feed_java_sources = [
diff --git a/chrome/android/java/res/drawable-night/virtual_card_enrollment_illustration.xml b/chrome/android/java/res/drawable-night/virtual_card_enrollment_illustration.xml
new file mode 100644
index 0000000..c509f34
--- /dev/null
+++ b/chrome/android/java/res/drawable-night/virtual_card_enrollment_illustration.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="125dp"
+    android:height="88dp"
+    android:viewportWidth="125"
+    android:viewportHeight="88">
+  <path
+      android:strokeWidth="1"
+      android:pathData="M0.584,68.234C0.584,69.471 1.587,70.474 2.824,70.474H83.67C84.906,70.474 85.91,69.471 85.91,68.234V19.795C85.91,18.558 84.906,17.555 83.67,17.555H2.824C1.587,17.555 0.584,18.558 0.584,19.795V68.234Z"
+      android:fillColor="#9AA0A6"
+      android:strokeColor="#9AA0A6"/>
+  <path
+      android:pathData="M72.032,54.814H12.966C11.724,54.814 10.721,53.811 10.721,52.569V51.522C10.721,50.281 11.724,49.277 12.966,49.277H72.032C73.274,49.277 74.277,50.281 74.277,51.522V52.569C74.27,53.811 73.266,54.814 72.032,54.814Z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M16.734,41.986H8.866C7.66,41.986 6.686,41.011 6.686,39.806V34.825C6.686,33.619 7.66,32.645 8.866,32.645H16.734C17.94,32.645 18.915,33.619 18.915,34.825V39.806C18.915,41.011 17.94,41.986 16.734,41.986Z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:pathData="M23.737,63.513H12.641C11.58,63.513 10.721,62.654 10.721,61.593V59.896C10.721,58.835 11.58,57.976 12.641,57.976H23.737C24.798,57.976 25.657,58.835 25.657,59.896V61.593C25.657,62.654 24.798,63.513 23.737,63.513Z"
+      android:fillColor="#BDC1C6"/>
+  <path
+      android:strokeWidth="1"
+      android:pathData="M10.217,32.645V41.986"
+      android:fillColor="#00000000"
+      android:strokeColor="#9AA0A6"/>
+  <path
+      android:strokeWidth="1"
+      android:pathData="M18.915,37.315H6.693"
+      android:fillColor="#00000000"
+      android:strokeColor="#9AA0A6"/>
+  <path
+      android:pathData="M75.771,88C100.072,88 119.771,68.3 119.771,44C119.771,19.699 100.072,0 75.771,0C51.471,0 31.771,19.699 31.771,44C31.771,68.3 51.471,88 75.771,88Z"
+      android:fillColor="#185ABC"/>
+  <path
+      android:pathData="M83.67,16.972H41.07C35.252,24.429 31.779,33.807 31.779,44C31.779,54.208 35.266,63.592 41.099,71.057H83.677C85.236,71.057 86.5,69.794 86.5,68.234V19.795C86.492,18.235 85.229,16.972 83.67,16.972Z"
+      android:fillColor="#4285F4"/>
+  <path
+      android:pathData="M92.059,41.849L101.941,51.732L123.981,29.685"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#34A853"
+      android:strokeLineCap="round"/>
+  <path
+      android:pathData="M60.512,29.778L71.586,34.702V42.138C71.586,49.292 66.871,55.882 60.512,57.846C54.152,55.882 49.438,49.284 49.438,42.138V34.702L60.512,29.778Z"
+      android:strokeLineJoin="round"
+      android:strokeWidth="2"
+      android:fillColor="#00000000"
+      android:strokeColor="#ffffff"
+      android:strokeLineCap="round"/>
+</vector>
diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml
index 1085a45..96f4adb1 100644
--- a/chrome/android/java/res/menu/main_menu.xml
+++ b/chrome/android/java/res/menu/main_menu.xml
@@ -153,11 +153,9 @@
             android:icon="@drawable/gm_filled_cardboard_24" />
         <item android:id="@+id/managed_by_divider_line_id"
             android:title="@null" />
-        <item android:id="@+id/managed_by_standard_menu_id"
+        <item android:id="@+id/managed_by_menu_id"
             android:title="@string/managed_browser"
             android:icon="@drawable/ic_business" />
-        <item android:id="@+id/managed_by_menu_id"
-            android:title="@string/managed" />
     </group>
 
     <!-- Items shown only in the tab switcher -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
index e07ff22..361157d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -2652,9 +2652,7 @@
             return true;
         }
 
-        if (id == R.id.managed_by_standard_menu_id) {
-            assert ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_MANAGEMENT_PAGE);
-
+        if (id == R.id.managed_by_menu_id) {
             openChromeManagementPage();
             return true;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 46cd340..7b7231a3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -1200,19 +1200,14 @@
     }
 
     protected void updateManagedByMenuItem(Menu menu, @Nullable Tab currentTab) {
-        MenuItem managedByMenuItem = menu.findItem(R.id.managed_by_menu_id);
         MenuItem managedByDividerLine = menu.findItem(R.id.managed_by_divider_line_id);
-        MenuItem managedByStandardMenuItem = menu.findItem(R.id.managed_by_standard_menu_id);
+        MenuItem managedByMenuItem = menu.findItem(R.id.managed_by_menu_id);
 
         boolean managedByMenuItemVisible =
                 currentTab != null && shouldShowManagedByMenuItem(currentTab);
-        boolean chromeManagementPageEnabled =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_MANAGEMENT_PAGE);
 
-        managedByMenuItem.setVisible(managedByMenuItemVisible && !chromeManagementPageEnabled);
-        managedByDividerLine.setVisible(managedByMenuItemVisible && chromeManagementPageEnabled);
-        managedByStandardMenuItem.setVisible(
-                managedByMenuItemVisible && chromeManagementPageEnabled);
+        managedByDividerLine.setVisible(managedByMenuItemVisible);
+        managedByMenuItem.setVisible(managedByMenuItemVisible);
     }
 
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index 153e517..77973a0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -270,10 +270,6 @@
                 page = getBuilder().buildLaunchpadPage(tab);
                 break;
             case NativePageType.MANAGEMENT:
-                if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CHROME_MANAGEMENT_PAGE)) {
-                    return null;
-                }
-
                 page = getBuilder().buildManagementPage(tab);
                 break;
             default:
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
index 5a7d09f..1bb7ec3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java
@@ -7,41 +7,25 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.support.test.InstrumentationRegistry;
-import android.util.Base64;
 
-import androidx.test.filters.LargeTest;
 import androidx.test.filters.MediumTest;
 
-import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import org.chromium.base.ActivityState;
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.IntentUtils;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.Criteria;
-import org.chromium.base.test.util.CriteriaHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
 import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory.CustomTabNavigationDelegate;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.tab.InterceptNavigationDelegateTabHelper;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
 import org.chromium.chrome.browser.tab.TabTestUtils;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
-import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
-import org.chromium.components.external_intents.InterceptNavigationDelegateImpl;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.net.test.EmbeddedTestServerRule;
-
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Tests for external navigation handling of Custom Tabs generated by Chrome.
@@ -52,12 +36,6 @@
     @Rule
     public CustomTabActivityTestRule mActivityRule = new CustomTabActivityTestRule();
 
-    @Rule
-    public ChromeTabbedActivityTestRule mChromeActivityTestRule =
-            new ChromeTabbedActivityTestRule();
-
-    public EmbeddedTestServerRule mServerRule = new EmbeddedTestServerRule();
-
     private Intent getCustomTabFromChromeIntent(final String url, final boolean markFromChrome) {
         return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
             Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
@@ -74,7 +52,6 @@
         mActivityRule.startCustomTabActivityWithIntent(intent);
     }
 
-    @DisabledTest(message = "https://crbug.com/1197727")
     @Test
     @Feature("CustomTabFromChrome")
     @MediumTest
@@ -89,53 +66,4 @@
         Assert.assertFalse(customTabDelegateFactory.getExternalNavigationDelegate()
                                    instanceof CustomTabNavigationDelegate);
     }
-
-    @Test
-    @Feature("CustomTabFromChrome")
-    @LargeTest
-    @DisabledTest(message = "https://crbug.com/1197727")
-    public void testIntentWithRedirectToApp() {
-        final String redirectUrl = "https://maps.google.com/maps?q=1600+amphitheatre+parkway";
-        final String initialUrl =
-                mServerRule.getServer().getURL("/chrome/test/data/android/redirect/js_redirect.html"
-                        + "?replace_text="
-                        + Base64.encodeToString(
-                                ApiCompatibilityUtils.getBytesUtf8("PARAM_URL"), Base64.URL_SAFE)
-                        + ":"
-                        + Base64.encodeToString(
-                                ApiCompatibilityUtils.getBytesUtf8(redirectUrl), Base64.URL_SAFE));
-
-        mActivityRule.launchActivity(getCustomTabFromChromeIntent(initialUrl, true));
-        mActivityRule.waitForActivityNativeInitializationComplete();
-
-        final AtomicReference<InterceptNavigationDelegateImpl> navigationDelegate =
-                new AtomicReference<>();
-
-        CriteriaHelper.pollUiThread(() -> {
-            return mActivityRule.getActivity().getActivityTab() != null;
-        }, "Tab never initialized.");
-
-        CriteriaHelper.pollUiThread(() -> {
-            Tab tab = mActivityRule.getActivity().getActivityTab();
-            InterceptNavigationDelegateImpl delegate =
-                    InterceptNavigationDelegateTabHelper.get(tab);
-            if (delegate == null) return false;
-            navigationDelegate.set(delegate);
-            return true;
-        }, "Navigation delegate never initialized.");
-
-        CriteriaHelper.pollUiThread(() -> {
-            Criteria.checkThat(navigationDelegate.get().getLastOverrideUrlLoadingResultForTests(),
-                    Matchers.notNullValue());
-            Criteria.checkThat(navigationDelegate.get()
-                                       .getLastOverrideUrlLoadingResultForTests()
-                                       .getResultType(),
-                    Matchers.is(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT));
-        });
-
-        CriteriaHelper.pollUiThread(() -> {
-            int state = ApplicationStatus.getStateForActivity(mActivityRule.getActivity());
-            return state == ActivityState.STOPPED || state == ActivityState.DESTROYED;
-        }, "Activity never stopped or destroyed.");
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
index 839fa42..cb06445 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/externalnav/UrlOverridingTest.java
@@ -21,6 +21,7 @@
 import android.support.test.runner.lifecycle.Stage;
 import android.text.TextUtils;
 import android.util.Base64;
+import android.util.Pair;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
@@ -39,17 +40,22 @@
 import org.mockito.quality.Strictness;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
+import org.chromium.base.IntentUtils;
 import org.chromium.base.PackageManagerUtils;
 import org.chromium.base.test.util.ApplicationTestUtils;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.PackageManagerWrapper;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.blink_public.common.BlinkFeatures;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.LaunchIntentDispatcher;
+import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.init.AsyncInitializationActivity;
@@ -64,6 +70,7 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.external_intents.ExternalNavigationHandler;
+import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResult;
 import org.chromium.components.external_intents.ExternalNavigationHandler.OverrideUrlLoadingResultType;
 import org.chromium.components.external_intents.InterceptNavigationDelegateImpl;
 import org.chromium.components.external_intents.RedirectHandler;
@@ -84,6 +91,7 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -98,6 +106,9 @@
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
+    @Rule
+    public CustomTabActivityTestRule mCustomTabActivityRule = new CustomTabActivityTestRule();
+
     private static final String BASE_PATH = "/chrome/test/data/android/url_overriding/";
     private static final String NAVIGATION_FROM_TIMEOUT_PAGE =
             BASE_PATH + "navigation_from_timer.html";
@@ -192,6 +203,8 @@
 
     private static class TestContext extends ContextWrapper {
         private boolean mResolveToNonBrowserPackage;
+        private String mHostToMatch;
+        private IntentFilter mFilterForHostMatch;
 
         public TestContext(Context baseContext) {
             super(baseContext);
@@ -210,6 +223,11 @@
             return false;
         }
 
+        private void setIntentFilterForHost(String host, IntentFilter filter) {
+            mHostToMatch = host;
+            mFilterForHostMatch = filter;
+        }
+
         @Override
         public PackageManager getPackageManager() {
             return new PackageManagerWrapper(super.getPackageManager()) {
@@ -225,6 +243,13 @@
                     // images.
                     if (targetsPlay(intent)) return null;
 
+                    if (mHostToMatch != null && intent.getData() != null
+                            && intent.getData().getHost().equals(mHostToMatch)) {
+                        ResolveInfo info = newResolveInfo(NON_BROWSER_PACKAGE);
+                        info.filter = mFilterForHostMatch;
+                        return Arrays.asList(info);
+                    }
+
                     return TestContext.super.getPackageManager().queryIntentActivities(
                             intent, flags);
                 }
@@ -275,6 +300,17 @@
         }
     }
 
+    private Intent getCustomTabFromChromeIntent(final String url, final boolean markFromChrome) {
+        return TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
+            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent = LaunchIntentDispatcher.createCustomTabActivityIntent(
+                    InstrumentationRegistry.getTargetContext(), intent);
+            IntentUtils.addTrustedIntentExtras(intent);
+            return intent;
+        });
+    }
+
     private void loadUrlAndWaitForIntentUrl(
             final String url, boolean needClick, boolean shouldLaunchExternalIntent) {
         loadUrlAndWaitForIntentUrl(url, needClick, false, shouldLaunchExternalIntent, url, true);
@@ -300,8 +336,19 @@
         final Tab[] latestTabHolder = new Tab[1];
         final InterceptNavigationDelegateImpl[] latestDelegateHolder =
                 new InterceptNavigationDelegateImpl[1];
+
+        AtomicInteger lastResultValue = new AtomicInteger();
+
         latestTabHolder[0] = tab;
         latestDelegateHolder[0] = getInterceptNavigationDelegate(tab);
+
+        Callback<Pair<GURL, OverrideUrlLoadingResult>> resultCallback =
+                (Pair<GURL, OverrideUrlLoadingResult> result) -> {
+            if (result.first.getSpec().equals(url)) return;
+            lastResultValue.set(result.second.getResultType());
+        };
+
+        latestDelegateHolder[0].setResultCallbackForTesting(resultCallback);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             tab.addObserver(new TestTabObserver(finishCallback, failCallback, destroyedCallback));
 
@@ -313,19 +360,17 @@
                     newTab.addObserver(
                             new TestTabObserver(finishCallback, failCallback, destroyedCallback));
                     latestTabHolder[0] = newTab;
+                    latestDelegateHolder[0].setResultCallbackForTesting(null);
                     latestDelegateHolder[0] = getInterceptNavigationDelegate(newTab);
+                    latestDelegateHolder[0].setResultCallbackForTesting(resultCallback);
                 }
             };
             mActivityTestRule.getActivity().getTabModelSelector().addObserver(selectorObserver);
         });
 
         mActivityTestRule.getActivity().onUserInteraction();
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                tab.loadUrl(new LoadUrlParams(url, PageTransition.LINK));
-            }
-        });
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { tab.loadUrl(new LoadUrlParams(url, PageTransition.LINK)); });
 
         if (finishCallback.getCallCount() == 0) {
             try {
@@ -336,9 +381,6 @@
             }
         }
 
-        TestThreadUtils.runOnUiThreadBlocking(
-                () -> { latestDelegateHolder[0].clearLastOverrideUrlLoadingResultForTests(); });
-
         if (needClick) {
             if (clickTargetId == null) {
                 TouchCommon.singleClickView(tab.getView());
@@ -402,15 +444,11 @@
             // fallback and normal navigation. See crbug.com/487364 for more.
             Tab latestTab = latestTabHolder[0];
             InterceptNavigationDelegateImpl delegate = latestDelegateHolder[0];
-            Criteria.checkThat(
-                    delegate.getLastOverrideUrlLoadingResultForTests(), Matchers.notNullValue());
             if (shouldLaunchExternalIntent) {
-                Criteria.checkThat(
-                        delegate.getLastOverrideUrlLoadingResultForTests().getResultType(),
+                Criteria.checkThat(lastResultValue.get(),
                         Matchers.is(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT));
             } else {
-                Criteria.checkThat(
-                        delegate.getLastOverrideUrlLoadingResultForTests().getResultType(),
+                Criteria.checkThat(lastResultValue.get(),
                         Matchers.not(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT));
             }
             if (expectedFinalUrl == null) return;
@@ -805,7 +843,8 @@
                 }
             }
         };
-        mActivityTestRule.startMainActivityWithURL(mTestServer.getURL(NAVIGATION_FROM_BFCACHE));
+        String url = mTestServer.getURL(NAVIGATION_FROM_BFCACHE);
+        mActivityTestRule.startMainActivityWithURL(url);
 
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
@@ -824,6 +863,12 @@
         finishCallback.waitForCallback(0);
         syncHelper.notifyCalled();
 
+        AtomicInteger lastResultValue = new AtomicInteger();
+        delegate.setResultCallbackForTesting((Pair<GURL, OverrideUrlLoadingResult> result) -> {
+            if (result.first.getSpec().equals(url)) return;
+            lastResultValue.set(result.second.getResultType());
+        });
+
         // Press back to go back to first page with BFCache.
         TestThreadUtils.runOnUiThreadBlocking(
                 () -> mActivityTestRule.getActivity().onBackPressed());
@@ -838,8 +883,7 @@
         finishCallback.waitForCallback(2);
         // With RedirectHandler state cleared, this should be treated as a navigation without a
         // user gesture, and so should not allow external navigation.
-        Assert.assertEquals(OverrideUrlLoadingResultType.NO_OVERRIDE,
-                delegate.getLastOverrideUrlLoadingResultForTests().getResultType());
+        Assert.assertEquals(OverrideUrlLoadingResultType.NO_OVERRIDE, lastResultValue.get());
         Assert.assertTrue(mLastNavigationHandle.get().getUrl().getSpec().startsWith("intent://"));
         syncHelper.notifyCalled();
     }
@@ -949,4 +993,33 @@
                     () -> { Criteria.checkThat(mActivityMonitor.getHits(), Matchers.is(1)); });
         }
     }
+
+    @Test
+    @Feature("CustomTabFromChrome")
+    @LargeTest
+    public void testIntentWithRedirectToApp() {
+        final String redirectUrl = "https://example.com/path";
+        final String initialUrl =
+                mTestServer.getURL("/chrome/test/data/android/redirect/js_redirect.html"
+                        + "?replace_text="
+                        + Base64.encodeToString(
+                                ApiCompatibilityUtils.getBytesUtf8("PARAM_URL"), Base64.URL_SAFE)
+                        + ":"
+                        + Base64.encodeToString(
+                                ApiCompatibilityUtils.getBytesUtf8(redirectUrl), Base64.URL_SAFE));
+
+        IntentFilter filter = new IntentFilter(Intent.ACTION_VIEW);
+        filter.addCategory(Intent.CATEGORY_BROWSABLE);
+        filter.addDataAuthority("example.com", null);
+        filter.addDataScheme("https");
+        ActivityMonitor monitor = InstrumentationRegistry.getInstrumentation().addMonitor(
+                filter, new Instrumentation.ActivityResult(Activity.RESULT_OK, null), true);
+        mTestContext.setIntentFilterForHost("example.com", filter);
+
+        mCustomTabActivityRule.launchActivity(getCustomTabFromChromeIntent(initialUrl, true));
+
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat(monitor.getHits(), Matchers.is(1));
+        }, 10000L, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
index a3691f1..751b85b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
@@ -204,7 +204,6 @@
         setBookmarkItemRowEnabled(false);
         setReadingListItemRowEnabled(false);
         setShoppingListItemRowEnabled(false);
-        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CHROME_MANAGEMENT_PAGE, true);
         FeatureList.setTestValues(mTestValues);
     }
 
@@ -235,11 +234,6 @@
         mTestValues.addFeatureFlagOverride(ChromeFeatureList.SHOPPING_LIST, enabled);
     }
 
-    private void setChromeManagementPageEnabled(boolean enabled) {
-        mTestValues.addFeatureFlagOverride(ChromeFeatureList.CHROME_MANAGEMENT_PAGE, enabled);
-        FeatureList.setTestValues(mTestValues);
-    }
-
     @After
     public void tearDown() {
         ThreadUtils.setThreadAssertsDisabledForTesting(false);
@@ -415,7 +409,7 @@
                 R.id.divider_line_id, R.id.share_row_menu_id, R.id.find_in_page_id,
                 R.id.translate_id, R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id,
                 R.id.auto_dark_web_contents_row_menu_id, R.id.divider_line_id, R.id.preferences_id,
-                R.id.help_id, R.id.managed_by_divider_line_id, R.id.managed_by_standard_menu_id};
+                R.id.help_id, R.id.managed_by_divider_line_id, R.id.managed_by_menu_id};
         assertMenuItemsAreEqual(menu, expectedItems);
     }
 
@@ -822,8 +816,7 @@
     }
 
     @Test
-    public void managedByMenuItem_ChromeManagementPageDisabled() {
-        setChromeManagementPageEnabled(false);
+    public void managedByMenuItem_ChromeManagementPage() {
         setUpMocksForPageMenu();
         setMenuOptions(new MenuOptions().withShowAddToHomeScreen());
         doReturn(true).when(mAppMenuPropertiesDelegate).shouldShowManagedByMenuItem(any(Tab.class));
@@ -833,34 +826,9 @@
         mAppMenuPropertiesDelegate.prepareMenu(menu, null);
 
         MenuItem managedByMenuItem = menu.findItem(R.id.managed_by_menu_id);
-        MenuItem managedByStandardMenuItem = menu.findItem(R.id.managed_by_standard_menu_id);
 
         Assert.assertNotNull(managedByMenuItem);
         Assert.assertTrue(managedByMenuItem.isVisible());
-
-        Assert.assertNotNull(managedByStandardMenuItem);
-        Assert.assertTrue(!managedByStandardMenuItem.isVisible());
-    }
-
-    @Test
-    public void managedByMenuItem_ChromeManagementPageEnabled() {
-        setChromeManagementPageEnabled(true);
-        setUpMocksForPageMenu();
-        setMenuOptions(new MenuOptions().withShowAddToHomeScreen());
-        doReturn(true).when(mAppMenuPropertiesDelegate).shouldShowManagedByMenuItem(any(Tab.class));
-
-        Assert.assertEquals(MenuGroup.PAGE_MENU, mAppMenuPropertiesDelegate.getMenuGroup());
-        Menu menu = createTestMenu();
-        mAppMenuPropertiesDelegate.prepareMenu(menu, null);
-
-        MenuItem managedByMenuItem = menu.findItem(R.id.managed_by_menu_id);
-        MenuItem managedByStandardMenuItem = menu.findItem(R.id.managed_by_standard_menu_id);
-
-        Assert.assertNotNull(managedByMenuItem);
-        Assert.assertTrue(!managedByMenuItem.isVisible());
-
-        Assert.assertNotNull(managedByStandardMenuItem);
-        Assert.assertTrue(managedByStandardMenuItem.isVisible());
     }
 
     private void setUpMocksForPageMenu() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java
index 1d9ea9a..15ee21f9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialogTest.java
@@ -37,6 +37,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.LegalMessageLine;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.chrome.browser.ui.autofill.FakeModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 
@@ -57,7 +59,7 @@
 
     @Before
     public void setUp() {
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.APP);
         mVirtualCardEnrollmentFields = VirtualCardEnrollmentFields.create(
                 "card label", Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8));
         mVirtualCardEnrollmentFields.mGoogleLegalMessages.add(createLegalMessageLine("google"));
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java
index d196491..0eabd05 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialogTest.java
@@ -32,6 +32,8 @@
 import org.chromium.base.Callback;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
+import org.chromium.chrome.browser.ui.autofill.FakeModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 
@@ -50,7 +52,7 @@
 
     @Before
     public void setUp() {
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.APP);
         mDialog = new AutofillVirtualCardUnenrollmentDialog(
                 ApplicationProvider.getApplicationContext(), mModalDialogManager, mCallbackMock);
         mDialog.show();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
index 2b7db31..582973b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -81,7 +81,7 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Features.EnableFeatures({ChromeFeatureList.WEB_FEED, ChromeFeatureList.READ_LATER,
-        ChromeFeatureList.BOOKMARKS_REFRESH, ChromeFeatureList.CHROME_MANAGEMENT_PAGE})
+        ChromeFeatureList.BOOKMARKS_REFRESH})
 @Features.DisableFeatures({ChromeFeatureList.READ_LATER, ChromeFeatureList.SHOPPING_LIST})
 public class TabbedAppMenuPropertiesDelegateUnitTest {
     // Costants defining flags that determines multi-window menu items visibility.
@@ -230,7 +230,7 @@
                 R.id.find_in_page_id, R.id.add_to_homescreen_id,
                 R.id.request_desktop_site_row_menu_id, R.id.auto_dark_web_contents_row_menu_id,
                 R.id.divider_line_id, R.id.preferences_id, R.id.help_id,
-                R.id.managed_by_divider_line_id, R.id.managed_by_standard_menu_id};
+                R.id.managed_by_divider_line_id, R.id.managed_by_menu_id};
         assertMenuItemsAreEqual(menu, expectedItems);
     }
 
diff --git a/chrome/android/modules/cablev2_authenticator/public/BUILD.gn b/chrome/android/modules/cablev2_authenticator/public/BUILD.gn
index 1350cdf3..25df020 100644
--- a/chrome/android/modules/cablev2_authenticator/public/BUILD.gn
+++ b/chrome/android/modules/cablev2_authenticator/public/BUILD.gn
@@ -12,8 +12,8 @@
     "//base:base_java",
     "//components/module_installer/android:module_installer_java",
     "//components/module_installer/android:module_interface_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
   ]
   annotation_processor_deps =
       [ "//components/module_installer/android:module_interface_processor" ]
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 0dc40a86..6f21dc7 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4008,6 +4008,8 @@
       "metrics/first_web_contents_profiler_base.cc",
       "metrics/first_web_contents_profiler_base.h",
       "metrics/incognito_observer_desktop.cc",
+      "metrics/power/power_metrics.cc",
+      "metrics/power/power_metrics.h",
       "metrics/power/power_metrics_reporter.cc",
       "metrics/power/power_metrics_reporter.h",
       "metrics/power/usage_scenario.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7001c5b..abbc711 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -217,7 +217,6 @@
 #include "components/browser_ui/photo_picker/android/features.h"
 #include "components/content_creation/notes/core/note_features.h"
 #include "components/content_creation/reactions/core/reactions_features.h"
-#include "components/external_intents/android/external_intents_features.h"
 #include "components/translate/content/android/translate_message.h"
 #include "components/webapps/browser/android/features.h"
 #else  // BUILDFLAG(IS_ANDROID)
@@ -4276,6 +4275,10 @@
     {"theme-refactor-android", flag_descriptions::kThemeRefactorAndroidName,
      flag_descriptions::kThemeRefactorAndroidDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kThemeRefactorAndroid)},
+    {"back-gesture-refactor-android",
+     flag_descriptions::kBackGestureRefactorAndroidName,
+     flag_descriptions::kBackGestureRefactorAndroidDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kBackGestureRefactorAndroid)},
 #endif  // BUILDFLAG(IS_ANDROID)
     {"disallow-doc-written-script-loads",
      flag_descriptions::kDisallowDocWrittenScriptsUiName,
@@ -6252,7 +6255,7 @@
      FEATURE_VALUE_TYPE(autofill::features::kAutofillCreditCardUploadFeedback)},
 
     {"font-access", flag_descriptions::kFontAccessAPIName,
-     flag_descriptions::kFontAccessAPIDescription, kOsAll,
+     flag_descriptions::kFontAccessAPIDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(blink::features::kFontAccess)},
 
     {"mouse-subframe-no-implicit-capture",
@@ -6657,12 +6660,6 @@
 #endif  // BUILDFLAG(ENABLE_PAINT_PREVIEW) && BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_ANDROID)
-    {"block-external-form-redirects-no-gesture",
-     flag_descriptions::kIntentBlockExternalFormRedirectsNoGestureName,
-     flag_descriptions::kIntentBlockExternalFormRedirectsNoGestureDescription,
-     kOsAndroid,
-     FEATURE_VALUE_TYPE(
-         external_intents::kIntentBlockExternalFormRedirectsNoGesture)},
     {"recover-from-never-save-android",
      flag_descriptions::kRecoverFromNeverSaveAndroidName,
      flag_descriptions::kRecoverFromNeverSaveAndroidDescription, kOsAndroid,
@@ -8002,11 +7999,6 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #if BUILDFLAG(IS_ANDROID)
-    {"enable-chrome-management-page-android",
-     flag_descriptions::kChromeManagementPageAndroidName,
-     flag_descriptions::kChromeManagementPageAndroidDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(policy::features::kChromeManagementPageAndroid)},
-
     {"context-menu-popup-style", flag_descriptions::kContextMenuPopupStyleName,
      flag_descriptions::kContextMenuPopupStyleDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kContextMenuPopupStyle)},
diff --git a/chrome/browser/android/examples/custom_tabs_client/BUILD.gn b/chrome/browser/android/examples/custom_tabs_client/BUILD.gn
index 009a5a3..9d4a7664 100644
--- a/chrome/browser/android/examples/custom_tabs_client/BUILD.gn
+++ b/chrome/browser/android/examples/custom_tabs_client/BUILD.gn
@@ -51,10 +51,7 @@
     "src/res/values/strings.xml",
   ]
   android_manifest = "src/AndroidManifest.xml"
-  deps = [
-    "//third_party/android_deps:android_support_v7_appcompat_java",
-    "//third_party/android_deps:material_design_java",
-  ]
+  deps = [ "//third_party/android_deps:material_design_java" ]
 }
 
 android_apk("custom_tabs_client_example_apk") {
@@ -75,11 +72,9 @@
 
   deps = [
     ":chrome_tabs_client_example_apk_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_browser_browser_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
   ]
 }
diff --git a/chrome/browser/ash/crosapi/authentication_ash.cc b/chrome/browser/ash/crosapi/authentication_ash.cc
index c38c45b..098ece16 100644
--- a/chrome/browser/ash/crosapi/authentication_ash.cc
+++ b/chrome/browser/ash/crosapi/authentication_ash.cc
@@ -61,20 +61,21 @@
     bool success,
     std::unique_ptr<TokenInfo> token_info,
     const std::string& error_message) {
-  mojom::CreateQuickUnlockPrivateTokenInfoResultPtr result_ptr =
-      mojom::CreateQuickUnlockPrivateTokenInfoResult::New();
+  mojom::CreateQuickUnlockPrivateTokenInfoResultPtr result;
   if (success) {
     DCHECK(token_info);
     crosapi::mojom::QuickUnlockPrivateTokenInfoPtr out_token_info =
         crosapi::mojom::QuickUnlockPrivateTokenInfo::New();
     out_token_info->token = token_info->token;
     out_token_info->lifetime_seconds = token_info->lifetime_seconds;
-    result_ptr->set_token_info(std::move(out_token_info));
+    result = mojom::CreateQuickUnlockPrivateTokenInfoResult::NewTokenInfo(
+        std::move(out_token_info));
   } else {
     DCHECK(!error_message.empty());
-    result_ptr->set_error_message(error_message);
+    result = mojom::CreateQuickUnlockPrivateTokenInfoResult::NewErrorMessage(
+        error_message);
   }
-  std::move(callback).Run(std::move(result_ptr));
+  std::move(callback).Run(std::move(result));
 
   extended_authenticator->SetConsumer(nullptr);
 }
diff --git a/chrome/browser/ash/crosapi/keystore_service_ash.cc b/chrome/browser/ash/crosapi/keystore_service_ash.cc
index 2cfb9bd..0d37650 100644
--- a/chrome/browser/ash/crosapi/keystore_service_ash.cc
+++ b/chrome/browser/ash/crosapi/keystore_service_ash.cc
@@ -243,13 +243,15 @@
     void* challenge_key_ptr,
     const ash::attestation::TpmChallengeKeyResult& result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  crosapi::mojom::ChallengeAttestationOnlyKeystoreResultPtr result_ptr =
-      mojom::ChallengeAttestationOnlyKeystoreResult::New();
+  crosapi::mojom::ChallengeAttestationOnlyKeystoreResultPtr result_ptr;
   if (result.IsSuccess()) {
-    result_ptr->set_challenge_response(std::vector<uint8_t>(
-        result.challenge_response.begin(), result.challenge_response.end()));
+    result_ptr =
+        mojom::ChallengeAttestationOnlyKeystoreResult::NewChallengeResponse(
+            std::vector<uint8_t>(result.challenge_response.begin(),
+                                 result.challenge_response.end()));
   } else {
-    result_ptr->set_error_message(result.GetErrorMessage());
+    result_ptr = mojom::ChallengeAttestationOnlyKeystoreResult::NewErrorMessage(
+        result.GetErrorMessage());
   }
   std::move(callback).Run(std::move(result_ptr));
 
@@ -317,12 +319,13 @@
     void* challenge_key_ptr,
     const ash::attestation::TpmChallengeKeyResult& result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  crosapi::mojom::DEPRECATED_KeystoreStringResultPtr result_ptr =
-      mojom::DEPRECATED_KeystoreStringResult::New();
+  crosapi::mojom::DEPRECATED_KeystoreStringResultPtr result_ptr;
   if (result.IsSuccess()) {
-    result_ptr->set_challenge_response(result.challenge_response);
+    result_ptr = mojom::DEPRECATED_KeystoreStringResult::NewChallengeResponse(
+        result.challenge_response);
   } else {
-    result_ptr->set_error_message(result.GetErrorMessage());
+    result_ptr = mojom::DEPRECATED_KeystoreStringResult::NewErrorMessage(
+        result.GetErrorMessage());
   }
   std::move(callback).Run(std::move(result_ptr));
 
@@ -354,7 +357,7 @@
     chromeos::platform_keys::Status status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  mojom::GetKeyStoresResultPtr result_ptr = mojom::GetKeyStoresResult::New();
+  mojom::GetKeyStoresResultPtr result_ptr;
 
   if (status == chromeos::platform_keys::Status::kSuccess) {
     std::vector<mojom::KeystoreType> key_stores;
@@ -368,9 +371,9 @@
           break;
       }
     }
-    result_ptr->set_key_stores(std::move(key_stores));
+    result_ptr = mojom::GetKeyStoresResult::NewKeyStores(std::move(key_stores));
   } else {
-    result_ptr->set_error(
+    result_ptr = mojom::GetKeyStoresResult::NewError(
         chromeos::platform_keys::StatusToKeystoreError(status));
   }
 
@@ -395,8 +398,7 @@
     chromeos::platform_keys::Status status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  mojom::DEPRECATED_GetKeyStoresResultPtr result_ptr =
-      mojom::DEPRECATED_GetKeyStoresResult::New();
+  mojom::DEPRECATED_GetKeyStoresResultPtr result_ptr;
 
   if (status == chromeos::platform_keys::Status::kSuccess) {
     std::vector<mojom::KeystoreType> key_stores;
@@ -410,9 +412,10 @@
           break;
       }
     }
-    result_ptr->set_key_stores(std::move(key_stores));
+    result_ptr = mojom::DEPRECATED_GetKeyStoresResult::NewKeyStores(
+        std::move(key_stores));
   } else {
-    result_ptr->set_error_message(
+    result_ptr = mojom::DEPRECATED_GetKeyStoresResult::NewErrorMessage(
         chromeos::platform_keys::StatusToString(status));
   }
 
@@ -445,8 +448,7 @@
     chromeos::platform_keys::Status status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  mojom::KeystoreSelectClientCertificatesResultPtr result_ptr =
-      mojom::KeystoreSelectClientCertificatesResult::New();
+  mojom::KeystoreSelectClientCertificatesResultPtr result_ptr;
 
   if (status == chromeos::platform_keys::Status::kSuccess) {
     std::vector<std::vector<uint8_t>> output;
@@ -457,9 +459,10 @@
           data, data + CRYPTO_BUFFER_len(der_buffer));
       output.push_back(std::move(der_x509_certificate));
     }
-    result_ptr->set_certificates(std::move(output));
+    result_ptr = mojom::KeystoreSelectClientCertificatesResult::NewCertificates(
+        std::move(output));
   } else {
-    result_ptr->set_error(
+    result_ptr = mojom::KeystoreSelectClientCertificatesResult::NewError(
         chromeos::platform_keys::StatusToKeystoreError(status));
   }
 
@@ -491,8 +494,7 @@
     chromeos::platform_keys::Status status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  mojom::GetCertificatesResultPtr result_ptr =
-      mojom::GetCertificatesResult::New();
+  mojom::GetCertificatesResultPtr result_ptr;
 
   if (status == chromeos::platform_keys::Status::kSuccess) {
     std::vector<std::vector<uint8_t>> output;
@@ -503,9 +505,10 @@
           data, data + CRYPTO_BUFFER_len(der_buffer));
       output.push_back(std::move(der_x509_certificate));
     }
-    result_ptr->set_certificates(std::move(output));
+    result_ptr =
+        mojom::GetCertificatesResult::NewCertificates(std::move(output));
   } else {
-    result_ptr->set_error(
+    result_ptr = mojom::GetCertificatesResult::NewError(
         chromeos::platform_keys::StatusToKeystoreError(status));
   }
 
@@ -540,8 +543,7 @@
     chromeos::platform_keys::Status status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  mojom::DEPRECATED_GetCertificatesResultPtr result_ptr =
-      mojom::DEPRECATED_GetCertificatesResult::New();
+  mojom::DEPRECATED_GetCertificatesResultPtr result_ptr;
 
   if (status == chromeos::platform_keys::Status::kSuccess) {
     std::vector<std::vector<uint8_t>> output;
@@ -552,9 +554,10 @@
           data, data + CRYPTO_BUFFER_len(der_buffer));
       output.push_back(std::move(der_x509_certificate));
     }
-    result_ptr->set_certificates(std::move(output));
+    result_ptr = mojom::DEPRECATED_GetCertificatesResult::NewCertificates(
+        std::move(output));
   } else {
-    result_ptr->set_error_message(
+    result_ptr = mojom::DEPRECATED_GetCertificatesResult::NewErrorMessage(
         chromeos::platform_keys::StatusToString(status));
   }
 
@@ -729,7 +732,7 @@
       chromeos::platform_keys::GetPublicKeyAndAlgorithm(certificate,
                                                         name.value());
 
-  mojom::GetPublicKeyResultPtr result_ptr = mojom::GetPublicKeyResult::New();
+  mojom::GetPublicKeyResultPtr result_ptr;
   if (output.status == chromeos::platform_keys::Status::kSuccess) {
     absl::optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
         signing_algorithm =
@@ -741,9 +744,10 @@
       success_result_ptr->public_key = std::move(output.public_key);
       success_result_ptr->algorithm_properties =
           std::move(signing_algorithm.value());
-      result_ptr->set_success_result(std::move(success_result_ptr));
+      result_ptr = mojom::GetPublicKeyResult::NewSuccessResult(
+          std::move(success_result_ptr));
     } else {
-      result_ptr->set_error(
+      result_ptr = mojom::GetPublicKeyResult::NewError(
           crosapi::mojom::KeystoreError::kUnsupportedAlgorithmType);
     }
   } else {
@@ -775,8 +779,7 @@
       chromeos::platform_keys::GetPublicKeyAndAlgorithm(certificate,
                                                         name.value());
 
-  mojom::DEPRECATED_GetPublicKeyResultPtr result_ptr =
-      mojom::DEPRECATED_GetPublicKeyResult::New();
+  mojom::DEPRECATED_GetPublicKeyResultPtr result_ptr;
   if (output.status == chromeos::platform_keys::Status::kSuccess) {
     absl::optional<crosapi::mojom::KeystoreSigningAlgorithmPtr>
         signing_algorithm =
@@ -788,12 +791,14 @@
       success_result_ptr->public_key = std::move(output.public_key);
       success_result_ptr->algorithm_properties =
           std::move(signing_algorithm.value());
-      result_ptr->set_success_result(std::move(success_result_ptr));
+      result_ptr = mojom::DEPRECATED_GetPublicKeyResult::NewSuccessResult(
+          std::move(success_result_ptr));
     } else {
-      result_ptr->set_error_message(kUnsupportedAlgorithmType);
+      result_ptr = mojom::DEPRECATED_GetPublicKeyResult::NewErrorMessage(
+          kUnsupportedAlgorithmType);
     }
   } else {
-    result_ptr->set_error_message(
+    result_ptr = mojom::DEPRECATED_GetPublicKeyResult::NewErrorMessage(
         chromeos::platform_keys::StatusToString(output.status));
   }
   std::move(callback).Run(std::move(result_ptr));
@@ -860,14 +865,14 @@
     const std::string& public_key,
     absl::optional<crosapi::mojom::KeystoreError> error) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  crosapi::mojom::DEPRECATED_ExtensionKeystoreBinaryResultPtr result_ptr =
-      mojom::DEPRECATED_ExtensionKeystoreBinaryResult::New();
+  crosapi::mojom::DEPRECATED_ExtensionKeystoreBinaryResultPtr result_ptr;
   if (!error) {
-    result_ptr->set_blob(
+    result_ptr = mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewBlob(
         std::vector<uint8_t>(public_key.begin(), public_key.end()));
   } else {
-    result_ptr->set_error_message(
-        chromeos::platform_keys::KeystoreErrorToString(error.value()));
+    result_ptr =
+        mojom::DEPRECATED_ExtensionKeystoreBinaryResult::NewErrorMessage(
+            chromeos::platform_keys::KeystoreErrorToString(error.value()));
   }
   std::move(callback).Run(std::move(result_ptr));
 }
@@ -1013,13 +1018,12 @@
     const std::string& public_key,
     chromeos::platform_keys::Status status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  crosapi::mojom::KeystoreBinaryResultPtr result_ptr =
-      mojom::KeystoreBinaryResult::New();
+  crosapi::mojom::KeystoreBinaryResultPtr result_ptr;
   if (status == chromeos::platform_keys::Status::kSuccess) {
-    result_ptr->set_blob(
+    result_ptr = mojom::KeystoreBinaryResult::NewBlob(
         std::vector<uint8_t>(public_key.begin(), public_key.end()));
   } else {
-    result_ptr->set_error(
+    result_ptr = mojom::KeystoreBinaryResult::NewError(
         chromeos::platform_keys::StatusToKeystoreError(status));
   }
   std::move(callback).Run(std::move(result_ptr));
@@ -1141,8 +1145,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   using KeyTag = crosapi::mojom::KeyTag;
 
-  crosapi::mojom::GetKeyTagsResultPtr result_ptr =
-      mojom::GetKeyTagsResult::New();
+  crosapi::mojom::GetKeyTagsResultPtr result_ptr;
 
   if (status == chromeos::platform_keys::Status::kSuccess) {
     DCHECK(corporate.has_value());
@@ -1153,9 +1156,9 @@
     if (corporate.value()) {
       tags |= static_cast<uint64_t>(KeyTag::kCorporate);
     }
-    result_ptr->set_tags(tags);
+    result_ptr = crosapi::mojom::GetKeyTagsResult::NewTags(tags);
   } else {
-    result_ptr->set_error(
+    result_ptr = crosapi::mojom::GetKeyTagsResult::NewError(
         chromeos::platform_keys::StatusToKeystoreError(status));
   }
 
diff --git a/chrome/browser/ash/crosapi/login_screen_storage_ash.cc b/chrome/browser/ash/crosapi/login_screen_storage_ash.cc
index 08c0c67..03e6db3 100644
--- a/chrome/browser/ash/crosapi/login_screen_storage_ash.cc
+++ b/chrome/browser/ash/crosapi/login_screen_storage_ash.cc
@@ -82,14 +82,13 @@
 void LoginScreenStorageAsh::OnRetrieved(RetrieveCallback callback,
                                         absl::optional<std::string> data,
                                         absl::optional<std::string> error) {
-  mojom::LoginScreenStorageRetrieveResultPtr result_ptr =
-      mojom::LoginScreenStorageRetrieveResult::New();
+  mojom::LoginScreenStorageRetrieveResultPtr result;
   if (error) {
-    result_ptr->set_error_message(*error);
+    result = mojom::LoginScreenStorageRetrieveResult::NewErrorMessage(*error);
   } else if (data) {
-    result_ptr->set_data(*data);
+    result = mojom::LoginScreenStorageRetrieveResult::NewData(*data);
   }
-  std::move(callback).Run(std::move(result_ptr));
+  std::move(callback).Run(std::move(result));
 }
 
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/login_screen_storage_ash_unittest.cc b/chrome/browser/ash/crosapi/login_screen_storage_ash_unittest.cc
index 7d6ba25b..ff51752 100644
--- a/chrome/browser/ash/crosapi/login_screen_storage_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/login_screen_storage_ash_unittest.cc
@@ -222,8 +222,7 @@
       .WillOnce(WithArgs<1>(Invoke(LoginScreenStorageRetrieveSuccess)));
 
   mojom::LoginScreenStorageRetrieveResultPtr expected_result_ptr =
-      mojom::LoginScreenStorageRetrieveResult::New();
-  expected_result_ptr->set_data(kData);
+      mojom::LoginScreenStorageRetrieveResult::NewData(kData);
 
   base::RunLoop run_loop;
   login_screen_storage_remote_->Retrieve(
@@ -237,8 +236,7 @@
       .WillOnce(WithArgs<1>(Invoke(LoginScreenStorageRetrieveError)));
 
   mojom::LoginScreenStorageRetrieveResultPtr expected_result_ptr =
-      mojom::LoginScreenStorageRetrieveResult::New();
-  expected_result_ptr->set_error_message(kError);
+      mojom::LoginScreenStorageRetrieveResult::NewErrorMessage(kError);
 
   base::RunLoop run_loop;
   login_screen_storage_remote_->Retrieve(
diff --git a/chrome/browser/ash/crosapi/login_state_ash_unittest.cc b/chrome/browser/ash/crosapi/login_state_ash_unittest.cc
index c0873a7..837687f 100644
--- a/chrome/browser/ash/crosapi/login_state_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/login_state_ash_unittest.cc
@@ -90,8 +90,7 @@
     session_manager_->SetSessionState(test.session_state);
 
     mojom::GetSessionStateResultPtr expected_result_ptr =
-        mojom::GetSessionStateResult::New();
-    expected_result_ptr->set_session_state(test.expected);
+        mojom::GetSessionStateResult::NewSessionState(test.expected);
 
     base::RunLoop run_loop;
     login_state_remote_->GetSessionState(
diff --git a/chrome/browser/ash/crosapi/network_settings_translation_unittest.cc b/chrome/browser/ash/crosapi/network_settings_translation_unittest.cc
index 0776f47..55101de 100644
--- a/chrome/browser/ash/crosapi/network_settings_translation_unittest.cc
+++ b/chrome/browser/ash/crosapi/network_settings_translation_unittest.cc
@@ -118,11 +118,8 @@
 
 TEST(NetworkSettingsTranslationTest, CrosapiProxyToProxyConfigDirect) {
   crosapi::mojom::ProxyConfigPtr ptr = crosapi::mojom::ProxyConfig::New();
-  crosapi::mojom::ProxySettingsPtr proxy = crosapi::mojom::ProxySettings::New();
-  crosapi::mojom::ProxySettingsDirectPtr direct =
-      crosapi::mojom::ProxySettingsDirect::New();
-  proxy->set_direct(std::move(direct));
-  ptr->proxy_settings = std::move(proxy);
+  ptr->proxy_settings = crosapi::mojom::ProxySettings::NewDirect(
+      crosapi::mojom::ProxySettingsDirect::New());
 
   EXPECT_EQ(CrosapiProxyToProxyConfig(std::move(ptr)).GetDictionary(),
             ProxyConfigDictionary::CreateDirect());
@@ -130,12 +127,10 @@
 
 TEST(NetworkSettingsTranslationTest, CrosapiProxyToProxyConfigWpad) {
   crosapi::mojom::ProxyConfigPtr ptr = crosapi::mojom::ProxyConfig::New();
-  crosapi::mojom::ProxySettingsPtr proxy = crosapi::mojom::ProxySettings::New();
   crosapi::mojom::ProxySettingsWpadPtr wpad =
       crosapi::mojom::ProxySettingsWpad::New();
   wpad->pac_url = GURL("pac.pac");
-  proxy->set_wpad(std::move(wpad));
-  ptr->proxy_settings = std::move(proxy);
+  ptr->proxy_settings = crosapi::mojom::ProxySettings::NewWpad(std::move(wpad));
 
   EXPECT_EQ(CrosapiProxyToProxyConfig(std::move(ptr)).GetDictionary(),
             ProxyConfigDictionary::CreateAutoDetect());
@@ -143,26 +138,22 @@
 
 TEST(NetworkSettingsTranslationTest, CrosapiProxyToProxyConfigPac) {
   crosapi::mojom::ProxyConfigPtr ptr = crosapi::mojom::ProxyConfig::New();
-  crosapi::mojom::ProxySettingsPtr proxy = crosapi::mojom::ProxySettings::New();
   crosapi::mojom::ProxySettingsPacPtr pac =
       crosapi::mojom::ProxySettingsPac::New();
   pac->pac_url = GURL(kPacUrl);
   pac->pac_mandatory = true;
-  proxy->set_pac(pac.Clone());
-  ptr->proxy_settings = proxy.Clone();
+  ptr->proxy_settings = crosapi::mojom::ProxySettings::NewPac(pac.Clone());
   EXPECT_EQ(CrosapiProxyToProxyConfig(ptr.Clone()).GetDictionary(),
             GetPacProxyConfig(kPacUrl, true));
 
   pac->pac_mandatory = false;
-  proxy->set_pac(pac.Clone());
-  ptr->proxy_settings = std::move(proxy);
+  ptr->proxy_settings = crosapi::mojom::ProxySettings::NewPac(pac.Clone());
   EXPECT_EQ(CrosapiProxyToProxyConfig(std::move(ptr)).GetDictionary(),
             GetPacProxyConfig(kPacUrl, false));
 }
 
 TEST(NetworkSettingsTranslationTest, CrosapiProxyToProxyConfigManual) {
   crosapi::mojom::ProxyConfigPtr ptr = crosapi::mojom::ProxyConfig::New();
-  crosapi::mojom::ProxySettingsPtr proxy = crosapi::mojom::ProxySettings::New();
   crosapi::mojom::ProxySettingsManualPtr manual =
       crosapi::mojom::ProxySettingsManual::New();
   crosapi::mojom::ProxyLocationPtr location =
@@ -180,8 +171,8 @@
   location->port = 82;
   manual->socks_proxies.push_back(std::move(location));
   manual->exclude_domains = {"localhost", "google.com"};
-  proxy->set_manual(std::move(manual));
-  ptr->proxy_settings = std::move(proxy);
+  ptr->proxy_settings =
+      crosapi::mojom::ProxySettings::NewManual(std::move(manual));
   EXPECT_EQ(CrosapiProxyToProxyConfig(std::move(ptr)).GetDictionary(),
             GetManualProxyConfig("http=proxy1:80;http=proxy2:80;https=secure_"
                                  "proxy:81;socks=socks_proxy:82",
diff --git a/chrome/browser/ash/crosapi/networking_attributes_ash.cc b/chrome/browser/ash/crosapi/networking_attributes_ash.cc
index b5c5389..9292b2a 100644
--- a/chrome/browser/ash/crosapi/networking_attributes_ash.cc
+++ b/chrome/browser/ash/crosapi/networking_attributes_ash.cc
@@ -77,10 +77,8 @@
     details->ipv6_address = ipv6_address;
   }
 
-  mojom::GetNetworkDetailsResultPtr result_ptr =
-      mojom::GetNetworkDetailsResult::New();
-  result_ptr->set_network_details(std::move(details));
-  std::move(callback).Run(std::move(result_ptr));
+  std::move(callback).Run(
+      mojom::GetNetworkDetailsResult::NewNetworkDetails(std::move(details)));
 }
 
 }  // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/networking_attributes_ash_unittest.cc b/chrome/browser/ash/crosapi/networking_attributes_ash_unittest.cc
index d9d69e6..0ea9467 100644
--- a/chrome/browser/ash/crosapi/networking_attributes_ash_unittest.cc
+++ b/chrome/browser/ash/crosapi/networking_attributes_ash_unittest.cc
@@ -205,8 +205,7 @@
   AddUser(/*is_affiliated=*/false);
 
   mojom::GetNetworkDetailsResultPtr expected_result_ptr =
-      mojom::GetNetworkDetailsResult::New();
-  expected_result_ptr->set_error_message(kErrorUserNotAffiliated);
+      mojom::GetNetworkDetailsResult::NewErrorMessage(kErrorUserNotAffiliated);
 
   base::RunLoop run_loop;
   networking_attributes_remote_->GetNetworkDetails(
@@ -219,8 +218,8 @@
   AddUser();
 
   mojom::GetNetworkDetailsResultPtr expected_result_ptr =
-      mojom::GetNetworkDetailsResult::New();
-  expected_result_ptr->set_error_message(kErrorNetworkNotConnected);
+      mojom::GetNetworkDetailsResult::NewErrorMessage(
+          kErrorNetworkNotConnected);
 
   base::RunLoop run_loop;
   networking_attributes_remote_->GetNetworkDetails(
@@ -244,8 +243,8 @@
   expected_network_details->ipv4_address = ipv4_expected;
   expected_network_details->ipv6_address = ipv6_expected;
   mojom::GetNetworkDetailsResultPtr expected_result_ptr =
-      mojom::GetNetworkDetailsResult::New();
-  expected_result_ptr->set_network_details(std::move(expected_network_details));
+      mojom::GetNetworkDetailsResult::NewNetworkDetails(
+          std::move(expected_network_details));
 
   base::RunLoop run_loop;
   networking_attributes_remote_->GetNetworkDetails(
diff --git a/chrome/browser/ash/crosapi/translate_proxy_config_to_crosapi.cc b/chrome/browser/ash/crosapi/translate_proxy_config_to_crosapi.cc
index 67dc3def..dbed595 100644
--- a/chrome/browser/ash/crosapi/translate_proxy_config_to_crosapi.cc
+++ b/chrome/browser/ash/crosapi/translate_proxy_config_to_crosapi.cc
@@ -95,19 +95,19 @@
     GURL dhcp_wpad_url) {
   crosapi::mojom::ProxyConfigPtr proxy_config =
       crosapi::mojom::ProxyConfig::New();
-  crosapi::mojom::ProxySettingsPtr proxy = crosapi::mojom::ProxySettings::New();
   crosapi::mojom::ProxySettingsDirectPtr direct =
       crosapi::mojom::ProxySettingsDirect::New();
 
   ProxyPrefs::ProxyMode mode;
   if (!proxy_dict || !proxy_dict->GetMode(&mode)) {
-    proxy->set_direct(std::move(direct));
-    proxy_config->proxy_settings = std::move(proxy);
+    proxy_config->proxy_settings =
+        crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
     return proxy_config;
   }
   switch (mode) {
     case ProxyPrefs::MODE_DIRECT:
-      proxy->set_direct(std::move(direct));
+      proxy_config->proxy_settings =
+          crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
       break;
     case ProxyPrefs::MODE_AUTO_DETECT: {
       crosapi::mojom::ProxySettingsWpadPtr wpad =
@@ -119,13 +119,15 @@
         // Fallback to WPAD via DNS.
         wpad->pac_url = GURL("http://wpad/wpad.dat");
       }
-      proxy->set_wpad(std::move(wpad));
+      proxy_config->proxy_settings =
+          crosapi::mojom::ProxySettings::NewWpad(std::move(wpad));
       break;
     }
     case ProxyPrefs::MODE_PAC_SCRIPT: {
       std::string pac_url;
       if (!proxy_dict->GetPacUrl(&pac_url)) {
-        proxy->set_direct(std::move(direct));
+        proxy_config->proxy_settings =
+            crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
         LOG(ERROR) << "No pac URL for pac_script proxy mode.";
         break;
       }
@@ -136,13 +138,15 @@
           crosapi::mojom::ProxySettingsPac::New();
       pac->pac_url = GURL(pac_url);
       pac->pac_mandatory = pac_mandatory;
-      proxy->set_pac(std::move(pac));
+      proxy_config->proxy_settings =
+          crosapi::mojom::ProxySettings::NewPac(std::move(pac));
       break;
     }
     case ProxyPrefs::MODE_FIXED_SERVERS: {
       crosapi::mojom::ProxySettingsManualPtr manual =
           TranslateManualProxySettings(proxy_dict);
-      proxy->set_manual(std::move(manual));
+      proxy_config->proxy_settings =
+          crosapi::mojom::ProxySettings::NewManual(std::move(manual));
       break;
     }
     case ProxyPrefs::MODE_SYSTEM:
@@ -153,10 +157,10 @@
       break;
     default:
       LOG(ERROR) << "Incorrect proxy mode.";
-      proxy->set_direct(std::move(direct));
+      proxy_config->proxy_settings =
+          crosapi::mojom::ProxySettings::NewDirect(std::move(direct));
   }
 
-  proxy_config->proxy_settings = std::move(proxy);
   return proxy_config;
 }
 
diff --git a/chrome/browser/ash/crosapi/video_frame_handler_ash.cc b/chrome/browser/ash/crosapi/video_frame_handler_ash.cc
index 5e2f64c..ebc53223 100644
--- a/chrome/browser/ash/crosapi/video_frame_handler_ash.cc
+++ b/chrome/browser/ash/crosapi/video_frame_handler_ash.cc
@@ -77,23 +77,19 @@
   crosapi_gpu_handle->stride = buffer_handle.stride;
 
   if (buffer_handle.type == gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER) {
-    auto crosapi_platform_handle =
-        crosapi::mojom::GpuMemoryBufferPlatformHandle::New();
-    crosapi_platform_handle->set_shared_memory_handle(
-        std::move(buffer_handle.region));
-    crosapi_gpu_handle->platform_handle = std::move(crosapi_platform_handle);
+    crosapi_gpu_handle->platform_handle =
+        crosapi::mojom::GpuMemoryBufferPlatformHandle::NewSharedMemoryHandle(
+            std::move(buffer_handle.region));
   } else if (buffer_handle.type == gfx::GpuMemoryBufferType::NATIVE_PIXMAP) {
-    auto crosapi_platform_handle =
-        crosapi::mojom::GpuMemoryBufferPlatformHandle::New();
     auto crosapi_native_pixmap_handle =
         crosapi::mojom::NativePixmapHandle::New();
     crosapi_native_pixmap_handle->planes =
         std::move(buffer_handle.native_pixmap_handle.planes);
     crosapi_native_pixmap_handle->modifier =
         buffer_handle.native_pixmap_handle.modifier;
-    crosapi_platform_handle->set_native_pixmap_handle(
-        std::move(crosapi_native_pixmap_handle));
-    crosapi_gpu_handle->platform_handle = std::move(crosapi_platform_handle);
+    crosapi_gpu_handle->platform_handle =
+        crosapi::mojom::GpuMemoryBufferPlatformHandle::NewNativePixmapHandle(
+            std::move(crosapi_native_pixmap_handle));
   }
   return crosapi_gpu_handle;
 }
diff --git a/chrome/browser/ash/crostini/crostini_browser_test_util.cc b/chrome/browser/ash/crostini/crostini_browser_test_util.cc
index 7ef5397..0946f4a 100644
--- a/chrome/browser/ash/crostini/crostini_browser_test_util.cc
+++ b/chrome/browser/ash/crostini/crostini_browser_test_util.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/ash/crostini/crostini_pref_names.h"
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/ash/crostini/fake_crostini_features.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_browser_main.h"
 #include "chrome/browser/chrome_browser_main_extra_parts.h"
@@ -132,6 +134,10 @@
 void CrostiniBrowserTestBase::SetUpOnMainThread() {
   browser()->profile()->GetPrefs()->SetBoolean(
       crostini::prefs::kCrostiniEnabled, true);
+
+  guest_os::GuestOsService::GetForProfile(browser()->profile())
+      ->WaylandServer()
+      ->OverrideServerForTesting(vm_tools::launch::TERMINA, nullptr, {});
 }
 
 void CrostiniBrowserTestBase::SetConnectionType(
diff --git a/chrome/browser/ash/crostini/crostini_capabilities.cc b/chrome/browser/ash/crostini/crostini_capabilities.cc
new file mode 100644
index 0000000..27d8862
--- /dev/null
+++ b/chrome/browser/ash/crostini/crostini_capabilities.cc
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/crostini/crostini_capabilities.h"
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ash/crostini/crostini_features.h"
+#include "third_party/cros_system_api/constants/vm_tools.h"
+
+namespace crostini {
+
+void CrostiniCapabilities::Build(
+    Profile* profile,
+    base::OnceCallback<void(std::unique_ptr<guest_os::GuestOsCapabilities>)>
+        callback) {
+  std::string reason;
+  if (!CrostiniFeatures::Get()->IsAllowedNow(profile, &reason)) {
+    LOG(WARNING) << "Crostini is not allowed: " << reason;
+    std::move(callback).Run(nullptr);
+    return;
+  }
+  // WrapUnique is used because the constructor is private.
+  std::move(callback).Run(base::WrapUnique(new CrostiniCapabilities()));
+}
+
+CrostiniCapabilities::~CrostiniCapabilities() = default;
+
+std::string CrostiniCapabilities::GetSecurityContext() const {
+  return vm_tools::kConciergeSecurityContext;
+}
+
+}  // namespace crostini
diff --git a/chrome/browser/ash/crostini/crostini_capabilities.h b/chrome/browser/ash/crostini/crostini_capabilities.h
new file mode 100644
index 0000000..f1c2d0a
--- /dev/null
+++ b/chrome/browser/ash/crostini/crostini_capabilities.h
@@ -0,0 +1,35 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_CROSTINI_CROSTINI_CAPABILITIES_H_
+#define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_CAPABILITIES_H_
+
+#include "base/callback_forward.h"
+#include "chrome/browser/ash/guest_os/guest_os_capabilities.h"
+
+class Profile;
+
+namespace crostini {
+
+class CrostiniCapabilities : public guest_os::GuestOsCapabilities {
+ public:
+  // Builds an instance of the capabilities for the given |profile|.
+  static void Build(
+      Profile* profile,
+      base::OnceCallback<void(std::unique_ptr<guest_os::GuestOsCapabilities>)>
+          callback);
+
+  ~CrostiniCapabilities() override;
+
+  // exo::Capabilities overrides:
+  std::string GetSecurityContext() const override;
+
+ private:
+  // Private constructor to force use of Build().
+  CrostiniCapabilities() = default;
+};
+
+}  // namespace crostini
+
+#endif  // CHROME_BROWSER_ASH_CROSTINI_CROSTINI_CAPABILITIES_H_
diff --git a/chrome/browser/ash/crostini/crostini_manager.cc b/chrome/browser/ash/crostini/crostini_manager.cc
index 4d41b57..4cd1440 100644
--- a/chrome/browser/ash/crostini/crostini_manager.cc
+++ b/chrome/browser/ash/crostini/crostini_manager.cc
@@ -45,6 +45,9 @@
 #include "chrome/browser/ash/file_manager/volume_manager.h"
 #include "chrome/browser/ash/guest_os/guest_os_share_path.h"
 #include "chrome/browser/ash/guest_os/guest_os_stability_monitor.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service_factory.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h"
 #include "chrome/browser/ash/policy/handlers/powerwash_requirements_checker.h"
 #include "chrome/browser/ash/scheduler_configuration_manager.h"
 #include "chrome/browser/ash/usb/cros_usb_detector.h"
@@ -261,6 +264,7 @@
                                const base::FilePath& result_path);
   // chromeos::SchedulerConfigurationManagerBase::Observer:
   void OnConfigurationSet(bool success, size_t num_cores_disabled) override;
+  void OnWaylandServerCreated(guest_os::GuestOsWaylandServer::Result result);
   void StartTerminaVmFinished(bool success);
   void SharePathsFinished(bool success, const std::string& failure_reason);
   void StartLxdFinished(CrostiniResult result);
@@ -314,6 +318,7 @@
   CrostiniManager::RestartId restart_id_;
   bool is_running_ = false;
   bool did_successful_full_restart_ = false;
+  size_t num_cores_disabled_ = 0;
   mojom::InstallerState stage_ = mojom::InstallerState::kStart;
   CrostiniResult result_ = CrostiniResult::NEVER_FINISHED;
 
@@ -666,9 +671,27 @@
   g_browser_process->platform_part()
       ->scheduler_configuration_manager()
       ->RemoveObserver(this);
+  num_cores_disabled_ = num_cores_disabled;
+
+  guest_os::GuestOsServiceFactory::GetForProfile(profile_)
+      ->WaylandServer()
+      ->Get(vm_tools::launch::TERMINA,
+            base::BindOnce(&CrostiniRestarter::OnWaylandServerCreated,
+                           weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CrostiniManager::CrostiniRestarter::OnWaylandServerCreated(
+    guest_os::GuestOsWaylandServer::Result result) {
+  if (!result) {
+    LOG(ERROR) << "Wayland server creation failed: "
+               << static_cast<int>(result.Error());
+    FinishRestart(CrostiniResult::WAYLAND_SERVER_CREATION_FAILED);
+    return;
+  }
   StartStage(mojom::InstallerState::kStartTerminaVm);
   crostini_manager_->StartTerminaVm(
-      container_id_.vm_name, disk_path_, num_cores_disabled,
+      container_id_.vm_name, disk_path_, result.Value()->server_path(),
+      num_cores_disabled_,
       base::BindOnce(&CrostiniRestarter::StartTerminaVmFinished,
                      weak_ptr_factory_.GetWeakPtr()));
 }
@@ -1242,6 +1265,7 @@
 
 void CrostiniManager::StartTerminaVm(std::string name,
                                      const base::FilePath& disk_path,
+                                     const base::FilePath& wayland_path,
                                      size_t num_cores_disabled,
                                      BoolCallback callback) {
   if (name.empty()) {
@@ -1286,6 +1310,7 @@
   request.set_name(std::move(name));
   request.set_start_termina(true);
   request.set_owner_id(owner_id_);
+  request.mutable_vm()->set_wayland_server(wayland_path.AsUTF8Unsafe());
   if (base::FeatureList::IsEnabled(chromeos::features::kCrostiniGpuSupport))
     request.set_enable_gpu(true);
   if (profile_->GetPrefs()->GetBoolean(prefs::kCrostiniMicAllowed) &&
diff --git a/chrome/browser/ash/crostini/crostini_manager.h b/chrome/browser/ash/crostini/crostini_manager.h
index c06456f0..aefe8a3 100644
--- a/chrome/browser/ash/crostini/crostini_manager.h
+++ b/chrome/browser/ash/crostini/crostini_manager.h
@@ -278,8 +278,11 @@
       std::string name,
       // Path to the disk image on the host.
       const base::FilePath& disk_path,
+      // Path to the wayland server's socket, per go/secure-exo-ids.
+      const base::FilePath& wayland_path,
       // The number of logical CPU cores that are currently disabled.
       size_t num_cores_disabled,
+      // A callback to invoke with the result of the launch request.
       BoolCallback callback);
 
   // Checks the arguments for stopping a Termina VM. Stops the Termina VM via
diff --git a/chrome/browser/ash/crostini/crostini_manager_unittest.cc b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
index 7f469136..3ff80d1 100644
--- a/chrome/browser/ash/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/ash/crostini/crostini_manager_unittest.cc
@@ -24,6 +24,8 @@
 #include "chrome/browser/ash/crostini/crostini_types.mojom-shared.h"
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/ash/crostini/fake_crostini_features.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/policy/handlers/powerwash_requirements_checker.h"
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
@@ -245,6 +247,10 @@
     g_browser_process->platform_part()
         ->InitializeSchedulerConfigurationManager();
 
+    guest_os::GuestOsService::GetForProfile(profile())
+        ->WaylandServer()
+        ->OverrideServerForTesting(vm_tools::launch::TERMINA, nullptr, {});
+
     chromeos::CryptohomeMiscClient::InitializeFake();
     chromeos::FakeCryptohomeMiscClient::Get()->set_requires_powerwash(false);
     policy::PowerwashRequirementsChecker::InitializeSynchronouslyForTesting();
@@ -354,7 +360,7 @@
   const base::FilePath& disk_path = base::FilePath("unused");
 
   crostini_manager()->StartTerminaVm(
-      "", disk_path, 0,
+      "", disk_path, {}, 0,
       base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_EQ(fake_concierge_client_->start_termina_vm_call_count(), 0);
@@ -367,7 +373,7 @@
       false);
 
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_EQ(fake_concierge_client_->start_termina_vm_call_count(), 0);
@@ -377,7 +383,7 @@
   const base::FilePath& disk_path = base::FilePath();
 
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_EQ(fake_concierge_client_->start_termina_vm_call_count(), 0);
@@ -407,7 +413,7 @@
   NotificationDisplayServiceTester notification_service(profile());
 
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_EQ(fake_concierge_client_->start_termina_vm_call_count(), 0);
@@ -444,7 +450,7 @@
   NotificationDisplayServiceTester notification_service(profile());
 
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_EQ(fake_concierge_client_->start_termina_vm_call_count(), 0);
@@ -465,7 +471,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectFailure, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_GE(fake_concierge_client_->start_termina_vm_call_count(), 1);
@@ -485,7 +491,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_GE(fake_concierge_client_->start_termina_vm_call_count(), 1);
@@ -499,7 +505,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
   EXPECT_GE(fake_concierge_client_->start_termina_vm_call_count(), 1);
@@ -518,7 +524,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      ContainerId::GetDefault().vm_name, disk_path, 0,
+      ContainerId::GetDefault().vm_name, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
 
@@ -540,7 +546,7 @@
 
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      ContainerId::GetDefault().vm_name, disk_path, 0,
+      ContainerId::GetDefault().vm_name, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
   run_loop()->Run();
 
@@ -556,7 +562,7 @@
   // Start the Vm.
   EnsureTerminaInstalled();
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop()->QuitClosure()));
 
   // Check that the Vm start is not recorded until tremplin starts.
@@ -1376,7 +1382,7 @@
 
   base::RunLoop run_loop2;
   crostini_manager()->StartTerminaVm(
-      kVmName, disk_path, 0,
+      kVmName, disk_path, {}, 0,
       base::BindOnce(&ExpectSuccess, run_loop2.QuitClosure()));
   run_loop2.Run();
   EXPECT_GE(fake_concierge_client_->start_termina_vm_call_count(), 1);
diff --git a/chrome/browser/ash/crostini/crostini_simple_types.h b/chrome/browser/ash/crostini/crostini_simple_types.h
index 8459575..778887d1 100644
--- a/chrome/browser/ash/crostini/crostini_simple_types.h
+++ b/chrome/browser/ash/crostini/crostini_simple_types.h
@@ -98,7 +98,8 @@
   VSH_CONNECT_FAILED = 65,
   CONTAINER_STOP_FAILED = 66,
   CONTAINER_STOP_CANCELLED = 67,
-  kMaxValue = CONTAINER_STOP_CANCELLED,
+  WAYLAND_SERVER_CREATION_FAILED = 68,
+  kMaxValue = WAYLAND_SERVER_CREATION_FAILED,
   // When adding a new value, check you've followed the steps in the comment at
   // the top of this enum.
 };
diff --git a/chrome/browser/ash/crostini/crostini_test_helper.cc b/chrome/browser/ash/crostini/crostini_test_helper.cc
index af368b9..bfea145a 100644
--- a/chrome/browser/ash/crostini/crostini_test_helper.cc
+++ b/chrome/browser/ash/crostini/crostini_test_helper.cc
@@ -11,12 +11,15 @@
 #include "chrome/browser/ash/crostini/crostini_pref_names.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/vm_launch/launch.pb.h"
 #include "components/crx_file/id_util.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/scoped_user_manager.h"
@@ -46,6 +49,10 @@
 
   current_apps_.set_vm_name(kCrostiniDefaultVmName);
   current_apps_.set_container_name(kCrostiniDefaultContainerName);
+
+  guest_os::GuestOsService::GetForProfile(profile_)
+      ->WaylandServer()
+      ->OverrideServerForTesting(vm_tools::launch::TERMINA, nullptr, {});
 }
 
 CrostiniTestHelper::~CrostiniTestHelper() {
diff --git a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
index 619c53a..9bfbaf7 100644
--- a/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
+++ b/chrome/browser/ash/guest_os/guest_os_share_path_unittest.cc
@@ -24,6 +24,8 @@
 #include "chrome/browser/ash/file_manager/volume_manager_factory.h"
 #include "chrome/browser/ash/file_system_provider/service_factory.h"
 #include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_service.h"
+#include "chrome/browser/ash/guest_os/public/guest_os_wayland_server.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/component_updater/fake_cros_component_manager.h"
 #include "chrome/common/chrome_features.h"
@@ -292,6 +294,11 @@
     drivefs_ =
         base::FilePath("/media/fuse/drivefs-84675c855b63e12f384d45f033826980");
 
+    // Setup for a fake wayland server (needed when CrostiniManager makes a VM).
+    guest_os::GuestOsService::GetForProfile(profile())
+        ->WaylandServer()
+        ->OverrideServerForTesting(vm_tools::launch::TERMINA, nullptr, {});
+
     // Create 'vm-running' VM instance which is running.
     crostini::CrostiniManager::GetForProfile(profile())->AddRunningVmForTesting(
         "vm-running");
diff --git a/chrome/browser/ash/guest_os/infra/cached_callback.h b/chrome/browser/ash/guest_os/infra/cached_callback.h
index f30ec60..b47b108 100644
--- a/chrome/browser/ash/guest_os/infra/cached_callback.h
+++ b/chrome/browser/ash/guest_os/infra/cached_callback.h
@@ -79,6 +79,10 @@
     return std::move(real);
   }
 
+  void CacheForTesting(std::unique_ptr<T> real) {
+    OnRealResultFound(RealResult(std::move(real)));
+  }
+
  protected:
   using RealResult = borealis::Expected<std::unique_ptr<T>, E>;
 
diff --git a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
index 5eeb9a3e..dbf9555 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
+++ b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.cc
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "chrome/browser/ash/borealis/borealis_capabilities.h"
 #include "chrome/browser/ash/borealis/borealis_service.h"
+#include "chrome/browser/ash/crostini/crostini_capabilities.h"
 #include "chrome/browser/ash/guest_os/guest_os_capabilities.h"
 #include "chrome/browser/ash/guest_os/infra/cached_callback.h"
 #include "chrome/browser/ash/guest_os/public/guest_os_service.h"
@@ -78,11 +79,13 @@
                               base::WeakPtr<GuestOsCapabilities> cap_ptr,
                               bool success,
                               const base::FilePath& path) {
-    if (success) {
-      std::move(callback).Run(Success(cap_ptr, path));
+    if (!success) {
+      std::move(callback).Run(Failure(ServerFailure::kFailedToSpawn));
       return;
     }
-    std::move(callback).Run(Failure(ServerFailure::kFailedToSpawn));
+    DCHECK(cap_ptr);
+    DCHECK(!path.empty());
+    std::move(callback).Run(Success(cap_ptr, path));
   }
 
   static void OnCapabilitiesCreated(RealCallback callback,
@@ -108,12 +111,12 @@
 GuestOsWaylandServer::ServerDetails::ServerDetails(
     base::WeakPtr<GuestOsCapabilities> capabilities,
     base::FilePath path)
-    : capabilities_(capabilities), server_path_(std::move(path)) {
-  DCHECK(capabilities_);
-  DCHECK(!server_path_.empty());
-}
+    : capabilities_(capabilities), server_path_(std::move(path)) {}
 
 GuestOsWaylandServer::ServerDetails::~ServerDetails() {
+  // In tests, this is used to avoid dealing with the real server controller.
+  if (server_path_.empty())
+    return;
   GuestOsCapabilities::MaybeRemoveServer(capabilities_, server_path_);
 }
 
@@ -139,6 +142,9 @@
   capability_holders_[vm_tools::launch::BOREALIS] =
       std::make_unique<CapabilityHolder>(base::BindRepeating(
           &borealis::BorealisCapabilities::Build, profile_));
+  capability_holders_[vm_tools::launch::TERMINA] =
+      std::make_unique<CapabilityHolder>(base::BindRepeating(
+          &crostini::CrostiniCapabilities::Build, profile_));
 }
 
 GuestOsWaylandServer::~GuestOsWaylandServer() = default;
@@ -159,4 +165,12 @@
   capability_holders_[vm_type] = std::make_unique<CapabilityHolder>(factory);
 }
 
+void GuestOsWaylandServer::OverrideServerForTesting(
+    vm_tools::launch::VmType vm_type,
+    base::WeakPtr<GuestOsCapabilities> capabilities,
+    base::FilePath path) {
+  capability_holders_[vm_type]->CacheForTesting(  // IN-TEST
+      std::make_unique<ServerDetails>(capabilities, std::move(path)));
+}
+
 }  // namespace guest_os
diff --git a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
index e2da39d..d17d43ab 100644
--- a/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
+++ b/chrome/browser/ash/guest_os/public/guest_os_wayland_server.h
@@ -91,6 +91,11 @@
           void(base::OnceCallback<void(std::unique_ptr<GuestOsCapabilities>)>)>
           factory);
 
+  // Used in tests to skip actually trying to allocate a server socket via exo.
+  void OverrideServerForTesting(vm_tools::launch::VmType vm_type,
+                                base::WeakPtr<GuestOsCapabilities> capabilities,
+                                base::FilePath path);
+
  private:
   class CapabilityHolder;
 
diff --git a/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.cc b/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.cc
index 9e3c7079..1a2fd87 100644
--- a/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.cc
+++ b/chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.cc
@@ -4,15 +4,20 @@
 
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h"
 
+#include "ash/components/proximity_auth/proximity_auth_pref_names.h"
 #include "ash/components/proximity_auth/screenlock_bridge.h"
+#include "ash/constants/ash_features.h"
 #include "base/guid.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/notifications/notification_display_service.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/prefs/pref_service.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/chromeos/devicetype_utils.h"
@@ -30,6 +35,9 @@
 const char kEasyUnlockPairingChangeAppliedNotifierId[] =
     "easyunlock_notification_ids.pairing_change_applied";
 
+const char kSmartLockSignInRemovedNotifierId[] =
+    "easyunlock_notification_ids.sign_in_removed";
+
 // Convenience function for creating a Notification.
 std::unique_ptr<message_center::Notification> CreateNotification(
     const std::string& id,
@@ -55,6 +63,39 @@
 
 EasyUnlockNotificationController::~EasyUnlockNotificationController() {}
 
+// static
+bool EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+    Profile* profile) {
+  if (!profile->GetPrefs()->GetBoolean(
+          proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled))
+    return false;
+
+  if (!base::FeatureList::IsEnabled(ash::features::kSmartLockSignInRemoved))
+    return false;
+
+  if (profile->GetPrefs()->GetBoolean(
+          prefs::kHasSeenSmartLockSignInRemovedNotification))
+    return false;
+
+  return true;
+}
+
+void EasyUnlockNotificationController::ShowSignInRemovedNotification() {
+  PrefService* pref_service = profile_->GetPrefs();
+  pref_service->SetBoolean(prefs::kHasSeenSmartLockSignInRemovedNotification,
+                           true);
+
+  ShowNotification(CreateNotification(
+      kSmartLockSignInRemovedNotifierId,
+      l10n_util::GetStringUTF16(
+          IDS_SMART_LOCK_SIGN_IN_REMOVED_NOTIFICATION_TITLE),
+      l10n_util::GetStringUTF16(
+          IDS_SMART_LOCK_SIGN_IN_REMOVED_NOTIFICATION_MESSAGE),
+      ui::ImageModel(), {},
+      new NotificationDelegate(kSmartLockSignInRemovedNotifierId,
+                               weak_ptr_factory_.GetWeakPtr())));
+}
+
 void EasyUnlockNotificationController::ShowChromebookAddedNotification() {
   message_center::RichNotificationData rich_notification_data;
   rich_notification_data.buttons.push_back(
@@ -139,6 +180,11 @@
       profile_, chromeos::settings::mojom::kSmartLockSubpagePath);
 }
 
+void EasyUnlockNotificationController::LaunchMultiDeviceSettings() {
+  chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
+      profile_, chromeos::settings::mojom::kMultiDeviceFeaturesSubpagePath);
+}
+
 void EasyUnlockNotificationController::LockScreen() {
   proximity_auth::ScreenlockBridge::Get()->Lock();
 }
@@ -171,7 +217,13 @@
     DCHECK_EQ(1, *button_index);
   }
 
-  notification_controller_->LaunchEasyUnlockSettings();
+  // The kSmartLockSignInRemoved flag removes the easy unlock settings page, so
+  // check flag to determine which route should be launched.
+  if (base::FeatureList::IsEnabled(ash::features::kSmartLockSignInRemoved)) {
+    notification_controller_->LaunchMultiDeviceSettings();
+  } else {
+    notification_controller_->LaunchEasyUnlockSettings();
+  }
 }
 
 }  // namespace ash
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 5ff9c36..b8570d73 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
@@ -28,6 +28,18 @@
 
   virtual ~EasyUnlockNotificationController();
 
+  // TODO(b/227674947): Eventually remove this method after Sign in with Smart
+  // Lock has been removed and enough time has elapsed for users to be notified.
+  // Returns whether the kSignInRemovedNotification should be shown for the
+  // provided profile.
+  static bool ShouldShowSignInRemovedNotification(Profile* profile);
+
+  // TODO(b/227674947): Eventually remove this method after Sign in with Smart
+  // Lock has been removed and enough time has elapsed for users to be notified.
+  // Shows the notification explaining that Sign in with Smart Lock has been
+  // removed.
+  virtual void ShowSignInRemovedNotification();
+
   // Shows the notification when EasyUnlock is synced to a new Chromebook.
   virtual void ShowChromebookAddedNotification();
 
@@ -43,6 +55,7 @@
  protected:
   // Exposed for testing.
   virtual void LaunchEasyUnlockSettings();
+  virtual void LaunchMultiDeviceSettings();
   virtual void LockScreen();
 
  private:
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 86f7dc7c..109f5cc 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
@@ -4,9 +4,14 @@
 
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h"
 
+#include "ash/components/proximity_auth/proximity_auth_pref_names.h"
+#include "ash/constants/ash_features.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_types.h"
@@ -31,6 +36,7 @@
 
   // EasyUnlockNotificationController:
   MOCK_METHOD0(LaunchEasyUnlockSettings, void());
+  MOCK_METHOD0(LaunchMultiDeviceSettings, void());
   MOCK_METHOD0(LockScreen, void());
 };
 
@@ -61,6 +67,70 @@
 };
 
 TEST_F(EasyUnlockNotificationControllerTest,
+       TestShouldShowSignInRemovedNotification) {
+  TestingProfile* test_profile = profile();
+  PrefService* pref_service = test_profile->GetPrefs();
+
+  ASSERT_FALSE(
+      EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+          test_profile));
+
+  // Check returns false when kSmartLockSignInRemoved isn't enabled.
+  pref_service->SetBoolean(
+      proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled, true);
+  pref_service->SetBoolean(prefs::kHasSeenSmartLockSignInRemovedNotification,
+                           false);
+  ASSERT_FALSE(
+      EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+          test_profile));
+
+  // Check returns false when kHasSeenSmartLockSignInRemovedNotification is
+  // true.
+  base::test::ScopedFeatureList feature_list(
+      ash::features::kSmartLockSignInRemoved);
+  pref_service->SetBoolean(prefs::kHasSeenSmartLockSignInRemovedNotification,
+                           true);
+  ASSERT_FALSE(
+      EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+          test_profile));
+
+  // Check returns false when kProximityAuthIsChromeOSLoginEnabled is false.
+  pref_service->SetBoolean(
+      proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled, false);
+  pref_service->SetBoolean(prefs::kHasSeenSmartLockSignInRemovedNotification,
+                           false);
+  ASSERT_FALSE(
+      EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+          test_profile));
+
+  pref_service->SetBoolean(
+      proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled, true);
+  ASSERT_TRUE(
+      EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+          test_profile));
+}
+
+TEST_F(EasyUnlockNotificationControllerTest,
+       TestShowSignInRemovedNotification) {
+  base::test::ScopedFeatureList feature_list(
+      ash::features::kSmartLockSignInRemoved);
+  const char kNotificationId[] = "easyunlock_notification_ids.sign_in_removed";
+
+  notification_controller_->ShowSignInRemovedNotification();
+  absl::optional<message_center::Notification> notification =
+      display_service_->GetNotification(kNotificationId);
+  ASSERT_TRUE(notification);
+
+  // 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);
+}
+
+TEST_F(EasyUnlockNotificationControllerTest,
        TestShowChromebookAddedNotification) {
   const char kNotificationId[] = "easyunlock_notification_ids.chromebook_added";
 
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index b7289dd..b927e00 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -64,6 +64,7 @@
 #include "chrome/browser/ash/login/chrome_restart_request.h"
 #include "chrome/browser/ash/login/demo_mode/demo_session.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h"
+#include "chrome/browser/ash/login/easy_unlock/easy_unlock_notification_controller.h"
 #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h"
 #include "chrome/browser/ash/login/existing_user_controller.h"
 #include "chrome/browser/ash/login/helper.h"
@@ -1934,6 +1935,13 @@
       ->browser_policy_connector_ash()
       ->GetAdbSideloadingAllowanceModePolicyHandler()
       ->ShowAdbSideloadingPolicyChangeNotificationIfNeeded();
+
+  if (EasyUnlockNotificationController::ShouldShowSignInRemovedNotification(
+          profile)) {
+    easy_unlock_notification_controller_ =
+        std::make_unique<EasyUnlockNotificationController>(profile);
+    easy_unlock_notification_controller_->ShowSignInRemovedNotification();
+  }
 }
 
 void UserSessionManager::MaybeLaunchSettings(Profile* profile) {
@@ -2366,6 +2374,7 @@
   token_observers_.clear();
   always_on_vpn_manager_.reset();
   u2f_notification_.reset();
+  easy_unlock_notification_controller_.reset();
   help_app_notification_controller_.reset();
   password_service_voted_.reset();
   password_was_saved_ = false;
diff --git a/chrome/browser/ash/login/session/user_session_manager.h b/chrome/browser/ash/login/session/user_session_manager.h
index 8e79bfe..7ddfd25 100644
--- a/chrome/browser/ash/login/session/user_session_manager.h
+++ b/chrome/browser/ash/login/session/user_session_manager.h
@@ -64,6 +64,7 @@
 class OnboardingUserActivityCounter;
 class StubAuthenticatorBuilder;
 class TokenHandleFetcher;
+class EasyUnlockNotificationController;
 
 namespace test {
 class UserSessionManagerTestApi;
@@ -674,6 +675,11 @@
   std::unique_ptr<HelpAppNotificationController>
       help_app_notification_controller_;
 
+  // TODO(b/227674947): Eventually delete this after Sign in with Smart Lock has
+  // been removed and enough time has elapsed for users to be notified.
+  std::unique_ptr<EasyUnlockNotificationController>
+      easy_unlock_notification_controller_;
+
   bool token_handle_backfill_tried_for_testing_ = false;
 
   std::unique_ptr<OnboardingUserActivityCounter>
diff --git a/chrome/browser/ash/preferences.cc b/chrome/browser/ash/preferences.cc
index a6d5cdb6..d52d4ab 100644
--- a/chrome/browser/ash/preferences.cc
+++ b/chrome/browser/ash/preferences.cc
@@ -398,6 +398,11 @@
 
   registry->RegisterBooleanPref(::prefs::kLanguageImeMenuActivated, false);
 
+  // TODO(b/227674947): Eventually delete this after Sign in with Smart Lock has
+  // been removed and enough time has elapsed for users to be notified.
+  registry->RegisterBooleanPref(
+      ::prefs::kHasSeenSmartLockSignInRemovedNotification, false);
+
   registry->RegisterInt64Pref(::prefs::kHatsLastInteractionTimestamp, 0);
 
   registry->RegisterInt64Pref(::prefs::kHatsSurveyCycleEndTimestamp, 0);
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 79fab20..f78cafa 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1333,6 +1333,8 @@
     "../ash/crostini/ansible/ansible_management_service.h",
     "../ash/crostini/ansible/ansible_management_service_factory.cc",
     "../ash/crostini/ansible/ansible_management_service_factory.h",
+    "../ash/crostini/crostini_capabilities.cc",
+    "../ash/crostini/crostini_capabilities.h",
     "../ash/crostini/crostini_disk.cc",
     "../ash/crostini/crostini_disk.h",
     "../ash/crostini/crostini_engagement_metrics_service.cc",
diff --git a/chrome/browser/content_creation/notes/internal/android/BUILD.gn b/chrome/browser/content_creation/notes/internal/android/BUILD.gn
index 8477540..11ee6d995 100644
--- a/chrome/browser/content_creation/notes/internal/android/BUILD.gn
+++ b/chrome/browser/content_creation/notes/internal/android/BUILD.gn
@@ -46,8 +46,11 @@
     "//components/image_fetcher:java",
     "//components/url_formatter/android:url_formatter_java",
     "//content/public/android:content_java_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//ui/android:ui_java",
     "//url:gurl_java",
   ]
diff --git a/chrome/browser/dev_ui_browser_resources.grd b/chrome/browser/dev_ui_browser_resources.grd
index 9e2bc2a..988ef38 100644
--- a/chrome/browser/dev_ui_browser_resources.grd
+++ b/chrome/browser/dev_ui_browser_resources.grd
@@ -49,7 +49,7 @@
       <include name="IDR_PREDICTORS_RESOURCE_PREFETCH_PREDICTOR_JS" file="${root_gen_dir}\chrome\browser\resources\predictors\resource_prefetch_predictor.js" use_base_dir="false" type="BINDATA" />
       <include name="IDR_MEDIA_SESSION_MOJOM_WEBUI_JS" file="${root_gen_dir}\mojom-webui\services\media_session\public\mojom\media_session.mojom-webui.js" use_base_dir="false" type="BINDATA" />
 
-      <if expr="is_android or is_linux">
+      <if expr="is_android or is_linux or chromeos_ash or chromeos_lacros">
         <include name="IDR_SANDBOX_INTERNALS_HTML" file="resources\sandbox_internals\sandbox_internals.html" preprocess="true" type="BINDATA" />
         <include name="IDR_SANDBOX_INTERNALS_JS" file="resources\sandbox_internals\sandbox_internals.js" preprocess="true" type="BINDATA" />
       </if>
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 46e0b224..6b2b7f6 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -442,10 +442,6 @@
   if (throttle_)
     throttle_->ResumeThrottle();
 
-  if (reattach_complete_callback_) {
-    std::move(reattach_complete_callback_).Run();
-  }
-
   life_stage_ = kClosing;
 
   UpdateBrowserWindow();
@@ -468,6 +464,13 @@
     base::SequencedTaskRunnerHandle::Get()->DeleteSoon(
         FROM_HERE, std::move(owned_main_web_contents_));
   }
+
+  // This should be run after we remove |this| from
+  // |g_devtools_window_instances| as |reattach_complete_callback| may try to
+  // access it.
+  if (reattach_complete_callback_) {
+    std::move(reattach_complete_callback_).Run();
+  }
 }
 
 // static
diff --git a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
index bda9aa2..1815aa6 100644
--- a/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
+++ b/chrome/browser/extensions/api/activity_log_private/activity_log_private_api.cc
@@ -88,13 +88,13 @@
 }
 
 void ActivityLogAPI::OnExtensionActivity(scoped_refptr<Action> activity) {
-  std::unique_ptr<base::ListValue> value(new base::ListValue());
+  base::Value::List value;
   ExtensionActivity activity_arg = activity->ConvertToExtensionActivity();
-  value->Append(activity_arg.ToValue());
+  value.Append(base::Value::FromUniquePtrValue(activity_arg.ToValue()));
   auto event = std::make_unique<Event>(
       events::ACTIVITY_LOG_PRIVATE_ON_EXTENSION_ACTIVITY,
       activity_log_private::OnExtensionActivity::kEventName,
-      std::move(*value).TakeListDeprecated(), browser_context_);
+      base::Value(std::move(value)).TakeListDeprecated(), browser_context_);
   EventRouter::Get(browser_context_)->BroadcastEvent(std::move(event));
 }
 
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
index 74465113..0fde775 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
@@ -510,7 +510,7 @@
     LOG(WARNING) << "PIN request succeeded";
     api::certificate_provider::PinResponseDetails details;
     details.user_input = std::make_unique<std::string>(value);
-    create_results->Append(details.ToValue());
+    create_results->Append(base::Value::FromUniquePtrValue(details.ToValue()));
   } else {
     // TODO(crbug.com/1046860): Remove logging after stabilizing the feature.
     LOG(WARNING) << "PIN request canceled";
diff --git a/chrome/browser/extensions/api/commands/commands.cc b/chrome/browser/extensions/api/commands/commands.cc
index ab97d64..87d7970 100644
--- a/chrome/browser/extensions/api/commands/commands.cc
+++ b/chrome/browser/extensions/api/commands/commands.cc
@@ -13,22 +13,20 @@
 
 namespace {
 
-std::unique_ptr<base::DictionaryValue> CreateCommandValue(
-    const extensions::Command& command,
-    bool active) {
-  std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
-  result->SetStringKey("name", command.command_name());
-  result->SetStringKey("description", command.description());
-  result->SetStringKey("shortcut", active
-                                       ? command.accelerator().GetShortcutText()
-                                       : std::u16string());
+base::Value::Dict CreateCommandValue(const extensions::Command& command,
+                                     bool active) {
+  base::Value::Dict result;
+  result.Set("name", command.command_name());
+  result.Set("description", command.description());
+  result.Set("shortcut", active ? command.accelerator().GetShortcutText()
+                                : std::u16string());
   return result;
 }
 
 }  // namespace
 
 ExtensionFunction::ResponseAction GetAllCommandsFunction::Run() {
-  std::unique_ptr<base::ListValue> command_list(new base::ListValue());
+  base::Value::List command_list;
 
   extensions::CommandService* command_service =
       extensions::CommandService::Get(browser_context());
@@ -41,14 +39,14 @@
   if (command_service->GetExtensionActionCommand(
           extension_->id(), extensions::ActionInfo::TYPE_BROWSER,
           extensions::CommandService::ALL, &browser_action, &active)) {
-    command_list->Append(CreateCommandValue(browser_action, active));
+    command_list.Append(CreateCommandValue(browser_action, active));
   }
 
   extensions::Command page_action;
   if (command_service->GetExtensionActionCommand(
           extension_->id(), extensions::ActionInfo::TYPE_PAGE,
           extensions::CommandService::ALL, &page_action, &active)) {
-    command_list->Append(CreateCommandValue(page_action, active));
+    command_list.Append(CreateCommandValue(page_action, active));
   }
 
   extensions::CommandMap named_commands;
@@ -64,9 +62,8 @@
     ui::Accelerator shortcut_assigned = command.accelerator();
     active = (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN);
 
-    command_list->Append(CreateCommandValue(iter->second, active));
+    command_list.Append(CreateCommandValue(iter->second, active));
   }
 
-  return RespondNow(
-      OneArgument(base::Value::FromUniquePtrValue(std::move(command_list))));
+  return RespondNow(OneArgument(base::Value(std::move(command_list))));
 }
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 84929283..fe0ad38 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -229,9 +229,10 @@
     ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
         ExtensionTabUtil::GetScrubTabBehavior(extension, context_type,
                                               web_contents);
-    args->Append(ExtensionTabUtil::CreateTabObject(
-                     web_contents, scrub_tab_behavior, extension)
-                     ->ToValue());
+    args->Append(base::Value::FromUniquePtrValue(
+        ExtensionTabUtil::CreateTabObject(web_contents, scrub_tab_behavior,
+                                          extension)
+            ->ToValue()));
 
     DispatchEventToExtension(web_contents->GetBrowserContext(),
                              extension_action.extension_id(), histogram_value,
diff --git a/chrome/browser/extensions/api/font_settings/font_settings_api.cc b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
index 6eab088..fa759c2 100644
--- a/chrome/browser/extensions/api/font_settings/font_settings_api.cc
+++ b/chrome/browser/extensions/api/font_settings/font_settings_api.cc
@@ -159,10 +159,10 @@
   }
   std::string font_name = pref->GetValue()->GetString();
   base::ListValue args;
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetStringKey(kFontIdKey, font_name);
-  dict->SetStringKey(kGenericFamilyKey, generic_family);
-  dict->SetStringKey(kScriptKey, script);
+  base::Value::Dict dict;
+  dict.Set(kFontIdKey, font_name);
+  dict.Set(kGenericFamilyKey, generic_family);
+  dict.Set(kScriptKey, script);
   args.Append(std::move(dict));
 
   extensions::preference_helpers::DispatchEventToExtensions(
@@ -181,8 +181,8 @@
   CHECK(pref);
 
   base::ListValue args;
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->Set(key, base::Value::ToUniquePtrValue(pref->GetValue()->Clone()));
+  base::Value::Dict dict;
+  dict.Set(key, pref->GetValue()->Clone());
   args.Append(std::move(dict));
 
   extensions::preference_helpers::DispatchEventToExtensions(
@@ -296,7 +296,7 @@
 
 ExtensionFunction::ResponseValue
 FontSettingsGetFontListFunction::CopyFontsToResult(base::ListValue* fonts) {
-  std::unique_ptr<base::ListValue> result(new base::ListValue());
+  base::Value::List result;
   for (const auto& entry : fonts->GetListDeprecated()) {
     if (!entry.is_list()) {
       NOTREACHED();
@@ -305,27 +305,21 @@
     const base::Value::ConstListView font_list_value =
         entry.GetListDeprecated();
 
-    if (font_list_value.size() < 2 || !font_list_value[0].is_string()) {
+    if (font_list_value.size() < 2 || !font_list_value[0].is_string() ||
+        !font_list_value[1].is_string()) {
       NOTREACHED();
       return Error("");
     }
     const std::string& name = font_list_value[0].GetString();
-
-    if (!font_list_value[1].is_string()) {
-      NOTREACHED();
-      return Error("");
-    }
     const std::string& localized_name = font_list_value[1].GetString();
 
-    std::unique_ptr<base::DictionaryValue> font_name(
-        new base::DictionaryValue());
-    font_name->Set(kFontIdKey, std::make_unique<base::Value>(name));
-    font_name->Set(kDisplayNameKey,
-                   std::make_unique<base::Value>(localized_name));
-    result->Append(std::move(font_name));
+    base::Value::Dict font_name;
+    font_name.Set(kFontIdKey, name);
+    font_name.Set(kDisplayNameKey, localized_name);
+    result.Append(std::move(font_name));
   }
 
-  return OneArgument(base::Value::FromUniquePtrValue(std::move(result)));
+  return OneArgument(base::Value(std::move(result)));
 }
 
 ExtensionFunction::ResponseAction ClearFontPrefExtensionFunction::Run() {
diff --git a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
index b919d537..8554cf30 100644
--- a/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/language_settings_private/language_settings_private_api_unittest.cc
@@ -184,14 +184,14 @@
                                                        profile());
   EXPECT_TRUE(actual) << function->GetError();
 
-  base::ListValue expected;
-  auto expected_status = std::make_unique<base::DictionaryValue>();
-  expected_status->SetStringKey("languageCode", "fr");
-  expected_status->SetBoolKey("isReady", false);
-  expected_status->SetBoolKey("isDownloading", true);
-  expected_status->SetBoolKey("downloadFailed", false);
+  base::Value::List expected;
+  base::Value::Dict expected_status;
+  expected_status.Set("languageCode", "fr");
+  expected_status.Set("isReady", false);
+  expected_status.Set("isDownloading", true);
+  expected_status.Set("downloadFailed", false);
   expected.Append(std::move(expected_status));
-  EXPECT_EQ(expected, *actual);
+  EXPECT_EQ(base::Value(std::move(expected)), *actual);
 }
 
 TEST_F(LanguageSettingsPrivateApiTest, SetLanguageAlwaysTranslateStateTest) {
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
index a2fa171..25c81021 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_delegate.cc
@@ -46,7 +46,7 @@
   for (const auto& it : keys) {
     std::unique_ptr<base::Value> pref = GetPref(it.first);
     if (!pref->is_none())
-      prefs->Append(std::move(pref));
+      prefs->Append(base::Value::FromUniquePtrValue(std::move(pref)));
   }
 
   return std::move(prefs);
diff --git a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
index 16989cb..eee6474 100644
--- a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
+++ b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
@@ -1450,25 +1450,24 @@
 static void UnlimitedSyncStorageTestCallback(ValueStore* sync_storage) {
   // Sync storage should still run out after ~100K; the unlimitedStorage
   // permission can't apply to sync.
-  std::unique_ptr<base::Value> kilobyte = settings_test_util::CreateKilobyte();
+  base::Value kilobyte = settings_test_util::CreateKilobyte();
   for (int i = 0; i < 100; ++i) {
-    sync_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i), *kilobyte);
+    sync_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i), kilobyte);
   }
 
-  EXPECT_FALSE(sync_storage->Set(ValueStore::DEFAULTS, "WillError", *kilobyte)
+  EXPECT_FALSE(sync_storage->Set(ValueStore::DEFAULTS, "WillError", kilobyte)
                    .status()
                    .ok());
 }
 
 static void UnlimitedLocalStorageTestCallback(ValueStore* local_storage) {
   // Local storage should never run out.
-  std::unique_ptr<base::Value> megabyte = settings_test_util::CreateMegabyte();
+  base::Value megabyte = settings_test_util::CreateMegabyte();
   for (int i = 0; i < 7; ++i) {
-    local_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i),
-                       *megabyte);
+    local_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i), megabyte);
   }
 
-  EXPECT_TRUE(local_storage->Set(ValueStore::DEFAULTS, "WontError", *megabyte)
+  EXPECT_TRUE(local_storage->Set(ValueStore::DEFAULTS, "WontError", megabyte)
                   .status()
                   .ok());
 }
diff --git a/chrome/browser/extensions/api/terminal/terminal_private_browsertest.cc b/chrome/browser/extensions/api/terminal/terminal_private_browsertest.cc
index 188dd05..979c64d 100644
--- a/chrome/browser/extensions/api/terminal/terminal_private_browsertest.cc
+++ b/chrome/browser/extensions/api/terminal/terminal_private_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include <memory>
 
+#include "chrome/browser/ash/crostini/crostini_browser_test_util.h"
 #include "chrome/browser/ash/crostini/fake_crostini_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/system_features_disable_list_policy_handler.h"
@@ -20,14 +21,15 @@
 
 namespace extensions {
 
-class TerminalPrivateBrowserTest : public InProcessBrowserTest {
+class TerminalPrivateBrowserTest : public CrostiniBrowserTestBase {
  public:
   TerminalPrivateBrowserTest(const TerminalPrivateBrowserTest&) = delete;
   TerminalPrivateBrowserTest& operator=(const TerminalPrivateBrowserTest&) =
       delete;
 
  protected:
-  TerminalPrivateBrowserTest() = default;
+  TerminalPrivateBrowserTest()
+      : CrostiniBrowserTestBase(/*register_termina=*/false) {}
 
   void ExpectJsResult(const std::string& script, const std::string& expected) {
     content::WebContents* web_contents =
@@ -50,12 +52,11 @@
     })}))";
 
   // 'vmshell not allowed' when crostini is not allowed.
-  crostini::FakeCrostiniFeatures crostini_features;
-  crostini_features.set_could_be_allowed(true);
-  crostini_features.set_is_allowed_now(false);
+  fake_crostini_features_.set_could_be_allowed(true);
+  fake_crostini_features_.set_is_allowed_now(false);
   ExpectJsResult(script, "vmshell not allowed");
 
-  crostini_features.set_is_allowed_now(true);
+  fake_crostini_features_.set_is_allowed_now(true);
   ExpectJsResult(script, "success");
 
   // openTerminalProcess not defined.
diff --git a/chrome/browser/extensions/api/top_sites/top_sites_api.cc b/chrome/browser/extensions/api/top_sites/top_sites_api.cc
index 0f180bf..b38d307 100644
--- a/chrome/browser/extensions/api/top_sites/top_sites_api.cc
+++ b/chrome/browser/extensions/api/top_sites/top_sites_api.cc
@@ -37,22 +37,21 @@
 
 void TopSitesGetFunction::OnMostVisitedURLsAvailable(
     const history::MostVisitedURLList& data) {
-  std::unique_ptr<base::ListValue> pages_value(new base::ListValue);
-  for (size_t i = 0; i < data.size(); i++) {
-    const history::MostVisitedURL& url = data[i];
+  base::Value::List pages_value;
+  for (const auto& url : data) {
     if (!url.url.is_empty()) {
-      std::unique_ptr<base::DictionaryValue> page_value(
-          new base::DictionaryValue());
-      page_value->SetStringKey("url", url.url.spec());
-      if (url.title.empty())
-        page_value->SetStringKey("title", url.url.spec());
-      else
-        page_value->SetStringKey("title", url.title);
-      pages_value->Append(std::move(page_value));
+      base::Value::Dict page_value;
+      page_value.Set("url", url.url.spec());
+      if (url.title.empty()) {
+        page_value.Set("title", url.url.spec());
+      } else {
+        page_value.Set("title", url.title);
+      }
+      pages_value.Append(std::move(page_value));
     }
   }
 
-  Respond(OneArgument(base::Value::FromUniquePtrValue(std::move(pages_value))));
+  Respond(OneArgument(base::Value(std::move(pages_value))));
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
index 7af2e37f..f34056f 100644
--- a/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
+++ b/chrome/browser/extensions/api/virtual_keyboard_private/chrome_virtual_keyboard_delegate.cc
@@ -608,7 +608,7 @@
     return;
 
   auto event_args = std::make_unique<base::ListValue>();
-  event_args->Append(std::move(settings));
+  event_args->Append(base::Value::FromUniquePtrValue(std::move(settings)));
 
   auto event = std::make_unique<extensions::Event>(
       extensions::events::VIRTUAL_KEYBOARD_PRIVATE_ON_KEYBOARD_CONFIG_CHANGED,
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
index 3a456e0..86a4e3e 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_api_helpers.cc
@@ -112,33 +112,30 @@
       navigation_handle->GetRenderFrameHost();
   ui::PageTransition transition_type = navigation_handle->GetPageTransition();
 
-  std::unique_ptr<base::ListValue> args(new base::ListValue());
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetIntKey(web_navigation_api_constants::kTabIdKey,
-                  ExtensionTabUtil::GetTabId(web_contents));
-  dict->SetStringKey(web_navigation_api_constants::kUrlKey, url.spec());
-  dict->SetIntKey(web_navigation_api_constants::kProcessIdKey,
-                  frame_host->GetProcess()->GetID());
-  dict->SetIntKey(web_navigation_api_constants::kFrameIdKey,
-                  ExtensionApiFrameIdMap::GetFrameId(frame_host));
-  dict->SetIntKey(web_navigation_api_constants::kParentFrameIdKey,
-                  ExtensionApiFrameIdMap::GetParentFrameId(frame_host));
-  dict->SetStringKey(
-      web_navigation_api_constants::kDocumentIdKey,
-      ExtensionApiFrameIdMap::GetDocumentId(frame_host).ToString());
+  base::Value::List args;
+  base::Value::Dict dict;
+  dict.Set(web_navigation_api_constants::kTabIdKey,
+           ExtensionTabUtil::GetTabId(web_contents));
+  dict.Set(web_navigation_api_constants::kUrlKey, url.spec());
+  dict.Set(web_navigation_api_constants::kProcessIdKey,
+           frame_host->GetProcess()->GetID());
+  dict.Set(web_navigation_api_constants::kFrameIdKey,
+           ExtensionApiFrameIdMap::GetFrameId(frame_host));
+  dict.Set(web_navigation_api_constants::kParentFrameIdKey,
+           ExtensionApiFrameIdMap::GetParentFrameId(frame_host));
+  dict.Set(web_navigation_api_constants::kDocumentIdKey,
+           ExtensionApiFrameIdMap::GetDocumentId(frame_host).ToString());
   // Only set the parentDocumentId value if we have a parent.
   if (content::RenderFrameHost* parent_frame_host =
           frame_host->GetParentOrOuterDocument()) {
-    dict->SetStringKey(
+    dict.Set(
         web_navigation_api_constants::kParentDocumentIdKey,
         ExtensionApiFrameIdMap::GetDocumentId(parent_frame_host).ToString());
   }
-  dict->SetStringKey(
-      web_navigation_api_constants::kFrameTypeKey,
-      ToString(ExtensionApiFrameIdMap::GetFrameType(frame_host)));
-  dict->SetStringKey(
-      web_navigation_api_constants::kDocumentLifecycleKey,
-      ToString(ExtensionApiFrameIdMap::GetDocumentLifecycle(frame_host)));
+  dict.Set(web_navigation_api_constants::kFrameTypeKey,
+           ToString(ExtensionApiFrameIdMap::GetFrameType(frame_host)));
+  dict.Set(web_navigation_api_constants::kDocumentLifecycleKey,
+           ToString(ExtensionApiFrameIdMap::GetDocumentLifecycle(frame_host)));
 
   if (navigation_handle->WasServerRedirect()) {
     transition_type = ui::PageTransitionFromInt(
@@ -152,8 +149,8 @@
   if (ui::PageTransitionCoreTypeIs(transition_type,
                                    ui::PAGE_TRANSITION_AUTO_TOPLEVEL))
     transition_type_string = "start_page";
-  dict->SetStringKey(web_navigation_api_constants::kTransitionTypeKey,
-                     transition_type_string);
+  dict.Set(web_navigation_api_constants::kTransitionTypeKey,
+           transition_type_string);
   base::Value qualifiers(base::Value::Type::LIST);
   if (transition_type & ui::PAGE_TRANSITION_CLIENT_REDIRECT)
     qualifiers.Append("client_redirect");
@@ -163,17 +160,17 @@
     qualifiers.Append("forward_back");
   if (transition_type & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)
     qualifiers.Append("from_address_bar");
-  dict->SetKey(web_navigation_api_constants::kTransitionQualifiersKey,
-               std::move(qualifiers));
-  dict->SetDoubleKey(web_navigation_api_constants::kTimeStampKey,
-                     MilliSecondsFromTime(base::Time::Now()));
-  args->Append(std::move(dict));
+  dict.Set(web_navigation_api_constants::kTransitionQualifiersKey,
+           std::move(qualifiers));
+  dict.Set(web_navigation_api_constants::kTimeStampKey,
+           MilliSecondsFromTime(base::Time::Now()));
+  args.Append(std::move(dict));
 
   content::BrowserContext* browser_context =
       navigation_handle->GetWebContents()->GetBrowserContext();
-  auto event = std::make_unique<Event>(histogram_value, event_name,
-                                       std::move(*args).TakeListDeprecated(),
-                                       browser_context);
+  auto event = std::make_unique<Event>(
+      histogram_value, event_name,
+      base::Value(std::move(args)).TakeListDeprecated(), browser_context);
   DispatchEvent(browser_context, std::move(event), url);
 }
 
diff --git a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
index 16286fe6..5fd8942 100644
--- a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
+++ b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc
@@ -117,10 +117,9 @@
 
  protected:
   void AppendTabIdToRequestInfo(base::ListValue* params, int tab_id) {
-    std::unique_ptr<base::DictionaryValue> request_info(
-        new base::DictionaryValue());
-    request_info->SetIntKey("tabId", tab_id);
-    params->Append(std::move(request_info));
+    base::Value::Dict request_info;
+    request_info.Set("tabId", tab_id);
+    params->Append(base::Value(std::move(request_info)));
   }
 
   std::unique_ptr<base::Value> InvokeGetSinks() {
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c2e850d..1d67de5 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -565,16 +565,16 @@
     "expiry_milestone": 110
   },
   {
+    "name": "back-gesture-refactor-android",
+    "owners": ["lazzzis@google.com", "jinsukkim", "twellington"],
+    "expiry_milestone": 109
+  },
+  {
     "name": "biometric-reauth-password-filling",
     "owners": [ "ioanap" ],
     "expiry_milestone": 104
   },
   {
-    "name": "block-external-form-redirects-no-gesture",
-    "owners": [ "jochen", "tedchoc" ],
-    "expiry_milestone": 89
-  },
-  {
     "name": "block-insecure-private-network-requests",
     "owners": [ "titouan", "chrome-security-owp-team@google.com" ],
     "expiry_milestone": 107
@@ -3316,8 +3316,8 @@
   },
   {
     "name": "font-access",
-    "owners": [ "cmp", "pwnall" ],
-    "expiry_milestone": 102
+    "owners": [ "dslee" ],
+    "expiry_milestone": 105
   },
   {
     "name": "force-color-profile",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b6446bd..96bdafc 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2977,6 +2977,10 @@
     "When enabled, app menu should show 'Mobile site' when showing desktop "
     "site, instead of showing 'Desktop Site' with checkbox";
 
+const char kBackGestureRefactorAndroidName[] = "Back Gesture Refactor";
+const char kBackGestureRefactorAndroidDescription[] =
+    "Enable Back Gesture Refactor.";
+
 const char kCCTIncognitoName[] = "Chrome Custom Tabs Incognito mode";
 const char kCCTIncognitoDescription[] =
     "Enables incognito mode for Chrome Custom Tabs, on Android.";
@@ -3148,13 +3152,6 @@
 const char kEnableAutofillRefreshStyleDescription[] =
     "Enable modernized style for Autofill on Android";
 
-const char kChromeManagementPageAndroidName[] =
-    "Enable chrome://management page on Android";
-const char kChromeManagementPageAndroidDescription[] =
-    "Enable chrome://management page on Android, which aims to inform the user "
-    "if their browser is managed by their employer along with other useful "
-    "information.";
-
 const char kEnableCommandLineOnNonRootedName[] =
     "Enable command line on non-rooted devices";
 const char kEnableCommandLineOnNoRootedDescription[] =
@@ -3203,12 +3200,6 @@
 const char kInstantStartDescription[] =
     "Show start surface before native library is loaded.";
 
-const char kIntentBlockExternalFormRedirectsNoGestureName[] =
-    "Block intents from form submissions without user gesture";
-const char kIntentBlockExternalFormRedirectsNoGestureDescription[] =
-    "Require a user gesture that triggered a form submission in order to "
-    "allow for redirecting to an external intent.";
-
 const char kInterestFeedV2Name[] = "Interest Feed v2";
 const char kInterestFeedV2Description[] =
     "Show content suggestions on the New Tab Page and Start Surface using the "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7426a92..dd3f32f8 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1678,6 +1678,9 @@
 extern const char kAppMenuMobileSiteOptionName[];
 extern const char kAppMenuMobileSiteOptionDescription[];
 
+extern const char kBackGestureRefactorAndroidName[];
+extern const char kBackGestureRefactorAndroidDescription[];
+
 extern const char kCCTIncognitoName[];
 extern const char kCCTIncognitoDescription[];
 
@@ -1768,9 +1771,6 @@
 extern const char kEnableAutofillRefreshStyleName[];
 extern const char kEnableAutofillRefreshStyleDescription[];
 
-extern const char kChromeManagementPageAndroidName[];
-extern const char kChromeManagementPageAndroidDescription[];
-
 extern const char kEnableCommandLineOnNonRootedName[];
 extern const char kEnableCommandLineOnNoRootedDescription[];
 
@@ -1805,9 +1805,6 @@
 extern const char kInstantStartName[];
 extern const char kInstantStartDescription[];
 
-extern const char kIntentBlockExternalFormRedirectsNoGestureName[];
-extern const char kIntentBlockExternalFormRedirectsNoGestureDescription[];
-
 extern const char kInterestFeedV2Name[];
 extern const char kInterestFeedV2Description[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 64502580..275401f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -245,6 +245,7 @@
     &kPageAnnotationsService,
     &kBookmarksImprovedSaveFlow,
     &kBookmarksRefresh,
+    &kBackGestureRefactorAndroid,
     &kProbabilisticCryptidRenderer,
     &kReachedCodeProfiler,
     &kImproveReaderModePrompt,
@@ -352,7 +353,6 @@
     &password_manager::features::kUnifiedPasswordManagerAndroid,
     &password_manager::features::kPasswordEditDialogWithDetails,
     &performance_hints::features::kContextMenuPerformanceInfo,
-    &policy::features::kChromeManagementPageAndroid,
     &privacy_sandbox::kPrivacySandboxSettings3,
     &query_tiles::features::kQueryTiles,
     &query_tiles::features::kQueryTilesInNTP,
@@ -681,6 +681,9 @@
 const base::Feature kBookmarksRefresh{"BookmarksRefresh",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kBackGestureRefactorAndroid{
+    "BackGestureRefactorAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kProbabilisticCryptidRenderer{
     "ProbabilisticCryptidRenderer", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index db401d1d..69e86b0 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -110,6 +110,7 @@
 extern const base::Feature kPageAnnotationsService;
 extern const base::Feature kBookmarksImprovedSaveFlow;
 extern const base::Feature kBookmarksRefresh;
+extern const base::Feature kBackGestureRefactorAndroid;
 extern const base::Feature kProbabilisticCryptidRenderer;
 extern const base::Feature kReachedCodeProfiler;
 extern const base::Feature kReengagementNotification;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 64d5cc5..0e3ce1f4 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -233,6 +233,7 @@
     public static final String APP_LAUNCHPAD = "AppLaunchpad";
     public static final String APP_MENU_MOBILE_SITE_OPTION = "AppMenuMobileSiteOption";
     public static final String BACKGROUND_THREAD_POOL = "BackgroundThreadPool";
+    public static final String BACK_GESTURE_REFACTOR = "BackGestureRefactorAndroid";
     public static final String BIOMETRIC_TOUCH_TO_FILL = "BiometricTouchToFill";
     public static final String BULK_TAB_RESTORE = "BulkTabRestore";
     public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList";
@@ -256,7 +257,6 @@
             "CCTReportParallelRequestStatus";
     public static final String CLOSE_TAB_SUGGESTIONS = "CloseTabSuggestions";
     public static final String DONT_AUTO_HIDE_BROWSER_CONTROLS = "DontAutoHideBrowserControls";
-    public static final String CHROME_MANAGEMENT_PAGE = "ChromeManagementPageAndroid";
     public static final String CHROME_NEW_DOWNLOAD_TAB = "ChromeNewDownloadTab";
     public static final String CHROME_SHARE_LONG_SCREENSHOT = "ChromeShareLongScreenshot";
     public static final String CHROME_SHARING_HUB = "ChromeSharingHub";
diff --git a/chrome/browser/image_editor/public/BUILD.gn b/chrome/browser/image_editor/public/BUILD.gn
index 6fb3007..1b45913 100644
--- a/chrome/browser/image_editor/public/BUILD.gn
+++ b/chrome/browser/image_editor/public/BUILD.gn
@@ -10,7 +10,6 @@
   deps = [
     "//base:base_java",
     "//chrome/browser/share:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//ui/android:ui_no_recycler_view_java",
   ]
 }
diff --git a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
index 06c05f5..409d86b 100644
--- a/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
+++ b/chrome/browser/media/webrtc/system_media_capture_permissions_mac.mm
@@ -10,12 +10,12 @@
 #include "base/callback_helpers.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
+#include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/no_destructor.h"
-#include "base/notreached.h"
+#include "base/task/post_task.h"
 #include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
 #include "chrome/browser/media/webrtc/media_authorization_wrapper_mac.h"
 #include "chrome/common/chrome_features.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -60,14 +60,14 @@
                                  const base::TaskTraits& traits) override {
     if (@available(macOS 10.14, *)) {
       __block base::OnceClosure block_callback = std::move(callback);
-      [AVCaptureDevice
-          requestAccessForMediaType:media_type
-                  completionHandler:^(BOOL granted) {
-                    base::ThreadPool::PostTask(FROM_HERE, traits,
-                                               std::move(block_callback));
-                  }];
+      [AVCaptureDevice requestAccessForMediaType:media_type
+                               completionHandler:^(BOOL granted) {
+                                 base::PostTask(FROM_HERE, traits,
+                                                std::move(block_callback));
+                               }];
     } else {
       NOTREACHED();
+      base::PostTask(FROM_HERE, traits, std::move(callback));
     }
   }
 };
@@ -120,7 +120,7 @@
                                          base::OnceClosure callback,
                                          const base::TaskTraits& traits) {
   if (UsingFakeMediaDevices()) {
-    base::ThreadPool::PostTask(FROM_HERE, traits, std::move(callback));
+    base::PostTask(FROM_HERE, traits, std::move(callback));
     return;
   }
 
@@ -128,10 +128,11 @@
     GetMediaAuthorizationWrapper().RequestAccessForMediaType(
         media_type, std::move(callback), traits);
   } else {
+    NOTREACHED();
     // Should never happen since for pre-10.14 system permissions don't exist
     // and checking them in CheckSystemAudioCapturePermission() will always
     // return allowed, and this function should not be called.
-    NOTREACHED();
+    base::PostTask(FROM_HERE, traits, std::move(callback));
   }
 }
 
diff --git a/chrome/browser/metrics/power/power_metrics.cc b/chrome/browser/metrics/power/power_metrics.cc
new file mode 100644
index 0000000..73e67ef
--- /dev/null
+++ b/chrome/browser/metrics/power/power_metrics.cc
@@ -0,0 +1,215 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/power/power_metrics.h"
+
+#include <string>
+
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+#include "chrome/browser/performance_monitor/process_metrics_recorder_util.h"
+#include "chrome/browser/performance_monitor/process_monitor.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
+#endif
+
+namespace {
+
+constexpr const char* kBatteryDischargeRateHistogramName =
+    "Power.BatteryDischargeRate2";
+constexpr const char* kBatteryDischargeModeHistogramName =
+    "Power.BatteryDischargeMode";
+
+#if BUILDFLAG(IS_MAC)
+// Reports `proportion` of a time used to a histogram in permyriad (1/100 %).
+// `proportion` is 0.5 if half a CPU core or half total GPU time is used. It can
+// be above 1.0 if more than 1 CPU core is used. CPU and GPU usage is often
+// below 1% so it's useful to report with 1/10000 granularity (otherwise most
+// samples end up in the same bucket).
+void UsageTimeHistogram(const std::string& histogram_name,
+                        double proportion,
+                        int max_proportion) {
+  // Multiplicator to convert `proportion` to permyriad (1/100 %).
+  // For example, 1.0 * kScaleFactor = 10000 1/100 % = 100 %.
+  constexpr int kScaleFactor = 100 * 100;
+
+  base::UmaHistogramCustomCounts(
+      histogram_name, std::lroundl(proportion * kScaleFactor),
+      /* min=*/1, /* exclusive_max=*/max_proportion * kScaleFactor,
+      /* buckets=*/50);
+}
+
+// Max proportion for CPU time histograms. This used to be 64 but was reduced to
+// 2 because data shows that less than 0.2% of samples are above that.
+constexpr int kMaxCPUProportion = 2;
+
+// Max proportion for GPU time histograms. It's not possible to use more than
+// 100% of total GPU time.
+constexpr int kMaxGPUProportion = 1;
+#endif  // BUILDFLAG(IS_MAC)
+
+}  // namespace
+
+void ReportAggregatedProcessMetricsHistograms(
+    const performance_monitor::ProcessMonitor::Metrics&
+        aggregated_process_metrics,
+    const std::vector<const char*>& suffixes) {
+  for (const char* suffix : suffixes) {
+    std::string complete_suffix = base::StrCat({"Total", suffix});
+    performance_monitor::RecordProcessHistograms(complete_suffix.c_str(),
+                                                 aggregated_process_metrics);
+  }
+}
+
+void ReportBatteryHistograms(base::TimeDelta interval_duration,
+                             BatteryDischarge battery_discharge,
+                             const std::vector<const char*>& suffixes) {
+  // Ratio by which the time elapsed can deviate from
+  // |ProcessMonitor::kGatherInterval| without invalidating this sample.
+  constexpr double kTolerableTimeElapsedRatio = 0.10;
+  constexpr double kTolerablePositiveDrift = (1. + kTolerableTimeElapsedRatio);
+  constexpr double kTolerableNegativeDrift = (1. - kTolerableTimeElapsedRatio);
+
+  if (battery_discharge.mode == BatteryDischargeMode::kDischarging &&
+      interval_duration > performance_monitor::ProcessMonitor::kGatherInterval *
+                              kTolerablePositiveDrift) {
+    // Too much time passed since the last record. Either the task took
+    // too long to get executed or system sleep took place.
+    battery_discharge.mode = BatteryDischargeMode::kInvalidInterval;
+  }
+
+  if (battery_discharge.mode == BatteryDischargeMode::kDischarging &&
+      interval_duration < performance_monitor::ProcessMonitor::kGatherInterval *
+                              kTolerableNegativeDrift) {
+    // The recording task executed too early after the previous one, possibly
+    // because the previous task took too long to execute.
+    battery_discharge.mode = BatteryDischargeMode::kInvalidInterval;
+  }
+
+  for (const char* suffix : suffixes) {
+    base::UmaHistogramEnumeration(
+        base::StrCat({kBatteryDischargeModeHistogramName, suffix}),
+        battery_discharge.mode);
+
+    if (battery_discharge.mode == BatteryDischargeMode::kDischarging) {
+      DCHECK(battery_discharge.rate.has_value());
+      base::UmaHistogramCounts1000(
+          base::StrCat({kBatteryDischargeRateHistogramName, suffix}),
+          *battery_discharge.rate);
+    }
+  }
+}
+
+#if BUILDFLAG(IS_MAC)
+void ReportShortIntervalHistograms(
+    const char* scenario_suffix,
+    absl::optional<power_metrics::CoalitionResourceUsageRate>
+        coalition_resource_usage_rate) {
+  if (!coalition_resource_usage_rate.has_value())
+    return;
+
+  for (const char* suffix : {"", scenario_suffix}) {
+    UsageTimeHistogram(
+        base::StrCat(
+            {"PerformanceMonitor.ResourceCoalition.CPUTime2_10sec", suffix}),
+        coalition_resource_usage_rate->cpu_time_per_second, kMaxCPUProportion);
+  }
+}
+
+void ReportResourceCoalitionHistograms(
+    const power_metrics::CoalitionResourceUsageRate& rate,
+    const std::vector<const char*>& suffixes) {
+  // Calling this function with an empty suffix list is probably a mistake.
+  DCHECK(!suffixes.empty());
+
+  // TODO(crbug.com/1229884): Review the units and buckets once we have
+  // sufficient data from the field.
+
+  for (const char* scenario_suffix : suffixes) {
+    // Suffixes are expected to be empty or starting by a period.
+    DCHECK(::strlen(scenario_suffix) == 0U || scenario_suffix[0] == '.');
+
+    UsageTimeHistogram(
+        base::StrCat(
+            {"PerformanceMonitor.ResourceCoalition.CPUTime2", scenario_suffix}),
+        rate.cpu_time_per_second, kMaxCPUProportion);
+    UsageTimeHistogram(
+        base::StrCat(
+            {"PerformanceMonitor.ResourceCoalition.GPUTime2", scenario_suffix}),
+        rate.gpu_time_per_second, kMaxGPUProportion);
+
+    // Report the metrics based on a count (e.g. wakeups) with a millievent/sec
+    // granularity. In theory it doesn't make much sense to talk about a
+    // milliwakeups but the wakeup rate should ideally be lower than one per
+    // second in some scenarios and this will provide more granularity.
+    constexpr int kMilliFactor = 1000;
+    auto scale_sample = [](double sample) -> int {
+      // Round the sample to the nearest integer value.
+      return std::roundl(sample * kMilliFactor);
+    };
+    base::UmaHistogramCounts1M(
+        base::StrCat(
+            {"PerformanceMonitor.ResourceCoalition.InterruptWakeupsPerSecond",
+             scenario_suffix}),
+        scale_sample(rate.interrupt_wakeups_per_second));
+    base::UmaHistogramCounts1M(
+        base::StrCat({"PerformanceMonitor.ResourceCoalition."
+                      "PlatformIdleWakeupsPerSecond",
+                      scenario_suffix}),
+        scale_sample(rate.platform_idle_wakeups_per_second));
+    base::UmaHistogramCounts10M(
+        base::StrCat(
+            {"PerformanceMonitor.ResourceCoalition.BytesReadPerSecond2",
+             scenario_suffix}),
+        rate.bytesread_per_second);
+    base::UmaHistogramCounts10M(
+        base::StrCat(
+            {"PerformanceMonitor.ResourceCoalition.BytesWrittenPerSecond2",
+             scenario_suffix}),
+        rate.byteswritten_per_second);
+
+    // EnergyImpact is reported in centi-EI, so scaled up by a factor of 100
+    // for the histogram recording.
+    if (rate.energy_impact_per_second.has_value()) {
+      constexpr double kEnergyImpactScalingFactor = 100.0;
+      base::UmaHistogramCounts100000(
+          base::StrCat({"PerformanceMonitor.ResourceCoalition.EnergyImpact",
+                        scenario_suffix}),
+          std::roundl(rate.energy_impact_per_second.value() *
+                      kEnergyImpactScalingFactor));
+    }
+
+    // As of Feb 2, 2022, the value of `rate->power_nw` is always zero on Intel.
+    // Don't report it to avoid polluting the data.
+    if (base::mac::GetCPUType() == base::mac::CPUType::kArm) {
+      constexpr int kMilliWattPerWatt = 1000;
+      constexpr int kNanoWattPerMilliWatt = 1000 * 1000;
+      // The maximum is 10 watts, which is larger than the 99.99th percentile
+      // as of Feb 2, 2022.
+      base::UmaHistogramCustomCounts(
+          base::StrCat(
+              {"PerformanceMonitor.ResourceCoalition.Power2", scenario_suffix}),
+          std::roundl(rate.power_nw / kNanoWattPerMilliWatt),
+          /* min=*/1, /* exclusive_max=*/10 * kMilliWattPerWatt,
+          /* buckets=*/50);
+    }
+
+    auto record_qos_level = [&](size_t index, const char* qos_suffix) {
+      UsageTimeHistogram(
+          base::StrCat({"PerformanceMonitor.ResourceCoalition.QoSLevel.",
+                        qos_suffix, scenario_suffix}),
+          rate.qos_time_per_second[index], kMaxCPUProportion);
+    };
+
+    record_qos_level(THREAD_QOS_DEFAULT, "Default");
+    record_qos_level(THREAD_QOS_MAINTENANCE, "Maintenance");
+    record_qos_level(THREAD_QOS_BACKGROUND, "Background");
+    record_qos_level(THREAD_QOS_UTILITY, "Utility");
+    record_qos_level(THREAD_QOS_LEGACY, "Legacy");
+    record_qos_level(THREAD_QOS_USER_INITIATED, "UserInitiated");
+    record_qos_level(THREAD_QOS_USER_INTERACTIVE, "UserInteractive");
+  }
+}
+#endif  // BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/metrics/power/power_metrics.h b/chrome/browser/metrics/power/power_metrics.h
new file mode 100644
index 0000000..7b5401c
--- /dev/null
+++ b/chrome/browser/metrics/power/power_metrics.h
@@ -0,0 +1,63 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_METRICS_POWER_POWER_METRICS_H_
+#define CHROME_BROWSER_METRICS_POWER_POWER_METRICS_H_
+
+#include <vector>
+
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "chrome/browser/performance_monitor/process_monitor.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "chrome/browser/metrics/power/coalition_resource_usage_provider_mac.h"
+#include "components/power_metrics/resource_coalition_mac.h"
+#endif
+
+// Report aggregated process metrics to histograms with |suffixes|.
+void ReportAggregatedProcessMetricsHistograms(
+    const performance_monitor::ProcessMonitor::Metrics&
+        aggregated_process_metrics,
+    const std::vector<const char*>& suffixes);
+
+// Any change to this enum should be reflected in the corresponding enums.xml
+// and ukm.xml
+enum class BatteryDischargeMode {
+  kDischarging = 0,
+  kPluggedIn = 1,
+  kStateChanged = 2,
+  kChargeLevelUnavailable = 3,
+  kNoBattery = 4,
+  kBatteryLevelIncreased = 5,
+  kInvalidInterval = 6,
+  kMacFullyCharged = 7,
+  kMaxValue = kMacFullyCharged
+};
+
+struct BatteryDischarge {
+  BatteryDischargeMode mode;
+  // Discharge rate in 1/10000 of full capacity per minute.
+  absl::optional<int64_t> rate;
+};
+
+// Report battery metrics to histograms with |suffixes|.
+void ReportBatteryHistograms(base::TimeDelta interval_duration,
+                             BatteryDischarge battery_discharge,
+                             const std::vector<const char*>& suffixes);
+
+#if BUILDFLAG(IS_MAC)
+void ReportShortIntervalHistograms(
+    const char* scenario_suffix,
+    absl::optional<power_metrics::CoalitionResourceUsageRate>
+        coalition_resource_usage_rate);
+
+// Report resource coalition metrics to histograms with |suffixes|.
+void ReportResourceCoalitionHistograms(
+    const power_metrics::CoalitionResourceUsageRate& rate,
+    const std::vector<const char*>& suffixes);
+#endif  // BUILDFLAG(IS_MAC)
+
+#endif  // CHROME_BROWSER_METRICS_POWER_POWER_METRICS_H_
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.cc b/chrome/browser/metrics/power/power_metrics_reporter.cc
index d5fc70c..65a9a39b 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter.cc
@@ -22,47 +22,13 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 #if BUILDFLAG(IS_MAC)
-#include "base/mac/mac_util.h"
 #include "components/power_metrics/resource_coalition_mac.h"
 #endif  // BUILDFLAG(IS_MAC)
 
 namespace {
-
-constexpr const char* kBatteryDischargeRateHistogramName =
-    "Power.BatteryDischargeRate2";
-constexpr const char* kBatteryDischargeModeHistogramName =
-    "Power.BatteryDischargeMode";
 constexpr const char* kBatterySamplingDelayHistogramName =
     "Power.BatterySamplingDelay";
 
-#if BUILDFLAG(IS_MAC)
-// Reports `proportion` of a time used to a histogram in permyriad (1/100 %).
-// `proportion` is 0.5 if half a CPU core or half total GPU time is used. It can
-// be above 1.0 if more than 1 CPU core is used. CPU and GPU usage is often
-// below 1% so it's useful to report with 1/10000 granularity (otherwise most
-// samples end up in the same bucket).
-void UsageTimeHistogram(const std::string& histogram_name,
-                        double proportion,
-                        int max_proportion) {
-  // Multiplicator to convert `proportion` to permyriad (1/100 %).
-  // For example, 1.0 * kScaleFactor = 10000 1/100 % = 100 %.
-  constexpr int kScaleFactor = 100 * 100;
-
-  base::UmaHistogramCustomCounts(
-      histogram_name, std::lroundl(proportion * kScaleFactor),
-      /* min=*/1, /* exclusive_max=*/max_proportion * kScaleFactor,
-      /* buckets=*/50);
-}
-
-// Max proportion for CPU time histograms. This used to be 64 but was reduced to
-// 2 because data shows that less than 0.2% of samples are above that.
-constexpr int kMaxCPUProportion = 2;
-
-// Max proportion for GPU time histograms. It's not possible to use more than
-// 100% of total GPU time.
-constexpr int kMaxGPUProportion = 1;
-#endif  // BUILDFLAG(IS_MAC)
-
 // Calculates the UKM bucket |value| falls in and returns it. This uses an
 // exponential bucketing approach with an exponent base of 1.3, resulting in
 // 17 buckets for an interval of 120 seconds.
@@ -195,21 +161,6 @@
 }
 
 #if BUILDFLAG(IS_MAC)
-// static
-void PowerMetricsReporter::ReportShortIntervalHistograms(
-    const char* scenario_suffix,
-    absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate) {
-  if (!coalition_resource_usage_rate.has_value())
-    return;
-
-  for (const char* suffix : {"", scenario_suffix}) {
-    UsageTimeHistogram(
-        base::StrCat(
-            {"PerformanceMonitor.ResourceCoalition.CPUTime2_10sec", suffix}),
-        coalition_resource_usage_rate->cpu_time_per_second, kMaxCPUProportion);
-  }
-}
-
 void PowerMetricsReporter::MaybeEmitHighCPUTraceEvent(
     const ScenarioParams& short_interval_scenario_params,
     absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate) {
@@ -231,47 +182,6 @@
 }
 #endif  // BUILDFLAG(IS_MAC)
 
-// static
-void PowerMetricsReporter::ReportBatteryHistograms(
-    base::TimeDelta interval_duration,
-    BatteryDischarge battery_discharge,
-    const std::vector<const char*>& suffixes) {
-  // Ratio by which the time elapsed can deviate from
-  // |ProcessMonitor::kGatherInterval| without invalidating this sample.
-  constexpr double kTolerableTimeElapsedRatio = 0.10;
-  constexpr double kTolerablePositiveDrift = (1. + kTolerableTimeElapsedRatio);
-  constexpr double kTolerableNegativeDrift = (1. - kTolerableTimeElapsedRatio);
-
-  if (battery_discharge.mode == BatteryDischargeMode::kDischarging &&
-      interval_duration >
-          ProcessMonitor::kGatherInterval * kTolerablePositiveDrift) {
-    // Too much time passed since the last record. Either the task took
-    // too long to get executed or system sleep took place.
-    battery_discharge.mode = BatteryDischargeMode::kInvalidInterval;
-  }
-
-  if (battery_discharge.mode == BatteryDischargeMode::kDischarging &&
-      interval_duration <
-          ProcessMonitor::kGatherInterval * kTolerableNegativeDrift) {
-    // The recording task executed too early after the previous one, possibly
-    // because the previous task took too long to execute.
-    battery_discharge.mode = BatteryDischargeMode::kInvalidInterval;
-  }
-
-  for (const char* suffix : suffixes) {
-    base::UmaHistogramEnumeration(
-        base::StrCat({kBatteryDischargeModeHistogramName, suffix}),
-        battery_discharge.mode);
-
-    if (battery_discharge.mode == BatteryDischargeMode::kDischarging) {
-      DCHECK(battery_discharge.rate.has_value());
-      base::UmaHistogramCounts1000(
-          base::StrCat({kBatteryDischargeRateHistogramName, suffix}),
-          *battery_discharge.rate);
-    }
-  }
-}
-
 void PowerMetricsReporter::OnFirstBatteryStateSampled(
     const BatteryLevelProvider::BatteryState& battery_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -343,116 +253,6 @@
     std::move(on_battery_sampled_for_testing_).Run();
 }
 
-// static
-void PowerMetricsReporter::ReportAggregatedProcessMetricsHistograms(
-    const ProcessMonitor::Metrics& aggregated_process_metrics,
-    const std::vector<const char*>& suffixes) {
-  for (const char* suffix : suffixes) {
-    std::string complete_suffix = base::StrCat({"Total", suffix});
-    performance_monitor::RecordProcessHistograms(complete_suffix.c_str(),
-                                                 aggregated_process_metrics);
-  }
-}
-
-#if BUILDFLAG(IS_MAC)
-// static
-void PowerMetricsReporter::ReportResourceCoalitionHistograms(
-    const power_metrics::CoalitionResourceUsageRate& rate,
-    const std::vector<const char*>& suffixes) {
-  // Calling this function with an empty suffix list is probably a mistake.
-  DCHECK(!suffixes.empty());
-
-  // TODO(crbug.com/1229884): Review the units and buckets once we have
-  // sufficient data from the field.
-
-  for (const char* scenario_suffix : suffixes) {
-    // Suffixes are expected to be empty or starting by a period.
-    DCHECK(::strlen(scenario_suffix) == 0U || scenario_suffix[0] == '.');
-
-    UsageTimeHistogram(
-        base::StrCat(
-            {"PerformanceMonitor.ResourceCoalition.CPUTime2", scenario_suffix}),
-        rate.cpu_time_per_second, kMaxCPUProportion);
-    UsageTimeHistogram(
-        base::StrCat(
-            {"PerformanceMonitor.ResourceCoalition.GPUTime2", scenario_suffix}),
-        rate.gpu_time_per_second, kMaxGPUProportion);
-
-    // Report the metrics based on a count (e.g. wakeups) with a millievent/sec
-    // granularity. In theory it doesn't make much sense to talk about a
-    // milliwakeups but the wakeup rate should ideally be lower than one per
-    // second in some scenarios and this will provide more granularity.
-    constexpr int kMilliFactor = 1000;
-    auto scale_sample = [](double sample) -> int {
-      // Round the sample to the nearest integer value.
-      return std::roundl(sample * kMilliFactor);
-    };
-    base::UmaHistogramCounts1M(
-        base::StrCat(
-            {"PerformanceMonitor.ResourceCoalition.InterruptWakeupsPerSecond",
-             scenario_suffix}),
-        scale_sample(rate.interrupt_wakeups_per_second));
-    base::UmaHistogramCounts1M(
-        base::StrCat({"PerformanceMonitor.ResourceCoalition."
-                      "PlatformIdleWakeupsPerSecond",
-                      scenario_suffix}),
-        scale_sample(rate.platform_idle_wakeups_per_second));
-    base::UmaHistogramCounts10M(
-        base::StrCat(
-            {"PerformanceMonitor.ResourceCoalition.BytesReadPerSecond2",
-             scenario_suffix}),
-        rate.bytesread_per_second);
-    base::UmaHistogramCounts10M(
-        base::StrCat(
-            {"PerformanceMonitor.ResourceCoalition.BytesWrittenPerSecond2",
-             scenario_suffix}),
-        rate.byteswritten_per_second);
-
-    // EnergyImpact is reported in centi-EI, so scaled up by a factor of 100
-    // for the histogram recording.
-    if (rate.energy_impact_per_second.has_value()) {
-      constexpr double kEnergyImpactScalingFactor = 100.0;
-      base::UmaHistogramCounts100000(
-          base::StrCat({"PerformanceMonitor.ResourceCoalition.EnergyImpact",
-                        scenario_suffix}),
-          std::roundl(rate.energy_impact_per_second.value() *
-                      kEnergyImpactScalingFactor));
-    }
-
-    // As of Feb 2, 2022, the value of `rate->power_nw` is always zero on Intel.
-    // Don't report it to avoid polluting the data.
-    if (base::mac::GetCPUType() == base::mac::CPUType::kArm) {
-      constexpr int kMilliWattPerWatt = 1000;
-      constexpr int kNanoWattPerMilliWatt = 1000 * 1000;
-      // The maximum is 10 watts, which is larger than the 99.99th percentile
-      // as of Feb 2, 2022.
-      base::UmaHistogramCustomCounts(
-          base::StrCat(
-              {"PerformanceMonitor.ResourceCoalition.Power2", scenario_suffix}),
-          std::roundl(rate.power_nw / kNanoWattPerMilliWatt),
-          /* min=*/1, /* exclusive_max=*/10 * kMilliWattPerWatt,
-          /* buckets=*/50);
-    }
-
-    auto record_qos_level = [&](size_t index, const char* qos_suffix) {
-      UsageTimeHistogram(
-          base::StrCat({"PerformanceMonitor.ResourceCoalition.QoSLevel.",
-                        qos_suffix, scenario_suffix}),
-          rate.qos_time_per_second[index], kMaxCPUProportion);
-    };
-
-    record_qos_level(THREAD_QOS_DEFAULT, "Default");
-    record_qos_level(THREAD_QOS_MAINTENANCE, "Maintenance");
-    record_qos_level(THREAD_QOS_BACKGROUND, "Background");
-    record_qos_level(THREAD_QOS_UTILITY, "Utility");
-    record_qos_level(THREAD_QOS_LEGACY, "Legacy");
-    record_qos_level(THREAD_QOS_USER_INITIATED, "UserInitiated");
-    record_qos_level(THREAD_QOS_USER_INTERACTIVE, "UserInteractive");
-  }
-}
-#endif  // BUILDFLAG(IS_MAC)
-
-// static
 void PowerMetricsReporter::ReportUKMs(
     const UsageScenarioDataStore::IntervalData& interval_data,
     const ProcessMonitor::Metrics& metrics,
@@ -526,8 +326,7 @@
   builder.Record(ukm_recorder);
 }
 
-PowerMetricsReporter::BatteryDischarge
-PowerMetricsReporter::GetBatteryDischargeDuringInterval(
+BatteryDischarge PowerMetricsReporter::GetBatteryDischargeDuringInterval(
     const BatteryLevelProvider::BatteryState& new_battery_state,
     base::TimeDelta interval_duration) {
   auto previous_battery_state =
diff --git a/chrome/browser/metrics/power/power_metrics_reporter.h b/chrome/browser/metrics/power/power_metrics_reporter.h
index 5277a97d..897e479 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter.h
+++ b/chrome/browser/metrics/power/power_metrics_reporter.h
@@ -5,14 +5,13 @@
 #ifndef CHROME_BROWSER_METRICS_POWER_POWER_METRICS_REPORTER_H_
 #define CHROME_BROWSER_METRICS_POWER_POWER_METRICS_REPORTER_H_
 
-#include <stdint.h>
 #include <memory>
 #include <utility>
 
-#include "base/gtest_prod_util.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/metrics/power/battery_level_provider.h"
+#include "chrome/browser/metrics/power/power_metrics.h"
 #include "chrome/browser/metrics/power/usage_scenario.h"
 #include "chrome/browser/metrics/usage_scenario/usage_scenario_data_store.h"
 #include "chrome/browser/metrics/usage_scenario/usage_scenario_tracker.h"
@@ -94,26 +93,6 @@
   static int64_t GetBucketForSampleForTesting(base::TimeDelta value);
 
  protected:
-  // Any change to this enum should be reflected in the corresponding enums.xml
-  // and ukm.xml
-  enum class BatteryDischargeMode {
-    kDischarging = 0,
-    kPluggedIn = 1,
-    kStateChanged = 2,
-    kChargeLevelUnavailable = 3,
-    kNoBattery = 4,
-    kBatteryLevelIncreased = 5,
-    kInvalidInterval = 6,
-    kMacFullyCharged = 7,
-    kMaxValue = kMacFullyCharged
-  };
-
-  struct BatteryDischarge {
-    PowerMetricsReporter::BatteryDischargeMode mode;
-    // Discharge rate in 1/10000 of full capacity per minute.
-    absl::optional<int64_t> rate;
-  };
-
   static void ReportLongIntervalHistograms(
       const UsageScenarioDataStore::IntervalData& interval_data,
       const ProcessMonitor::Metrics& aggregated_process_metrics,
@@ -127,33 +106,12 @@
   );
 
 #if BUILDFLAG(IS_MAC)
-  static void ReportShortIntervalHistograms(
-      const char* scenario_suffix,
-      absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate);
-
   // Emit trace event when CPU usage is high for 10 secondes or more.
   void MaybeEmitHighCPUTraceEvent(
       const ScenarioParams& short_interval_scenario_params,
       absl::optional<CoalitionResourceUsageRate> coalition_resource_usage_rate);
 #endif  // BUILDFLAG(IS_MAC)
 
-  // Report battery metrics to histograms with |suffixes|.
-  static void ReportBatteryHistograms(base::TimeDelta interval_duration,
-                                      BatteryDischarge battery_discharge,
-                                      const std::vector<const char*>& suffixes);
-
-  // Report aggregated process metrics to histograms with |suffixes|.
-  static void ReportAggregatedProcessMetricsHistograms(
-      const ProcessMonitor::Metrics& aggregated_process_metrics,
-      const std::vector<const char*>& suffixes);
-
-#if BUILDFLAG(IS_MAC)
-  // Report resource coalition metrics to histograms with |suffixes|.
-  static void ReportResourceCoalitionHistograms(
-      const power_metrics::CoalitionResourceUsageRate& rate,
-      const std::vector<const char*>& suffixes);
-#endif  // BUILDFLAG(IS_MAC)
-
  private:
   // ProcessMonitor::Observer:
   void OnAggregatedMetricsSampled(
diff --git a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
index 395e6a06..a356187 100644
--- a/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
+++ b/chrome/browser/metrics/power/power_metrics_reporter_unittest.cc
@@ -22,7 +22,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_MAC)
-#include "base/mac/mac_util.h"
 #include "chrome/browser/metrics/power/coalition_resource_usage_provider_test_util_mac.h"
 #include "components/power_metrics/resource_coalition_mac.h"
 #endif
@@ -36,9 +35,6 @@
 
 constexpr base::TimeDelta kExpectedMetricsCollectionInterval =
     base::Seconds(120);
-constexpr double kTolerableTimeElapsedRatio = 0.10;
-constexpr double kTolerablePositiveDrift = 1 + kTolerableTimeElapsedRatio;
-constexpr double kTolerableNegativeDrift = 1 - kTolerableTimeElapsedRatio;
 
 performance_monitor::ProcessMonitor::Metrics GetFakeProcessMetrics() {
   performance_monitor::ProcessMonitor::Metrics metrics;
@@ -46,25 +42,6 @@
   return metrics;
 }
 
-#if BUILDFLAG(IS_MAC)
-power_metrics::CoalitionResourceUsageRate GetFakeResourceUsageRate() {
-  power_metrics::CoalitionResourceUsageRate rate;
-  rate.cpu_time_per_second = 0.5;
-  rate.interrupt_wakeups_per_second = 10;
-  rate.platform_idle_wakeups_per_second = 11;
-  rate.bytesread_per_second = 12;
-  rate.byteswritten_per_second = 13;
-  rate.gpu_time_per_second = 0.6;
-  rate.energy_impact_per_second = 15;
-  rate.power_nw = 1000000;
-
-  for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i)
-    rate.qos_time_per_second[i] = 0.1 * i;
-
-  return rate;
-}
-#endif  // BUILDFLAG(IS_MAC)
-
 struct HistogramSampleExpectation {
   std::string histogram_name_prefix;
   base::Histogram::Sample sample;
@@ -90,25 +67,6 @@
 
 using UkmEntry = ukm::builders::PowerUsageScenariosIntervalData;
 
-class PowerMetricsReporterAccess : public PowerMetricsReporter {
- public:
-  using PowerMetricsReporter::PowerMetricsReporter;
-
-  // Expose members of PowerMetricsReporter publicly on
-  // PowerMetricsReporterAccess.
-  using PowerMetricsReporter::BatteryDischarge;
-  using PowerMetricsReporter::BatteryDischargeMode;
-  using PowerMetricsReporter::ReportBatteryHistograms;
-  using PowerMetricsReporter::ReportLongIntervalHistograms;
-#if BUILDFLAG(IS_MAC)
-  using PowerMetricsReporter::ReportResourceCoalitionHistograms;
-  using PowerMetricsReporter::ReportShortIntervalHistograms;
-#endif  // BUILDFLAG(IS_MAC)
-};
-
-using BatteryDischargeMode = PowerMetricsReporterAccess::BatteryDischargeMode;
-using BatteryDischarge = PowerMetricsReporterAccess::BatteryDischarge;
-
 class FakeBatteryLevelProvider : public BatteryLevelProvider {
  public:
   explicit FakeBatteryLevelProvider(
@@ -628,66 +586,6 @@
       BatteryDischargeMode::kBatteryLevelIncreased, 1);
 }
 
-TEST_F(PowerMetricsReporterUnitTest, BatteryDischargeCaptureIsTooEarly) {
-  UsageScenarioDataStore::IntervalData interval_data;
-
-  PowerMetricsReporterAccess::ReportBatteryHistograms(
-      (kExpectedMetricsCollectionInterval * kTolerableNegativeDrift) -
-          base::Seconds(1),
-      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500},
-      GetLongIntervalSuffixes(interval_data));
-
-  histogram_tester_.ExpectTotalCount(kBatteryDischargeRateHistogramName, 0);
-  histogram_tester_.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
-                                       BatteryDischargeMode::kInvalidInterval,
-                                       1);
-}
-
-TEST_F(PowerMetricsReporterUnitTest, BatteryDischargeCaptureIsEarly) {
-  UsageScenarioDataStore::IntervalData interval_data;
-
-  PowerMetricsReporterAccess::ReportBatteryHistograms(
-      (kExpectedMetricsCollectionInterval * kTolerableNegativeDrift) +
-          base::Seconds(1),
-      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500},
-      GetLongIntervalSuffixes(interval_data));
-
-  histogram_tester_.ExpectUniqueSample(kBatteryDischargeRateHistogramName, 2500,
-                                       1);
-  histogram_tester_.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
-                                       BatteryDischargeMode::kDischarging, 1);
-}
-
-TEST_F(PowerMetricsReporterUnitTest, BatteryDischargeCaptureIsTooLate) {
-  UsageScenarioDataStore::IntervalData interval_data;
-
-  PowerMetricsReporterAccess::ReportBatteryHistograms(
-      (kExpectedMetricsCollectionInterval * kTolerablePositiveDrift) +
-          base::Seconds(1),
-      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500},
-      GetLongIntervalSuffixes(interval_data));
-
-  histogram_tester_.ExpectTotalCount(kBatteryDischargeRateHistogramName, 0);
-  histogram_tester_.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
-                                       BatteryDischargeMode::kInvalidInterval,
-                                       1);
-}
-
-TEST_F(PowerMetricsReporterUnitTest, BatteryDischargeCaptureIsLate) {
-  UsageScenarioDataStore::IntervalData interval_data;
-
-  PowerMetricsReporterAccess::ReportBatteryHistograms(
-      (kExpectedMetricsCollectionInterval * kTolerablePositiveDrift) -
-          base::Seconds(1),
-      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500},
-      GetLongIntervalSuffixes(interval_data));
-
-  histogram_tester_.ExpectUniqueSample(kBatteryDischargeRateHistogramName, 2500,
-                                       1);
-  histogram_tester_.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
-                                       BatteryDischargeMode::kDischarging, 1);
-}
-
 TEST_F(PowerMetricsReporterUnitTest, UKMsNoTab) {
   UsageScenarioDataStore::IntervalData fake_interval_data;
 
@@ -789,82 +687,4 @@
   histogram_tester_.ExpectUniqueSample(
       "PerformanceMonitor.ResourceCoalition.CPUTime2_10sec", 6000, 1);
 }
-
-TEST_F(PowerMetricsReporterUnitTest, ShortIntervalHistograms_Emitted) {
-  const char* kScenarioSuffix = ".AllTabsHidden_Audio";
-
-  PowerMetricsReporterAccess::ReportShortIntervalHistograms(
-      kScenarioSuffix, GetFakeResourceUsageRate());
-
-  const std::vector<const char*> suffixes({"", kScenarioSuffix});
-  ExpectHistogramSamples(
-      &histogram_tester_, suffixes,
-      {{"PerformanceMonitor.ResourceCoalition.CPUTime2_10sec", 5000}});
-}
-
-TEST_F(PowerMetricsReporterUnitTest, ResourceCoalitionHistograms) {
-  base::HistogramTester histogram_tester;
-
-  const std::vector<const char*> suffixes = {"", ".Foo", ".Bar"};
-  PowerMetricsReporterAccess::ReportResourceCoalitionHistograms(
-      GetFakeResourceUsageRate(), suffixes);
-
-  ExpectHistogramSamples(
-      &histogram_tester, suffixes,
-      {// These histograms reports the CPU/GPU times as a percentage of
-       // time with a permyriad granularity, 10% (0.1) will be represented
-       // as 1000.
-       {"PerformanceMonitor.ResourceCoalition.CPUTime2", 5000},
-       {"PerformanceMonitor.ResourceCoalition.GPUTime2", 6000},
-       // These histograms report counts with a millievent/second
-       // granularity.
-       {"PerformanceMonitor.ResourceCoalition.InterruptWakeupsPerSecond",
-        10000},
-       {"PerformanceMonitor.ResourceCoalition."
-        "PlatformIdleWakeupsPerSecond",
-        11000},
-       {"PerformanceMonitor.ResourceCoalition.BytesReadPerSecond2", 12},
-       {"PerformanceMonitor.ResourceCoalition.BytesWrittenPerSecond2", 13},
-       // EI is reported in centi-EI so the data needs to be multiplied by
-       // 100.0.
-       {"PerformanceMonitor.ResourceCoalition.EnergyImpact", 1500},
-       // The QoS histograms also reports the CPU times as a percentage of
-       // time with a permyriad granularity.
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Default", 0},
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Maintenance", 1000},
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Background", 2000},
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Utility", 3000},
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Legacy", 4000},
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.UserInitiated", 5000},
-       {"PerformanceMonitor.ResourceCoalition.QoSLevel.UserInteractive",
-        6000}});
-
-  if (base::mac::GetCPUType() == base::mac::CPUType::kArm) {
-    ExpectHistogramSamples(
-        &histogram_tester, suffixes,
-        {// Power is reported in milliwatts (mj/s), the data
-         // is in nj/s so it has to be divided by 1000000.
-         {"PerformanceMonitor.ResourceCoalition.Power2", 1}});
-  } else {
-    histogram_tester.ExpectTotalCount(
-        "PerformanceMonitor.ResourceCoalition.Power2", 0);
-  }
-}
-
-// Verify that no energy impact histogram is reported when
-// `CoalitionResourceUsageRate::energy_impact_per_second` is nullopt.
-TEST_F(PowerMetricsReporterUnitTest,
-       ReportResourceCoalitionHistograms_NoEnergyImpact) {
-  base::HistogramTester histogram_tester;
-  power_metrics::CoalitionResourceUsageRate rate = GetFakeResourceUsageRate();
-  rate.energy_impact_per_second.reset();
-
-  std::vector<const char*> suffixes = {"", ".Foo"};
-  PowerMetricsReporterAccess::ReportResourceCoalitionHistograms(rate, suffixes);
-
-  histogram_tester.ExpectTotalCount(
-      "PerformanceMonitor.ResourceCoalition.EnergyImpact", 0);
-  histogram_tester.ExpectTotalCount(
-      "PerformanceMonitor.ResourceCoalition.EnergyImpact.Foo", 0);
-}
 #endif  // BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/metrics/power/power_metrics_unittest.cc b/chrome/browser/metrics/power/power_metrics_unittest.cc
new file mode 100644
index 0000000..19ba754
--- /dev/null
+++ b/chrome/browser/metrics/power/power_metrics_unittest.cc
@@ -0,0 +1,254 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/power/power_metrics.h"
+
+#include "base/strings/strcat.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if BUILDFLAG(IS_MAC)
+#include "base/mac/mac_util.h"
+#endif
+
+namespace {
+
+constexpr const char* kBatteryDischargeRateHistogramName =
+    "Power.BatteryDischargeRate2";
+constexpr const char* kBatteryDischargeModeHistogramName =
+    "Power.BatteryDischargeMode";
+
+constexpr base::TimeDelta kExpectedMetricsCollectionInterval =
+    base::Seconds(120);
+constexpr double kTolerableTimeElapsedRatio = 0.10;
+constexpr double kTolerablePositiveDrift = 1 + kTolerableTimeElapsedRatio;
+constexpr double kTolerableNegativeDrift = 1 - kTolerableTimeElapsedRatio;
+
+#if BUILDFLAG(IS_MAC)
+power_metrics::CoalitionResourceUsageRate GetFakeResourceUsageRate() {
+  power_metrics::CoalitionResourceUsageRate rate;
+  rate.cpu_time_per_second = 0.5;
+  rate.interrupt_wakeups_per_second = 10;
+  rate.platform_idle_wakeups_per_second = 11;
+  rate.bytesread_per_second = 12;
+  rate.byteswritten_per_second = 13;
+  rate.gpu_time_per_second = 0.6;
+  rate.energy_impact_per_second = 15;
+  rate.power_nw = 1000000;
+
+  for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i)
+    rate.qos_time_per_second[i] = 0.1 * i;
+
+  return rate;
+}
+#endif  // BUILDFLAG(IS_MAC)
+
+struct HistogramSampleExpectation {
+  std::string histogram_name_prefix;
+  base::Histogram::Sample sample;
+};
+
+// For each histogram named after the combination of prefixes from
+// `expectations` and suffixes from `suffixes`, verifies that there is a unique
+// sample `expectation.sample`.
+void ExpectHistogramSamples(
+    base::HistogramTester* histogram_tester,
+    const std::vector<const char*>& suffixes,
+    const std::vector<HistogramSampleExpectation>& expectations) {
+  for (const char* suffix : suffixes) {
+    for (const auto& expectation : expectations) {
+      std::string histogram_name =
+          base::StrCat({expectation.histogram_name_prefix, suffix});
+      SCOPED_TRACE(histogram_name);
+      histogram_tester->ExpectUniqueSample(histogram_name, expectation.sample,
+                                           1);
+    }
+  }
+}
+
+}  // namespace
+
+TEST(PowerMetricsTest, ReportAggregatedProcessMetricsHistograms) {
+  base::HistogramTester histogram_tester;
+  const std::vector<const char*> suffixes = {"", ".Foo", ".Bar"};
+
+  performance_monitor::ProcessMonitor::Metrics process_metrics;
+  process_metrics.cpu_usage = 0.20;
+#if BUILDFLAG(IS_WIN)
+  process_metrics.precise_cpu_usage = 0.30;
+#endif
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
+    BUILDFLAG(IS_AIX)
+  // Returns the number of average idle cpu wakeups per second since the last
+  // time the metric was sampled.
+  process_metrics.idle_wakeups = 51;
+#endif
+#if BUILDFLAG(IS_MAC)
+  // The number of average "package idle exits" per second since the last
+  // time the metric was sampled. See base/process/process_metrics.h for a
+  // more detailed explanation.
+  process_metrics.package_idle_wakeups = 52;
+
+  // "Energy Impact" is a synthetic power estimation metric displayed by macOS
+  // in Activity Monitor and the battery menu.
+  process_metrics.energy_impact = 10.00;
+#endif
+
+  ReportAggregatedProcessMetricsHistograms(process_metrics, suffixes);
+
+  ExpectHistogramSamples(&histogram_tester, suffixes, {
+    {"PerformanceMonitor.AverageCPU2.Total", 20},
+#if BUILDFLAG(IS_WIN)
+        {"PerformanceMonitor.AverageCPU3.Total", 30},
+#endif
+
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
+    BUILDFLAG(IS_AIX)
+        {"PerformanceMonitor.IdleWakeups.Total", 51},
+#endif
+
+#if BUILDFLAG(IS_MAC)
+        {"PerformanceMonitor.PackageExitIdleWakeups.Total", 52},
+        {"PerformanceMonitor.EnergyImpact.Total", 10},
+#endif
+  });
+}
+
+TEST(PowerMetricsTest, BatteryDischargeCaptureIsTooEarly) {
+  base::HistogramTester histogram_tester;
+  std::vector<const char*> suffixes = {""};
+
+  ReportBatteryHistograms(
+      (kExpectedMetricsCollectionInterval * kTolerableNegativeDrift) -
+          base::Seconds(1),
+      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500}, suffixes);
+
+  histogram_tester.ExpectTotalCount(kBatteryDischargeRateHistogramName, 0);
+  histogram_tester.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
+                                      BatteryDischargeMode::kInvalidInterval,
+                                      1);
+}
+
+TEST(PowerMetricsTest, BatteryDischargeCaptureIsEarly) {
+  base::HistogramTester histogram_tester;
+  std::vector<const char*> suffixes = {""};
+
+  ReportBatteryHistograms(
+      (kExpectedMetricsCollectionInterval * kTolerableNegativeDrift) +
+          base::Seconds(1),
+      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500}, suffixes);
+
+  histogram_tester.ExpectUniqueSample(kBatteryDischargeRateHistogramName, 2500,
+                                      1);
+  histogram_tester.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
+                                      BatteryDischargeMode::kDischarging, 1);
+}
+
+TEST(PowerMetricsTest, BatteryDischargeCaptureIsTooLate) {
+  base::HistogramTester histogram_tester;
+  std::vector<const char*> suffixes = {""};
+
+  ReportBatteryHistograms(
+      (kExpectedMetricsCollectionInterval * kTolerablePositiveDrift) +
+          base::Seconds(1),
+      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500}, suffixes);
+
+  histogram_tester.ExpectTotalCount(kBatteryDischargeRateHistogramName, 0);
+  histogram_tester.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
+                                      BatteryDischargeMode::kInvalidInterval,
+                                      1);
+}
+
+TEST(PowerMetricsTest, BatteryDischargeCaptureIsLate) {
+  base::HistogramTester histogram_tester;
+  std::vector<const char*> suffixes = {""};
+
+  ReportBatteryHistograms(
+      (kExpectedMetricsCollectionInterval * kTolerablePositiveDrift) -
+          base::Seconds(1),
+      BatteryDischarge{BatteryDischargeMode::kDischarging, 2500}, suffixes);
+
+  histogram_tester.ExpectUniqueSample(kBatteryDischargeRateHistogramName, 2500,
+                                      1);
+  histogram_tester.ExpectUniqueSample(kBatteryDischargeModeHistogramName,
+                                      BatteryDischargeMode::kDischarging, 1);
+}
+
+#if BUILDFLAG(IS_MAC)
+TEST(PowerMetricsTest, ReportShortIntervalHistograms) {
+  base::HistogramTester histogram_tester;
+  const char* kScenarioSuffix = ".AllTabsHidden_Audio";
+
+  ReportShortIntervalHistograms(kScenarioSuffix, GetFakeResourceUsageRate());
+
+  const std::vector<const char*> suffixes({"", kScenarioSuffix});
+  ExpectHistogramSamples(
+      &histogram_tester, suffixes,
+      {{"PerformanceMonitor.ResourceCoalition.CPUTime2_10sec", 5000}});
+}
+
+TEST(PowerMetricsTest, ReportResourceCoalitionHistograms) {
+  base::HistogramTester histogram_tester;
+
+  const std::vector<const char*> suffixes = {"", ".Foo", ".Bar"};
+  ReportResourceCoalitionHistograms(GetFakeResourceUsageRate(), suffixes);
+
+  ExpectHistogramSamples(
+      &histogram_tester, suffixes,
+      {// These histograms reports the CPU/GPU times as a percentage of
+       // time with a permyriad granularity, 10% (0.1) will be represented
+       // as 1000.
+       {"PerformanceMonitor.ResourceCoalition.CPUTime2", 5000},
+       {"PerformanceMonitor.ResourceCoalition.GPUTime2", 6000},
+       // These histograms report counts with a millievent/second
+       // granularity.
+       {"PerformanceMonitor.ResourceCoalition.InterruptWakeupsPerSecond",
+        10000},
+       {"PerformanceMonitor.ResourceCoalition."
+        "PlatformIdleWakeupsPerSecond",
+        11000},
+       {"PerformanceMonitor.ResourceCoalition.BytesReadPerSecond2", 12},
+       {"PerformanceMonitor.ResourceCoalition.BytesWrittenPerSecond2", 13},
+       // EI is reported in centi-EI so the data needs to be multiplied by
+       // 100.0.
+       {"PerformanceMonitor.ResourceCoalition.EnergyImpact", 1500},
+       // The QoS histograms also reports the CPU times as a percentage of
+       // time with a permyriad granularity.
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Default", 0},
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Maintenance", 1000},
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Background", 2000},
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Utility", 3000},
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.Legacy", 4000},
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.UserInitiated", 5000},
+       {"PerformanceMonitor.ResourceCoalition.QoSLevel.UserInteractive",
+        6000}});
+
+  if (base::mac::GetCPUType() == base::mac::CPUType::kArm) {
+    ExpectHistogramSamples(
+        &histogram_tester, suffixes,
+        {// Power is reported in milliwatts (mj/s), the data
+         // is in nj/s so it has to be divided by 1000000.
+         {"PerformanceMonitor.ResourceCoalition.Power2", 1}});
+  } else {
+    histogram_tester.ExpectTotalCount(
+        "PerformanceMonitor.ResourceCoalition.Power2", 0);
+  }
+}
+
+// Verify that no energy impact histogram is reported when
+// `CoalitionResourceUsageRate::energy_impact_per_second` is nullopt.
+TEST(PowerMetricsTest, ReportResourceCoalitionHistograms_NoEnergyImpact) {
+  base::HistogramTester histogram_tester;
+  power_metrics::CoalitionResourceUsageRate rate = GetFakeResourceUsageRate();
+  rate.energy_impact_per_second.reset();
+
+  std::vector<const char*> suffixes = {"", ".Foo"};
+  ReportResourceCoalitionHistograms(rate, suffixes);
+
+  histogram_tester.ExpectTotalCount(
+      "PerformanceMonitor.ResourceCoalition.EnergyImpact", 0);
+  histogram_tester.ExpectTotalCount(
+      "PerformanceMonitor.ResourceCoalition.EnergyImpact.Foo", 0);
+}
+#endif  // BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/notifications/BUILD.gn b/chrome/browser/notifications/BUILD.gn
index ffbd09a..8adb53a3 100644
--- a/chrome/browser/notifications/BUILD.gn
+++ b/chrome/browser/notifications/BUILD.gn
@@ -77,8 +77,8 @@
       "//components/content_settings/android:content_settings_enums_java",
       "//components/url_formatter/android:url_formatter_java",
       "//content/public/android:content_full_java",
-      "//third_party/android_deps:android_support_v7_appcompat_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
+      "//third_party/androidx:androidx_core_core_java",
       "//ui/android:ui_no_recycler_view_java",
     ]
 
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 67a339a..2ae27f1c 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -557,7 +557,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
   BubbleObserver prompt_observer(WebContents());
   std::string fill_and_submit =
       "document.getElementById('username_action_mutation').value = 'temp';"
@@ -698,7 +698,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if XHR without navigation occurs and the form has been filled
   // out we try and save the password. Note that in general the submission
@@ -727,7 +727,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if XHR without navigation occurs and the form has been filled
   // out we try and save the password. Note that in general the submission
@@ -757,7 +757,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if XHR without navigation occurs and the form has NOT been
   // filled out we don't prompt.
@@ -783,7 +783,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if XHR without navigation occurs and the form has NOT been
   // filled out we don't prompt.
@@ -826,7 +826,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if XHR without navigation occurs and the form has been filled
   // out we try and save the password. Note that in general the submission
@@ -855,7 +855,7 @@
 
   // Need to pay attention for a message that Fetch has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if Fetch without navigation occurs and the form has been filled
   // out we try and save the password. Note that in general the submission
@@ -886,7 +886,7 @@
 
   // Need to pay attention for a message that Fetch has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if Fetch without navigation occurs and the form has NOT been
   // filled out we don't prompt.
@@ -912,7 +912,7 @@
 
   // Need to pay attention for a message that Fetch has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   // Verify that if Fetch without navigation occurs and the form has NOT been
   // filled out we don't prompt.
@@ -2167,7 +2167,7 @@
 
   // Need to pay attention for a message that XHR has finished since there
   // is no navigation to wait for.
-  content::DOMMessageQueue message_queue;
+  content::DOMMessageQueue message_queue(WebContents());
 
   BubbleObserver prompt_observer(WebContents());
   std::string fill_and_submit =
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index 76ab0414..13e0bf5 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -54,14 +54,11 @@
   "common/abstract_earcons.js",
   "common/background_bridge.js",
   "common/braille_interface.js",
-  "common/bridge_helper.js",
   "common/extension_bridge.js",
   "common/key_sequence.js",
   "common/log_types.js",
   "common/msgs.js",
-  "common/panel_bridge.js",
   "common/panel_command.js",
-  "common/panel_node_menu_data.js",
   "common/phonetic_data.js",
   "common/spannable.js",
   "common/tree_dumper.js",
@@ -116,9 +113,6 @@
   "background/math_handler.js",
   "background/media_automation_handler.js",
   "background/page_load_sound_handler.js",
-  "background/panel/i_search.js",
-  "background/panel/panel_background.js",
-  "background/panel/panel_node_menu_background.js",
   "background/pointer_handler.js",
   "background/prefs.js",
   "background/range_automation_handler.js",
@@ -138,7 +132,7 @@
   "log_page/log.js",
   "options/bluetooth_braille_display_ui.js",
   "options/options.js",
-  "panel/i_search_ui.js",
+  "panel/i_search.js",
   "panel/panel.js",
   "panel/panel_interface.js",
   "panel/panel_menu.js",
@@ -446,8 +440,6 @@
       "background/logging/log_store_test.js",
       "background/output/locale_output_helper_test.js",
       "background/output/output_test.js",
-      "background/panel/i_search_test.js",
-      "background/panel/panel_node_menu_background_test.js",
       "background/portals_test.js",
       "background/settings_test.js",
       "background/smart_sticky_mode_test.js",
@@ -456,6 +448,7 @@
       "learn_mode/learn_mode_test.js",
       "options/bluetooth_braille_display_ui_test.js",
       "options/options_test.js",
+      "panel/i_search_test.js",
       "panel/panel_test.js",
       "panel/panel_test_base.js",
       "panel/tutorial_test.js",
@@ -464,7 +457,6 @@
       "../common/testing/accessibility_test_base.js",
       "../common/testing/assert_additions.js",
       "../common/testing/callback_helper.js",
-      "../common/testing/documents.js",
       "../common/testing/e2e_test_base.js",
       "testing/chromevox_e2e_test_base.js",
       "testing/chromevox_next_e2e_test_base.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
index 218ff480..eb48cf9d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background.js
@@ -19,7 +19,6 @@
 import {MathHandler} from './math_handler.js';
 import {MediaAutomationHandler} from './media_automation_handler.js';
 import {PageLoadSoundHandler} from './page_load_sound_handler.js';
-import {PanelBackground} from './panel/panel_background.js';
 import {RangeAutomationHandler} from './range_automation_handler.js';
 
 /**
@@ -114,7 +113,6 @@
 
     FindHandler.init();
     DownloadHandler.init();
-    PanelBackground.init();
     JaPhoneticData.init(JaPhoneticMap.MAP);
 
     chrome.accessibilityPrivate.onAnnounceForAccessibility.addListener(
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 497a4c3..932f172 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,7 +173,7 @@
 /** @type {?BrailleBackground} */
 BrailleBackground.instance_ = null;
 
-BridgeHelper.registerHandler(
+BackgroundBridge.registerHandler(
     /*target=*/ 'BrailleBackground', 'backTranslate',
     (cells) => new Promise(resolve => {
       BrailleBackground.getInstance()
@@ -182,7 +182,7 @@
           .backTranslate(cells, resolve);
     }));
 
-BridgeHelper.registerHandler(
+BackgroundBridge.registerHandler(
     /*target=*/ 'BrailleBackground', 'refreshBrailleTable',
     (brailleTable) =>
         BrailleBackground.getInstance().getTranslatorManager().refresh(
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/loader.js
index cc46aa9..6c74ec0 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');
@@ -35,10 +35,7 @@
 goog.require('NavBraille');
 goog.require('Output');
 goog.require('OutputEventType');
-goog.require('PanelBridge');
 goog.require('PanelCommand');
-goog.require('PanelNodeMenuData');
-goog.require('PanelNodeMenuItemData');
 goog.require('PhoneticData');
 goog.require('QueueMode');
 goog.require('Spannable');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js
deleted file mode 100644
index 71a057e..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search.js
+++ /dev/null
@@ -1,113 +0,0 @@
-// 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 Class that incrementally searches the ChromeVox menus.
- */
-export class ISearch {
-  /**
-   * @param {!cursors.Cursor} cursor
-   */
-  constructor(cursor) {
-    if (!cursor.node) {
-      throw 'Incremental search started from invalid range.';
-    }
-
-    const leaf =
-        AutomationUtil.findNodePre(
-            cursor.node, constants.Dir.FORWARD, AutomationPredicate.leaf) ||
-        cursor.node;
-
-    /** @type {!cursors.Cursor} */
-    this.cursor = cursors.Cursor.fromNode(leaf);
-
-    /** @private {number} */
-    this.callbackId_ = 0;
-  }
-
-  /**
-   * Performs a search.
-   * @param {string} searchStr
-   * @param {constants.Dir} dir
-   * @param {boolean=} opt_nextObject
-   */
-  search(searchStr, dir, opt_nextObject) {
-    this.clear();
-    const step = () => {
-      searchStr = searchStr.toLocaleLowerCase();
-      let result = this.cursor.node;
-
-      if (opt_nextObject) {
-        // We want to start/continue the search at the next object.
-        result = AutomationUtil.findNextNode(
-            this.cursor.node, dir, AutomationPredicate.object);
-      }
-
-      do {
-        // Ask native to search the underlying data for a performance boost.
-        result =
-            result.getNextTextMatch(searchStr, dir === constants.Dir.BACKWARD);
-      } while (result && !AutomationPredicate.object(result));
-
-      if (result) {
-        this.cursor = cursors.Cursor.fromNode(result);
-        const start = result.name.toLocaleLowerCase().indexOf(searchStr);
-        const end = start + searchStr.length;
-        this.onSearchResultChanged_(result, start, end);
-      } else {
-        this.onSearchReachedBoundary_(this.cursor.node);
-      }
-    };
-
-    this.callbackId_ = setTimeout(step, 0);
-  }
-
-  clear() {
-    clearTimeout(this.callbackId_);
-  }
-
-  /**
-   * @param {!chrome.automation.AutomationNode} boundaryNode
-   * @private
-   */
-  onSearchReachedBoundary_(boundaryNode) {
-    this.output_(boundaryNode);
-    ChromeVox.earcons.playEarcon(Earcon.WRAP);
-  }
-
-  /**
-   * @param {!chrome.automation.AutomationNode} node
-   * @param {number} start
-   * @param {number} end
-   * @private
-   */
-  onSearchResultChanged_(node, start, end) {
-    this.output_(node, start, end);
-  }
-
-  /**
-   * @param {!chrome.automation.AutomationNode} node
-   * @param {number=} opt_start
-   * @param {number=} opt_end
-   * @private
-   */
-  output_(node, opt_start, opt_end) {
-    Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH);
-    const o = new Output();
-    if (opt_start && opt_end) {
-      o.withString([
-        node.name.substr(0, opt_start),
-        node.name.substr(opt_start, opt_end - opt_start),
-        node.name.substr(opt_end)
-      ].join(', '));
-      o.format('$role', node);
-    } else {
-      o.withRichSpeechAndBraille(
-          cursors.Range.fromNode(node), null, OutputEventType.NAVIGATE);
-    }
-    o.go();
-
-    ChromeVoxState.instance.setCurrentRange(cursors.Range.fromNode(node));
-  }
-}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search_test.js
deleted file mode 100644
index de1678d2..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/i_search_test.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Include test fixture.
-GEN_INCLUDE(
-    ['//chrome/browser/resources/chromeos/accessibility/chromevox/testing/' +
-     'chromevox_next_e2e_test_base.js']);
-
-/**
- * Test fixture for ISearch.
- */
-ChromeVoxISearchTest = class extends ChromeVoxNextE2ETest {
-  constructor() {
-    super();
-    this.expect_ = [];
-  }
-
-  /** @override */
-  async setUpDeferred() {
-    await super.setUpDeferred();
-    await importModule('ISearch', '/chromevox/background/panel/i_search.js');
-  }
-
-  overrideOutputFunctions(iSearch) {
-    iSearch.onSearchReachedBoundary_ = (node) => {
-      const expectCallback = this.expect_.shift();
-      expectCallback({node, isBoundary: true});
-    };
-    iSearch.onSearchResultChanged_ = (node, start, end) => {
-      const expectCallback = this.expect_.shift();
-      expectCallback({node, start, end});
-    };
-  }
-
-  expect(str) {
-    return new Promise(resolve => {
-      this.expect_.push(this.newCallback(args => {
-        const node = args.node;
-        let actual = node.name || node.role;
-        if (args.start && args.end) {
-          actual =
-              'start=' + args.start + ' end=' + args.end + ' text=' + actual;
-        }
-        if (args.isBoundary) {
-          actual = 'boundary=' + actual;
-        }
-        assertEquals(str, actual);
-        resolve();
-      }));
-    });
-  }
-
-  get linksAndHeadingsDoc() {
-    return `
-      <p>start</p>
-      <a href='#a'>Home</a>
-      <a href='#b'>About US</a>
-      <p>
-        <h1>Latest Breaking News</h1>
-        <a href='foo'>See more...</a>
-      </p>
-      <a href='#bar'>Questions?</a>
-      <h2>Privacy Policy</h2>
-      <p>end<span>of test</span></p>
-    `;
-  }
-};
-
-TEST_F('ChromeVoxISearchTest', 'Simple', async function() {
-  const rootNode = await this.runWithLoadedTree(this.linksAndHeadingsDoc);
-  const search = new ISearch(new cursors.Cursor(rootNode, 0));
-  this.overrideOutputFunctions(search);
-
-  // Simple forward search.
-  search.search('US', 'forward');
-  await this.expect('start=6 end=8 text=About US');
-
-  search.search('start', 'backward');
-  await this.expect('start');
-
-  // Boundary (beginning).
-  search.search('foo', 'backward');
-  await this.expect('boundary=start');
-
-  // Boundary (end).
-  search.search('foo', 'forward');
-  // Search "focus" doesn't move.
-  await this.expect('boundary=start');
-
-  // Mixed case substring.
-  search.search('bReak', 'forward');
-  await this.expect('start=7 end=12 text=Latest Breaking News');
-
-  search.search('bReaki', 'forward');
-  // Incremental search stays on the current node.
-  await this.expect('start=7 end=13 text=Latest Breaking News');
-
-  search.search('bReakio', 'forward');
-  // No results for the search.
-  await this.expect('boundary=Latest Breaking News');
-});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
deleted file mode 100644
index d09712a1..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_background.js
+++ /dev/null
@@ -1,78 +0,0 @@
-// 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 Handles logic for the ChromeVox panel that requires state from
- * the background context.
- */
-import {ISearch} from './i_search.js';
-
-// This class is imported for its side effects.
-import {PanelNodeMenuBackground} from './panel_node_menu_background.js';
-
-export class PanelBackground {
-  /** @private */
-  constructor() {
-    /** @private {ISearch} */
-    this.iSearch_;
-  }
-
-  static init() {
-    if (PanelBackground.instance) {
-      throw 'Error: PanelBackground initiated more than once';
-    }
-    PanelBackground.instance = new PanelBackground();
-  }
-
-  /** @private */
-  createNewISearch_() {
-    if (this.iSearch_) {
-      this.iSearch_.clear();
-    }
-    this.iSearch_ = new ISearch(ChromeVoxState.instance.currentRange.start);
-  }
-
-  /**
-   * @param {string} searchStr
-   * @param {constants.Dir} dir
-   * @param {boolean=} opt_nextObject
-   * @private
-   */
-  incrementalSearch_(searchStr, dir, opt_nextObject) {
-    if (!this.iSearch_) {
-      console.error(
-          'Trying to incrementally search when no ISearch has been created.');
-      return;
-    }
-
-    this.iSearch_.search(searchStr, dir, opt_nextObject);
-  }
-
-  /** @private */
-  setRangeToISearchNode_() {
-    if (!this.iSearch_) {
-      console.error(
-          'Setting range to ISearch node when no ISearch in progress');
-      return;
-    }
-
-    const node = this.iSearch_.cursor.node;
-    if (!node) {
-      return;
-    }
-    ChromeVoxState.instance.navigateToRange(cursors.Range.fromNode(node));
-  }
-}
-
-BridgeHelper.registerHandler(
-    /* target= */ 'PanelBackground', 'createNewISearch',
-    () => PanelBackground.instance.createNewISearch_());
-BridgeHelper.registerHandler(
-    /* target= */ 'PanelBackground', 'incrementalSearch',
-    ({searchStr, dir, opt_nextObject}) =>
-        PanelBackground.instance.incrementalSearch_(
-            searchStr, dir, opt_nextObject));
-BridgeHelper.registerHandler(
-    /* target= */ 'PanelBackground', 'setRangeToISearchNode',
-    () => PanelBackground.instance.setRangeToISearchNode_());
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
deleted file mode 100644
index b03913a4..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background.js
+++ /dev/null
@@ -1,244 +0,0 @@
-// 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 Calculates the menu items for the node menus in the ChromeVox
- * panel.
- */
-
-export class PanelNodeMenuBackground {
-  /**
-   * @param {!AutomationPredicate.Unary} predicate
-   * @param {boolean} isActiveMenu
-   * @private
-   */
-  constructor(predicate, isActiveMenu) {
-    /** @private {chrome.automation.AutomationNode} */
-    this.node_ = ChromeVoxState.instance.currentRange.start;
-    /** @private {AutomationTreeWalker|undefined} */
-    this.walker_;
-    /** @private {number} */
-    this.nodeCount_ = 0;
-    /** @private {!AutomationPredicate.Unary} */
-    this.predicate_ = predicate;
-    /** @private {boolean} */
-    this.isActiveMenu_ = isActiveMenu;
-    /** @private {boolean} */
-    this.hasMenuItems_ = false;
-
-    /** @private {!Array<!function()>} */
-    this.menuActionCallbacks_ = [];
-    /** @private {number} */
-    this.menuId_ = PanelNodeMenuBackground.instances_.length;
-    PanelNodeMenuBackground.instances_.push(this);
-
-    if (this.isActiveMenu_) {
-      // Put this at the front of the queue so it is calculated first.
-      PanelNodeMenuBackground.populateQueue_.unshift(this);
-    } else {
-      PanelNodeMenuBackground.populateQueue_.push(this);
-    }
-  }
-
-  /**
-   * @param {string=} opt_activateMenuTitle
-   * @return {!Array<!PanelNodeMenuData>}
-   */
-  static createAllPanelNodeMenuData(opt_activateMenuTitle) {
-    const dataArray = [];
-    for (const mapping of PanelNodeMenuBackground.roleListMenuMapping) {
-      const isActiveMenu = mapping.menuTitle === opt_activateMenuTitle;
-      dataArray.push(PanelNodeMenuBackground.createData_(
-          mapping.menuTitle, mapping.predicate, isActiveMenu));
-    }
-
-    // Start populating node menus after we return.
-    setTimeout(PanelNodeMenuBackground.populateAll_, 0);
-    return dataArray;
-  }
-
-  /**
-   * @param {number} menuId
-   * @param {number} callbackId
-   */
-  static performMenuActionCallback(menuId, callbackId) {
-    if (menuId >= PanelNodeMenuBackground.instances_.length || menuId < 0) {
-      throw 'Error: Invalid menu ID provided to performMenuActionCallback';
-    }
-    const instance = PanelNodeMenuBackground.instances_[menuId];
-
-    if (callbackId >= instance.menuActionCallbacks_.length || callbackId < 0) {
-      return;
-    }
-
-    instance.menuActionCallbacks_[callbackId]();
-  }
-
-  /**
-   * @param {string} menuTitle
-   * @param {!AutomationPredicate.Unary} predicate
-   * @param {boolean} isActiveMenu
-   * @return {!PanelNodeMenuData}
-   * @private
-   */
-  static createData_(menuTitle, predicate, isActiveMenu) {
-    return (new PanelNodeMenuBackground(predicate, isActiveMenu))
-        .getData_(menuTitle);
-  }
-
-  static populateAll_() {
-    for (const instance of PanelNodeMenuBackground.populateQueue_) {
-      instance.populate_();
-    }
-  }
-
-  /**
-   * @param {string} menuTitle
-   * @private
-   */
-  getData_(menuTitle) {
-    return new PanelNodeMenuData(menuTitle, this.menuId_);
-  }
-
-
-  /**
-   * Create the AutomationTreeWalker and kick off the search to find
-   * nodes that match the predicate for this menu.
-   * @private
-   */
-  populate_() {
-    if (!this.node_) {
-      this.finish_();
-      return;
-    }
-
-    const root = AutomationUtil.getTopLevelRoot(this.node_);
-    if (!root) {
-      this.finish_();
-      return;
-    }
-
-    this.walker_ = new AutomationTreeWalker(root, constants.Dir.FORWARD, {
-      visit(node) {
-        return !AutomationPredicate.shouldIgnoreNode(node);
-      }
-    });
-    this.findMoreNodes_();
-  }
-
-  /**
-   * Iterate over nodes from the tree walker. If a node matches the
-   * predicate, add an item to the menu data.
-   *
-   * Unless |this.isActiveMenu_| is true, after MAX_NODES_BEFORE_YIELDING nodes
-   * have been scanned, call setTimeout to defer searching. This frees
-   * up the main event loop to keep ChromeVox responsive, otherwise it basically
-   * freezes up until all of the nodes have been found.
-   * @private
-   */
-  findMoreNodes_() {
-    let menuItems = [];
-    while (this.walker_.next().node) {
-      const node = this.walker_.node;
-      if (this.predicate_(node)) {
-        const isActive = this.isActiveMenu_ && node === this.node_;
-
-        const output = new Output();
-        const range = cursors.Range.fromNode(node);
-        output.withoutHints();
-        output.withSpeech(range, range, OutputEventType.NAVIGATE);
-        const label = output.toString();
-
-        const indexInArray = this.menuActionCallbacks_.length;
-        this.menuActionCallbacks_.push(
-            () => ChromeVoxState.instance.navigateToRange(
-                cursors.Range.fromNode(node)));
-
-        menuItems.push(
-            new PanelNodeMenuItemData(label, indexInArray, isActive));
-      }
-
-      if (menuItems.length >= PanelNodeMenuBackground.BATCH_SIZE) {
-        this.sendMenuItemData_(menuItems);
-        menuItems = [];
-      }
-
-      this.nodeCount_++;
-      // If this is not the active menu, yield periodically to avoid blocking
-      // the user.
-      if (!this.isActiveMenu_ &&
-          this.nodeCount_ >=
-              PanelNodeMenuBackground.MAX_NODES_BEFORE_YIELDING) {
-        this.nodeCount_ = 0;
-
-        if (menuItems.length) {
-          this.sendMenuItemData_(menuItems);
-          menuItems = [];
-        }
-
-        window.setTimeout(() => this.findMoreNodes_(), 0);
-        return;
-      }
-    }
-
-    if (menuItems.length) {
-      this.sendMenuItemData_(menuItems);
-    }
-    this.finish_();
-  }
-
-  /**
-   * @param {!Array<!PanelNodeMenuItemData>} menuItems
-   * @private
-   */
-  async sendMenuItemData_(menuItems) {
-    this.hasMenuItems_ = true;
-    await PanelBridge.addPanelNodeMenuItems(this.menuId_, menuItems);
-  }
-
-  /** @private */
-  finish_() {
-    if (!this.hasMenuItems_) {
-      this.sendMenuItemData_([new PanelNodeMenuItemData(
-          Msgs.getMsg('panel_menu_item_none'), -1, false)]);
-    }
-  }
-}
-
-/**
- * The number of matching nodes to find before sending the data to the panel UI.
- * @const {number}
- */
-PanelNodeMenuBackground.BATCH_SIZE = 5;
-
-/**
- * The number of nodes to search before yielding to other tasks.
- * @const {number}
- */
-PanelNodeMenuBackground.MAX_NODES_BEFORE_YIELDING = 100;
-
-PanelNodeMenuBackground.roleListMenuMapping = [
-  {menuTitle: 'role_heading', predicate: AutomationPredicate.heading},
-  {menuTitle: 'role_landmark', predicate: AutomationPredicate.landmark},
-  {menuTitle: 'role_link', predicate: AutomationPredicate.link}, {
-    menuTitle: 'panel_menu_form_controls',
-    predicate: AutomationPredicate.formField
-  },
-  {menuTitle: 'role_table', predicate: AutomationPredicate.table}
-];
-
-/** @private {!Array<!PanelNodeMenuBackground>} */
-PanelNodeMenuBackground.instances_ = [];
-/** @private {!Array<!PanelNodeMenuBackground>} */
-PanelNodeMenuBackground.populateQueue_ = [];
-
-BridgeHelper.registerHandler(
-    /* target= */ 'PanelNodeMenuBackground', 'createAllPanelNodeMenuData',
-    (opt_activateMenuTitle) =>
-        PanelNodeMenuBackground.createAllPanelNodeMenuData(
-            opt_activateMenuTitle));
-BridgeHelper.registerHandler(
-    /* target= */ 'PanelNodeMenuBackground', 'performMenuActionCallback',
-    ({menuId, callbackId}) =>
-        PanelNodeMenuBackground.performMenuActionCallback(menuId, callbackId));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js
deleted file mode 100644
index 985eda6..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/panel/panel_node_menu_background_test.js
+++ /dev/null
@@ -1,417 +0,0 @@
-// 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.
-
-GEN_INCLUDE([
-  '../../../common/testing/documents.js',
-  '../../testing/chromevox_next_e2e_test_base.js',
-]);
-
-// Fake Msgs object.
-const Msgs = {
-  getMsg: () => 'None'
-};
-
-// Fake PanelBridge.
-const PanelBridge = {
-  addPanelNodeMenuItems: (id, items) => PanelBridge.calls.append({id, items}),
-  calls: [],
-};
-
-/** Test fixture for PanelNodeMenuBackground. */
-ChromeVoxPanelNodeMenuBackgroundTest = class extends ChromeVoxNextE2ETest {
-  /** @override */
-  async setUpDeferred() {
-    await super.setUpDeferred();
-    await importModule(
-        'PanelNodeMenuBackground',
-        '/chromevox/background/panel/panel_node_menu_background.js');
-    this.expectedMenuCount = PanelNodeMenuBackground.roleListMenuMapping.length;
-  }
-
-  // Yield so populateAll_ has a chance to run.
-  yieldForPopulation() {
-    return new Promise(resolve => setTimeout(resolve, 0));
-  }
-
-  get headingsDoc() {
-    return `<h1>Heading 1</h1>
-        <h2>Heading 2</h2>
-        <h3>Heading 3</h3>`;
-  }
-
-  get landmarksDoc() {
-    return [
-        Documents.application,
-        Documents.banner,
-        Documents.complementary,
-        Documents.form,
-        Documents.main,
-        Documents.navigation,
-        Documents.region,
-        Documents.search
-    ].join('\n');
-  }
-
-  get linksDoc() {
-    return `<a href="#a">Link 1</a>
-        <a href="#b">Link 2</a>
-        <a href="#c">Link 3</a>
-        <a href="#d">Link 4</a>
-        <p>Not a link</p>`;
-  }
-
-  get formControlsDoc() {
-    return [
-        Documents.button,
-        Documents.textInput,
-        Documents.textarea,
-        `<p>Static text</p>`,
-        Documents.checkbox,
-        Documents.color,
-        Documents.slider,
-        Documents.switch,
-        Documents.tab,
-        Documents.tree,
-        `<script>
-          document.getElementById('tree').focus();
-        </script>`
-    ].join('\n');
-  }
-
-  get mixedDoc() {
-    return [
-        Documents.button,
-        Documents.table,
-        Documents.region,
-        Documents.link,
-        Documents.header
-    ].join('\n');
-  }
-
-  get tablesDoc() {
-    return [
-        Documents.grid,
-        Documents.table
-    ].join('\n');
-  }
-};
-
-TEST_F(
-    'ChromeVoxPanelNodeMenuBackgroundTest', 'EmptyDocument', async function() {
-      await this.runWithLoadedTree('');
-      const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData();
-      assertEquals(this.expectedMenuCount, menus.length);
-
-      // Verify that the menus are created in the expected order.
-      assertEquals('role_heading', menus[0].title);
-      assertEquals('role_landmark', menus[1].title);
-      assertEquals('role_link', menus[2].title);
-      assertEquals('panel_menu_form_controls', menus[3].title);
-      assertEquals('role_table', menus[4].title);
-
-      // Verify that menu ids are unique.
-      for (const menu of menus) {
-        for (const otherMenu of menus) {
-          if (menu !== otherMenu) {
-            assertNotEquals(menu.menuId, otherMenu.menuId);
-          }
-        }
-      }
-
-      await this.yieldForPopulation();
-
-      // With an empty document, we expect one call to addPanelNodeMenuItems per
-      // menu.
-      assertEquals(this.expectedMenuCount, PanelBridge.calls.length);
-
-      // We expect the calls to add menu items indicating there are no relevant
-      // nodes.
-      for (const call of PanelBridge.calls) {
-        assertEquals(1, call.items.length);
-        assertEquals('None', call.items[0].title);
-        assertEquals(-1, call.items[0].callbackId);
-        assertFalse(call.items[0].isActive);
-      }
-    });
-
-TEST_F('ChromeVoxPanelNodeMenuBackgroundTest', 'Headings', async function() {
-  await this.runWithLoadedTree(this.headingsDoc);
-  const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData();
-
-  assertEquals(this.expectedMenuCount, menus.length);
-  assertEquals('role_heading', menus[0].title);
-  const headingMenuId = menus[0].menuId;
-
-  await this.yieldForPopulation();
-
-  // The heading menu should have less than 5 items, which is the batch size, so
-  // we expect only one call for each menu (the others all being empty).
-  assertEquals(this.expectedMenuCount, PanelBridge.calls.length);
-
-  let headingCall;
-  // All calls to add menu items other than the heading menu call should
-  // indicate there are no relevant nodes.
-  for (const call of PanelBridge.calls) {
-    if (call.id === headingMenuId) {
-      headingCall = call;
-      continue;
-    }
-    assertEquals(1, call.items.length);
-    assertEquals('None', call.items[0].title);
-  }
-
-  // Check the contents of the heading call match what we expect based on the
-  // provided document.
-  const headingMenuItems = headingCall.items;
-  assertEquals(3, headingMenuItems.length);
-  assertEquals('Heading 1', headingMenuItems[0].title);
-  assertEquals('Heading 2', headingMenuItems[1].title);
-  assertEquals('Heading 3', headingMenuItems[2].title);
-
-  for (const item of headingMenuItems) {
-    assertFalse(item.isActive);
-    // Verify the callback IDs are unique.
-    for (const otherItem of headingMenuItems) {
-      if (item !== otherItem) {
-        assertNotEquals(item.callbackId, otherItem.callbackId);
-      }
-    }
-  }
-});
-
-TEST_F('ChromeVoxPanelNodeMenuBackgroundTest', 'Landmarks', async function() {
-  await this.runWithLoadedTree(this.landmarksDoc);
-  const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData();
-
-  assertEquals(this.expectedMenuCount, menus.length);
-  assertEquals('role_landmark', menus[1].title);
-  const landmarkMenuId = menus[1].menuId;
-
-  await this.yieldForPopulation();
-
-  // The landmarks menu will have a total of 2 calls, as it has more than 5
-  // items (the batch size). However, only one batch of landmarks occurs per
-  // call to yieldForPopulation, so expect one call per menu (the others all
-  // being empty).
-  assertEquals(this.expectedMenuCount, PanelBridge.calls.length);
-
-  let landmarkCall;
-  // All calls to add menu items other than the landmark menu call should
-  // indicate there are no relevant nodes.
-  for (const call of PanelBridge.calls) {
-    if (call.id === landmarkMenuId) {
-      landmarkCall = call;
-      continue;
-    }
-    assertEquals(1, call.items.length);
-    assertEquals('None', call.items[0].title);
-  }
-
-  // Check the contents of the landmark call match what we expect based on the
-  // provided document.
-  const landmarkMenuItems = landmarkCall.items;
-  assertEquals(5, landmarkMenuItems.length);
-
-  // Wait for the rest of the menu items to populate.
-  PanelBridge.calls = [];
-  await this.yieldForPopulation();
-  assertEquals(1, PanelBridge.calls.length);
-
-  assertEquals(landmarkMenuId, PanelBridge.calls[0].id);
-  landmarkMenuItems.append(PanelBridge.calls[0].items);
-  assertEquals(8, landmarkMenuItems.length);
-
-  assertEquals('application', landmarkMenuItems[0].title);
-  assertEquals('banner', landmarkMenuItems[1].title);
-  assertEquals('complementary', landmarkMenuItems[2].title);
-  assertEquals('form', landmarkMenuItems[3].title);
-  assertEquals('main', landmarkMenuItems[4].title);
-  assertEquals('navigation', landmarkMenuItems[5].title);
-  assertEquals('region', landmarkMenuItems[6].title);
-  assertEquals('search', landmarkMenuItems[7].title);
-
-  for (const item of landmarkMenuItems) {
-    assertFalse(item.isActive);
-    // Verify the callback IDs are unique.
-    for (const otherItem of landmarkMenuItems) {
-      if (item !== otherItem) {
-        assertNotEquals(item.callbackId, otherItem.callbackId);
-      }
-    }
-  }
-});
-
-TEST_F('ChromeVoxPanelNodeMenuBackgroundTest', 'Links', async function() {
-  await this.runWithLoadedTree(this.linksDoc);
-  const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData();
-
-  assertEquals(this.expectedMenuCount, menus.length);
-  assertEquals('role_link', menus[2].title);
-  const linkMenuId = menus[2].menuId;
-
-  await this.yieldForPopulation();
-
-  // The link menu should have less than 5 items, which is the batch size, so
-  // we expect only one call for each menu (the others all being empty).
-  assertEquals(this.expectedMenuCount, PanelBridge.calls.length);
-
-  let linkCall;
-  // All calls to add menu items other than the link menu call should
-  // indicate there are no relevant nodes.
-  for (const call of PanelBridge.calls) {
-    if (call.id === linkMenuId) {
-      linkCall = call;
-      continue;
-    }
-    assertEquals(1, call.items.length);
-    assertEquals('None', call.items.title);
-  }
-
-  // Check the contents of the link call match what we expect based on the
-  // provided document.
-  const linkMenuItems = linkCall.items;
-  assertEquals(4, linkMenuItems.length);
-  assertEquals('Link 1', linkMenuItems[0].title);
-  assertEquals('Link 2', linkMenuItems[1].title);
-  assertEquals('Link 3', linkMenuItems[2].title);
-  assertEquals('Link 4', linkMenuItems[3].title);
-
-  for (const item of linkMenuItems) {
-    assertFalse(item.isActive);
-    // Verify the callback IDs are unique.
-    for (const otherItem of linkMenuItems) {
-      if (item !== otherItem) {
-        assertNotEquals(item.callbackId, otherItem.callbackId);
-      }
-    }
-  }
-});
-
-TEST_F(
-    'ChromeVoxPanelNodeMenuBackgroundTest', 'FormControls', async function() {
-      await this.runWithLoadedTree(this.formControlsDoc);
-      const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData(
-          'panel_menu_form_controls');
-
-      assertEquals(this.expectedMenuCount, menus.length);
-      assertEquals('panel_menu_form_controls', menus[3].title);
-      const formMenuId = menus[3].menuId;
-
-      // The form controls menu will have a total of 2 calls, as it has more
-      // than 5 items (the batch size). And because it is specified as the
-      // activated menu, we expect both of those calls to happen before any
-      // others.
-      assertEquals(this.expectedMenuCount + 1, PanelBridge.calls.length);
-      const formCall1 = PanelBridge.calls.shift();
-      assertEquals(formMenuId, formCall1.id);
-      const formCall2 = PanelBridge.calls.shift();
-      assertEquals(formMenuId, formCall2.id);
-
-      // All calls to add menu items other than the form control menu call
-      // should indicate there are no relevant nodes.
-      for (const call of PanelBridge.calls) {
-        assertNotEquals(formMenuId, call.id);
-        assertEquals(1, call.items.length);
-        assertEquals('None', call.items[0].title);
-      }
-
-      // Check the contents of the form control call match what we expect based
-      // on the provided document.
-      const formMenuItems = formCall1.items;
-      formMenuItems.append(formCall2.items);
-      assertEquals(9, formMenuItems.length);
-
-      assertEquals('button', formMenuItems[0].title);
-      assertEquals('textInput', formMenuItems[1].title);
-      assertEquals('textarea', formMenuItems[2].title);
-      assertEquals('checkbox', formMenuItems[3].title);
-      assertEquals('color', formMenuItems[4].title);
-      assertEquals('slider', formMenuItems[5].title);
-      assertEquals('switch', formMenuItems[6].title);
-      assertEquals('tab', formMenuItems[7].title);
-      assertEquals('tree', formMenuItems[8].title);
-
-      // Because this menu is activated, and the focus is on the tree, we expect
-      // it to be active as well.
-      const treeItem = formMenuItems.pop();
-      assertTrue(treeItem.isActive);
-
-      for (const item of formMenuItems) {
-        assertFalse(item.isActive);
-      }
-    });
-
-TEST_F('ChromeVoxPanelNodeMenuBackgroundTest', 'Tables', async function() {
-  await this.runWithLoadedTree(this.tablesDoc);
-  const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData();
-
-  assertEquals(this.expectedMenuCount, menus.length);
-  assertEquals('role_table', menus[4].title);
-  const tableMenuId = menus[4].menuId;
-
-  await this.yieldForPopulation();
-
-  // The table menu should have less than 5 items, which is the batch size, so
-  // we expect only one call for each menu (the others all being empty).
-  assertEquals(this.expectedMenuCount, PanelBridge.calls.length);
-
-  let tableCall;
-  // All calls to add menu items other than the table menu call should
-  // indicate there are no relevant nodes.
-  for (const call of PanelBridge.calls) {
-    if (call.id === tableMenuId) {
-      tableCall = call;
-      continue;
-    }
-    assertEquals(1, call.items.length);
-    assertEquals('None', call.items.title);
-  }
-
-  // Check the contents of the table call match what we expect based on the
-  // provided document.
-  const tableMenuItems = tableCall.items;
-  assertEquals(2, tableMenuItems.length);
-  assertEquals('grid', tableMenuItems[0].title);
-  assertEquals('table', tableMenuItems[1].title);
-
-  assertFalse(tableMenuItems[0].isActive);
-  assertFalse(tableMenuItems[1].isActive);
-  assertNotEquals(tableMenuItems[0].callbackId, tableMenuItems[1].callbackId);
-});
-
-TEST_F('ChromeVoxPanelNodeMenuBackgroundTest', 'MixedData', async function() {
-  await this.runWithLoadedTree(this.mixedDoc);
-  const menus = PanelNodeMenuBackground.createAllPanelNodeMenuData();
-
-  assertEquals('role_heading', menus[0].title);
-  const headingId = menus[0].menuId;
-  assertEquals('role_landmark', menus[1].title);
-  const landmarkId = menus[1].menuId;
-  assertEquals('role_link', menus[2].title);
-  const linkId = menus[2].menuId;
-  assertEquals('panel_menu_form_controls', menus[3].title);
-  const formId = menus[3].menuId;
-  assertEquals('role_table', menus[4].title);
-  const tableId = menus[4].menuId;
-
-  await this.yieldForPopulation();
-  assertEquals(this.expectedMenuCount, PanelBridge.calls.length);
-
-  const headingCall = PanelBridge.calls.find(call => call.id === headingId);
-  const landmarkCall = PanelBridge.calls.find(call => call.id === landmarkId);
-  const linkCall = PanelBridge.calls.find(call => call.id === linkId);
-  const formCall = PanelBridge.calls.find(call => call.id === formId);
-  const tableCall = PanelBridge.calls.find(call => call.id === tableId);
-
-  assertEquals(1, headingCall.items.length);
-  assertEquals('header', headingCall.items[0].title);
-  assertEquals(1, landmarkCall.items.length);
-  assertEquals('region', landmarkCall.items[0].title);
-  assertEquals(1, linkCall.items.length);
-  assertEquals('link', linkCall.items[0].title);
-  assertEquals(1, formCall.items.length);
-  assertEquals('button', formCall.items[0].title);
-  assertEquals(1, tableCall.items.length);
-  assertEquals('table', tableCall.items[0].title);
-});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
index aad17ddb..5d1a4d9 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();
 
-BridgeHelper.registerHandler(
-    /* target= */ 'ChromeVoxPrefs', 'getPrefs',
-    () => ChromeVoxPrefs.instance.getPrefs());
-BridgeHelper.registerHandler(
-    /* target= */ 'ChromeVoxPrefs', 'setLoggingPrefs',
+const target = 'ChromeVoxPrefs';
+BackgroundBridge.registerHandler(
+    target, 'getPrefs', () => ChromeVoxPrefs.instance.getPrefs());
+BackgroundBridge.registerHandler(
+    target, 'setLoggingPrefs',
     ({key, value}) => ChromeVoxPrefs.instance.setLoggingPrefs(key, value));
-BridgeHelper.registerHandler(
-    /* target= */ 'ChromeVoxPrefs', 'setPref',
+BackgroundBridge.registerHandler(
+    target, 'setPref',
     ({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 403062b..773e279 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/background_bridge.js
@@ -3,15 +3,12 @@
 // found in the LICENSE file.
 
 /**
- * @fileoverview Provides an interface for non-background renderer contexts
- * (options, panel, etc.) to communicate with the background.
+ * @fileoverview Provides an interface for non-background contexts (options,
+ * panel, etc.) to communicate with the background.
  */
 
 goog.provide('BackgroundBridge');
 
-goog.require('BridgeHelper');
-goog.require('PanelNodeMenuData');
-
 BackgroundBridge.BrailleBackground = {
   /**
    * Translate braille cells into text.
@@ -19,13 +16,13 @@
    * @return {!Promise<?string>}
    */
   async backTranslate(cells) {
-    return BridgeHelper.sendMessage(
+    return BackgroundBridge.sendMessage_(
         'BrailleBackground', 'backTranslate', cells);
   },
 
   /** @param {string} brailleTable The table for this translator to use. */
   async refreshBrailleTable(brailleTable) {
-    return BridgeHelper.sendMessage(
+    return BackgroundBridge.sendMessage_(
         'BrailleBackground', 'refreshBrailleTable', brailleTable);
   },
 };
@@ -37,7 +34,7 @@
    *     localStorage.
    */
   async getPrefs() {
-    return BridgeHelper.sendMessage('ChromeVoxPrefs', 'getPrefs');
+    return BackgroundBridge.sendMessage_('ChromeVoxPrefs', 'getPrefs');
   },
 
   /**
@@ -46,7 +43,7 @@
    * @param {boolean} value The new value of the pref.
    */
   async setLoggingPrefs(key, value) {
-    return BridgeHelper.sendMessage(
+    return BackgroundBridge.sendMessage_(
         'ChromeVoxPrefs', 'setLoggingPrefs', {key, value});
   },
 
@@ -56,50 +53,68 @@
    * @param {Object|string|boolean} value The new value of the pref.
    */
   async setPref(key, value) {
-    return BridgeHelper.sendMessage('ChromeVoxPrefs', 'setPref', {key, value});
+    return BackgroundBridge.sendMessage_(
+        'ChromeVoxPrefs', 'setPref', {key, value});
   },
 };
 
-BackgroundBridge.PanelBackground = {
-  async createNewISearch() {
-    return BridgeHelper.sendMessage('PanelBackground', 'createNewISearch');
-  },
+// Helper functions:
 
-  /**
-   * Performs a search.
-   * @param {string} searchStr
-   * @param {constants.Dir} dir
-   * @param {boolean=} opt_nextObject
-   */
-  async incrementalSearch(searchStr, dir, opt_nextObject) {
-    return BridgeHelper.sendMessage(
-        'PanelBackground', 'incrementalSearch',
-        {searchStr, dir, opt_nextObject});
-  },
+/** @private {!Object<string, Object<string, Function>>} */
+BackgroundBridge.handlers = {};
 
-  async setRangeToISearchNode() {
-    return BridgeHelper.sendMessage('PanelBackground', 'setRangeToISearchNode');
-  },
+/**
+ * @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.PanelNodeMenuBackground = {
-  /**
-   * @param {string=} opt_activateMenuTitle
-   * @return {!Promise<!Array<!PanelNodeMenuData>>}
-   */
-  async createAllPanelNodeMenuData(opt_activateMenuTitle) {
-    return BridgeHelper.sendMessage(
-        'PanelNodeMenuBackground', 'createAllPanelNodeMenuData',
-        opt_activateMenuTitle);
-  },
-
-  /**
-   * @param {number} menuId
-   * @param {number} callbackId
-   */
-  async performMenuActionCallback(menuId, callbackId) {
-    return BridgeHelper.sendMessage(
-        'PanelNodeMenuBackground', 'performMenuActionCallback',
-        {menuId, callbackId});
-  },
+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_helper.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_helper.js
deleted file mode 100644
index 3c98d91..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/bridge_helper.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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');
-
-BridgeHelper.castTo = (type) => {
-  return (obj) => {
-    if (obj === null || obj === undefined) {
-      return obj;
-    }
-    Object.setPrototypeOf(obj, type.prototype);
-    return obj;
-  };
-};
-
-/** @private {!Object<string, Object<string, Function>>} */
-BridgeHelper.handlers_ = {};
-
-/**
- * This function should only be used by Bridges (e.g. BackgroundBridge,
- * PanelBridge) and not called directly by other classes.
- *
- * @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.
- */
-BridgeHelper.sendMessage = (target, action, value) => {
-  return new Promise(
-      resolve => chrome.runtime.sendMessage({target, action, value}, resolve));
-};
-
-/**
- * @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.
- */
-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/common/panel_bridge.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_bridge.js
deleted file mode 100644
index e0976e67..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_bridge.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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 Provides an interface for other renderer contexts (e.g. the
- * background context) to communicate with the ChromeVox panel.
- */
-
-goog.provide('PanelBridge');
-
-PanelBridge = {
-  /**
-   * @param {number} menuId
-   * @param {!Array<!PanelNodeMenuItemData>} itemArray
-   */
-  async addPanelNodeMenuItems(menuId, itemArray) {
-    return BridgeHelper.sendMessage(
-        'Panel', 'addPanelNodeMenuItems', {menuId, itemArray});
-  },
-};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_node_menu_data.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_node_menu_data.js
deleted file mode 100644
index 70eafc3..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/panel_node_menu_data.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// 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 Structures to hold the data for the panel's node menus.
- */
-goog.provide('PanelNodeMenuData');
-goog.provide('PanelNodeMenuItemData');
-
-PanelNodeMenuData = class {
-  /**
-   * @param {string} title
-   * @param {number} menuId
-   */
-  constructor(title, menuId) {
-    /** @public {string} */
-    this.title = title;
-    /** @public {number} */
-    this.menuId = menuId;
-  }
-};
-
-PanelNodeMenuItemData = class {
-  /**
-   * @param {string} title
-   * @param {number} callbackId
-   * @param {boolean} isActive True when the menu was explicitly activated and
-   *     the node is focused.
-   */
-  constructor(title, callbackId, isActive) {
-    /** @public {string} */
-    this.title = title;
-    /** @public {number} */
-    this.callbackId = callbackId;
-    /** @public {boolean} */
-    this.isActive = isActive;
-  }
-};
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search.js
new file mode 100644
index 0000000..f32ec5d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search.js
@@ -0,0 +1,262 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Objects related to incremental search.
+ */
+
+import {PanelInterface} from './panel_interface.js';
+
+const AutomationNode = chrome.automation.AutomationNode;
+const Dir = constants.Dir;
+const RoleType = chrome.automation.RoleType;
+
+/**
+ * An interface implemented by objects that wish to handle events related to
+ * incremental search.
+ * @interface
+ */
+class ISearchHandler {
+  constructor() {}
+
+  /**
+   * Called when there are no remaining nodes in the document matching
+   * search.
+   * @param {!AutomationNode} boundaryNode The last node before reaching either
+   * the start or end of the document.
+   */
+  onSearchReachedBoundary(boundaryNode) {}
+
+  /**
+   * Called when search result node changes.
+   * @param {!AutomationNode} node The new search result.
+   * @param {number} start The index into the name where the search match
+   *     starts.
+   * @param {number} end The index into the name where the search match ends.
+   */
+  onSearchResultChanged(node, start, end) {}
+}
+
+
+/**
+ * Controls an incremental search.
+ */
+export class ISearch {
+  /**
+   * @param {!cursors.Cursor} cursor
+   */
+  constructor(cursor) {
+    if (!cursor.node) {
+      throw 'Incremental search started from invalid range.';
+    }
+
+    /** @private {ISearchHandler} */
+    this.handler_;
+
+    const leaf = AutomationUtil.findNodePre(
+                     cursor.node, Dir.FORWARD, AutomationPredicate.leaf) ||
+        cursor.node;
+
+    /** @type {!cursors.Cursor} */
+    this.cursor = cursors.Cursor.fromNode(leaf);
+
+    /** @private {number} */
+    this.callbackId_ = 0;
+
+    // Global exports.
+    /** Exported for this background script. */
+    ChromeVox = chrome.extension.getBackgroundPage()['ChromeVox'];
+  }
+
+  /**
+   * @param {!ISearchHandler} handler
+   */
+  set handler(handler) {
+    this.handler_ = handler;
+  }
+
+  /**
+   * Performs a search.
+   * @param {string} searchStr
+   * @param {constants.Dir} dir
+   * @param {boolean=} opt_nextObject
+   */
+  search(searchStr, dir, opt_nextObject) {
+    clearTimeout(this.callbackId_);
+    const step = function() {
+      searchStr = searchStr.toLocaleLowerCase();
+      const node = this.cursor.node;
+      let result = node;
+
+      if (opt_nextObject) {
+        // We want to start/continue the search at the next object.
+        result =
+            AutomationUtil.findNextNode(node, dir, AutomationPredicate.object);
+      }
+
+      do {
+        // Ask native to search the underlying data for a performance boost.
+        result = result.getNextTextMatch(searchStr, dir === Dir.BACKWARD);
+      } while (result && !AutomationPredicate.object(result));
+
+      if (result) {
+        this.cursor = cursors.Cursor.fromNode(result);
+        const start = result.name.toLocaleLowerCase().indexOf(searchStr);
+        const end = start + searchStr.length;
+        this.handler_.onSearchResultChanged(result, start, end);
+      } else {
+        this.handler_.onSearchReachedBoundary(this.cursor.node);
+      }
+    };
+
+    this.callbackId_ = setTimeout(step.bind(this), 0);
+  }
+
+  clear() {
+    clearTimeout(this.callbackId_);
+  }
+}
+
+
+/**
+ * @implements {ISearchHandler}
+ */
+export class ISearchUI {
+  /**
+   * @param {Element} input
+   */
+  constructor(input) {
+    /** @type {ChromeVoxState} @private */
+    this.background_ =
+        chrome.extension.getBackgroundPage()['ChromeVoxState']['instance'];
+    this.iSearch_ = new ISearch(this.background_.currentRange.start);
+    this.input_ = input;
+    this.dir_ = Dir.FORWARD;
+    this.iSearch_.handler = this;
+
+    this.onKeyDown = this.onKeyDown.bind(this);
+    this.onTextInput = this.onTextInput.bind(this);
+
+    input.addEventListener('keydown', this.onKeyDown, true);
+    input.addEventListener('textInput', this.onTextInput, false);
+  }
+
+  /**
+   * @param {Element} input
+   * @return {ISearchUI}
+   */
+  static init(input) {
+    if (ISearchUI.instance_) {
+      ISearchUI.instance_.destroy();
+    }
+
+    if (!input) {
+      throw 'Expected search input';
+    }
+
+    ISearchUI.instance_ = new ISearchUI(input);
+    input.focus();
+    input.select();
+    return ISearchUI.instance_;
+  }
+
+  /**
+   * Listens to key down events.
+   * @param {Event} evt
+   * @return {boolean}
+   */
+  onKeyDown(evt) {
+    switch (evt.key) {
+      case 'ArrowUp':
+        this.dir_ = Dir.BACKWARD;
+        break;
+      case 'ArrowDown':
+        this.dir_ = Dir.FORWARD;
+        break;
+      case 'Escape':
+        PanelInterface.instance.closeMenusAndRestoreFocus();
+        return false;
+      case 'Enter':
+        PanelInterface.instance.setPendingCallback(function() {
+          const node = this.iSearch_.cursor.node;
+          if (!node) {
+            return;
+          }
+          chrome.extension.getBackgroundPage()
+              .ChromeVoxState.instance['navigateToRange'](
+                  cursors.Range.fromNode(node));
+        }.bind(this));
+        PanelInterface.instance.closeMenusAndRestoreFocus();
+        return false;
+      default:
+        return false;
+    }
+    this.iSearch_.search(this.input_.value, this.dir_, true);
+    evt.preventDefault();
+    evt.stopPropagation();
+    return false;
+  }
+
+  /**
+   * Listens to text input events.
+   * @param {Event} evt
+   * @return {boolean}
+   */
+  onTextInput(evt) {
+    const searchStr = evt.target.value + evt.data;
+    this.iSearch_.clear();
+    this.iSearch_.search(searchStr, this.dir_);
+    return true;
+  }
+
+  /**
+   * @override
+   */
+  onSearchReachedBoundary(boundaryNode) {
+    this.output_(boundaryNode);
+    ChromeVox.earcons.playEarcon(Earcon.WRAP);
+  }
+
+  /**
+   * @override
+   */
+  onSearchResultChanged(node, start, end) {
+    this.output_(node, start, end);
+  }
+
+  /**
+   * @param {!AutomationNode} node
+   * @param {number=} opt_start
+   * @param {number=} opt_end
+   * @private
+   */
+  output_(node, opt_start, opt_end) {
+    Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH);
+    const o = new Output();
+    if (opt_start && opt_end) {
+      o.withString([
+        node.name.substr(0, opt_start),
+        node.name.substr(opt_start, opt_end - opt_start),
+        node.name.substr(opt_end)
+      ].join(', '));
+      o.format('$role', node);
+    } else {
+      o.withRichSpeechAndBraille(
+          cursors.Range.fromNode(node), null, OutputEventType.NAVIGATE);
+    }
+    o.go();
+
+    this.background_.setCurrentRange(cursors.Range.fromNode(node));
+  }
+
+  /** Unregisters event handlers. */
+  destroy() {
+    this.iSearch_.handler_ = null;
+    this.iSearch_ = null;
+    const input = this.input_;
+    this.input_ = null;
+    input.removeEventListener('keydown', this.onKeyDown, true);
+    input.removeEventListener('textInput', this.onTextInput, false);
+  }
+}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_test.js
new file mode 100644
index 0000000..eaea1b8
--- /dev/null
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_test.js
@@ -0,0 +1,117 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Include test fixture.
+GEN_INCLUDE([
+  '//chrome/browser/resources/chromeos/accessibility/chromevox/testing/chromevox_next_e2e_test_base.js'
+]);
+
+/**
+ * Test fixture for ISearch.
+ */
+ChromeVoxISearchTest = class extends ChromeVoxNextE2ETest {
+  /** @override */
+  get runtimeDeps() {
+    return ['ISearch', 'ISearchHandler'];
+  }
+
+  /** @override */
+  async setUpDeferred() {
+    await super.setUpDeferred();
+    await importModule('ISearch', '/chromevox/panel/i_search.js');
+  }
+
+  get linksAndHeadingsDoc() {
+    return `
+      <p>start</p>
+      <a href='#a'>Home</a>
+      <a href='#b'>About US</a>
+      <p>
+        <h1>Latest Breaking News</h1>
+        <a href='foo'>See more...</a>
+      </p>
+      <a href='#bar'>Questions?</a>
+      <h2>Privacy Policy</h2>
+      <p>end<span>of test</span></p>
+    `;
+  }
+};
+
+
+/**
+ * @implements {ISearchHandler}
+ */
+class FakeISearchHandler {
+  constructor(testObj) {
+    this.test = testObj;
+    this.expect_ = [];
+  }
+
+  /** @override */
+  onSearchReachedBoundary(boundaryNode) {
+    this.expect_.shift()({node: boundaryNode, isBoundary: true});
+  }
+
+  /** @override */
+  onSearchResultChanged(node, start, end) {
+    this.expect_.shift()({node, start, end});
+  }
+
+  expect(str, opt_callback) {
+    this.expect_.push(this.test.newCallback(function(args) {
+      const node = args.node;
+      let actual = node.name || node.role;
+      if (args.start && args.end) {
+        actual = 'start=' + args.start + ' end=' + args.end + ' text=' + actual;
+      }
+      if (args.isBoundary) {
+        actual = 'boundary=' + actual;
+      }
+      assertEquals(str, actual);
+      opt_callback && opt_callback();
+    }));
+  }
+}
+
+
+TEST_F('ChromeVoxISearchTest', 'Simple', async function() {
+  const rootNode = await this.runWithLoadedTree(this.linksAndHeadingsDoc);
+  const handler = new FakeISearchHandler(this);
+  const search = new ISearch(new cursors.Cursor(rootNode, 0));
+  search.handler = handler;
+
+  // Simple forward search.
+  search.search('US', 'forward');
+  handler.expect(
+      'start=6 end=8 text=About US',
+      search.search.bind(search, 'start', 'backward'));
+
+  handler.expect(
+      'start',
+      // Boundary (beginning).
+      search.search.bind(search, 'foo', 'backward'));
+
+  handler.expect(
+      'boundary=start',
+      // Boundary (end).
+      search.search.bind(search, 'foo', 'forward'));
+
+  // Search "focus" doesn't move.
+  handler.expect(
+      'boundary=start',
+      // Mixed case substring.
+      search.search.bind(search, 'bReak', 'forward'));
+
+  handler.expect(
+      'start=7 end=12 text=Latest Breaking News',
+      search.search.bind(search, 'bReaki', 'forward'));
+
+  // Incremental search stays on the current node.
+  handler.expect(
+      'start=7 end=13 text=Latest Breaking News',
+      search.search.bind(search, 'bReakio', 'forward'));
+
+  // No results for the search.
+  handler.expect('boundary=Latest Breaking News');
+});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js
deleted file mode 100644
index 2cb4ca2..0000000
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/i_search_ui.js
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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 The UI for searching the ChromeVox panel menus incrementally.
- */
-
-import {PanelInterface} from './panel_interface.js';
-
-export class ISearchUI {
-  /**
-   * @param {Element} input
-   */
-  constructor(input) {
-    /** @private {Element} */
-    this.input_ = input;
-    /** @private {constants.Dir} */
-    this.dir_ = constants.Dir.FORWARD;
-
-    this.onKeyDown = (event) => this.onKeyDown_(event);
-    this.onTextInput = (event) => this.onTextInput_(event);
-
-    input.addEventListener('keydown', this.onKeyDown, true);
-    input.addEventListener('textInput', this.onTextInput, false);
-  }
-
-  /** @param {Element} input */
-  static async init(input) {
-    if (ISearchUI.instance_) {
-      ISearchUI.instance_.destroy();
-    }
-
-    if (!input) {
-      throw 'Expected search input';
-    }
-
-    await BackgroundBridge.PanelBackground.createNewISearch();
-    ISearchUI.instance_ = new ISearchUI(input);
-    input.focus();
-    input.select();
-  }
-
-  /**
-   * Listens to key down events.
-   * @param {Event} evt
-   * @return {boolean}
-   * @private
-   */
-  onKeyDown_(evt) {
-    switch (evt.key) {
-      case 'ArrowUp':
-        this.dir_ = constants.Dir.BACKWARD;
-        break;
-      case 'ArrowDown':
-        this.dir_ = constants.Dir.FORWARD;
-        break;
-      case 'Escape':
-        PanelInterface.instance.closeMenusAndRestoreFocus();
-        return false;
-      case 'Enter':
-        PanelInterface.instance.setPendingCallback(
-            async () =>
-                await BackgroundBridge.PanelBackground.setRangeToISearchNode());
-        PanelInterface.instance.closeMenusAndRestoreFocus();
-        return false;
-      default:
-        return false;
-    }
-    BackgroundBridge.PanelBackground.incrementalSearch(
-        this.input_.value, this.dir_, true);
-    evt.preventDefault();
-    evt.stopPropagation();
-    return false;
-  }
-
-  /**
-   * Listens to text input events.
-   * @param {Event} evt
-   * @return {boolean}
-   * @private
-   */
-  onTextInput_(evt) {
-    const searchStr = evt.target.value + evt.data;
-    BackgroundBridge.PanelBackground.incrementalSearch(searchStr, this.dir_);
-    return true;
-  }
-
-  /** Unregisters event handlers. */
-  destroy() {
-    const input = this.input_;
-    this.input_ = null;
-    input.removeEventListener('keydown', this.onKeyDown, true);
-    input.removeEventListener('textInput', this.onTextInput, false);
-  }
-}
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
index 9d189391..a2b33f6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -279,7 +279,7 @@
   /**
    * Open / show the ChromeVox Menus.
    * @param {Event=} opt_event An optional event that triggered this.
-   * @param {string=} opt_activateMenuTitle Title msg id of menu to open.
+   * @param {*=} opt_activateMenuTitle Title msg id of menu to open.
    */
   static onOpenMenus(opt_event, opt_activateMenuTitle) {
     // If the menu was already open, close it now and exit early.
@@ -503,11 +503,24 @@
             Panel.onClose();
           });
 
-      const nodeMenuData =
-          await BackgroundBridge.PanelNodeMenuBackground
-              .createAllPanelNodeMenuData(opt_activateMenuTitle);
-      for (const data of nodeMenuData) {
-        Panel.nodeMenus_[data.menuId] = Panel.addNodeMenu(data);
+      const roleListMenuMapping = [
+        {menuTitle: 'role_heading', predicate: AutomationPredicate.heading},
+        {menuTitle: 'role_landmark', predicate: AutomationPredicate.landmark},
+        {menuTitle: 'role_link', predicate: AutomationPredicate.link}, {
+          menuTitle: 'panel_menu_form_controls',
+          predicate: AutomationPredicate.formField
+        },
+        {menuTitle: 'role_table', predicate: AutomationPredicate.table}
+      ];
+
+      for (let i = 0; i < roleListMenuMapping.length; ++i) {
+        const menuTitle = roleListMenuMapping[i].menuTitle;
+        const predicate = roleListMenuMapping[i].predicate;
+        // Create node menus asynchronously (because it may require
+        // searching a long document) unless that's the specific menu the
+        // user requested.
+        const async = (menuTitle !== opt_activateMenuTitle);
+        Panel.addNodeMenu(menuTitle, node, predicate, async);
       }
 
       if (node && node.standardActions) {
@@ -559,12 +572,12 @@
   }
 
   /** Open incremental search. */
-  static async onSearch() {
+  static onSearch() {
     Panel.setMode(PanelMode.SEARCH);
     Panel.clearMenus();
     Panel.pendingCallback_ = null;
     Panel.updateFromPrefs();
-    await ISearchUI.init(Panel.searchInput_);
+    ISearchUI.init(Panel.searchInput_);
   }
 
   /**
@@ -745,12 +758,15 @@
   }
 
   /**
-   * Create a new node menu with the given data and add it to the menu bar.
-   * @param {!PanelNodeMenuData} data
-   * @return {!PanelNodeMenu}
+   * Create a new node menu with the given name and add it to the menu bar.
+   * @param {string} menuMsg The msg id of the new menu to add.
+   * @param {!chrome.automation.AutomationNode} node
+   * @param {AutomationPredicate.Unary} pred
+   * @param {boolean} defer If true, defers populating the menu.
+   * @return {PanelMenu} The menu just created.
    */
-  static addNodeMenu(data) {
-    const menu = new PanelNodeMenu(data);
+  static addNodeMenu(menuMsg, node, pred, defer) {
+    const menu = new PanelNodeMenu(menuMsg, node, pred, defer);
     $('menu-bar').appendChild(menu.menuBarItemElement);
     menu.menuBarItemElement.addEventListener('mouseover', function() {
       Panel.activateMenu(menu, true /* activateFirstItem */);
@@ -1296,8 +1312,6 @@
   showContextMenu: 'show_context_menu'
 };
 
-/** @private {!Array<!PanelNodeMenu>} */
-Panel.nodeMenus_ = [];
 
 /**
  * @private {string}
@@ -1343,7 +1357,3 @@
 function $(id) {
   return document.getElementById(id);
 }
-
-BridgeHelper.registerHandler(
-    /* target= */ 'Panel', 'addPanelNodeMenuItems',
-    ({menuId, itemArray}) => Panel.nodeMenus_[menuId].addMenuItems(itemArray));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
index 2dd5c4a0..a1cfa02 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_loader.js
@@ -8,7 +8,6 @@
 
 goog.require('AutomationTreeWalker');
 goog.require('AutomationUtil');
-goog.require('BridgeHelper');
 goog.require('ChromeVoxState');
 goog.require('EventSourceType');
 goog.require('KeyCode');
@@ -18,8 +17,6 @@
 goog.require('Output');
 goog.require('Output');
 goog.require('PanelCommand');
-goog.require('PanelNodeMenuData');
-goog.require('PanelNodeMenuItemData');
 goog.require('QueueMode');
 goog.require('constants');
 goog.require('cursors.Cursor');
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js
index c7ab8b5..99abd0c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_menu.js
@@ -311,24 +311,27 @@
 
 
 export class PanelNodeMenu extends PanelMenu {
-  /** @param {!PanelNodeMenuData} data */
-  constructor(data) {
-    super(data.title);
+  /**
+   * @param {string} menuMsg The msg id of the menu.
+   * @param {chrome.automation.AutomationNode} node ChromeVox's current
+   *     position.
+   * @param {AutomationPredicate.Unary} pred Filter to use on the document.
+   * @param {boolean} async If true, populates the menu asynchronously by
+   *     posting a task after searching each chunk of nodes.
+   */
+  constructor(menuMsg, node, pred, async) {
+    super(menuMsg);
+    /** @private {AutomationNode} */
+    this.node_ = node;
+    /** @private {AutomationPredicate.Unary} */
+    this.pred_ = pred;
+    /** @private {boolean} */
+    this.async_ = async;
+    /** @private {AutomationTreeWalker|undefined} */
+    this.walker_;
     /** @private {number} */
-    this.id_ = data.menuId;
-  }
-
-  /** @param {!Array<!PanelNodeMenuItemData>} dataArray */
-  addMenuItems(dataArray) {
-    for (const data of dataArray) {
-      this.addMenuItem(
-          data.title, '', '', '',
-          () => BackgroundBridge.PanelNodeMenuBackground
-                    .performMenuActionCallback(this.id_, data.callbackId));
-      if (data.isActive) {
-        this.activeIndex_ = this.items_.length - 1;
-      }
-    }
+    this.nodeCount_ = 0;
+    this.populate_();
   }
 
   /** @override */
@@ -341,9 +344,100 @@
       this.activateItem(index);
     }
   }
+
+  /**
+   * Create the AutomationTreeWalker and kick off the search to find
+   * nodes that match the predicate for this menu.
+   * @private
+   */
+  populate_() {
+    if (!this.node_) {
+      this.finish_();
+      return;
+    }
+
+    const root = AutomationUtil.getTopLevelRoot(this.node_);
+    if (!root) {
+      this.finish_();
+      return;
+    }
+
+    this.walker_ = new AutomationTreeWalker(root, constants.Dir.FORWARD, {
+      visit(node) {
+        return !AutomationPredicate.shouldIgnoreNode(node);
+      }
+    });
+    this.nodeCount_ = 0;
+    this.findMoreNodes_();
+  }
+
+  /**
+   * Iterate over nodes from the tree walker. If a node matches the
+   * predicate, add an item to the menu.
+   *
+   * If |this.async_| is true, then after MAX_NODES_BEFORE_ASYNC nodes
+   * have been scanned, call setTimeout to defer searching. This frees
+   * up the main event loop to keep the panel menu responsive, otherwise
+   * it basically freezes up until all of the nodes have been found.
+   * @private
+   */
+  findMoreNodes_() {
+    while (this.walker_.next().node) {
+      const node = this.walker_.node;
+      if (this.pred_(node)) {
+        const output = new Output();
+        const range = cursors.Range.fromNode(node);
+        output.withoutHints();
+        output.withSpeech(range, range, OutputEventType.NAVIGATE);
+        const label = output.toString();
+        this.addMenuItem(label, '', '', '', (function() {
+                           const savedNode = node;
+                           return function() {
+                             chrome.extension.getBackgroundPage()
+                                 .ChromeVoxState.instance['navigateToRange'](
+                                     cursors.Range.fromNode(savedNode));
+                           };
+                         }()));
+
+        if (node === this.node_ && !this.async_) {
+          this.activeIndex_ = this.items_.length - 1;
+        }
+      }
+
+      if (this.async_) {
+        this.nodeCount_++;
+        if (this.nodeCount_ >= PanelNodeMenu.MAX_NODES_BEFORE_ASYNC) {
+          this.nodeCount_ = 0;
+          window.setTimeout(this.findMoreNodes_.bind(this), 0);
+          return;
+        }
+      }
+    }
+    this.finish_();
+  }
+
+  /**
+   * Called when we've finished searching for nodes. If no matches were
+   * found, adds an item to the menu indicating none were found.
+   * @private
+   */
+  finish_() {
+    if (!this.items_.length) {
+      this.addMenuItem(
+          Msgs.getMsg('panel_menu_item_none'), '', '', '', function() {});
+    }
+  }
 }
 
 /**
+ * The number of nodes to search before posting a task to finish
+ * searching.
+ * @const {number}
+ */
+PanelNodeMenu.MAX_NODES_BEFORE_ASYNC = 100;
+
+
+/**
  * Implements a menu that allows users to dynamically search the contents of the
  * ChromeVox menus.
  */
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
index 7f87abf..f06a41c0 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test.js
@@ -123,9 +123,9 @@
 });
 
 TEST_F('ChromeVoxPanelTest', 'SearchMenu', async function() {
+  const mockFeedback = this.createMockFeedback();
   await this.runWithLoadedTree(this.linksDoc);
   new PanelCommand(PanelCommandType.OPEN_MENUS).send();
-  const mockFeedback = this.createMockFeedback();
   await this.waitForMenu('panel_search_menu');
   await mockFeedback
       .expectSpeech('Search the menus', /Type to search the menus/)
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js
index 325bef8d..1db3358 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel_test_base.js
@@ -31,7 +31,6 @@
    * chrome.extension.getViews for it.
    */
   getPanel() {
-    assertNotNullNorUndefined(this.getPanelWindow().Panel);
     return this.getPanelWindow().Panel;
   }
 };
diff --git a/chrome/browser/resources/chromeos/accessibility/common/testing/documents.js b/chrome/browser/resources/chromeos/accessibility/common/testing/documents.js
deleted file mode 100644
index ee0b8062..0000000
--- a/chrome/browser/resources/chromeos/accessibility/common/testing/documents.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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 document fragments to allow for easier test
- * creation.
- */
-
-const Documents = {
-  application: `<div role="application" id="application">application</div>`,
-  banner: `<div role="banner" id="banner">banner</div>`,
-  button: `<button id="button">button</button>`,
-  checkbox: `<input type="checkbox" id="checkbox"></input>
-      <label for="checkbox">checkbox</label>`,
-  color: `<input type="color" id="color"></input>
-      <label for="color">color</label>`,
-  complementary:
-      `<div role="complementary" id="complementary">Complementary</div>`,
-  form: `<form aria-label="form" id="form"></form>`,
-  grid: `<div role="grid" id="grid">grid</div>`,
-  header: `<h1 id="header">header</header>`,
-  link: `<a href="#link" id="link">link</a>`,
-  main: `<div role="main" id="main">main</div>`,
-  navigation: `<nav id="navigation">navigation</nav>`,
-  region: `<div role="region" id="region">region</div>`,
-  search: `<input type="text" role="search" id="search"></input>
-      <label for="search">search</label>`,
-  slider: `<input type="range" id="slider"></input>
-      <label for="slider">slider</label>`,
-  switch: `<button role="switch" id="switch" aria-checked=true>switch</button>
-    <script>
-        const switchElement = document.getElementById("switch");
-        switchElement.onclick =
-            () => switchElement.ariaChecked = !switchElement.ariaChecked;
-    </script>`,
-  tab: `<div role="tab" id="tab">tab</div>`,
-  table: `<table id="table" aria-label="table"></table>`,
-  textarea: `<textarea aria-label="textarea" id="textarea"></textarea>`,
-  textInput:
-      `<input type="text" aria-label="textInput" id="textInput"></input>`,
-  tree: `<div role="tree" id="tree">tree</div>`,
-};
diff --git a/chrome/browser/resources/chromeos/login/oobe_polymer3.js b/chrome/browser/resources/chromeos/login/oobe_polymer3.js
index 5d8415f..0cd080f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_polymer3.js
+++ b/chrome/browser/resources/chromeos/login/oobe_polymer3.js
@@ -13,6 +13,7 @@
 import 'chrome://oobe/test_api/test_api.m.js';
 import {i18nTemplate} from 'chrome://resources/js/i18n_template_no_process.m.js';
 import {commonScreensList, loginScreensList, oobeScreensList} from 'chrome://oobe/screens.js';
+import {MultiTapDetector} from './multi_tap_detector.m.js';
 // clang-format on
 
 /**
@@ -48,17 +49,25 @@
   }
 }
 
-function initializeDebugger() {
-  if (document.readyState === 'loading') {
-    return;
-  }
-  document.removeEventListener('DOMContentLoaded', initializeDebugger);
-  OobeDebugger.DebuggerUI.getInstance().register(document.body);
-}
-
 // Create the global values attached to `window` that are used
 // for accessing OOBE controls from the browser side.
 function prepareGlobalValues(globalValue) {
+  // '$(id)' is an alias for 'document.getElementById(id)'. It is defined
+  // in chrome://resources/js/util.m.js. If this function is not exposed
+  // via the global object, it would not be available to tests that inject
+  // JavaScript directly into the renderer.
+  window.$ = $;
+
+  window.MultiTapDetector = MultiTapDetector;
+
+  // Install a global error handler so stack traces are included in logs.
+  window.onerror = function(message, file, line, column, error) {
+    if (error && error.stack) {
+      console.error(error.stack);
+    }
+  };
+
+  // TODO(crbug.com/1229130) - Remove the necessity for these global objects.
   if (globalValue.cr == undefined) {
     globalValue.cr = {};
   }
@@ -74,6 +83,35 @@
   globalValue.Oobe = Oobe;
 }
 
+function initializeOobe() {
+  if (document.readyState === 'loading') {
+    return;
+  }
+  document.removeEventListener('DOMContentLoaded', initializeOobe);
+
+  // Initialize the on-screen debugger if present.
+  if (OobeDebugger.DebuggerUI) {
+    OobeDebugger.DebuggerUI.getInstance().register(document.body);
+  }
+
+  try {
+    Oobe.initialize();
+  } finally {
+    // TODO(crbug.com/712078): Do not set readyForTesting in case of that
+    // initialize() is failed. Currently, in some situation, initialize()
+    // raises an exception unexpectedly. It means testing APIs should not
+    // be called then. However, checking it here now causes bots failures
+    // unfortunately. So, as a short term workaround, here set
+    // readyForTesting even on failures, just to make test bots happy.
+    Oobe.readyForTesting = true;
+  }
+
+  // Mark initialization complete and wake any callers that might be waiting
+  // for OOBE to load.
+  cr.ui.Oobe.initializationComplete = true;
+  cr.ui.Oobe.initCallbacks.forEach(resolvePromise => resolvePromise());
+}
+
 (function (root) {
     i18nTemplate.process(document, loadTimeData);
     prepareGlobalValues(window);
@@ -83,14 +121,9 @@
     const isOobeFlow = loadTimeData.getBoolean('isOobeFlow');
     addScreensToMainContainer(isOobeFlow ? oobeScreensList : loginScreensList);
 
-    Oobe.initialize();
-
-    // Initialize the debugger if it has been defined.
-    if (OobeDebugger.DebuggerUI) {
-      if (document.readyState === 'loading') {
-          document.addEventListener('DOMContentLoaded', initializeDebugger);
-        } else {
-          initializeDebugger();
-      }
+    if (document.readyState === 'loading') {
+        document.addEventListener('DOMContentLoaded', initializeOobe);
+      } else {
+        initializeOobe();
     }
 })(window);
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.ts b/chrome/browser/resources/new_tab_page/modules/modules.ts
index 068f3c35..a25ee4a2 100644
--- a/chrome/browser/resources/new_tab_page/modules/modules.ts
+++ b/chrome/browser/resources/new_tab_page/modules/modules.ts
@@ -47,6 +47,11 @@
 const SHORT_CLASS_NAME: string = 'short';
 const TALL_CLASS_NAME: string = 'tall';
 
+// When a pair of short module containers are by each other, they are considered
+// siblings and wrapped in another container.
+const SHORT_MODULE_SIBLING_1: string = 'short-module-sibling-one';
+const SHORT_MODULE_SIBLING_2: string = 'short-module-sibling-two';
+
 /** Container for the NTP modules. */
 export class ModulesElement extends PolymerElement {
   static get is() {
@@ -197,6 +202,10 @@
         this.modulesShownToUser = !moduleContainer.hidden;
       }
       if (loadTimeData.getBoolean('modulesRedesignedLayoutEnabled')) {
+        // Remove short module sibling container class name from short modules
+        // that were in a sibling container before.
+        moduleContainer.classList.toggle(SHORT_MODULE_SIBLING_1, false);
+        moduleContainer.classList.toggle(SHORT_MODULE_SIBLING_2, false);
         // Wrap pairs of sibling short modules in a container. All other
         // modules will be placed in a container of their own.
         if ((moduleContainer.classList.contains(SHORT_CLASS_NAME) ||
@@ -208,6 +217,7 @@
           // hidden modules to the sibling container, so if a user reverts a
           // module from its hidden state, the module assumes its original
           // position.
+          moduleContainer.classList.toggle(SHORT_MODULE_SIBLING_2, true);
           moduleContainerParent = shortModuleSiblingsContainer;
           this.$.modules.appendChild(shortModuleSiblingsContainer);
           // If another visible short module is added, a visible tall module is
@@ -229,6 +239,7 @@
           // Add current short module to a new container since the next one is
           // short as well by setting its parent to be
           // 'shortModuleSiblingsContainer'.
+          moduleContainer.classList.toggle(SHORT_MODULE_SIBLING_1, true);
           shortModuleSiblingsContainer =
               this.ownerDocument.createElement('div');
           shortModuleSiblingsContainer.classList.add(
@@ -536,6 +547,7 @@
           moduleContainers.indexOf((e.target as HTMLElement).parentElement!);
 
       const dragContainer = moduleContainers[dragIndex];
+      const dropContainer = moduleContainers[dropIndex];
 
       // To animate the modules as they are reordered we use the FLIP
       // (First, Last, Invert, Play) animation approach by @paullewis.
@@ -546,9 +558,25 @@
       const firstRects = undraggedModuleWrappers.map(moduleWrapper => {
         return moduleWrapper.getBoundingClientRect();
       });
-
-      moduleContainers.splice(dragIndex, 1);
-      moduleContainers.splice(dropIndex, 0, dragContainer);
+      // If a tall module is dragged to a short module sibling container, the
+      // modules in the sibling container should move together.
+      // We add or subtract 1, from the drop index, to make sure the tall module
+      // moves behind or in front of the first module in the sibling container.
+      if (dragContainer.classList.contains(TALL_CLASS_NAME) &&
+          dropContainer.classList.contains(SHORT_MODULE_SIBLING_1) &&
+          dragIndex < dropIndex) {
+        moduleContainers.splice(dragIndex, 1);
+        moduleContainers.splice(dropIndex + 1, 0, dragContainer);
+      } else if (
+          dragContainer.classList.contains(TALL_CLASS_NAME) &&
+          dropContainer.classList.contains(SHORT_MODULE_SIBLING_2) &&
+          dragIndex > dropIndex) {
+        moduleContainers.splice(dragIndex, 1);
+        moduleContainers.splice(dropIndex - 1, 0, dragContainer);
+      } else {
+        moduleContainers.splice(dragIndex, 1);
+        moduleContainers.splice(dropIndex, 0, dragContainer);
+      }
       this.appendModuleContainers_(moduleContainers);
 
       undraggedModuleWrappers.forEach((moduleWrapper, i) => {
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
index 1ab63ab..5c53104 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.js
@@ -108,7 +108,8 @@
   isPhoneHubCameraRollSetupRequired() {
     return this.isFeatureSupported(MultiDeviceFeature.PHONE_HUB_CAMERA_ROLL) &&
         this.pageContentData.cameraRollAccessStatus ===
-        PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED;
+        PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED &&
+        this.isFeatureAllowedByPolicy(MultiDeviceFeature.PHONE_HUB_CAMERA_ROLL);
   },
 
   /**
@@ -118,7 +119,8 @@
   isPhoneHubAppsSetupRequired() {
     return this.isFeatureSupported(MultiDeviceFeature.ECHE) &&
         this.pageContentData.isPhoneHubPermissionsDialogSupported &&
-        !this.pageContentData.isPhoneHubAppsAccessGranted;
+        !this.pageContentData.isPhoneHubAppsAccessGranted &&
+        this.isFeatureAllowedByPolicy(MultiDeviceFeature.ECHE);
   },
 
   /**
@@ -127,7 +129,9 @@
    */
   isPhoneHubNotificationsSetupRequired() {
     return this.pageContentData.notificationAccessStatus ===
-        PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED;
+        PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED &&
+        this.isFeatureAllowedByPolicy(
+            MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS);
   },
 
   /**
diff --git a/chrome/browser/resources/vr/assets/PRESUBMIT.py b/chrome/browser/resources/vr/assets/PRESUBMIT.py
index b5c0082..3a38c1c 100644
--- a/chrome/browser/resources/vr/assets/PRESUBMIT.py
+++ b/chrome/browser/resources/vr/assets/PRESUBMIT.py
@@ -44,13 +44,16 @@
         extension in {'sha1', 'png', 'wav'} and action in {'A', 'D'}):
       changed_asset_files[dirname].append((action, basename_without_extension))
     if (extension == 'sha1' or basename == 'vr_assets_component_files.json'):
-      changed_assets = True
+      # See if there are actually changes or if it's just --files or --all:
+      if file.ChangedContents():
+        changed_assets = True
     if basename == 'vr_assets_component_files.json':
       changed_component_list = True
     if basename == 'VERSION':
-      changed_version = True
       old_version = parse_version.ParseVersion(file.OldContents())
       new_version = parse_version.ParseVersion(file.NewContents())
+      if new_version != old_version:
+        changed_version = True
 
   local_version_filename = input_api.os_path.join(
       input_api.os_path.dirname(input_api.AffectedFiles()[0].LocalPath()),
diff --git a/chrome/browser/resources/webui_gallery/BUILD.gn b/chrome/browser/resources/webui_gallery/BUILD.gn
index 199b563..7cc74663 100644
--- a/chrome/browser/resources/webui_gallery/BUILD.gn
+++ b/chrome/browser/resources/webui_gallery/BUILD.gn
@@ -2,15 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//chrome/common/features.gni")
 import("//tools/grit/grit_rule.gni")
+import("//tools/polymer/html_to_wrapper.gni")
+import("//tools/typescript/ts_library.gni")
 import("//ui/webui/resources/tools/generate_grd.gni")
+import("webui_gallery.gni")
 
 assert(!is_android)
 
 grit("resources") {
-  defines = chrome_grit_defines
-
   # These arguments are needed since the grd is generated at build time.
   enable_input_discovery_for_gn_analyze = false
   source = "$target_gen_dir/resources.grd"
@@ -28,6 +28,36 @@
 generate_grd("build_grd") {
   grd_prefix = "webui_gallery"
   out_grd = "$target_gen_dir/resources.grd"
-  input_files = [ "webui_gallery.html" ]
+  input_files = [
+    "demos/cr_button_demo.html",
+    "demos/cr_checkbox_demo.html",
+    "webui_gallery.html",
+  ]
   input_files_base_dir = rebase_path(".", "//")
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+}
+
+html_to_wrapper("html_wrapper_files") {
+  in_files = html_files
+}
+
+copy("copy_ts_files") {
+  sources = ts_files
+  outputs = [ "$target_gen_dir/{{source_file_part}}" ]
+}
+
+ts_library("build_ts") {
+  root_dir = target_gen_dir
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  in_files = ts_files + html_wrapper_files
+  deps = [
+    "//third_party/polymer/v3_0:library",
+    "//ui/webui/resources:library",
+  ]
+  extra_deps = [
+    ":copy_ts_files",
+    ":html_wrapper_files",
+  ]
 }
diff --git a/chrome/browser/resources/webui_gallery/app.html b/chrome/browser/resources/webui_gallery/app.html
new file mode 100644
index 0000000..45f316e
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/app.html
@@ -0,0 +1,43 @@
+<style include="cr-nav-menu-item-style">
+  :host {
+    display: flex;
+    height: 100%;
+  }
+
+  #sidebar {
+    border-inline-end: 1px solid black;
+    box-sizing: border-box;
+    width: 256px;
+  }
+
+  a {
+    text-decoration: none;
+  }
+
+  iframe {
+    border: none;
+    height: 100%;
+    width: 100%;
+  }
+
+  #main {
+    flex: 1;
+  }
+</style>
+<div id="sidebar">
+  <cr-menu-selector>
+    <iron-selector selectable="a"
+        attr-for-selected="href" on-iron-activate="onSelectorActivate_"
+        on-click="onLinkClick_" selected-attribute="selected">
+      <template is="dom-repeat" items="[[demos]]"
+          on-dom-change="onDomChange_" notify-dom-change>
+        <a role="menuitem" href="demos/[[item.url]]"
+            class="cr-nav-menu-item">[[item.name]]</a>
+      </template>
+    </iron-selector>
+  </cr-menu-selector>
+</div>
+
+<div id="main">
+  <iframe src="[[currentSrc_]]"></iframe>
+</div>
diff --git a/chrome/browser/resources/webui_gallery/app.ts b/chrome/browser/resources/webui_gallery/app.ts
new file mode 100644
index 0000000..8911714
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/app.ts
@@ -0,0 +1,72 @@
+// 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.
+
+import '//resources/cr_elements/cr_menu_selector/cr_menu_selector.js';
+import '//resources/cr_elements/cr_nav_menu_item_style.js';
+import '//resources/polymer/v3_0/iron-selector/iron-selector.js';
+
+import {assert} from '//resources/js/assert_ts.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './app.html.js';
+
+export class WebuiGalleryAppElement extends PolymerElement {
+  static get is() {
+    return 'webui-gallery-app';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {
+      demos: {
+        type: Array,
+        value: function() {
+          return [
+            {
+              name: 'cr-button demo',
+              url: 'cr_button_demo.html',
+            },
+            {
+              name: 'cr-checkbox demo',
+              url: 'cr_checkbox_demo.html',
+            }
+          ];
+        },
+      },
+
+      currentSrc_: String,
+    };
+  }
+
+  private currentSrc_: string;
+
+  private onDomChange_() {
+    const first = this.shadowRoot!.querySelector('a');
+    assert(first);
+    const selector = this.shadowRoot!.querySelector('iron-selector');
+    assert(selector);
+    selector.selected = first.href;
+    this.currentSrc_ = first.href;
+  }
+
+  private onLinkClick_(e: Event) {
+    e.preventDefault();
+  }
+
+  private onSelectorActivate_(e: CustomEvent<{selected: string}>) {
+    const demoUrl = e.detail.selected;
+    this.currentSrc_ = demoUrl;
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'webui-gallery-app': WebuiGalleryAppElement;
+  }
+}
+
+customElements.define(WebuiGalleryAppElement.is, WebuiGalleryAppElement);
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_button_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_button_demo.html
new file mode 100644
index 0000000..d2e07a4b
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_button_demo.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>cr-button demo</title>
+    <style>
+      html,
+      body {
+        height: 100%;
+        overflow: hidden;
+        width: 100%;
+      }
+
+      body {
+        margin: 0;
+      }
+    </style>
+  </head>
+  <body>
+    Demo content will be added here
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_gallery/demos/cr_checkbox_demo.html b/chrome/browser/resources/webui_gallery/demos/cr_checkbox_demo.html
new file mode 100644
index 0000000..55660ca
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/demos/cr_checkbox_demo.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>cr-checkbox demo</title>
+    <style>
+      html,
+      body {
+        height: 100%;
+        overflow: hidden;
+        width: 100%;
+      }
+
+      body {
+        margin: 0;
+      }
+    </style>
+  </head>
+  <body>
+    Demo content will be added here
+  </body>
+</html>
diff --git a/chrome/browser/resources/webui_gallery/tsconfig_base.json b/chrome/browser/resources/webui_gallery/tsconfig_base.json
new file mode 100644
index 0000000..de14880
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/tsconfig_base.json
@@ -0,0 +1,7 @@
+{
+  "extends": "../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "noUnusedLocals": false,
+    "strictPropertyInitialization": false
+  }
+}
diff --git a/chrome/browser/resources/webui_gallery/webui_gallery.gni b/chrome/browser/resources/webui_gallery/webui_gallery.gni
new file mode 100644
index 0000000..b11378e
--- /dev/null
+++ b/chrome/browser/resources/webui_gallery/webui_gallery.gni
@@ -0,0 +1,20 @@
+# 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.
+
+# Files holding a Polymer element definition AND have an equivalent .html file.
+web_component_files = [ "app.ts" ]
+
+# Files that are passed as input to html_to_wrapper().
+html_files = []
+foreach(f, web_component_files) {
+  html_files += [ string_replace(f, ".ts", ".html") ]
+}
+
+# Files that are generated by html_to_wrapper().
+html_wrapper_files = []
+foreach(f, html_files) {
+  html_wrapper_files += [ f + ".ts" ]
+}
+
+ts_files = web_component_files
diff --git a/chrome/browser/resources/webui_gallery/webui_gallery.html b/chrome/browser/resources/webui_gallery/webui_gallery.html
index 95448d1..6f9f6ef 100644
--- a/chrome/browser/resources/webui_gallery/webui_gallery.html
+++ b/chrome/browser/resources/webui_gallery/webui_gallery.html
@@ -1,12 +1,24 @@
-<!doctype html>
+<!DOCTYPE html>
 <html dir="$i18n{textdirection}" lang="$i18n{language}">
   <head>
     <meta charset="utf-8">
-    <title>WebUI Gallery</title>
+    <title>WebUI gallery</title>
     <style>
+      html,
+      body {
+        height: 100%;
+        overflow: hidden;
+        width: 100%;
+      }
+
+      body {
+        margin: 0;
+      }
     </style>
   </head>
   <body>
-    Coming soon
+    <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
+    <script type="module" src="app.js"></script>
+    <webui-gallery-app></webui-gallery-app>
   </body>
 </html>
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
index 7e1fc1a..6082ccb 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.cc
@@ -23,7 +23,10 @@
 #include "chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.h"
 #include "chrome/browser/safe_browsing/extension_telemetry/remote_host_contacted_signal_processor.h"
 #include "chrome/browser/safe_browsing/extension_telemetry/tabs_execute_script_signal_processor.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/prefs/pref_service.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
+#include "components/safe_browsing/core/browser/sync/sync_utils.h"
 #include "components/safe_browsing/core/common/features.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -271,7 +274,8 @@
   auto callback = base::BindOnce(&ExtensionTelemetryService::OnUploadComplete,
                                  weak_factory_.GetWeakPtr());
   active_uploader_ = std::make_unique<ExtensionTelemetryUploader>(
-      std::move(callback), url_loader_factory_, std::move(upload_data));
+      std::move(callback), url_loader_factory_, std::move(upload_data),
+      GetTokenFetcher());
   active_uploader_->Start();
 }
 
@@ -312,7 +316,8 @@
     auto callback = base::BindOnce(&ExtensionTelemetryService::OnUploadComplete,
                                    weak_factory_.GetWeakPtr());
     active_uploader_ = std::make_unique<ExtensionTelemetryUploader>(
-        std::move(callback), url_loader_factory_, std::move(upload_data));
+        std::move(callback), url_loader_factory_, std::move(upload_data),
+        GetTokenFetcher());
     active_uploader_->Start();
   } else {
     active_report_.reset();
@@ -388,6 +393,20 @@
   return telemetry_report_pb;
 }
 
+std::unique_ptr<SafeBrowsingTokenFetcher>
+ExtensionTelemetryService::GetTokenFetcher() {
+  DCHECK(!profile_->IsOffTheRecord() &&
+         IsEnhancedProtectionEnabled(*profile_->GetPrefs()));
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile_);
+  if (identity_manager &&
+      safe_browsing::SyncUtils::IsPrimaryAccountSignedIn(identity_manager)) {
+    return std::make_unique<SafeBrowsingPrimaryAccountTokenFetcher>(
+        identity_manager);
+  }
+  return nullptr;
+}
+
 void ExtensionTelemetryService::DumpReportForTest(
     const ExtensionTelemetryReportRequest& report) {
   base::Time creation_time =
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
index 064e764e..69c469f 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service.h
@@ -40,6 +40,7 @@
 class ExtensionTelemetryReportRequest_ExtensionInfo;
 class ExtensionTelemetryUploader;
 class ExtensionTelemetryPersister;
+class SafeBrowsingTokenFetcher;
 
 // This class process extension signals and reports telemetry for a given
 // profile (regular profile only). It is used exclusively on the UI thread.
@@ -107,6 +108,10 @@
 
   void UploadPersistedFile(std::string report, bool success);
 
+  // Creates access token fetcher based on profile log-in status.
+  // Returns nullptr when the user is not signed in.
+  std::unique_ptr<SafeBrowsingTokenFetcher> GetTokenFetcher();
+
   std::unique_ptr<safe_browsing::ExtensionTelemetryPersister> persister_;
 
   // The profile with which this instance of the service is associated.
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_factory.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_factory.cc
index b306e56..a64808ed 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_factory.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_factory.cc
@@ -70,5 +70,4 @@
     const {
   return true;
 }
-
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
index a3a7526..72c69c8 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_service_unittest.cc
@@ -179,7 +179,6 @@
   EXPECT_FALSE(IsTelemetryServiceEnabled());
 
   // Destruct and restart service and verify that it starts disabled.
-
   telemetry_service_ = std::make_unique<ExtensionTelemetryService>(
       &profile_, test_url_loader_factory_.GetSafeWeakWrapper(),
       extension_registry_, extension_prefs_);
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc
index c479ed4..8539549 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc
@@ -9,6 +9,9 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
+#include "components/safe_browsing/core/common/utils.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "net/base/load_flags.h"
@@ -44,7 +47,8 @@
       destination: GOOGLE_OWNED_SERVICE
     }
     policy {
-      cookies_allowed: NO
+      cookies_allowed: YES
+      cookies_store: "Safe Browsing cookie store"
       setting:
         "Users can enable this feature by selecting 'Enhanced protection' "
         "under the Security->Safe Browsing setting. The feature is disabled by "
@@ -107,17 +111,19 @@
 ExtensionTelemetryUploader::ExtensionTelemetryUploader(
     OnUploadCallback callback,
     const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
-    std::unique_ptr<std::string> upload_data)
+    std::unique_ptr<std::string> upload_data,
+    std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher)
     : callback_(std::move(callback)),
       url_loader_factory_(url_loader_factory),
       upload_data_(std::move(upload_data)),
       current_backoff_(base::Seconds(kInitialBackoffSeconds)),
-      num_upload_retries_(0) {}
+      num_upload_retries_(0),
+      token_fetcher_(std::move(token_fetcher)) {}
 
 void ExtensionTelemetryUploader::Start() {
   upload_start_time_ = base::TimeTicks::Now();
   RecordUploadSize(upload_data_->size());
-  SendRequest();
+  MaybeSendRequestWithAccessToken();
 }
 
 // static
@@ -125,12 +131,27 @@
   return kUploadUrl;
 }
 
-void ExtensionTelemetryUploader::SendRequest() {
+void ExtensionTelemetryUploader::MaybeSendRequestWithAccessToken() {
+  if (token_fetcher_) {
+    token_fetcher_->Start(base::BindOnce(
+        &ExtensionTelemetryUploader::SendRequest, weak_factory_.GetWeakPtr()));
+  } else {
+    SendRequest(std::string());
+  }
+}
+
+void ExtensionTelemetryUploader::SendRequest(const std::string& access_token) {
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = GURL(kUploadUrl);
   resource_request->method = "POST";
   resource_request->load_flags = net::LOAD_DISABLE_CACHE;
-  resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+  if (!access_token.empty()) {
+    SetAccessTokenAndClearCookieInResourceRequest(resource_request.get(),
+                                                  access_token);
+  } else {
+    resource_request->credentials_mode =
+        network::mojom::CredentialsMode::kInclude;
+  }
   url_loader_ = network::SimpleURLLoader::Create(
       std::move(resource_request),
       kSafeBrowsingExtensionTelemetryTrafficAnnotation);
@@ -172,8 +193,9 @@
     } else {
       content::GetUIThreadTaskRunner({})->PostDelayedTask(
           FROM_HERE,
-          base::BindOnce(&ExtensionTelemetryUploader::SendRequest,
-                         weak_factory_.GetWeakPtr()),
+          base::BindOnce(
+              &ExtensionTelemetryUploader::MaybeSendRequestWithAccessToken,
+              weak_factory_.GetWeakPtr()),
           current_backoff_);
       current_backoff_ *= kBackoffFactor;
       num_upload_retries_++;
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.h b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.h
index 4280380..acea194 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.h
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.h
@@ -11,6 +11,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "chrome/browser/profiles/profile.h"
 
 namespace network {
 class SharedURLLoaderFactory;
@@ -19,6 +20,8 @@
 
 namespace safe_browsing {
 
+class SafeBrowsingTokenFetcher;
+
 // An uploader of extension telemetry reports. An upload is initiated by
 // creating an instance of this object and then calling its StartUpload method.
 // The upload can be cancelled by deleting the uploader instance. The instance
@@ -37,7 +40,8 @@
   ExtensionTelemetryUploader(
       OnUploadCallback callback,
       const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
-      std::unique_ptr<std::string> upload_data);
+      std::unique_ptr<std::string> upload_data,
+      std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher);
 
   // Start the upload by sending a request. This method performs retries if
   // necessary and finally calls |callback_|. It must be called on the UI
@@ -47,8 +51,11 @@
   static std::string GetUploadURLForTest();
 
  private:
+  // Determines whether to send a request with access token.
+  void MaybeSendRequestWithAccessToken();
+
   // Sends a single network request.
-  void SendRequest();
+  void SendRequest(const std::string& access_token);
 
   // Callback when SimpleURLLoader gets the response.
   void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
@@ -65,6 +72,10 @@
   base::TimeDelta current_backoff_;
   int num_upload_retries_;
   base::TimeTicks upload_start_time_;
+  // The token fetcher used to attach OAuth access tokens to requests for
+  // appropriately consented users. It can be a nullptr when the user is
+  // not signed in.
+  std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher_;
 
   base::WeakPtrFactory<ExtensionTelemetryUploader> weak_factory_{this};
 };
diff --git a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader_unittest.cc b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader_unittest.cc
index 34a0996a..48d22b6 100644
--- a/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader_unittest.cc
+++ b/chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader_unittest.cc
@@ -5,19 +5,49 @@
 #include <string>
 #include <utility>
 
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
 #include "components/safe_browsing/core/common/proto/csd.pb.h"
+#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "content/public/test/browser_task_environment.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace safe_browsing {
 
+class TestSafeBrowsingTokenFetcher : public SafeBrowsingTokenFetcher {
+ public:
+  TestSafeBrowsingTokenFetcher() = default;
+  ~TestSafeBrowsingTokenFetcher() override { RunAccessTokenCallback(""); }
+
+  void Start(Callback callback) override {
+    callback_ = std::move(callback);
+    was_start_called_ = true;
+  }
+  void RunAccessTokenCallback(std::string token) {
+    if (callback_) {
+      std::move(callback_).Run(token);
+    }
+  }
+  bool WasStartCalled() { return was_start_called_; }
+  MOCK_METHOD1(OnInvalidAccessToken, void(const std::string&));
+
+ private:
+  Callback callback_;
+  bool was_start_called_ = false;
+};
+
 class ExtensionTelemetryUploaderTest : public testing::Test {
  public:
   void OnUploadTestCallback(bool success) { upload_success_ = success; }
@@ -32,7 +62,7 @@
         base::BindOnce(&ExtensionTelemetryUploaderTest::OnUploadTestCallback,
                        base::Unretained(this)),
         test_url_loader_factory_.GetSafeWeakWrapper(),
-        std::make_unique<std::string>(upload_data_));
+        std::make_unique<std::string>(upload_data_), nullptr);
   }
 
   std::string upload_data_;
@@ -43,6 +73,64 @@
   std::unique_ptr<ExtensionTelemetryUploader> uploader_;
 };
 
+TEST_F(ExtensionTelemetryUploaderTest, FetchAccessTokenForReport) {
+  auto token_fetcher = std::make_unique<TestSafeBrowsingTokenFetcher>();
+  auto* raw_token_fetcher = token_fetcher.get();
+  std::string access_token = "testing_access_token";
+  network::TestURLLoaderFactory test_url_loader_factory;
+  test_url_loader_factory.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        std::string header_value;
+        bool found_header = request.headers.GetHeader(
+            net::HttpRequestHeaders::kAuthorization, &header_value);
+        EXPECT_EQ(found_header, true);
+        EXPECT_EQ(header_value, "Bearer " + access_token);
+      }));
+
+  test_url_loader_factory.AddResponse(
+      ExtensionTelemetryUploader::GetUploadURLForTest(), upload_data_,
+      net::HTTP_OK);
+  std::unique_ptr<ExtensionTelemetryUploader> sign_in_uploader =
+      std::make_unique<ExtensionTelemetryUploader>(
+          base::BindOnce(&ExtensionTelemetryUploaderTest::OnUploadTestCallback,
+                         base::Unretained(this)),
+          test_url_loader_factory.GetSafeWeakWrapper(),
+          std::make_unique<std::string>(upload_data_),
+          std::move(token_fetcher));
+  sign_in_uploader->Start();
+  task_environment_.FastForwardUntilNoTasksRemain();
+  // Expects token fetcher to be called.
+  EXPECT_EQ(raw_token_fetcher->WasStartCalled(), true);
+  raw_token_fetcher->RunAccessTokenCallback(access_token);
+}
+
+TEST_F(ExtensionTelemetryUploaderTest, AttachZwiebackCookieForReport) {
+  network::TestURLLoaderFactory test_url_loader_factory;
+  test_url_loader_factory.SetInterceptor(
+      base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        std::string header_value;
+        bool found_header = request.headers.GetHeader(
+            net::HttpRequestHeaders::kAuthorization, &header_value);
+        // When access token is not fetched, header does not include access
+        // token.
+        EXPECT_EQ(found_header, false);
+        // Set the credential mode to kInclude by default.
+        EXPECT_EQ(request.credentials_mode,
+                  network::mojom::CredentialsMode::kInclude);
+      }));
+  test_url_loader_factory.AddResponse(
+      ExtensionTelemetryUploader::GetUploadURLForTest(), upload_data_,
+      net::HTTP_OK);
+  std::unique_ptr<ExtensionTelemetryUploader> sign_out_uploader =
+      std::make_unique<ExtensionTelemetryUploader>(
+          base::BindOnce(&ExtensionTelemetryUploaderTest::OnUploadTestCallback,
+                         base::Unretained(this)),
+          test_url_loader_factory.GetSafeWeakWrapper(),
+          std::make_unique<std::string>(upload_data_), nullptr);
+  sign_out_uploader->Start();
+  task_environment_.FastForwardUntilNoTasksRemain();
+}
+
 TEST_F(ExtensionTelemetryUploaderTest, AbortsWithoutRetries) {
   // Aborts upload without retries if response code < 500
   test_url_loader_factory_.AddResponse(
diff --git a/chrome/browser/safety_check/android/BUILD.gn b/chrome/browser/safety_check/android/BUILD.gn
index 99539bd..68a227b 100644
--- a/chrome/browser/safety_check/android/BUILD.gn
+++ b/chrome/browser/safety_check/android/BUILD.gn
@@ -53,10 +53,8 @@
     "//components/password_manager/core/browser:password_manager_java_enums",
     "//components/signin/public/android:java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:guava_android_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
     "//third_party/androidx:androidx_lifecycle_lifecycle_common_java8_java",
     "//third_party/androidx:androidx_lifecycle_lifecycle_livedata_core_java",
diff --git a/chrome/browser/settings/BUILD.gn b/chrome/browser/settings/BUILD.gn
index d2b04b8..baf3a8aa 100644
--- a/chrome/browser/settings/BUILD.gn
+++ b/chrome/browser/settings/BUILD.gn
@@ -12,7 +12,6 @@
     "//components/browser_ui/settings/android:java",
     "//components/prefs/android:java",
     "//components/user_prefs/android:java",
-    "//third_party/androidx:androidx_fragment_fragment_java",
   ]
 }
 
@@ -34,7 +33,6 @@
     "//chrome/test/android:chrome_java_test_support",
     "//components/browser_ui/settings/android:java",
     "//content/public/test/android:content_java_test_support",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:espresso_java",
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_support_test_runner:rules_java",
diff --git a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
index d1a3c94..513e7fa8 100644
--- a/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
+++ b/chrome/browser/site_isolation/site_per_process_interactive_browsertest.cc
@@ -227,7 +227,7 @@
   // Generate a few keyboard events and route them to currently focused frame.
   // We wait for replies to be sent back from the page, since keystrokes may
   // take time to propagate to the renderer's main thread.
-  content::DOMMessageQueue msg_queue;
+  content::DOMMessageQueue msg_queue(web_contents);
   std::string reply;
   SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('F'),
                    ui::DomCode::US_F, ui::VKEY_F, false, false, false, false);
@@ -307,7 +307,7 @@
 
   // Helper to simulate a tab press and wait for a focus message.
   auto press_tab_and_wait_for_message = [web_contents](bool reverse) {
-    content::DOMMessageQueue msg_queue;
+    content::DOMMessageQueue msg_queue(web_contents);
     std::string reply;
     SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, reverse /* shift */, false, false);
@@ -401,7 +401,7 @@
 
   // Helper to simulate a tab press and wait for a focus message.
   auto press_tab_and_wait_for_message = [web_contents](bool reverse) {
-    content::DOMMessageQueue msg_queue;
+    content::DOMMessageQueue msg_queue(web_contents);
     std::string reply;
     SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, reverse /* shift */, false, false);
@@ -543,7 +543,7 @@
 
   // Helper to simulate a tab press and wait for a focus message.
   auto press_tab_and_wait_for_message = [web_contents](bool reverse) {
-    content::DOMMessageQueue msg_queue;
+    content::DOMMessageQueue msg_queue(web_contents);
     std::string reply;
     SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
                      ui::VKEY_TAB, false, reverse /* shift */, false, false);
@@ -554,7 +554,7 @@
 
   auto click_element_and_wait_for_message =
       [web_contents](const gfx::Point& point) {
-        content::DOMMessageQueue msg_queue;
+        content::DOMMessageQueue msg_queue(web_contents);
 
         auto content_bounds = web_contents->GetContainerBounds();
         ui_controls::SendMouseMove(point.x() + content_bounds.x(),
@@ -777,7 +777,7 @@
   std::set<std::string> expected_events = {"main_frame", "child"};
   AddResizeListener(child, GetScreenSize());
   {
-    content::DOMMessageQueue queue;
+    content::DOMMessageQueue queue(web_contents);
     FullscreenNotificationObserver observer(browser());
     EXPECT_TRUE(ExecuteScript(child, "activateFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
@@ -812,7 +812,7 @@
   // original size.
   AddResizeListener(child, original_child_size);
   {
-    content::DOMMessageQueue queue;
+    content::DOMMessageQueue queue(web_contents);
     FullscreenNotificationObserver observer(browser());
     EXPECT_TRUE(ExecuteScript(child, "exitFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
@@ -876,7 +876,7 @@
   // (3) the browser has finished the fullscreen transition.
   std::set<std::string> expected_events = {"main_frame", "child", "grandchild"};
   {
-    content::DOMMessageQueue queue;
+    content::DOMMessageQueue queue(web_contents);
     FullscreenNotificationObserver fullscreen_observer(browser());
     EXPECT_TRUE(ExecuteScript(grandchild, "activateFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
@@ -906,7 +906,7 @@
   // Now exit fullscreen from the subframe.
   AddResizeListener(grandchild, original_grandchild_size);
   {
-    content::DOMMessageQueue queue;
+    content::DOMMessageQueue queue(web_contents);
     FullscreenNotificationObserver fullscreen_observer(browser());
     switch (exit_method) {
       case FullscreenExitMethod::JS_CALL:
@@ -1037,7 +1037,7 @@
   // response, (2) |c_middle| is resized to fill the whole screen, and (3) the
   // browser finishes the fullscreen transition.
   {
-    content::DOMMessageQueue queue;
+    content::DOMMessageQueue queue(web_contents);
     FullscreenNotificationObserver fullscreen_observer(browser());
     EXPECT_TRUE(ExecuteScript(c_middle, "activateFullscreen()"));
     WaitForMultipleFullscreenEvents(expected_events, queue);
@@ -1077,7 +1077,7 @@
   // resized back to its original size.
   AddResizeListener(c_middle, c_middle_original_size);
   {
-    content::DOMMessageQueue queue;
+    content::DOMMessageQueue queue(web_contents);
     FullscreenNotificationObserver fullscreen_observer(browser());
     ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_ESCAPE,
                                                 false, false, false, false));
diff --git a/chrome/browser/ssl/certificate_transparency_browsertest.cc b/chrome/browser/ssl/certificate_transparency_browsertest.cc
new file mode 100644
index 0000000..091ac58
--- /dev/null
+++ b/chrome/browser/ssl/certificate_transparency_browsertest.cc
@@ -0,0 +1,237 @@
+// 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 "base/run_loop.h"
+#include "base/task/current_thread.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/net/system_network_context_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ssl/cert_verifier_browser_test.h"
+#include "chrome/browser/ssl/ssl_browsertest_util.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/certificate_transparency/pref_names.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/browser/network_service_instance.h"
+#include "content/public/test/browser_test.h"
+#include "crypto/sha2.h"
+#include "net/base/hash_value.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/x509_util.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/test_data_directory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace {
+
+// Returns the Sha256 hash of the SPKI of |cert|.
+net::HashValue GetSPKIHash(const CRYPTO_BUFFER* cert) {
+  base::StringPiece spki_bytes;
+  EXPECT_TRUE(net::asn1::ExtractSPKIFromDERCert(
+      net::x509_util::CryptoBufferAsStringPiece(cert), &spki_bytes));
+  net::HashValue sha256(net::HASH_VALUE_SHA256);
+  crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length);
+  return sha256;
+}
+
+}  // namespace
+
+// Class used to run browser tests that verify SSL UI triggered due to
+// Certificate Transparency verification failures/successes.
+class CertificateTransparencyBrowserTest : public CertVerifierBrowserTest {
+ public:
+  CertificateTransparencyBrowserTest()
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
+        true);
+  }
+
+  CertificateTransparencyBrowserTest(
+      const CertificateTransparencyBrowserTest&) = delete;
+  CertificateTransparencyBrowserTest& operator=(
+      const CertificateTransparencyBrowserTest&) = delete;
+
+  ~CertificateTransparencyBrowserTest() override {
+    SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
+        absl::nullopt);
+  }
+
+  void SetUpOnMainThread() override {
+    CertVerifierBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule("*", "127.0.0.1");
+    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
+  }
+
+  void SetUp() override {
+    policy_provider_.SetDefaultReturns(
+        /*is_initialization_complete_return=*/true,
+        /*is_first_policy_load_complete_return=*/true);
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+
+    CertVerifierBrowserTest::SetUp();
+  }
+
+  // Sets the policy identified by |policy_name| to the value specified by
+  // |list_values|, ensuring that the corresponding list pref |pref_name| is
+  // updated to match. |policy_name| must specify a policy that is a list of
+  // string values.
+  void ConfigureStringListPolicy(PrefService* pref_service,
+                                 const char* policy_name,
+                                 const char* pref_name,
+                                 const std::vector<std::string>& list_values) {
+    base::Value policy_value(base::Value::Type::LIST);
+    for (const auto& value : list_values) {
+      policy_value.Append(value);
+    }
+    policy::PolicyMap policy_map;
+    policy_map.Set(policy_name, policy::POLICY_LEVEL_MANDATORY,
+                   policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
+                   std::move(policy_value), nullptr);
+
+    EXPECT_NO_FATAL_FAILURE(UpdateChromePolicy(policy_map));
+
+    const base::Value* pref_value = pref_service->GetList(pref_name);
+    ASSERT_TRUE(pref_value);
+    std::vector<std::string> pref_values;
+    for (const auto& value : pref_value->GetListDeprecated()) {
+      ASSERT_TRUE(value.is_string());
+      pref_values.push_back(value.GetString());
+    }
+    EXPECT_THAT(pref_values, testing::UnorderedElementsAreArray(list_values));
+  }
+
+  net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+ private:
+  void UpdateChromePolicy(const policy::PolicyMap& policies) {
+    policy_provider_.UpdateChromePolicy(policies);
+    ASSERT_TRUE(base::CurrentThread::Get());
+
+    base::RunLoop().RunUntilIdle();
+
+    content::FlushNetworkServiceInstanceForTesting();
+  }
+
+  net::EmbeddedTestServer https_server_;
+
+  testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
+};
+
+// Visit an HTTPS page that has a publicly trusted certificate issued after
+// the Certificate Transparency requirement date of April 2018. The connection
+// should be blocked, as the server will not be providing CT details, and the
+// Chrome CT Policy should be being enforced.
+IN_PROC_BROWSER_TEST_F(CertificateTransparencyBrowserTest,
+                       EnforcedAfterApril2018) {
+  ASSERT_TRUE(https_server()->Start());
+
+  net::CertVerifyResult verify_result;
+  verify_result.verified_cert =
+      net::ImportCertFromFile(net::GetTestCertsDirectory(), "may_2018.pem");
+  ASSERT_TRUE(verify_result.verified_cert);
+  verify_result.is_issued_by_known_root = true;
+  verify_result.public_key_hashes.push_back(
+      GetSPKIHash(verify_result.verified_cert->cert_buffer()));
+
+  mock_cert_verifier()->AddResultForCert(https_server()->GetCertificate().get(),
+                                         verify_result, net::OK);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server()->GetURL("/ssl/google.html")));
+
+  ssl_test_util::CheckSecurityState(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      net::CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED,
+      security_state::DANGEROUS,
+      ssl_test_util::AuthState::SHOWING_INTERSTITIAL);
+}
+
+// Visit an HTTPS page that has a publicly trusted certificate issued after
+// the Certificate Transparency requirement date of April 2018. The connection
+// would normally be blocked, as the server will not be providing CT details,
+// and the Chrome CT Policy should be being enforced; however, because a policy
+// configuration exists that disables CT enforcement for that cert, the
+// connection should succeed.
+IN_PROC_BROWSER_TEST_F(CertificateTransparencyBrowserTest,
+                       EnforcedAfterApril2018UnlessPoliciesSet) {
+  ASSERT_TRUE(https_server()->Start());
+
+  net::CertVerifyResult verify_result;
+  verify_result.verified_cert =
+      net::ImportCertFromFile(net::GetTestCertsDirectory(), "may_2018.pem");
+  ASSERT_TRUE(verify_result.verified_cert);
+  verify_result.is_issued_by_known_root = true;
+  verify_result.public_key_hashes.push_back(
+      GetSPKIHash(verify_result.verified_cert->cert_buffer()));
+
+  mock_cert_verifier()->AddResultForCert(https_server()->GetCertificate().get(),
+                                         verify_result, net::OK);
+
+  ASSERT_NO_FATAL_FAILURE(ConfigureStringListPolicy(
+      browser()->profile()->GetPrefs(),
+      policy::key::kCertificateTransparencyEnforcementDisabledForCas,
+      certificate_transparency::prefs::kCTExcludedSPKIs,
+      {verify_result.public_key_hashes.back().ToString()}));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server()->GetURL("/ssl/google.html")));
+  ssl_test_util::CheckSecurityState(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      ssl_test_util::CertError::NONE, security_state::SECURE,
+      ssl_test_util::AuthState::NONE);
+}
+
+// Visit an HTTPS page that has a certificate issued by a certificate authority
+// that is trusted in a root store that Chrome does not consider consistently
+// secure. In the case where the certificate was issued after the Certificate
+// Transparency requirement date of April 2018 the connection would normally be
+// blocked, as the server will not be providing CT details, and the Chrome CT
+// Policy should be being enforced; however, because a policy configuration
+// exists that disables CT enforcement for that Legacy cert, the connection
+// should succeed. For more detail, see /net/docs/certificate-transparency.md
+IN_PROC_BROWSER_TEST_F(CertificateTransparencyBrowserTest,
+                       LegacyEnforcedAfterApril2018UnlessPoliciesSet) {
+  ASSERT_TRUE(https_server()->Start());
+
+  net::CertVerifyResult verify_result;
+  verify_result.verified_cert =
+      net::ImportCertFromFile(net::GetTestCertsDirectory(), "may_2018.pem");
+  ASSERT_TRUE(verify_result.verified_cert);
+  verify_result.is_issued_by_known_root = true;
+
+  // We'll use a SPKI hash corresponding to the Federal Common Policy CA as
+  // captured at https://fpki.idmanagement.gov/announcements/mspkichanges/
+  const net::SHA256HashValue legacy_spki_hash = {
+      0x8e, 0x8b, 0x56, 0xf5, 0x91, 0x8a, 0x25, 0xbd, 0x85, 0xdc, 0xe7,
+      0x66, 0x63, 0xfd, 0x94, 0xcc, 0x23, 0x69, 0x0f, 0x10, 0xea, 0x95,
+      0x86, 0x61, 0x31, 0x71, 0xc6, 0xf8, 0x37, 0x88, 0x90, 0xd5};
+  verify_result.public_key_hashes.push_back(net::HashValue(legacy_spki_hash));
+
+  mock_cert_verifier()->AddResultForCert(https_server()->GetCertificate().get(),
+                                         verify_result, net::OK);
+
+  ASSERT_NO_FATAL_FAILURE(ConfigureStringListPolicy(
+      browser()->profile()->GetPrefs(),
+      policy::key::kCertificateTransparencyEnforcementDisabledForLegacyCas,
+      certificate_transparency::prefs::kCTExcludedLegacySPKIs,
+      {verify_result.public_key_hashes.back().ToString()}));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server()->GetURL("/ssl/google.html")));
+  ssl_test_util::CheckSecurityState(
+      browser()->tab_strip_model()->GetActiveWebContents(),
+      ssl_test_util::CertError::NONE, security_state::SECURE,
+      ssl_test_util::AuthState::NONE);
+}
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 70030f9..5be642f5 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -77,7 +77,6 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/test_launcher_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/certificate_transparency/pref_names.h"
 #include "components/content_settings/browser/page_specific_content_settings.h"
 #include "components/content_settings/common/content_settings_agent.mojom.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -1721,191 +1720,6 @@
                    ->initial_ssl_config->symantec_enforcement_disabled);
 }
 
-class CertificateTransparencySSLUITest : public CertVerifierBrowserTest {
- public:
-  CertificateTransparencySSLUITest()
-      : CertVerifierBrowserTest(),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
-        true);
-  }
-
-  CertificateTransparencySSLUITest(const CertificateTransparencySSLUITest&) =
-      delete;
-  CertificateTransparencySSLUITest& operator=(
-      const CertificateTransparencySSLUITest&) = delete;
-
-  ~CertificateTransparencySSLUITest() override {
-    SystemNetworkContextManager::SetEnableCertificateTransparencyForTesting(
-        absl::nullopt);
-  }
-
-  void SetUpOnMainThread() override {
-    CertVerifierBrowserTest::SetUpOnMainThread();
-    host_resolver()->AddRule("*", "127.0.0.1");
-    https_server_.AddDefaultHandlers(GetChromeTestDataDir());
-  }
-
-  void SetUp() override {
-    policy_provider_.SetDefaultReturns(
-        /*is_initialization_complete_return=*/true,
-        /*is_first_policy_load_complete_return=*/true);
-    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
-        &policy_provider_);
-
-    CertVerifierBrowserTest::SetUp();
-  }
-
-  // Sets the policy identified by |policy_name| to the value specified by
-  // |list_values|, ensuring that the corresponding list pref |pref_name| is
-  // updated to match. |policy_name| must specify a policy that is a list of
-  // string values.
-  void ConfigureStringListPolicy(PrefService* pref_service,
-                                 const char* policy_name,
-                                 const char* pref_name,
-                                 const std::vector<std::string>& list_values) {
-    base::Value policy_value(base::Value::Type::LIST);
-    for (const auto& value : list_values) {
-      policy_value.Append(value);
-    }
-    policy::PolicyMap policy_map;
-    policy_map.Set(policy_name, policy::POLICY_LEVEL_MANDATORY,
-                   policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_CLOUD,
-                   std::move(policy_value), nullptr);
-
-    EXPECT_NO_FATAL_FAILURE(UpdateChromePolicy(policy_map));
-
-    const base::Value* pref_value = pref_service->GetList(pref_name);
-    ASSERT_TRUE(pref_value);
-    std::vector<std::string> pref_values;
-    for (const auto& value : pref_value->GetListDeprecated()) {
-      ASSERT_TRUE(value.is_string());
-      pref_values.push_back(value.GetString());
-    }
-    EXPECT_THAT(pref_values, testing::UnorderedElementsAreArray(list_values));
-  }
-
-  net::EmbeddedTestServer* https_server() { return &https_server_; }
-
- private:
-  void UpdateChromePolicy(const policy::PolicyMap& policies) {
-    policy_provider_.UpdateChromePolicy(policies);
-    ASSERT_TRUE(base::CurrentThread::Get());
-
-    base::RunLoop().RunUntilIdle();
-
-    content::FlushNetworkServiceInstanceForTesting();
-  }
-
-  net::EmbeddedTestServer https_server_;
-
-  testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
-};
-
-// Visit an HTTPS page that has a publicly trusted certificate issued after
-// the Certificate Transparency requirement date of April 2018. The connection
-// should be blocked, as the server will not be providing CT details, and the
-// Chrome CT Policy should be being enforced.
-IN_PROC_BROWSER_TEST_F(CertificateTransparencySSLUITest,
-                       EnforcedAfterApril2018) {
-  ASSERT_TRUE(https_server()->Start());
-
-  net::CertVerifyResult verify_result;
-  verify_result.verified_cert =
-      net::ImportCertFromFile(net::GetTestCertsDirectory(), "may_2018.pem");
-  ASSERT_TRUE(verify_result.verified_cert);
-  verify_result.is_issued_by_known_root = true;
-  verify_result.public_key_hashes.push_back(
-      GetSPKIHash(verify_result.verified_cert->cert_buffer()));
-
-  mock_cert_verifier()->AddResultForCert(https_server()->GetCertificate().get(),
-                                         verify_result, net::OK);
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL("/ssl/google.html")));
-
-  ssl_test_util::CheckSecurityState(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      net::CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED,
-      security_state::DANGEROUS, AuthState::SHOWING_INTERSTITIAL);
-}
-
-// Visit an HTTPS page that has a publicly trusted certificate issued after
-// the Certificate Transparency requirement date of April 2018. The connection
-// would normally be blocked, as the server will not be providing CT details,
-// and the Chrome CT Policy should be being enforced; however, because a policy
-// configuration exists that disables CT enforcement for that cert, the
-// connection should succeed.
-IN_PROC_BROWSER_TEST_F(CertificateTransparencySSLUITest,
-                       EnforcedAfterApril2018UnlessPoliciesSet) {
-  ASSERT_TRUE(https_server()->Start());
-
-  net::CertVerifyResult verify_result;
-  verify_result.verified_cert =
-      net::ImportCertFromFile(net::GetTestCertsDirectory(), "may_2018.pem");
-  ASSERT_TRUE(verify_result.verified_cert);
-  verify_result.is_issued_by_known_root = true;
-  verify_result.public_key_hashes.push_back(
-      GetSPKIHash(verify_result.verified_cert->cert_buffer()));
-
-  mock_cert_verifier()->AddResultForCert(https_server()->GetCertificate().get(),
-                                         verify_result, net::OK);
-
-  ASSERT_NO_FATAL_FAILURE(ConfigureStringListPolicy(
-      browser()->profile()->GetPrefs(),
-      policy::key::kCertificateTransparencyEnforcementDisabledForCas,
-      certificate_transparency::prefs::kCTExcludedSPKIs,
-      {verify_result.public_key_hashes.back().ToString()}));
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL("/ssl/google.html")));
-  ssl_test_util::CheckSecurityState(
-      browser()->tab_strip_model()->GetActiveWebContents(), CertError::NONE,
-      security_state::SECURE, AuthState::NONE);
-}
-
-// Visit an HTTPS page that has a certificate issued by a certificate authority
-// that is trusted in a root store that Chrome does not consider consistently
-// secure. In the case where the certificate was issued after the Certificate
-// Transparency requirement date of April 2018 the connection would normally be
-// blocked, as the server will not be providing CT details, and the Chrome CT
-// Policy should be being enforced; however, because a policy configuration
-// exists that disables CT enforcement for that Legacy cert, the connection
-// should succeed. For more detail, see /net/docs/certificate-transparency.md
-IN_PROC_BROWSER_TEST_F(CertificateTransparencySSLUITest,
-                       LegacyEnforcedAfterApril2018UnlessPoliciesSet) {
-  ASSERT_TRUE(https_server()->Start());
-
-  net::CertVerifyResult verify_result;
-  verify_result.verified_cert =
-      net::ImportCertFromFile(net::GetTestCertsDirectory(), "may_2018.pem");
-  ASSERT_TRUE(verify_result.verified_cert);
-  verify_result.is_issued_by_known_root = true;
-
-  // We'll use a SPKI hash corresponding to the Federal Common Policy CA as
-  // captured at https://fpki.idmanagement.gov/announcements/mspkichanges/
-  const net::SHA256HashValue legacy_spki_hash = {
-      0x8e, 0x8b, 0x56, 0xf5, 0x91, 0x8a, 0x25, 0xbd, 0x85, 0xdc, 0xe7,
-      0x66, 0x63, 0xfd, 0x94, 0xcc, 0x23, 0x69, 0x0f, 0x10, 0xea, 0x95,
-      0x86, 0x61, 0x31, 0x71, 0xc6, 0xf8, 0x37, 0x88, 0x90, 0xd5};
-  verify_result.public_key_hashes.push_back(net::HashValue(legacy_spki_hash));
-
-  mock_cert_verifier()->AddResultForCert(https_server()->GetCertificate().get(),
-                                         verify_result, net::OK);
-
-  ASSERT_NO_FATAL_FAILURE(ConfigureStringListPolicy(
-      browser()->profile()->GetPrefs(),
-      policy::key::kCertificateTransparencyEnforcementDisabledForLegacyCas,
-      certificate_transparency::prefs::kCTExcludedLegacySPKIs,
-      {verify_result.public_key_hashes.back().ToString()}));
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(
-      browser(), https_server()->GetURL("/ssl/google.html")));
-  ssl_test_util::CheckSecurityState(
-      browser()->tab_strip_model()->GetActiveWebContents(), CertError::NONE,
-      security_state::SECURE, AuthState::NONE);
-}
-
 // Visit a HTTP page which request WSS connection to a server providing invalid
 // certificate. Close the page while WSS connection waits for SSLManager's
 // response from UI thread.
diff --git a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
index 3f5b166..bda27ed 100644
--- a/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browsertest.cc
@@ -1317,6 +1317,15 @@
   // Add iframe that is not detected as an ad-frame nor an embed.
   AddIframe(render_frame_host, kNonAdNonEmbed);
 
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
+  // LazyEmbeds and LazyAds must be disabled when the page is reloaded.
+  EXPECT_TRUE(render_frame_host->Reload());
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+  AddAdIframe(render_frame_host, kAdUrl);
+  AddIframe(render_frame_host, kEmbedUrl);
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
   // Navigating away from the test page (kMainFrameUrl) causes the document to
   // be unloaded. That will cause any buffered metrics to be flushed.
   content::NavigateToURLBlockUntilNavigationsComplete(web_contents(),
diff --git a/chrome/browser/test_dummy/internal/BUILD.gn b/chrome/browser/test_dummy/internal/BUILD.gn
index edffd86..a4d5bb2 100644
--- a/chrome/browser/test_dummy/internal/BUILD.gn
+++ b/chrome/browser/test_dummy/internal/BUILD.gn
@@ -17,8 +17,8 @@
     ":java_resources",
     "//base:base_java",
     "//chrome/browser/test_dummy:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
   ]
   sources = [ "android/java/src/org/chromium/chrome/browser/test_dummy/TestDummyImpl.java" ]
 
@@ -49,8 +49,8 @@
     "//chrome/android/modules/test_dummy/provider:java",
     "//chrome/android/modules/test_dummy/public:java",
     "//chrome/browser/test_dummy:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
   ]
   sources = [ "android/java/src/org/chromium/chrome/browser/test_dummy/TestDummyActivity.java" ]
 }
diff --git a/chrome/browser/touch_to_fill/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
index a06ec37..05e0cd0a 100644
--- a/chrome/browser/touch_to_fill/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/android/internal/BUILD.gn
@@ -51,7 +51,6 @@
 android_resources("java_resources") {
   deps = [
     ":java_strings_grd",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//ui/android:ui_java_resources",
   ]
   sources = [
diff --git a/chrome/browser/ui/android/appmenu/internal/BUILD.gn b/chrome/browser/ui/android/appmenu/internal/BUILD.gn
index 7f178a0..7a8f3bef 100644
--- a/chrome/browser/ui/android/appmenu/internal/BUILD.gn
+++ b/chrome/browser/ui/android/appmenu/internal/BUILD.gn
@@ -30,9 +30,9 @@
     "//chrome/browser/ui/android/appmenu:java",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/widget/android:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_core_core_java",
     "//ui/android:ui_java",
   ]
   resources_package = "org.chromium.chrome.browser.ui.appmenu.internal"
@@ -82,7 +82,6 @@
     "//components/browser_ui/widget/android:java",
     "//components/browser_ui/widget/android:test_support_java",
     "//content/public/test/android:content_java_test_support",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
diff --git a/chrome/browser/ui/android/autofill/internal/BUILD.gn b/chrome/browser/ui/android/autofill/internal/BUILD.gn
index 2689356f..6a40742 100644
--- a/chrome/browser/ui/android/autofill/internal/BUILD.gn
+++ b/chrome/browser/ui/android/autofill/internal/BUILD.gn
@@ -69,6 +69,7 @@
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
+    "//chrome/browser/ui/android/autofill/test:test_support_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_core_java",
     "//third_party/androidx:androidx_test_runner_java",
diff --git a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogBridgeTest.java b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogBridgeTest.java
index 8f585285..fc1a5c0 100644
--- a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogBridgeTest.java
+++ b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogBridgeTest.java
@@ -17,7 +17,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -25,9 +24,7 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.ui.autofill.data.AuthenticatorOption;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,38 +63,6 @@
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
     public JniMocker mMocker = new JniMocker();
-    private class FakeModalDialogManager extends ModalDialogManager {
-        private PropertyModel mShownDialogModel;
-
-        public FakeModalDialogManager() {
-            super(Mockito.mock(Presenter.class), 0);
-        }
-
-        @Override
-        public void showDialog(PropertyModel model, int dialogType) {
-            mShownDialogModel = model;
-        }
-
-        @Override
-        public void dismissDialog(PropertyModel model, int dismissalCause) {
-            model.get(ModalDialogProperties.CONTROLLER).onDismiss(model, dismissalCause);
-            mShownDialogModel = null;
-        }
-
-        public void clickPositiveButton() {
-            mShownDialogModel.get(ModalDialogProperties.CONTROLLER)
-                    .onClick(mShownDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
-        }
-
-        public void clickNegativeButton() {
-            mShownDialogModel.get(ModalDialogProperties.CONTROLLER)
-                    .onClick(mShownDialogModel, ModalDialogProperties.ButtonType.NEGATIVE);
-        }
-
-        public PropertyModel getShownDialogModel() {
-            return mShownDialogModel;
-        }
-    }
 
     @Before
     public void setUp() {
@@ -105,7 +70,7 @@
         reset(mNativeMock);
         mOptions.add(OPTION_1);
         mOptions.add(OPTION_2);
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.TAB);
         mAuthenticatorSelectionDialogBridge =
                 new AuthenticatorSelectionDialogBridge(NATIVE_AUTHENTICATOR_SELECTION_DIALOG_VIEW,
                         ApplicationProvider.getApplicationContext(), mModalDialogManager);
diff --git a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogTest.java b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogTest.java
index b0d622f..6b31539 100644
--- a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogTest.java
+++ b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AuthenticatorSelectionDialogTest.java
@@ -19,7 +19,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
@@ -27,7 +26,7 @@
 import org.chromium.chrome.browser.ui.autofill.data.AuthenticatorOption;
 import org.chromium.chrome.browser.ui.autofill.internal.R;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
-import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -59,38 +58,11 @@
     private AuthenticatorSelectionDialog mAuthenticatorSelectionDialog;
     @Mock
     private AuthenticatorSelectionDialog.Listener mAuthenticatorSelectedListener;
-    private class FakeModalDialogManager extends ModalDialogManager {
-        private PropertyModel mShownDialogModel;
-
-        public FakeModalDialogManager() {
-            super(Mockito.mock(Presenter.class), 0);
-        }
-
-        @Override
-        public void showDialog(PropertyModel model, int dialogType) {
-            mShownDialogModel = model;
-        }
-
-        @Override
-        public void dismissDialog(PropertyModel model, int dismissalCause) {
-            model.get(ModalDialogProperties.CONTROLLER).onDismiss(model, dismissalCause);
-            mShownDialogModel = null;
-        }
-
-        public void clickPositiveButton() {
-            mShownDialogModel.get(ModalDialogProperties.CONTROLLER)
-                    .onClick(mShownDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
-        }
-
-        public PropertyModel getShownDialogModel() {
-            return mShownDialogModel;
-        }
-    }
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.TAB);
         mAuthenticatorSelectionDialog =
                 new AuthenticatorSelectionDialog(ApplicationProvider.getApplicationContext(),
                         mAuthenticatorSelectedListener, mModalDialogManager);
diff --git a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillErrorDialogBridgeTest.java b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillErrorDialogBridgeTest.java
index a54e100..efc12be2 100644
--- a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillErrorDialogBridgeTest.java
+++ b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillErrorDialogBridgeTest.java
@@ -17,15 +17,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
-import org.chromium.ui.modaldialog.ModalDialogManager;
-import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 
 /**
  * Unit tests for {@link AutofillErrorDialogBridge}
@@ -48,38 +45,10 @@
     private AutofillErrorDialogBridge mAutofillErrorDialogBridge;
     private FakeModalDialogManager mModalDialogManager;
 
-    private class FakeModalDialogManager extends ModalDialogManager {
-        private PropertyModel mShownDialogModel;
-
-        public FakeModalDialogManager() {
-            super(Mockito.mock(Presenter.class), 0);
-        }
-
-        @Override
-        public void showDialog(PropertyModel model, int dialogType) {
-            mShownDialogModel = model;
-        }
-
-        @Override
-        public void dismissDialog(PropertyModel model, int dismissalCause) {
-            model.get(ModalDialogProperties.CONTROLLER).onDismiss(model, dismissalCause);
-            mShownDialogModel = null;
-        }
-
-        public void clickPositiveButton() {
-            mShownDialogModel.get(ModalDialogProperties.CONTROLLER)
-                    .onClick(mShownDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
-        }
-
-        public PropertyModel getShownDialogModel() {
-            return mShownDialogModel;
-        }
-    }
-
     @Before
     public void setUp() {
         reset(mNativeMock);
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.TAB);
         mAutofillErrorDialogBridge =
                 new AutofillErrorDialogBridge(NATIVE_AUTOFILL_ERROR_DIALOG_VIEW,
                         mModalDialogManager, ApplicationProvider.getApplicationContext());
diff --git a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillProgressDialogBridgeTest.java b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillProgressDialogBridgeTest.java
index 3f41c173..1e99c03 100644
--- a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillProgressDialogBridgeTest.java
+++ b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/AutofillProgressDialogBridgeTest.java
@@ -20,16 +20,14 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 import org.chromium.chrome.browser.ui.autofill.internal.R;
-import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
-import org.chromium.ui.modelutil.PropertyModel;
 
 /**
  * Unit tests for {@link AutofillProgressDialogBridge}
@@ -53,34 +51,6 @@
     private AutofillProgressDialogBridge mAutofillProgressDialogBridge;
     private FakeModalDialogManager mModalDialogManager;
 
-    private class FakeModalDialogManager extends ModalDialogManager {
-        private PropertyModel mShownDialogModel;
-
-        public FakeModalDialogManager() {
-            super(Mockito.mock(Presenter.class), 0);
-        }
-
-        @Override
-        public void showDialog(PropertyModel model, int dialogType) {
-            mShownDialogModel = model;
-        }
-
-        @Override
-        public void dismissDialog(PropertyModel model, int dismissalCause) {
-            model.get(ModalDialogProperties.CONTROLLER).onDismiss(model, dismissalCause);
-            mShownDialogModel = null;
-        }
-
-        public void clickNegativeButton() {
-            mShownDialogModel.get(ModalDialogProperties.CONTROLLER)
-                    .onClick(mShownDialogModel, ModalDialogProperties.ButtonType.NEGATIVE);
-        }
-
-        public PropertyModel getShownDialogModel() {
-            return mShownDialogModel;
-        }
-    }
-
     private void showProgressDialog() {
         mAutofillProgressDialogBridge.showDialog(PROGRESS_DIALOG_TITLE, PROGRESS_DIALOG_MESSAGE,
                 PROGRESS_DIALOG_BUTTON_LABEL, /* iconId= */ 0);
@@ -89,7 +59,7 @@
     @Before
     public void setUp() {
         reset(mNativeMock);
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.TAB);
         mAutofillProgressDialogBridge =
                 new AutofillProgressDialogBridge(NATIVE_AUTOFILL_PROGRESS_DIALOG_VIEW,
                         mModalDialogManager, ApplicationProvider.getApplicationContext());
diff --git a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/OtpVerificationDialogTest.java b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/OtpVerificationDialogTest.java
index 865a2520..7007b6ee 100644
--- a/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/OtpVerificationDialogTest.java
+++ b/chrome/browser/ui/android/autofill/internal/java/src/org/chromium/chrome/browser/ui/autofill/OtpVerificationDialogTest.java
@@ -21,13 +21,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.ui.autofill.internal.R;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
-import org.chromium.ui.modaldialog.ModalDialogManager;
+import org.chromium.ui.modaldialog.ModalDialogManager.ModalDialogType;
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -48,38 +47,10 @@
     @Mock
     private OtpVerificationDialogCoordinator.Delegate mDelegate;
 
-    private class FakeModalDialogManager extends ModalDialogManager {
-        private PropertyModel mShownDialogModel;
-
-        public FakeModalDialogManager() {
-            super(Mockito.mock(Presenter.class), 0);
-        }
-
-        @Override
-        public void showDialog(PropertyModel model, int dialogType) {
-            mShownDialogModel = model;
-        }
-
-        @Override
-        public void dismissDialog(PropertyModel model, int dismissalCause) {
-            model.get(ModalDialogProperties.CONTROLLER).onDismiss(model, dismissalCause);
-            mShownDialogModel = null;
-        }
-
-        public void clickPositiveButton() {
-            mShownDialogModel.get(ModalDialogProperties.CONTROLLER)
-                    .onClick(mShownDialogModel, ModalDialogProperties.ButtonType.POSITIVE);
-        }
-
-        public PropertyModel getShownDialogModel() {
-            return mShownDialogModel;
-        }
-    }
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mModalDialogManager = new FakeModalDialogManager();
+        mModalDialogManager = new FakeModalDialogManager(ModalDialogType.TAB);
         mOtpVerificationDialogView = (OtpVerificationDialogView) LayoutInflater
                                              .from(ApplicationProvider.getApplicationContext())
                                              .inflate(R.layout.otp_verification_dialog, null);
diff --git a/chrome/browser/ui/android/autofill/test/BUILD.gn b/chrome/browser/ui/android/autofill/test/BUILD.gn
new file mode 100644
index 0000000..b4e7452
--- /dev/null
+++ b/chrome/browser/ui/android/autofill/test/BUILD.gn
@@ -0,0 +1,22 @@
+# 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.
+
+import("//build/config/android/config.gni")
+import("//build/config/android/rules.gni")
+
+android_library("test_support_java") {
+  testonly = true
+
+  visibility = [
+    ":*",
+    "//chrome/android:chrome_junit_tests__java_binary",
+    "//chrome/browser/ui/android/autofill/internal:junit",
+  ]
+  sources = [ "java/src/org/chromium/chrome/browser/ui/autofill/FakeModalDialogManager.java" ]
+  deps = [
+    "//base:base_java",
+    "//third_party/mockito:mockito_java",
+    "//ui/android:ui_no_recycler_view_java",
+  ]
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/FakeModalDialogManager.java b/chrome/browser/ui/android/autofill/test/java/src/org/chromium/chrome/browser/ui/autofill/FakeModalDialogManager.java
similarity index 83%
rename from chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/FakeModalDialogManager.java
rename to chrome/browser/ui/android/autofill/test/java/src/org/chromium/chrome/browser/ui/autofill/FakeModalDialogManager.java
index 3330058..4f09d5d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/settings/FakeModalDialogManager.java
+++ b/chrome/browser/ui/android/autofill/test/java/src/org/chromium/chrome/browser/ui/autofill/FakeModalDialogManager.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.autofill.settings;
+package org.chromium.chrome.browser.ui.autofill;
 
 import org.mockito.Mockito;
 
@@ -10,17 +10,14 @@
 import org.chromium.ui.modaldialog.ModalDialogProperties;
 import org.chromium.ui.modelutil.PropertyModel;
 
-// TODO (crbug/1249597): Move this to a more suitable common directory, and deduplicate in other
-// places.
-
 /**
  * A fake ModalDialogManager for use in tests involving modals.
  */
 public class FakeModalDialogManager extends ModalDialogManager {
     private PropertyModel mShownDialogModel;
 
-    public FakeModalDialogManager() {
-        super(Mockito.mock(Presenter.class), ModalDialogType.APP);
+    public FakeModalDialogManager(int modalDialogType) {
+        super(Mockito.mock(Presenter.class), modalDialogType);
     }
 
     @Override
diff --git a/chrome/browser/ui/android/night_mode/BUILD.gn b/chrome/browser/ui/android/night_mode/BUILD.gn
index f19ee6a..a03083f 100644
--- a/chrome/browser/ui/android/night_mode/BUILD.gn
+++ b/chrome/browser/ui/android/night_mode/BUILD.gn
@@ -43,8 +43,9 @@
     "//components/ukm/android:java",
     "//components/user_prefs/android:java",
     "//content/public/android:content_full_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_preference_preference_java",
     "//ui/android:ui_no_recycler_view_java",
     "//ui/android:ui_utils_java",
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index 6e7ea29..d3cceb7a 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -190,11 +190,14 @@
     "//components/ukm/android:java",
     "//components/user_prefs/android:java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_collection_collection_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/metrics_proto:metrics_proto_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_utils_java",
@@ -410,11 +413,11 @@
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//testing/android/junit:junit_test_support",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/android_deps:robolectric_all_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_core_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_library_java",
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index 3dc6e0a..05a505b 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -147,10 +147,14 @@
     "//components/segmentation_platform/public:public_java",
     "//components/user_prefs/android:java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_interpolator_interpolator_java",
     "//third_party/androidx:androidx_preference_preference_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
     "//third_party/metrics_proto:metrics_proto_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_utils_java",
diff --git a/chrome/browser/ui/android/webid/internal/BUILD.gn b/chrome/browser/ui/android/webid/internal/BUILD.gn
index 5edab28..bffc42d 100644
--- a/chrome/browser/ui/android/webid/internal/BUILD.gn
+++ b/chrome/browser/ui/android/webid/internal/BUILD.gn
@@ -51,7 +51,6 @@
   deps = [
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//components/browser_ui/widget/android:java_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//ui/android:ui_java_resources",
   ]
   sources = [
diff --git a/chrome/browser/ui/app_list/app_list_notifier_impl.cc b/chrome/browser/ui/app_list/app_list_notifier_impl.cc
index 441ac9f..84642487 100644
--- a/chrome/browser/ui/app_list/app_list_notifier_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_notifier_impl.cc
@@ -65,6 +65,23 @@
   }
 }
 
+void AppListNotifierImpl::NotifyContinueSectionVisibilityChanged(
+    Location location,
+    bool visible) {
+  DCHECK(location == Location::kContinue || location == Location::kRecentApps);
+
+  continue_section_visibility_[location] = visible;
+  DoStateTransition(location, shown_ && query_.empty() && visible
+                                  ? State::kShown
+                                  : State::kNone);
+}
+
+bool AppListNotifierImpl::GetContinueSectionVisibility(
+    Location location) const {
+  const auto it = continue_section_visibility_.find(location);
+  return it != continue_section_visibility_.cend() && it->second;
+}
+
 void AppListNotifierImpl::NotifySearchQueryChanged(
     const std::u16string& query) {
   // In some cases the query can change after the launcher is closed, in
@@ -73,8 +90,14 @@
   if (shown_) {
     if (query.empty()) {
       DoStateTransition(Location::kList, State::kNone);
-      DoStateTransition(Location::kContinue, State::kShown);
-      DoStateTransition(Location::kRecentApps, State::kShown);
+      DoStateTransition(Location::kContinue,
+                        GetContinueSectionVisibility(Location::kContinue)
+                            ? State::kShown
+                            : State::kNone);
+      DoStateTransition(Location::kRecentApps,
+                        GetContinueSectionVisibility(Location::kRecentApps)
+                            ? State::kShown
+                            : State::kNone);
     } else {
       DoStateTransition(Location::kList, State::kShown);
       DoStateTransition(Location::kContinue, State::kNone);
@@ -110,8 +133,10 @@
   shown_ = shown;
 
   if (shown) {
-    DoStateTransition(Location::kContinue, State::kShown);
-    DoStateTransition(Location::kRecentApps, State::kShown);
+    if (GetContinueSectionVisibility(Location::kContinue))
+      DoStateTransition(Location::kContinue, State::kShown);
+    if (GetContinueSectionVisibility(Location::kRecentApps))
+      DoStateTransition(Location::kRecentApps, State::kShown);
     // kList is not shown until a search query is entered.
   } else {
     DoStateTransition(Location::kList, State::kNone);
diff --git a/chrome/browser/ui/app_list/app_list_notifier_impl.h b/chrome/browser/ui/app_list/app_list_notifier_impl.h
index 7e8a710..a6f85de 100644
--- a/chrome/browser/ui/app_list/app_list_notifier_impl.h
+++ b/chrome/browser/ui/app_list/app_list_notifier_impl.h
@@ -140,6 +140,8 @@
   void NotifyLaunched(Location location, const Result& result) override;
   void NotifyResultsUpdated(Location location,
                             const std::vector<Result>& results) override;
+  void NotifyContinueSectionVisibilityChanged(Location location,
+                                              bool visible) override;
   void NotifySearchQueryChanged(const std::u16string& query) override;
   bool FireImpressionTimerForTesting(Location location) override;
 
@@ -171,12 +173,22 @@
   // Returns the stored results for |location|.
   std::vector<Result> ResultsForLocation(Location location);
 
+  // Returns whether a continue section container (or recent apps container) are
+  // reported to be visible.
+  bool GetContinueSectionVisibility(Location location) const;
+
   ash::AppListController* const app_list_controller_;
 
   base::ObserverList<Observer> observers_;
 
   // The current state of each state machine.
   base::flat_map<Location, State> states_;
+
+  // The reported visibility state of app list continue section - used for
+  // `Location::kContinue` and `Location::kRecentApps`, which may remain hidden
+  // while app list is visible.
+  base::flat_map<Location, bool> continue_section_visibility_;
+
   // An impression timer for each state machine.
   base::flat_map<Location, std::unique_ptr<base::OneShotTimer>> timers_;
 
diff --git a/chrome/browser/ui/app_list/app_list_notifier_impl_old.cc b/chrome/browser/ui/app_list/app_list_notifier_impl_old.cc
index 7ffa48c..24c0d4a8 100644
--- a/chrome/browser/ui/app_list/app_list_notifier_impl_old.cc
+++ b/chrome/browser/ui/app_list/app_list_notifier_impl_old.cc
@@ -39,6 +39,14 @@
   observers_.RemoveObserver(observer);
 }
 
+void AppListNotifierImplOld::NotifyContinueSectionVisibilityChanged(
+    Location location,
+    bool visible) {
+  // App list is not expected to have continue section when "old" app list
+  // notifier is active.
+  NOTREACHED();
+}
+
 void AppListNotifierImplOld::NotifyLaunched(Location location,
                                             const Result& result) {
   launched_result_ = result;
diff --git a/chrome/browser/ui/app_list/app_list_notifier_impl_old.h b/chrome/browser/ui/app_list/app_list_notifier_impl_old.h
index 313fc1c..eb4eb64 100644
--- a/chrome/browser/ui/app_list/app_list_notifier_impl_old.h
+++ b/chrome/browser/ui/app_list/app_list_notifier_impl_old.h
@@ -139,6 +139,8 @@
   // AppListNotifier:
   void AddObserver(Observer* observer) override;
   void RemoveObserver(Observer* observer) override;
+  void NotifyContinueSectionVisibilityChanged(Location location,
+                                              bool visible) override;
   void NotifyLaunched(Location location, const Result& result) override;
   void NotifyResultsUpdated(Location location,
                             const std::vector<Result>& results) override;
diff --git a/chrome/browser/ui/browser_finder.h b/chrome/browser/ui/browser_finder.h
index 672b2cc..33e2b49c 100644
--- a/chrome/browser/ui/browser_finder.h
+++ b/chrome/browser/ui/browser_finder.h
@@ -92,6 +92,14 @@
 size_t GetTotalBrowserCount();
 
 // Returns the number of browsers with the Profile |profile|.
+// Note that:
+// 1. A profile may have non-browser windows. These are not counted.
+// 2. A profile may have child profiles that have windows.  Those are not
+//    counted. Thus, for example, a Guest profile (which is never displayed
+//    directly) will return 0. (For a Guest profile, only the child off-the-
+//    record profile is visible.)  Likewise, a parent profile with off-the-
+//    record (Incognito) child profiles that have windows will not count those
+//    child windows.
 size_t GetBrowserCount(Profile* profile);
 
 // Returns the number of tabbed browsers with the Profile |profile|.
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index b853c23..069d2ad 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -754,18 +754,7 @@
   return chrome::FindBrowserWithWebContents(web_contents()) != nullptr;
 }
 
-void ManagePasswordsUIController::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() ||
-      // Don't react to same-document (fragment) navigations.
-      navigation_handle->IsSameDocument()) {
-    return;
-  }
-
+void ManagePasswordsUIController::PrimaryPageChanged(content::Page& page) {
   // Keep the state if the bubble is currently open or the fallback for saving
   // should be still available.
   if (IsShowingBubble() || save_fallback_timer_.IsRunning()) {
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index 955b5e34..72f77dee 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -221,8 +221,7 @@
   bool IsShowingBubbleForTest() const { return IsShowingBubble(); }
 
   // content::WebContentsObserver:
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
+  void PrimaryPageChanged(content::Page& page) override;
   void OnVisibilityChanged(content::Visibility visibility) override;
 
  private:
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index c8dd889..5e360948 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -380,7 +380,7 @@
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
   const bool whats_new_enabled =
-      promotional_tabs_enabled && whats_new::ShouldShowForState(local_state);
+      whats_new::ShouldShowForState(local_state, promotional_tabs_enabled);
 
   auto* privacy_sandbox_serivce =
       PrivacySandboxServiceFactory::GetForProfile(profile_);
@@ -478,6 +478,11 @@
   LaunchResult launch_result =
       tabs.empty() ? LaunchResult::kNormally : LaunchResult::kWithGivenUrls;
 
+  if (whats_new_enabled && (launch_result == LaunchResult::kWithGivenUrls ||
+                            is_incognito_or_guest || is_post_crash_launch)) {
+    whats_new::LogStartupType(whats_new::StartupType::kIneligible);
+  }
+
   // Only the New Tab Page or command line URLs may be shown in incognito mode.
   // A similar policy exists for crash recovery launches, to prevent getting the
   // user stuck in a crash loop.
@@ -543,6 +548,8 @@
         StartupTabs new_features_tabs;
         new_features_tabs = provider.GetNewFeaturesTabs(whats_new_enabled);
         AppendTabs(new_features_tabs, &tabs);
+      } else if (whats_new_enabled) {
+        whats_new::LogStartupType(whats_new::StartupType::kOverridden);
       }
     }
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 501eb55..8493d84c 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -2269,6 +2269,14 @@
   }
 
   MoveTabsAndSetGroupImpl(indices, destination_index, new_group);
+
+  // Excluding the active tab, deselect all tabs being added to the group.
+  // See crbug/1301846 for more info.
+  const gfx::Range tab_indices =
+      group_model()->GetTabGroup(new_group)->ListTabs();
+  for (auto index = tab_indices.start(); index < tab_indices.end(); ++index)
+    if (active_index() != static_cast<int>(index) && IsTabSelected(index))
+      ToggleSelectionAt(index);
 }
 
 void TabStripModel::AddToExistingGroupImpl(
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index a102b2a..fe26d2d1 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -1466,6 +1466,11 @@
   EXPECT_EQ(tabstrip.GetTabGroupForTab(0), tabstrip.GetTabGroupForTab(1));
   EXPECT_NE(tabstrip.GetTabGroupForTab(0), original_group);
 
+  // Only the active tab will remain selected when adding multiple tabs to a
+  // group; all selected tabs continue to be selected when removing multiple
+  // tabs from a group.
+  tabstrip.ToggleSelectionAt(1);
+
   // Execute CommandToggleGrouped again. Expect both tabs to be ungrouped, since
   // they were in the same group.
   tabstrip.ExecuteContextMenuCommand(0, TabStripModel::CommandToggleGrouped);
diff --git a/chrome/browser/ui/views/chrome_views_delegate.h b/chrome/browser/ui/views/chrome_views_delegate.h
index a3508e6..acdcf2e9 100644
--- a/chrome/browser/ui/views/chrome_views_delegate.h
+++ b/chrome/browser/ui/views/chrome_views_delegate.h
@@ -52,10 +52,13 @@
 // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
 // of lacros-chrome is complete.
 #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
-  gfx::ImageSkia* GetDefaultWindowIcon() const override;
   bool WindowManagerProvidesTitleBar(bool maximized) override;
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+  gfx::ImageSkia* GetDefaultWindowIcon() const override;
+#endif
+
   void AddRef() override;
   void ReleaseRef() override;
   bool IsShuttingDown() const override;
diff --git a/chrome/browser/ui/views/chrome_views_delegate_linux.cc b/chrome/browser/ui/views/chrome_views_delegate_linux.cc
index 13723310c..5cd51ad 100644
--- a/chrome/browser/ui/views/chrome_views_delegate_linux.cc
+++ b/chrome/browser/ui/views/chrome_views_delegate_linux.cc
@@ -23,6 +23,7 @@
   return desktop_env == base::nix::DESKTOP_ENVIRONMENT_UNITY;
 }
 
+#if BUILDFLAG(IS_LINUX)
 int GetWindowIconResourceId() {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   switch (chrome::GetChannel()) {
@@ -36,6 +37,7 @@
 #endif
   return IDR_PRODUCT_LOGO_128;
 }
+#endif  // BUILDFLAG(IS_LINUX)
 
 }  // namespace
 
@@ -50,10 +52,12 @@
   return ::CreateNativeWidget(native_widget_type, params, delegate);
 }
 
+#if BUILDFLAG(IS_LINUX)
 gfx::ImageSkia* ChromeViewsDelegate::GetDefaultWindowIcon() const {
   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
   return rb.GetImageSkiaNamed(GetWindowIconResourceId());
 }
+#endif  // BUILDFLAG(IS_LINUX)
 
 bool ChromeViewsDelegate::WindowManagerProvidesTitleBar(bool maximized) {
   // On Ubuntu Unity, the system always provides a title bar for
diff --git a/chrome/browser/ui/views/page_action/pwa_install_view.cc b/chrome/browser/ui/views/page_action/pwa_install_view.cc
index 719fd1f..f3d6743 100644
--- a/chrome/browser/ui/views/page_action/pwa_install_view.cc
+++ b/chrome/browser/ui/views/page_action/pwa_install_view.cc
@@ -8,7 +8,6 @@
 
 #include "base/callback_helpers.h"
 #include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -139,8 +138,7 @@
   PrefService* prefs =
       Profile::FromBrowserContext(web_contents->GetBrowserContext())
           ->GetPrefs();
-  base::UmaHistogramEnumeration("WebApp.InstallIphPromo.Result",
-                                web_app::InstallIphResult::kIgnored);
+
   web_app::RecordInstallIphIgnored(
       prefs, web_app::GenerateAppIdFromManifest(manager->manifest()),
       base::Time::Now());
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_test_base.cc b/chrome/browser/ui/views/passwords/password_bubble_view_test_base.cc
index 360a37d5..1441e4f 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_test_base.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_test_base.cc
@@ -24,9 +24,6 @@
     return model_delegate_;
   }
 
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override {}
-
  private:
   base::WeakPtr<PasswordsModelDelegate> model_delegate_;
 };
@@ -64,8 +61,13 @@
   // |test_web_contents_|, and will be retrieved correctly via
   // ManagePasswordsUIController::FromWebContents in
   // PasswordsModelDelegateFromWebContents().
-  new TestManagePasswordsUIController(
-      test_web_contents_.get(), model_delegate_weak_ptr_factory_.GetWeakPtr());
+  TestManagePasswordsUIController* controller =
+      new TestManagePasswordsUIController(
+          test_web_contents_.get(),
+          model_delegate_weak_ptr_factory_.GetWeakPtr());
+  // Set a stub password manager client to avoid a DCHECK failure in
+  // |ManagePasswordsState|.
+  controller->set_client(&password_manager_client_);
 }
 
 PasswordBubbleViewTestBase::~PasswordBubbleViewTestBase() = default;
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_test_base.h b/chrome/browser/ui/views/passwords/password_bubble_view_test_base.h
index 1ccfdca..a8e450f 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_test_base.h
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_test_base.h
@@ -12,6 +12,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "components/password_manager/core/browser/mock_password_feature_manager.h"
+#include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
@@ -45,6 +46,7 @@
 
  private:
   content::RenderViewHostTestEnabler test_render_host_factories_;
+  password_manager::StubPasswordManagerClient password_manager_client_;
   std::unique_ptr<TestingProfile> profile_;
   std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
       identity_test_env_profile_adaptor_;
diff --git a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
index bc31aa90..6860694 100644
--- a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
+++ b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
@@ -266,12 +266,13 @@
   if (dialog_view_)
     dialog_view_->DisplayErrorMessage(error_message, accept_button);
 }
+
 bool EnterpriseStartupDialogImpl::IsShowing() {
   return dialog_view_;
 }
 
 // views::WidgetObserver:
-void EnterpriseStartupDialogImpl::OnWidgetClosing(views::Widget* widget) {
+void EnterpriseStartupDialogImpl::OnWidgetDestroying(views::Widget* widget) {
   dialog_view_->RemoveWidgetObserver(this);
   dialog_view_ = nullptr;
 }
diff --git a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
index 0a7c5b69..dc7b9c3 100644
--- a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
+++ b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
@@ -82,7 +82,7 @@
   bool IsShowing() override;
 
   // views::WidgetObserver:
-  void OnWidgetClosing(views::Widget* widget) override;
+  void OnWidgetDestroying(views::Widget* widget) override;
 
  private:
   // The dialog_view_ is owned by itself.
diff --git a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
index db469878..47a4378 100644
--- a/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
+++ b/chrome/browser/ui/views/side_search/side_search_browser_controller.cc
@@ -159,9 +159,14 @@
   METADATA_HEADER(HeaderView);
   HeaderView(base::RepeatingClosure callback, Browser* browser)
       : layout_(SetLayoutManager(std::make_unique<views::FlexLayout>())) {
+    constexpr int kHeaderHeight = 44;
+    SetPreferredSize(gfx::Size(0, kHeaderHeight));
+
+    constexpr int kHorizontalMargin = 8;
     layout_->SetOrientation(views::LayoutOrientation::kHorizontal)
         .SetMainAxisAlignment(views::LayoutAlignment::kStart)
-        .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+        .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
+        .SetInteriorMargin(gfx::Insets::VH(0, kHorizontalMargin));
 
     dse_image_view_ = AddChildView(std::make_unique<DseImageView>(browser));
     dse_image_view_->SetProperty(
@@ -245,9 +250,6 @@
     if (feedback_button_)
       feedback_button_->UpdateIcon();
     close_button_->UpdateIcon();
-
-    layout_->SetInteriorMargin(
-        GetLayoutInsets(LayoutInset::TOOLBAR_INTERIOR_MARGIN));
   }
 
   raw_ptr<DseImageView> dse_image_view_ = nullptr;
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index 3e6dc3c..82cbb63 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -52,6 +52,7 @@
   if (tab_strip_)
     tab_strip_->MoveTab(from_index, to_index, TabRendererData());
 }
+
 void FakeBaseTabStripController::MoveGroup(const tab_groups::TabGroupId& group,
                                            int to_index) {}
 
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
index 058b746..c596dd7 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.cc
@@ -4,16 +4,19 @@
 
 #include "chrome/browser/ui/views/tabs/fake_tab_slot_controller.h"
 
+#include "chrome/browser/ui/views/tabs/tab_container.h"
+#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
+
+FakeTabSlotController::FakeTabSlotController(
+    TabStripController* tab_strip_controller)
+    : tab_strip_controller_(tab_strip_controller) {}
+
 const ui::ListSelectionModel& FakeTabSlotController::GetSelectionModel() const {
   return selection_model_;
 }
 
 Tab* FakeTabSlotController::tab_at(int index) const {
-  return nullptr;
-}
-
-int FakeTabSlotController::GetActiveIndex() const {
-  return 0;
+  return tab_container_->GetTabAtModelIndex(index);
 }
 
 bool FakeTabSlotController::ToggleTabGroupCollapsedState(
@@ -123,27 +126,27 @@
 
 std::u16string FakeTabSlotController::GetGroupTitle(
     const tab_groups::TabGroupId& group_id) const {
-  return std::u16string();
+  return tab_strip_controller_->GetGroupTitle(group_id);
 }
 
 std::u16string FakeTabSlotController::GetGroupContentString(
     const tab_groups::TabGroupId& group) const {
-  return std::u16string();
+  return tab_strip_controller_->GetGroupContentString(group);
 }
 
 tab_groups::TabGroupColorId FakeTabSlotController::GetGroupColorId(
     const tab_groups::TabGroupId& group_id) const {
-  return tab_groups::TabGroupColorId();
+  return tab_strip_controller_->GetGroupColorId(group_id);
 }
 
 bool FakeTabSlotController::IsGroupCollapsed(
     const tab_groups::TabGroupId& group) const {
-  return false;
+  return tab_strip_controller_->IsGroupCollapsed(group);
 }
 
 absl::optional<int> FakeTabSlotController::GetLastTabInGroup(
     const tab_groups::TabGroupId& group) const {
-  return absl::nullopt;
+  return tab_strip_controller_->GetLastTabInGroup(group);
 }
 
 SkColor FakeTabSlotController::GetPaintedGroupColor(
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
index 68717f85..7343343a 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
@@ -10,13 +10,20 @@
 #include "ui/base/models/list_selection_model.h"
 #include "ui/gfx/color_palette.h"
 
+class TabContainer;
+class TabStripController;
+
 class FakeTabSlotController : public TabSlotController {
  public:
-  FakeTabSlotController() = default;
+  explicit FakeTabSlotController(
+      TabStripController* tab_strip_controller_ = nullptr);
   FakeTabSlotController(const FakeTabSlotController&) = delete;
   FakeTabSlotController& operator=(const FakeTabSlotController&) = delete;
   ~FakeTabSlotController() override = default;
 
+  void set_tab_container(TabContainer* tab_container) {
+    tab_container_ = tab_container;
+  }
   void set_active_tab(Tab* tab) { active_tab_ = tab; }
   void set_paint_throbber_to_layer(bool value) {
     paint_throbber_to_layer_ = value;
@@ -24,7 +31,6 @@
 
   const ui::ListSelectionModel& GetSelectionModel() const override;
   Tab* tab_at(int index) const override;
-  int GetActiveIndex() const override;
   void SelectTab(Tab* tab, const ui::Event& event) override {}
   void ExtendSelectionTo(Tab* tab) override {}
   void ToggleSelected(Tab* tab) override {}
@@ -104,6 +110,8 @@
   }
 
  private:
+  raw_ptr<TabStripController> tab_strip_controller_;
+  raw_ptr<TabContainer> tab_container_;
   ui::ListSelectionModel selection_model_;
   raw_ptr<Tab> active_tab_ = nullptr;
   bool paint_throbber_to_layer_ = true;
diff --git a/chrome/browser/ui/views/tabs/tab_container.cc b/chrome/browser/ui/views/tabs/tab_container.cc
index 01e9373..0cabdf4e 100644
--- a/chrome/browser/ui/views/tabs/tab_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_container.cc
@@ -354,11 +354,10 @@
 Tab* TabContainer::AddTab(std::unique_ptr<Tab> tab,
                           int model_index,
                           TabPinned pinned) {
-  absl::optional<tab_groups::TabGroupId> group = tab->group();
-  Tab* tab_ptr = AddChildViewAt(
-      std::move(tab), GetViewInsertionIndex(group, absl::nullopt, model_index));
+  Tab* tab_ptr = AddChildView(std::move(tab));
   tabs_view_model_.Add(tab_ptr, model_index);
   layout_helper_->InsertTabAt(model_index, tab_ptr, pinned);
+  OrderTabSlotView(tab_ptr);
 
   // Don't animate the first tab, it looks weird, and don't animate anything
   // if the containing window isn't visible yet.
@@ -375,10 +374,9 @@
 
 void TabContainer::MoveTab(int from_model_index, int to_model_index) {
   Tab* tab = GetTabAtModelIndex(from_model_index);
-  ReorderChildView(tab, GetViewInsertionIndex(tab->group(), from_model_index,
-                                              to_model_index));
   tabs_view_model_.Move(from_model_index, to_model_index);
   layout_helper_->MoveTab(tab->group(), from_model_index, to_model_index);
+  OrderTabSlotView(tab);
 
   layout_helper()->SetTabPinned(to_model_index, tab->data().pinned
                                                     ? TabPinned::kPinned
@@ -490,19 +488,15 @@
 
   layout_helper()->UpdateGroupHeaderIndex(group);
 
-  TabGroupHeader* group_header = group_views()[group]->header();
-  const int first_tab_model_index =
-      controller_->GetFirstTabInGroup(group).value();
-
-  MoveGroupHeader(group_header, first_tab_model_index);
+  OrderTabSlotView(group_views()[group]->header());
 }
 
 void TabContainer::OnGroupClosed(const tab_groups::TabGroupId& group) {
   bounds_animator().StopAnimatingView(group_views().at(group).get()->header());
   layout_helper()->RemoveGroupHeader(group);
+  group_views().erase(group);
 
   StartBasicAnimation();
-  group_views().erase(group);
 }
 
 void TabContainer::UpdateTabGroupVisuals(tab_groups::TabGroupId group_id) {
@@ -1202,22 +1196,6 @@
   }
 }
 
-void TabContainer::MoveGroupHeader(TabGroupHeader* group_header,
-                                   int first_tab_model_index) {
-  const int header_index = GetIndexOf(group_header);
-  const int first_tab_view_index =
-      GetViewIndexForModelIndex(first_tab_model_index);
-
-  // The header should be just before the first tab. If it isn't, reorder the
-  // header such that it is. Note that the index to reorder to is different
-  // depending on whether the header is before or after the tab, since the
-  // header itself occupies an index.
-  if (header_index < first_tab_view_index - 1)
-    ReorderChildView(group_header, first_tab_view_index - 1);
-  if (header_index > first_tab_view_index - 1)
-    ReorderChildView(group_header, first_tab_view_index);
-}
-
 void TabContainer::ResizeLayoutTabs() {
   // We've been called back after the TabStrip has been emptied out (probably
   // just prior to the window being destroyed). We need to do nothing here or
@@ -1291,56 +1269,23 @@
   mouse_watcher_ = nullptr;
 }
 
-int TabContainer::GetViewInsertionIndex(
-    absl::optional<tab_groups::TabGroupId> group,
-    absl::optional<int> from_model_index,
-    int to_model_index) const {
-  // -1 is treated a sentinel value to indicate a tab is newly added to the
-  // beginning of the tab strip.
-  if (to_model_index < 0)
-    return 0;
+void TabContainer::OrderTabSlotView(TabSlotView* slot_view) {
+  // |slot_view| is in the wrong place in children(). Fix it.
+  std::vector<TabSlotView*> slots = layout_helper_->GetTabSlotViews();
+  size_t target_slot_index =
+      std::find(slots.begin(), slots.end(), slot_view) - slots.begin();
+  // Find the index in children() that corresponds to |target_slot_index|.
+  size_t view_index = 0;
+  for (size_t slot_index = 0; slot_index < target_slot_index; slot_index++) {
+    // If we don't own this view, skip it *without* advancing in children().
+    if (slots[slot_index]->parent() != this)
+      continue;
+    if (view_index == children().size())
+      break;
+    view_index++;
+  }
 
-  // If to_model_index is beyond the end of the tab strip, then the tab is
-  // newly added to the end of the tab strip. In that case we can just return
-  // one beyond the view index of the last existing tab.
-  if (to_model_index >= GetTabCount())
-    return (GetTabCount() ? GetViewIndexForModelIndex(GetTabCount() - 1) + 1
-                          : 0);
-
-  // If there is no from_model_index, then the tab is newly added in the
-  // middle of the tab strip. In that case we treat it as coming from the end
-  // of the tab strip, since new views are ordered at the end by default.
-  if (!from_model_index.has_value())
-    from_model_index = GetTabCount();
-
-  DCHECK_NE(to_model_index, from_model_index.value());
-
-  // Since we don't have an absolute mapping from model index to view index,
-  // we anchor on the last known view index at the given to_model_index.
-  Tab* other_tab = GetTabAtModelIndex(to_model_index);
-  int other_view_index = GetViewIndexForModelIndex(to_model_index);
-
-  if (other_view_index <= 0)
-    return 0;
-
-  // When moving to the right, just use the anchor index because the tab will
-  // replace that position in both the model and the view. This happens
-  // because the tab itself occupies a lower index that the other tabs will
-  // shift into.
-  if (to_model_index > from_model_index.value())
-    return other_view_index;
-
-  // When moving to the left, the tab may end up on either the left or right
-  // side of a group header, depending on if it's in that group. This affects
-  // its view index but not its model index, so we adjust the former only.
-  if (other_tab->group().has_value() && other_tab->group() != group)
-    return other_view_index - 1;
-
-  return other_view_index;
-}
-
-int TabContainer::GetViewIndexForModelIndex(int model_index) const {
-  return GetIndexOf(GetTabAtModelIndex(model_index));
+  ReorderChildView(slot_view, view_index);
 }
 
 bool TabContainer::IsPointInTab(Tab* tab,
diff --git a/chrome/browser/ui/views/tabs/tab_container.h b/chrome/browser/ui/views/tabs/tab_container.h
index add426b..81ffefc6 100644
--- a/chrome/browser/ui/views/tabs/tab_container.h
+++ b/chrome/browser/ui/views/tabs/tab_container.h
@@ -25,7 +25,6 @@
 #include "ui/views/view_targeter_delegate.h"
 
 class TabStrip;
-class TabGroupHeader;
 class TabHoverCardController;
 class TabDragContext;
 
@@ -238,8 +237,6 @@
   // the removal of the tab at |model_index|.
   void UpdateClosingModeOnRemovedTab(int model_index, bool was_active);
 
-  void MoveGroupHeader(TabGroupHeader* group_header, int first_tab_model_index);
-
   // Perform an animated resize-relayout of the TabContainer immediately.
   void ResizeLayoutTabs();
 
@@ -257,16 +254,9 @@
   void AddMessageLoopObserver();
   void RemoveMessageLoopObserver();
 
-  // Returns the corresponding view index of a |tab| to be inserted at
-  // |to_model_index|. Used to reorder the child views of the tab container
-  // so that focus order stays consistent with the visual tab order.
-  // |from_model_index| is where the tab currently is, if it's being moved
-  // instead of added.
-  int GetViewInsertionIndex(absl::optional<tab_groups::TabGroupId> group,
-                            absl::optional<int> from_model_index,
-                            int to_model_index) const;
-
-  int GetViewIndexForModelIndex(int tab_model_index) const;
+  // Moves |slot_view| within children() to match |layout_helper_|'s slot
+  // ordering.
+  void OrderTabSlotView(TabSlotView* slot_view);
 
   // Returns true if the specified point in TabStrip coords is within the
   // hit-test region of the specified Tab.
diff --git a/chrome/browser/ui/views/tabs/tab_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_container_unittest.cc
index 28ed2e5..72da5b7 100644
--- a/chrome/browser/ui/views/tabs/tab_container_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_container_unittest.cc
@@ -43,20 +43,23 @@
     ChromeViewsTestBase::SetUp();
 
     tab_strip_controller_ = std::make_unique<FakeBaseTabStripController>();
-    tab_slot_controller_ = std::make_unique<FakeTabSlotController>();
+    tab_slot_controller_ =
+        std::make_unique<FakeTabSlotController>(tab_strip_controller_.get());
 
     std::unique_ptr<TabContainer> tab_container =
         std::make_unique<TabContainer>(
             tab_strip_controller_.get(), nullptr /*hover_card_controller*/,
             nullptr /*drag_context*/, tab_slot_controller_.get(),
             nullptr /*scroll_contents_view*/);
-    tab_container->SetAvailableWidthCallback(
-        base::BindRepeating([]() { return 500; }));
-    tab_container->SetBounds(0, 0, 500, GetLayoutConstant(TAB_HEIGHT));
-    tab_container->Layout();
+    tab_container->SetAvailableWidthCallback(base::BindRepeating(
+        [](TabContainerTest* test) { return test->tab_container_width_; },
+        this));
 
     widget_ = CreateTestWidget();
+    SetTabContainerWidth(1000);
     tab_container_ = widget_->SetContentsView(std::move(tab_container));
+
+    tab_slot_controller_->set_tab_container(tab_container_);
   }
 
   void TearDown() override {
@@ -143,6 +146,13 @@
       AddTabToGroup(index, new_group.value());
   }
 
+  std::vector<TabGroupViews*> ListGroupViews() const {
+    std::vector<TabGroupViews*> result;
+    for (auto const& group_view_pair : tab_container_->group_views())
+      result.push_back(group_view_pair.second.get());
+    return result;
+  }
+
   // Returns all TabSlotViews in the order that they have as ViewChildren of
   // TabContainer. This should match the actual order that they appear in
   // visually.
@@ -205,10 +215,18 @@
     return tab->HitTestPoint(point_in_tab_coords);
   }
 
+  void SetTabContainerWidth(int width) {
+    tab_container_width_ = width;
+    widget_->SetSize(
+        gfx::Size(tab_container_width_, GetLayoutConstant(TAB_HEIGHT)));
+  }
+
   std::unique_ptr<FakeBaseTabStripController> tab_strip_controller_;
   std::unique_ptr<FakeTabSlotController> tab_slot_controller_;
   raw_ptr<TabContainer> tab_container_;
   std::unique_ptr<views::Widget> widget_;
+
+  int tab_container_width_ = 0;
 };
 
 TEST_F(TabContainerTest, ExitsClosingModeAtStandardWidth) {
@@ -372,8 +390,6 @@
 }
 
 TEST_F(TabContainerTest, GetEventHandlerForOverlappingArea) {
-  tab_container_->SetSize(gfx::Size(1000, tab_container_->height()));
-
   Tab* left_tab = AddTab(0);
   Tab* active_tab = AddTab(1, absl::nullopt, TabActive::kActive);
   Tab* right_tab = AddTab(2);
@@ -429,8 +445,6 @@
 }
 
 TEST_F(TabContainerTest, GetTooltipHandler) {
-  tab_container_->SetSize(gfx::Size(1000, tab_container_->height()));
-
   Tab* left_tab = AddTab(0);
   Tab* active_tab = AddTab(1, absl::nullopt, TabActive::kActive);
   Tab* right_tab = AddTab(2);
@@ -479,7 +493,7 @@
       (right_tab->x() + most_right_tab->bounds().right() + 1) / 2,
       right_tab->bounds().bottom() - 1);
 
-  // Sanity check that the point is in both active and left tab.
+  // Sanity check that the point is in both tabs.
   ASSERT_TRUE(IsPointInTab(right_tab, unactive_overlap));
   ASSERT_TRUE(IsPointInTab(most_right_tab, unactive_overlap));
 
@@ -491,3 +505,204 @@
   // don't hit it.
   EXPECT_FALSE(tab_container_->GetTooltipHandlerForPoint(gfx::Point(-1, 2)));
 }
+
+TEST_F(TabContainerTest, GroupHeaderBasics) {
+  AddTab(0);
+
+  Tab* tab = tab_container_->GetTabAtModelIndex(0);
+  const int first_slot_x = tab->x();
+
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(0, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  std::vector<TabGroupViews*> views = ListGroupViews();
+  EXPECT_EQ(1u, views.size());
+  TabGroupHeader* header = views[0]->header();
+  EXPECT_EQ(first_slot_x, header->x());
+  EXPECT_GT(header->width(), 0);
+  EXPECT_EQ(header->bounds().right() - TabStyle::GetTabOverlap(), tab->x());
+  EXPECT_EQ(tab->height(), header->height());
+}
+
+TEST_F(TabContainerTest, GroupHeaderBetweenTabs) {
+  AddTab(0);
+  AddTab(1);
+  tab_container_->CompleteAnimationAndLayout();
+
+  const int second_slot_x = tab_container_->GetTabAtModelIndex(1)->x();
+
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(1, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupViews()[0]->header();
+  EXPECT_EQ(header->x(), second_slot_x);
+}
+
+TEST_F(TabContainerTest, GroupHeaderMovesRightWithTab) {
+  for (int i = 0; i < 4; i++)
+    AddTab(i);
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(1, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  MoveTab(1, 2);
+  tab_container_->CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupViews()[0]->header();
+  // Header is now left of tab 2.
+  EXPECT_LT(tab_container_->GetTabAtModelIndex(1)->x(), header->x());
+  EXPECT_LT(header->x(), tab_container_->GetTabAtModelIndex(2)->x());
+}
+
+TEST_F(TabContainerTest, GroupHeaderMovesLeftWithTab) {
+  for (int i = 0; i < 4; i++)
+    AddTab(i);
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(2, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  MoveTab(2, 1);
+  tab_container_->CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupViews()[0]->header();
+  // Header is now left of tab 1.
+  EXPECT_LT(tab_container_->GetTabAtModelIndex(0)->x(), header->x());
+  EXPECT_LT(header->x(), tab_container_->GetTabAtModelIndex(1)->x());
+}
+
+TEST_F(TabContainerTest, GroupHeaderDoesntMoveReorderingTabsInGroup) {
+  for (int i = 0; i < 4; i++)
+    AddTab(i);
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(1, group);
+  AddTabToGroup(2, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  TabGroupHeader* header = ListGroupViews()[0]->header();
+  const int initial_header_x = header->x();
+  Tab* tab1 = tab_container_->GetTabAtModelIndex(1);
+  const int initial_tab_1_x = tab1->x();
+  Tab* tab2 = tab_container_->GetTabAtModelIndex(2);
+  const int initial_tab_2_x = tab2->x();
+
+  MoveTab(1, 2);
+  tab_container_->CompleteAnimationAndLayout();
+
+  // Header has not moved.
+  EXPECT_EQ(initial_header_x, header->x());
+  EXPECT_EQ(initial_tab_1_x, tab2->x());
+  EXPECT_EQ(initial_tab_2_x, tab1->x());
+}
+
+TEST_F(TabContainerTest, GroupHeaderMovesOnRegrouping) {
+  for (int i = 0; i < 3; i++)
+    AddTab(i);
+  tab_groups::TabGroupId group0 = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(0, group0);
+  tab_groups::TabGroupId group1 = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(1, group1);
+  AddTabToGroup(2, group1);
+  tab_container_->CompleteAnimationAndLayout();
+
+  std::vector<TabGroupViews*> views = ListGroupViews();
+  auto views_it =
+      std::find_if(views.begin(), views.end(), [&group1](TabGroupViews* view) {
+        return view->header()->group() == group1;
+      });
+  ASSERT_TRUE(views_it != views.end());
+  TabGroupViews* group1_views = *views_it;
+
+  // Change groups in a way so that the header should swap with the tab, without
+  // an explicit MoveTab call.
+  MoveTabIntoGroup(1, group0);
+  tab_container_->CompleteAnimationAndLayout();
+
+  // Header is now right of tab 1.
+  EXPECT_LT(tab_container_->GetTabAtModelIndex(1)->x(),
+            group1_views->header()->x());
+  EXPECT_LT(group1_views->header()->x(),
+            tab_container_->GetTabAtModelIndex(2)->x());
+}
+
+TEST_F(TabContainerTest, UngroupedTabMovesLeftOfHeader) {
+  for (int i = 0; i < 2; i++)
+    AddTab(i);
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(0, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  MoveTab(1, 0);
+  tab_container_->CompleteAnimationAndLayout();
+
+  // Header is right of tab 0.
+  TabGroupHeader* header = ListGroupViews()[0]->header();
+  EXPECT_LT(tab_container_->GetTabAtModelIndex(0)->x(), header->x());
+  EXPECT_LT(header->x(), tab_container_->GetTabAtModelIndex(1)->x());
+}
+
+TEST_F(TabContainerTest, DeleteTabGroupViewsWhenEmpty) {
+  AddTab(0);
+  AddTab(1);
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(0, group);
+  AddTabToGroup(1, group);
+  RemoveTabFromGroup(0);
+
+  EXPECT_EQ(1u, ListGroupViews().size());
+  RemoveTabFromGroup(1);
+  EXPECT_EQ(0u, ListGroupViews().size());
+}
+
+TEST_F(TabContainerTest, GroupUnderlineBasics) {
+  AddTab(0);
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(0, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  std::vector<TabGroupViews*> views = ListGroupViews();
+  EXPECT_EQ(1u, views.size());
+  // Update underline manually in the absence of a real Paint cycle.
+  views[0]->UpdateBounds();
+
+  const TabGroupUnderline* underline = views[0]->underline();
+  EXPECT_EQ(underline->x(), TabGroupUnderline::GetStrokeInset());
+  EXPECT_GT(underline->width(), 0);
+  EXPECT_EQ(underline->bounds().right(),
+            tab_container_->GetTabAtModelIndex(0)->bounds().right() -
+                TabGroupUnderline::GetStrokeInset());
+  EXPECT_EQ(underline->height(), TabGroupUnderline::kStrokeThickness);
+
+  // Endpoints are different if the last grouped tab is active.
+  AddTab(1, absl::nullopt, TabActive::kActive);
+  MoveTabIntoGroup(1, group);
+  tab_container_->CompleteAnimationAndLayout();
+  views[0]->UpdateBounds();
+
+  EXPECT_EQ(underline->x(), TabGroupUnderline::GetStrokeInset());
+  EXPECT_EQ(underline->bounds().right(),
+            tab_container_->GetTabAtModelIndex(1)->bounds().right() +
+                TabGroupUnderline::kStrokeThickness);
+}
+
+TEST_F(TabContainerTest, GroupHighlightBasics) {
+  AddTab(0);
+
+  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
+  AddTabToGroup(0, group);
+  tab_container_->CompleteAnimationAndLayout();
+
+  std::vector<TabGroupViews*> views = ListGroupViews();
+  EXPECT_EQ(1u, views.size());
+
+  // The highlight bounds match the group view bounds. Grab this manually
+  // here, since there isn't a real paint cycle to trigger OnPaint().
+  gfx::Rect bounds = views[0]->GetBounds();
+  EXPECT_EQ(bounds.x(), 0);
+  EXPECT_GT(bounds.width(), 0);
+  EXPECT_EQ(bounds.right(),
+            tab_container_->GetTabAtModelIndex(0)->bounds().right());
+  EXPECT_EQ(bounds.height(),
+            tab_container_->GetTabAtModelIndex(0)->bounds().height());
+}
diff --git a/chrome/browser/ui/views/tabs/tab_group_views.cc b/chrome/browser/ui/views/tabs/tab_group_views.cc
index d927b1c0..045fda4e 100644
--- a/chrome/browser/ui/views/tabs/tab_group_views.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_views.cc
@@ -60,9 +60,6 @@
 
   header_->VisualsChanged();
   underline_->SchedulePaint();
-  const int active_index = tab_slot_controller_->GetActiveIndex();
-  if (active_index != TabStripModel::kNoTab)
-    tab_slot_controller_->tab_at(active_index)->SchedulePaint();
 }
 
 gfx::Rect TabGroupViews::GetBounds() const {
diff --git a/chrome/browser/ui/views/tabs/tab_slot_controller.h b/chrome/browser/ui/views/tabs/tab_slot_controller.h
index dce6378..7f01f26 100644
--- a/chrome/browser/ui/views/tabs/tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_slot_controller.h
@@ -54,9 +54,6 @@
   // Returns the tab at |index|.
   virtual Tab* tab_at(int index) const = 0;
 
-  // Returns the index of the active tab.
-  virtual int GetActiveIndex() const = 0;
-
   // Selects the tab. |event| is the event that causes |tab| to be selected.
   virtual void SelectTab(Tab* tab, const ui::Event& event) = 0;
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 73216b9b..5e00e54 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -979,6 +979,11 @@
   if (!is_collapsing)
     tab_container_->ExitTabClosingMode();
   tab_container_->StartBasicAnimation();
+
+  // The active tab may need to repaint its group stroke if it's in |group|.
+  int active_index = GetActiveIndex();
+  if (IsValidModelIndex(active_index))
+    tab_at(active_index)->SchedulePaint();
 }
 
 void TabStrip::ToggleTabGroup(const tab_groups::TabGroupId& group,
@@ -1145,6 +1150,10 @@
   tab_at(model_index)->SetTabNeedsAttention(attention);
 }
 
+int TabStrip::GetActiveIndex() const {
+  return controller_->GetActiveIndex();
+}
+
 int TabStrip::GetModelIndexOf(const TabSlotView* view) const {
   return tab_container_->GetModelIndexOf(view);
 }
@@ -1206,10 +1215,6 @@
   return tab_container_->GetTabAtModelIndex(index);
 }
 
-int TabStrip::GetActiveIndex() const {
-  return controller_->GetActiveIndex();
-}
-
 void TabStrip::SelectTab(Tab* tab, const ui::Event& event) {
   int model_index = GetModelIndexOf(tab);
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index ddfb6b8..629bcbd 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -185,6 +185,9 @@
     return tab_container_->group_views().at(id).get()->header();
   }
 
+  // Returns the active index, or kNoTab if no tab is active.
+  int GetActiveIndex() const;
+
   // Returns the index of the specified view in the model coordinate system, or
   // -1 if view is closing or not a tab.
   int GetModelIndexOf(const TabSlotView* view) const;
@@ -233,10 +236,9 @@
   // on drop.
   bool WantsToReceiveAllDragEvents() const;
 
-  // TabController:
+  // TabSlotController:
   const ui::ListSelectionModel& GetSelectionModel() const override;
   Tab* tab_at(int index) const override;
-  int GetActiveIndex() const override;
   void SelectTab(Tab* tab, const ui::Event& event) override;
   void ExtendSelectionTo(Tab* tab) override;
   void ToggleSelected(Tab* tab) override;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index bce9c9c..af1e0cff 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -582,223 +582,6 @@
   EXPECT_TRUE(second_tab->closing());
 }
 
-TEST_P(TabStripTest, GroupHeaderBasics) {
-  SetMaxTabStripWidth(1000);
-  bounds_animator()->SetAnimationDuration(base::TimeDelta());
-  controller_->AddTab(0, false);
-
-  Tab* tab = tab_strip_->tab_at(0);
-  const int first_slot_x = tab->x();
-
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(0, group);
-  CompleteAnimationAndLayout();
-
-  std::vector<TabGroupViews*> views = ListGroupViews();
-  EXPECT_EQ(1u, views.size());
-  TabGroupHeader* header = views[0]->header();
-  EXPECT_EQ(first_slot_x, header->x());
-  EXPECT_GT(header->width(), 0);
-  EXPECT_EQ(header->bounds().right() - TabStyle::GetTabOverlap(), tab->x());
-  EXPECT_EQ(tab->height(), header->height());
-}
-
-TEST_P(TabStripTest, GroupHeaderBetweenTabs) {
-  SetMaxTabStripWidth(1000);
-  bounds_animator()->SetAnimationDuration(base::TimeDelta());
-
-  controller_->AddTab(0, false);
-  controller_->AddTab(1, false);
-
-  const int second_slot_x = tab_strip_->tab_at(1)->x();
-
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(1, group);
-
-  TabGroupHeader* header = ListGroupViews()[0]->header();
-  EXPECT_EQ(header->x(), second_slot_x);
-}
-
-TEST_P(TabStripTest, GroupHeaderMovesRightWithTab) {
-  SetMaxTabStripWidth(2000);
-  for (int i = 0; i < 4; i++)
-    controller_->AddTab(i, false);
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(1, group);
-  CompleteAnimationAndLayout();
-
-  controller_->MoveTab(1, 2);
-  CompleteAnimationAndLayout();
-
-  TabGroupHeader* header = ListGroupViews()[0]->header();
-  // Header is now left of tab 2.
-  EXPECT_LT(tab_strip_->tab_at(1)->x(), header->x());
-  EXPECT_LT(header->x(), tab_strip_->tab_at(2)->x());
-}
-
-TEST_P(TabStripTest, GroupHeaderMovesLeftWithTab) {
-  SetMaxTabStripWidth(2000);
-  for (int i = 0; i < 4; i++)
-    controller_->AddTab(i, false);
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(2, group);
-  CompleteAnimationAndLayout();
-
-  controller_->MoveTab(2, 1);
-  CompleteAnimationAndLayout();
-
-  TabGroupHeader* header = ListGroupViews()[0]->header();
-  // Header is now left of tab 1.
-  EXPECT_LT(tab_strip_->tab_at(0)->x(), header->x());
-  EXPECT_LT(header->x(), tab_strip_->tab_at(1)->x());
-}
-
-TEST_P(TabStripTest, GroupHeaderDoesntMoveReorderingTabsInGroup) {
-  SetMaxTabStripWidth(2000);
-  for (int i = 0; i < 4; i++)
-    controller_->AddTab(i, false);
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(1, group);
-  controller_->MoveTabIntoGroup(2, group);
-  CompleteAnimationAndLayout();
-
-  TabGroupHeader* header = ListGroupViews()[0]->header();
-  const int initial_header_x = header->x();
-  Tab* tab1 = tab_strip_->tab_at(1);
-  const int initial_tab_1_x = tab1->x();
-  Tab* tab2 = tab_strip_->tab_at(2);
-  const int initial_tab_2_x = tab2->x();
-
-  controller_->MoveTab(1, 2);
-  CompleteAnimationAndLayout();
-
-  // Header has not moved.
-  EXPECT_EQ(initial_header_x, header->x());
-  EXPECT_EQ(initial_tab_1_x, tab2->x());
-  EXPECT_EQ(initial_tab_2_x, tab1->x());
-}
-
-TEST_P(TabStripTest, GroupHeaderMovesOnRegrouping) {
-  SetMaxTabStripWidth(2000);
-  for (int i = 0; i < 3; i++)
-    controller_->AddTab(i, false);
-  tab_groups::TabGroupId group0 = tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(0, group0);
-  tab_groups::TabGroupId group1 = tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(1, group1);
-  controller_->MoveTabIntoGroup(2, group1);
-  CompleteAnimationAndLayout();
-
-  std::vector<TabGroupViews*> views = ListGroupViews();
-  auto views_it =
-      std::find_if(views.begin(), views.end(), [&group1](TabGroupViews* view) {
-        return view->header()->group() == group1;
-      });
-  ASSERT_TRUE(views_it != views.end());
-  TabGroupViews* view = *views_it;
-
-  // Change groups in a way so that the header should swap with the tab, without
-  // an explicit MoveTab call.
-  controller_->MoveTabIntoGroup(1, group0);
-  CompleteAnimationAndLayout();
-
-  // Header is now right of tab 1.
-  EXPECT_LT(tab_strip_->tab_at(1)->x(), view->header()->x());
-  EXPECT_LT(view->header()->x(), tab_strip_->tab_at(2)->x());
-}
-
-TEST_P(TabStripTest, UngroupedTabMovesLeftOfHeader) {
-  SetMaxTabStripWidth(2000);
-  for (int i = 0; i < 2; i++)
-    controller_->AddTab(i, false);
-  tab_groups::TabGroupId group = tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(0, group);
-  CompleteAnimationAndLayout();
-
-  controller_->MoveTab(1, 0);
-  CompleteAnimationAndLayout();
-
-  // Header is right of tab 0.
-  TabGroupHeader* header = ListGroupViews()[0]->header();
-  EXPECT_LT(tab_strip_->tab_at(0)->x(), header->x());
-  EXPECT_LT(header->x(), tab_strip_->tab_at(1)->x());
-}
-
-TEST_P(TabStripTest, DeleteTabGroupViewsWhenEmpty) {
-  controller_->AddTab(0, false);
-  controller_->AddTab(1, false);
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(0, group);
-  controller_->MoveTabIntoGroup(1, group);
-  controller_->MoveTabIntoGroup(0, absl::nullopt);
-
-  EXPECT_EQ(1u, ListGroupViews().size());
-  controller_->MoveTabIntoGroup(1, absl::nullopt);
-  EXPECT_EQ(0u, ListGroupViews().size());
-}
-
-TEST_P(TabStripTest, GroupUnderlineBasics) {
-  SetMaxTabStripWidth(1000);
-  bounds_animator()->SetAnimationDuration(base::TimeDelta());
-  controller_->AddTab(0, false);
-
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(0, group);
-  CompleteAnimationAndLayout();
-
-  std::vector<TabGroupViews*> views = ListGroupViews();
-  EXPECT_EQ(1u, views.size());
-  // Update underline manually in the absence of a real Paint cycle.
-  views[0]->UpdateBounds();
-
-  const TabGroupUnderline* underline = views[0]->underline();
-  EXPECT_EQ(underline->x(), TabGroupUnderline::GetStrokeInset());
-  EXPECT_GT(underline->width(), 0);
-  EXPECT_EQ(underline->bounds().right(),
-            tab_strip_->tab_at(0)->bounds().right() -
-                TabGroupUnderline::GetStrokeInset());
-  EXPECT_EQ(underline->height(), TabGroupUnderline::kStrokeThickness);
-
-  // Endpoints are different if the last grouped tab is active.
-  controller_->AddTab(1, true);
-  controller_->MoveTabIntoGroup(1, group);
-  views[0]->UpdateBounds();
-
-  EXPECT_EQ(underline->x(), TabGroupUnderline::GetStrokeInset());
-  EXPECT_EQ(underline->bounds().right(),
-            tab_strip_->tab_at(1)->bounds().right() +
-                TabGroupUnderline::kStrokeThickness);
-}
-
-TEST_P(TabStripTest, GroupHighlightBasics) {
-  SetMaxTabStripWidth(1000);
-  bounds_animator()->SetAnimationDuration(base::TimeDelta());
-  controller_->AddTab(0, false);
-
-  absl::optional<tab_groups::TabGroupId> group =
-      tab_groups::TabGroupId::GenerateNew();
-  controller_->MoveTabIntoGroup(0, group);
-  CompleteAnimationAndLayout();
-
-  std::vector<TabGroupViews*> views = ListGroupViews();
-  EXPECT_EQ(1u, views.size());
-
-  // The highlight bounds match the group view bounds. Grab this manually
-  // here, since there isn't a real paint cycle to trigger OnPaint().
-  gfx::Rect bounds = views[0]->GetBounds();
-  EXPECT_EQ(bounds.x(), 0);
-  EXPECT_GT(bounds.width(), 0);
-  EXPECT_EQ(bounds.right(), tab_strip_->tab_at(0)->bounds().right());
-  EXPECT_EQ(bounds.height(), tab_strip_->tab_at(0)->bounds().height());
-}
-
 TEST_P(TabStripTest, ChangingLayoutTypeResizesTabs) {
   SetMaxTabStripWidth(1000);
 
diff --git a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
index d0bc25f..a3e3b80 100644
--- a/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
+++ b/chrome/browser/ui/views/web_apps/pwa_confirmation_bubble_view.cc
@@ -194,8 +194,6 @@
   if (iph_state_ == chrome::PwaInProductHelpState::kShown && web_app_info_) {
     web_app::AppId app_id = web_app::GenerateAppId(web_app_info_->manifest_id,
                                                    web_app_info_->start_url);
-    UMA_HISTOGRAM_ENUMERATION("WebApp.InstallIphPromo.Result",
-                              web_app::InstallIphResult::kCanceled);
     web_app::RecordInstallIphIgnored(prefs_, app_id, base::Time::Now());
   }
   if (callback_) {
@@ -214,8 +212,6 @@
   if (iph_state_ == chrome::PwaInProductHelpState::kShown) {
     web_app::AppId app_id = web_app::GenerateAppId(web_app_info_->manifest_id,
                                                    web_app_info_->start_url);
-    UMA_HISTOGRAM_ENUMERATION("WebApp.InstallIphPromo.Result",
-                              web_app::InstallIphResult::kInstalled);
     web_app::RecordInstallIphInstalled(prefs_, app_id);
     tracker_->NotifyEvent(feature_engagement::events::kDesktopPwaInstalled);
   }
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index e6df18d..4373c49d 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -2984,4 +2984,28 @@
   helper_.CheckNoToolbar();
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTest,
+                       WebAppIntegration_29SiteB) {
+  // Test contents are generated by script. Please do not modify!
+  // See `chrome/test/webapps/README.md` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallCreateShortcutTabbed(Site::kSiteB);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTest,
+                       WebAppIntegration_48SiteB) {
+  // Test contents are generated by script. Please do not modify!
+  // See `chrome/test/webapps/README.md` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyAppTabbedShortcut(Site::kSiteB);
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppIntegrationBrowserTest,
+                       WebAppIntegration_32SiteB) {
+  // Test contents are generated by script. Please do not modify!
+  // See `chrome/test/webapps/README.md` for more info.
+  // Sheriffs: Disabling this test is supported.
+  helper_.InstallPolicyAppTabbedNoShortcut(Site::kSiteB);
+}
+
 }  // namespace web_app::integration_tests
diff --git a/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc b/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc
index b90a6cc..c760e207 100644
--- a/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc
+++ b/chrome/browser/ui/webui/webui_gallery/webui_gallery_ui.cc
@@ -10,6 +10,7 @@
 #include "chrome/grit/webui_gallery_resources.h"
 #include "chrome/grit/webui_gallery_resources_map.h"
 #include "content/public/browser/web_ui_data_source.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "ui/base/webui/web_ui_util.h"
 
 namespace {
@@ -22,6 +23,14 @@
       source,
       base::make_span(kWebuiGalleryResources, kWebuiGalleryResourcesSize),
       IDR_WEBUI_GALLERY_WEBUI_GALLERY_HTML);
+
+  source->DisableTrustedTypesCSP();
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::FrameSrc, "frame-src 'self';");
+  source->OverrideContentSecurityPolicy(
+      network::mojom::CSPDirectiveName::FrameAncestors,
+      "frame-ancestors 'self';");
+
   return source;
 }
 
diff --git a/chrome/browser/ui/webui/whats_new/whats_new_util.cc b/chrome/browser/ui/webui/whats_new/whats_new_util.cc
index 4b01269..cea48abb 100644
--- a/chrome/browser/ui/webui/whats_new/whats_new_util.cc
+++ b/chrome/browser/ui/webui/whats_new/whats_new_util.cc
@@ -50,33 +50,49 @@
   g_is_remote_content_disabled = true;
 }
 
+void LogStartupType(StartupType type) {
+  base::UmaHistogramEnumeration("WhatsNew.StartupType", type);
+}
+
 bool IsRemoteContentDisabled() {
   return g_is_remote_content_disabled;
 }
 
-bool ShouldShowForState(PrefService* local_state) {
-  if (!local_state || !local_state->FindPreference(prefs::kLastWhatsNewVersion))
+bool ShouldShowForState(PrefService* local_state,
+                        bool promotional_tabs_enabled) {
+  LogStartupType(StartupType::kCalledShouldShow);
+
+  if (!promotional_tabs_enabled) {
+    whats_new::LogStartupType(whats_new::StartupType::kPromotionalTabsDisabled);
     return false;
+  }
+
+  if (!local_state ||
+      !local_state->FindPreference(prefs::kLastWhatsNewVersion)) {
+    LogStartupType(StartupType::kInvalidState);
+    return false;
+  }
 
   // Allow disabling the What's New experience in tests using the standard
   // kNoFirstRun switch. This behavior can be overridden using the
   // kForceWhatsNew switch for the What's New experience integration tests.
   const base::CommandLine* command_line =
       base::CommandLine::ForCurrentProcess();
-  if (command_line->HasSwitch(switches::kNoFirstRun) &&
-      !command_line->HasSwitch(switches::kForceWhatsNew)) {
+  if ((command_line->HasSwitch(switches::kNoFirstRun) &&
+       !command_line->HasSwitch(switches::kForceWhatsNew)) ||
+      !base::FeatureList::IsEnabled(features::kChromeWhatsNewUI)) {
+    LogStartupType(StartupType::kFeatureDisabled);
     return false;
   }
 
-  if (!base::FeatureList::IsEnabled(features::kChromeWhatsNewUI))
-    return false;
-
   int last_version = local_state->GetInteger(prefs::kLastWhatsNewVersion);
 
   // Don't show What's New if it's already been shown for the current major
   // milestone.
-  if (CHROME_VERSION_MAJOR <= last_version)
+  if (CHROME_VERSION_MAJOR <= last_version) {
+    LogStartupType(StartupType::kAlreadyShown);
     return false;
+  }
 
   // Set the last version here to indicate that What's New should not attempt
   // to display again for this milestone. This prevents the page from
diff --git a/chrome/browser/ui/webui/whats_new/whats_new_util.h b/chrome/browser/ui/webui/whats_new/whats_new_util.h
index 05f8e7e..7706a30 100644
--- a/chrome/browser/ui/webui/whats_new/whats_new_util.h
+++ b/chrome/browser/ui/webui/whats_new/whats_new_util.h
@@ -27,6 +27,26 @@
   kMaxValue = kLoadFailAndDoNotShow,
 };
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// The first value indicates that the logic for showing What's New is running.
+// At most one of the remaining values will be logged for each run. If none of
+// these values are logged, the page should try to load.
+enum class StartupType {
+  kCalledShouldShow = 0,
+  kPromotionalTabsDisabled = 1,
+  kInvalidState = 2,
+  kFeatureDisabled = 3,
+  kAlreadyShown = 4,
+  kIneligible = 5,
+  kOverridden = 6,
+  kMaxValue = kOverridden,
+};
+
+// Logs the type of startup (e.g. whether a user is eligible for What's New, and
+// whether we try to show the page).
+void LogStartupType(StartupType type);
+
 // Disables loading remote content for tests, because this can lead to a
 // redirect if it fails. Most tests don't expect redirects to occur.
 void DisableRemoteContentForTests();
@@ -47,7 +67,8 @@
 // and possibly miss some users instead of repeatedly triggering a network
 // request at startup and/or showing the same What's New page many times for a
 // given user.
-bool ShouldShowForState(PrefService* local_state);
+bool ShouldShowForState(PrefService* local_state,
+                        bool promotional_tabs_enabled);
 
 // Gets the server side URL for the What's New page for the current version of
 // Chrome. If |may_redirect| is true, return a server URL that will redirect to
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 ab20be6..101a92d 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
@@ -7,8 +7,12 @@
 #include <algorithm>
 
 #include "base/command_line.h"
+#include "base/json/json_reader.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_test_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
@@ -34,6 +38,9 @@
 
   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 =
         static_cast<extensions::TestExtensionSystem*>(
@@ -73,6 +80,17 @@
     return urls;
   }
 
+  void InitProvider() {
+    base::RunLoop run_loop;
+    web_app_provider_->on_registry_ready().Post(FROM_HERE,
+                                                run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  FakeWebAppProvider* provider() { return web_app_provider_; }
+
+ private:
+  FakeWebAppProvider* web_app_provider_;
 };
 
 TEST_F(ExternallyInstalledWebAppPrefsTest, BasicOps) {
@@ -201,4 +219,122 @@
                    .IsPlaceholderApp("app_id_string"));
 }
 
+// Case 1: Single App ID, no placeholder info (defaults to
+// false).
+TEST_F(ExternallyInstalledWebAppPrefsTest, NoPlaceholderInfoDefaultsToFalse) {
+  base::Value external_prefs = *base::JSONReader::Read(R"({
+    "https://app1.com/": {
+      "extension_id": "test_app1",
+      "install_source": 2
+    },
+    "https://app2.com/": {
+      "extension_id": "test_app1",
+      "install_source": 3
+    }
+  })");
+  profile()->GetPrefs()->Set(prefs::kWebAppsExtensionIDs,
+                             std::move(external_prefs));
+  base::flat_map<AppId, ExternallyInstalledWebAppPrefs::ParsedPrefs>
+      web_app_data_map =
+          ExternallyInstalledWebAppPrefs::GetAppIdToWebAppParsedData(
+              profile()->GetPrefs());
+  EXPECT_EQ(1u, web_app_data_map.size());
+  EXPECT_FALSE(web_app_data_map["test_app1"]
+                   .placeholder_map[WebAppManagement::Type::kPolicy]);
+  EXPECT_FALSE(web_app_data_map["test_app1"]
+                   .placeholder_map[WebAppManagement::Type::kSystem]);
+}
+
+// Case 2: Multiple entries with single app ID, with is_placeholder set to true.
+TEST_F(ExternallyInstalledWebAppPrefsTest,
+       SinglePlaceholderInfoDefaultsToTrue) {
+  base::Value external_prefs = *base::JSONReader::Read(R"({
+    "https://app1.com/": {
+      "extension_id": "test_app1",
+      "install_source": 0
+    },
+    "https://app2.com/": {
+      "extension_id": "test_app1",
+      "install_source": 0,
+      "is_placeholder": true
+    }
+  })");
+  profile()->GetPrefs()->Set(prefs::kWebAppsExtensionIDs,
+                             std::move(external_prefs));
+
+  base::flat_map<AppId, ExternallyInstalledWebAppPrefs::ParsedPrefs>
+      web_app_data_map =
+          ExternallyInstalledWebAppPrefs::GetAppIdToWebAppParsedData(
+              profile()->GetPrefs());
+  EXPECT_EQ(1u, web_app_data_map.size());
+  EXPECT_TRUE(web_app_data_map["test_app1"]
+                  .placeholder_map[WebAppManagement::Type::kDefault]);
+}
+
+TEST_F(ExternallyInstalledWebAppPrefsTest, MultiAppsMultiPlaceholderInfo) {
+  base::Value external_prefs = *base::JSONReader::Read(R"({
+    "https://app1.com/": {
+      "extension_id": "test_app1",
+      "install_source": 1
+    },
+    "https://app2.com/": {
+      "extension_id": "test_app1",
+      "install_source": 1
+    },
+    "https://app3.com/": {},
+    "https://app4.com/": {
+      "extension_id": "test_app4",
+      "install_source": 4,
+      "is_placeholder": true
+    },
+    "https://app5.com/": {
+      "extension_id": "test_app4",
+      "is_placeholder": true
+    },
+    "https://app6.com/": {
+      "install_source": 4
+    }
+  })");
+  profile()->GetPrefs()->Set(prefs::kWebAppsExtensionIDs,
+                             std::move(external_prefs));
+  base::flat_map<AppId, ExternallyInstalledWebAppPrefs::ParsedPrefs>
+      web_app_data_map =
+          ExternallyInstalledWebAppPrefs::GetAppIdToWebAppParsedData(
+              profile()->GetPrefs());
+  EXPECT_EQ(2u, web_app_data_map.size());
+  EXPECT_FALSE(web_app_data_map["test_app1"]
+                   .placeholder_map[WebAppManagement::Type::kDefault]);
+  EXPECT_TRUE(web_app_data_map["test_app4"]
+                  .placeholder_map[WebAppManagement::Type::kWebAppStore]);
+}
+
+TEST_F(ExternallyInstalledWebAppPrefsTest,
+       PlaceholderMigrationTestForSingleSource) {
+  InitProvider();
+  std::unique_ptr<WebApp> web_app = test::CreateWebApp(
+      GURL("https://app.com/"), WebAppManagement::Type::kDefault);
+  AppId app_id = web_app->app_id();
+  {
+    ScopedRegistryUpdate update(&provider()->sync_bridge());
+    update->CreateApp(std::move(web_app));
+  }
+  const WebApp* installed_app = provider()->registrar().GetAppById(app_id);
+  EXPECT_FALSE(provider()->registrar().IsPlaceholderApp(app_id));
+  EXPECT_EQ(0u, installed_app->management_to_external_config_map().size());
+
+  ExternallyInstalledWebAppPrefs external_prefs(profile()->GetPrefs());
+  external_prefs.Insert(GURL("https://app.com/install"), app_id,
+                        ExternalInstallSource::kExternalDefault);
+  external_prefs.SetIsPlaceholder(GURL("https://app.com/install"), true);
+
+  ExternallyInstalledWebAppPrefs::MigrateExternalPrefData(
+      profile()->GetPrefs(), &provider()->sync_bridge());
+
+  EXPECT_TRUE(provider()->registrar().IsPlaceholderApp(app_id));
+  EXPECT_EQ(1u, installed_app->management_to_external_config_map().size());
+  EXPECT_TRUE(installed_app->management_to_external_config_map()
+                  .at(WebAppManagement::Type::kDefault)
+                  .is_placeholder);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
index 41080349..761281a 100644
--- a/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
+++ b/chrome/browser/web_applications/externally_installed_web_app_prefs.cc
@@ -8,8 +8,8 @@
 #include <utility>
 #include <vector>
 
-#include "base/values.h"
-#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chrome/browser/web_applications/web_app_install_utils.h"
+#include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
@@ -78,6 +78,13 @@
 
 }  // namespace
 
+ExternallyInstalledWebAppPrefs::ParsedPrefs::ParsedPrefs() = default;
+
+ExternallyInstalledWebAppPrefs::ParsedPrefs::ParsedPrefs(
+    const ParsedPrefs& other) = default;
+
+ExternallyInstalledWebAppPrefs::ParsedPrefs::~ParsedPrefs() = default;
+
 // static
 void ExternallyInstalledWebAppPrefs::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
@@ -239,4 +246,96 @@
   return app_prefs->FindBoolKey(kIsPlaceholder).value_or(false);
 }
 
+// static
+base::flat_map<AppId, ExternallyInstalledWebAppPrefs::ParsedPrefs>
+ExternallyInstalledWebAppPrefs::GetAppIdToWebAppParsedData(
+    PrefService* pref_service) {
+  const base::Value* urls_to_dicts =
+      pref_service->GetDictionary(prefs::kWebAppsExtensionIDs);
+  base::flat_map<AppId, ExternallyInstalledWebAppPrefs::ParsedPrefs>
+      ids_to_parsed_data;
+  if (!urls_to_dicts)
+    return ids_to_parsed_data;
+
+  for (auto it : urls_to_dicts->DictItems()) {
+    ParsedPrefs parsed_prefs;
+    const base::Value* v = &it.second;
+    if (!v->is_dict()) {
+      continue;
+    }
+
+    auto* app_id = v->FindKey(kExtensionId);
+    if (!app_id || !app_id->is_string()) {
+      continue;
+    }
+
+    if (ids_to_parsed_data.contains(app_id->GetString())) {
+      parsed_prefs = ids_to_parsed_data[app_id->GetString()];
+    }
+
+    auto* source = v->FindKey(kInstallSource);
+    if (!source) {
+      ids_to_parsed_data[app_id->GetString()] = parsed_prefs;
+      continue;
+    }
+
+    WebAppManagement::Type source_type =
+        ConvertExternalInstallSourceToWebAppManagement(source->GetInt());
+
+    parsed_prefs.placeholder_map[source_type] =
+        v->FindBoolKey(kIsPlaceholder).value_or(false);
+    ids_to_parsed_data.insert_or_assign(app_id->GetString(),
+                                        std::move(parsed_prefs));
+  }
+  return ids_to_parsed_data;
+}
+
+// static
+WebAppManagement::Type
+ExternallyInstalledWebAppPrefs::ConvertExternalInstallSourceToWebAppManagement(
+    int source) {
+  switch (source) {
+    case 0:
+      return ConvertExternalInstallSourceToSource(
+          ExternalInstallSource::kInternalDefault);
+    case 1:
+      return ConvertExternalInstallSourceToSource(
+          ExternalInstallSource::kExternalDefault);
+    case 2:
+      return ConvertExternalInstallSourceToSource(
+          ExternalInstallSource::kExternalPolicy);
+    case 3:
+      return ConvertExternalInstallSourceToSource(
+          ExternalInstallSource::kSystemInstalled);
+    case 4:
+      return ConvertExternalInstallSourceToSource(ExternalInstallSource::kArc);
+    default:
+      NOTREACHED();
+      return WebAppManagement::Type::kSync;
+  }
+}
+
+// static
+void ExternallyInstalledWebAppPrefs::MigrateExternalPrefData(
+    PrefService* pref_service,
+    WebAppSyncBridge* sync_bridge) {
+  base::flat_map<AppId, ExternallyInstalledWebAppPrefs::ParsedPrefs>
+      pref_to_app_data = GetAppIdToWebAppParsedData(pref_service);
+
+  ScopedRegistryUpdate update(sync_bridge);
+  for (auto it : pref_to_app_data) {
+    WebApp* web_app = update->UpdateApp(it.first);
+    if (web_app) {
+      // Sync placeholder info across external prefs and web_app DB by
+      // overwriting.
+      for (auto placeholder_info : it.second.placeholder_map) {
+        if (web_app->GetSources().test(placeholder_info.first)) {
+          web_app->AddPlaceholderInfoToManagementExternalConfigMap(
+              placeholder_info.first, placeholder_info.second);
+        }
+      }
+    }
+  }
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/externally_installed_web_app_prefs.h b/chrome/browser/web_applications/externally_installed_web_app_prefs.h
index 378d0e0..b8820055 100644
--- a/chrome/browser/web_applications/externally_installed_web_app_prefs.h
+++ b/chrome/browser/web_applications/externally_installed_web_app_prefs.h
@@ -7,9 +7,11 @@
 
 #include <map>
 
+#include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
 #include "chrome/browser/web_applications/web_app_id.h"
+#include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
@@ -28,6 +30,15 @@
 // and system apps.
 class ExternallyInstalledWebAppPrefs {
  public:
+  // Used in the migration to the web_app DB.
+  struct ParsedPrefs {
+    ParsedPrefs();
+    ParsedPrefs(const ParsedPrefs& other);
+    ~ParsedPrefs();
+
+    base::flat_map<WebAppManagement::Type, bool> placeholder_map;
+  };
+
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
   static bool HasAppId(const PrefService* pref_service, const AppId& app_id);
@@ -68,7 +79,21 @@
   void SetIsPlaceholder(const GURL& url, bool is_placeholder);
   bool IsPlaceholderApp(const AppId& app_id) const;
 
+  // Converts the existing external_pref information to a map<AppId,
+  // ParsedPrefs> for simplified parsing and migrating to the web app DB.
+  static base::flat_map<AppId, ParsedPrefs> GetAppIdToWebAppParsedData(
+      PrefService* pref_service);
+
+  // Used to migrate the external pref data to the installed web_app DB.
+  static void MigrateExternalPrefData(PrefService* pref_service,
+                                      WebAppSyncBridge* sync_bridge);
+
  private:
+  // The install_source to web_app_management conversion refers to the
+  // ConvertExternalInstallSourceToSource() function in
+  // web_app_install_utils.cc.
+  static WebAppManagement::Type ConvertExternalInstallSourceToWebAppManagement(
+      int source);
   const raw_ptr<PrefService> pref_service_;
 };
 
diff --git a/chrome/browser/web_applications/externally_managed_app_install_task.cc b/chrome/browser/web_applications/externally_managed_app_install_task.cc
index a8193524..528891f6 100644
--- a/chrome/browser/web_applications/externally_managed_app_install_task.cc
+++ b/chrome/browser/web_applications/externally_managed_app_install_task.cc
@@ -312,6 +312,8 @@
   options.add_to_desktop = install_options_.add_to_desktop;
   options.add_to_quick_launch_bar = install_options_.add_to_quick_launch_bar;
 
+  web_app_info.is_placeholder = true;
+
   install_finalizer_->FinalizeInstall(
       web_app_info, options,
       base::BindOnce(
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index 2bb6109f..89e4c126 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -392,6 +392,13 @@
       std::move(management_to_external_config_map);
 }
 
+void WebApp::AddPlaceholderInfoToManagementExternalConfigMap(
+    WebAppManagement::Type type,
+    bool is_placeholder) {
+  DCHECK_NE(type, WebAppManagement::Type::kSync);
+  management_to_external_config_map_[type].is_placeholder = is_placeholder;
+}
+
 WebApp::ClientData::ClientData() = default;
 
 WebApp::ClientData::~ClientData() = default;
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index 6c2f97a8..0635bbf 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -375,6 +375,10 @@
       base::flat_map<WebAppManagement::Type, ExternalManagementConfig>
           management_to_external_config_map);
 
+  void AddPlaceholderInfoToManagementExternalConfigMap(
+      WebAppManagement::Type source_type,
+      bool is_placeholder);
+
   // For logging and debug purposes.
   bool operator==(const WebApp&) const;
   bool operator!=(const WebApp&) const;
diff --git a/chrome/browser/web_applications/web_app_constants.h b/chrome/browser/web_applications/web_app_constants.h
index 8d28081..20b439e 100644
--- a/chrome/browser/web_applications/web_app_constants.h
+++ b/chrome/browser/web_applications/web_app_constants.h
@@ -197,19 +197,6 @@
 
 std::string RunOnOsLoginModeToString(RunOnOsLoginMode mode);
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-// Records result of user reaction to install in-product help promo.
-enum class InstallIphResult {
-  // Installed the web app after IPH was shown.
-  kInstalled = 0,
-  // Clicked the install icon but canceled install after IPH was shown.
-  kCanceled = 1,
-  // Ignored IPH, didn't click install.
-  kIgnored = 2,
-  kMaxValue = kIgnored,
-};
-
 // Number of times IPH can be ignored for this app before it's muted.
 constexpr int kIphMuteAfterConsecutiveAppSpecificIgnores = 3;
 // Number of times IPH can be ignored for any app before it's muted.
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 4f5a43c..25efa1f 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -169,6 +169,13 @@
   web_app->SetParentAppId(options.parent_app_id);
   web_app->SetInstallSourceForMetrics(options.install_surface);
 
+  DCHECK(!(source == WebAppManagement::Type::kSync &&
+           web_app_info.is_placeholder));
+  if (source != WebAppManagement::Type::kSync) {
+    web_app->AddPlaceholderInfoToManagementExternalConfigMap(
+        source, web_app_info.is_placeholder);
+  }
+
   if (!options.locally_installed) {
     DCHECK(!(options.add_to_applications_menu || options.add_to_desktop ||
              options.add_to_quick_launch_bar))
diff --git a/chrome/browser/web_applications/web_app_install_info.h b/chrome/browser/web_applications/web_app_install_info.h
index 234d299a..d5eb105 100644
--- a/chrome/browser/web_applications/web_app_install_info.h
+++ b/chrome/browser/web_applications/web_app_install_info.h
@@ -12,7 +12,6 @@
 #include <string>
 #include <vector>
 
-#include "base/containers/flat_set.h"
 #include "base/values.h"
 #include "chrome/browser/web_applications/user_display_mode.h"
 #include "components/services/app_service/public/cpp/file_handler.h"
@@ -320,6 +319,11 @@
   // The declared permissions policy to apply as the baseline policy for all
   // documents belonging to the application.
   blink::ParsedPermissionsPolicy permissions_policy;
+
+  // See ExternallyManagedAppManager for placeholder app documentation.
+  // Intended to be a temporary app while we wait for the install_url to
+  // successfully load.
+  bool is_placeholder = false;
 };
 
 bool operator==(const IconSizes& icon_sizes1, const IconSizes& icon_sizes2);
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 9397e50c..7c2eecf 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -357,6 +357,9 @@
 void WebAppProvider::OnSyncBridgeReady() {
   DCHECK(!on_registry_ready_.is_signaled());
 
+  ExternallyInstalledWebAppPrefs::MigrateExternalPrefData(profile_->GetPrefs(),
+                                                          sync_bridge_.get());
+
   registrar_->Start();
   install_finalizer_->Start();
   icon_manager_->Start();
diff --git a/chrome/browser/webauthn/android/BUILD.gn b/chrome/browser/webauthn/android/BUILD.gn
index 43df9d9..5924d6e 100644
--- a/chrome/browser/webauthn/android/BUILD.gn
+++ b/chrome/browser/webauthn/android/BUILD.gn
@@ -18,8 +18,10 @@
     "//chrome/browser/notifications:java",
     "//components/browser_ui/notifications/android:java",
     "//content/public/android:content_main_dex_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
     "//ui/android:ui_no_recycler_view_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index c9d49d1..6918f3a 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1650974392-e3c4b143bcb76e45751440f0e305c074613bcddf.profdata
+chrome-mac-arm-main-1651017376-1a276e83e4fd9365ce83c3136dc9109129a5ecf8.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 8289608..58fbad5 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1650974392-05744d58a0384be9f8620fb5420a3ea2389f3b04.profdata
+chrome-mac-main-1651017376-e88bf04884399054fcc1833c787590b30c143ddb.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 080537d..a627d573 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1650974392-57db2f99b0f4372454c69a1a11a65d71dd821985.profdata
+chrome-win32-main-1651006646-398851e81db1e8c9f33d3737b1e7d1801d48afea.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 6e794c7..86b2f93 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1650985015-6310edfb8eafc2937ffb18731a4797fa5265b548.profdata
+chrome-win64-main-1651006646-53e454dcbfd173abb05a9d0432955069c28a19e0.profdata
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 8ef3705..f440ee69 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1607,6 +1607,9 @@
 // Preference storing Easy Unlock pairing data.
 const char kEasyUnlockPairing[] = "easy_unlock.pairing";
 
+const char kHasSeenSmartLockSignInRemovedNotification[] =
+    "easy_unlock.has_seen_smart_lock_sign_in_removed_notification";
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 // Used to indicate whether or not the toolbar redesign bubble has been shown
 // and acknowledged, and the last time the bubble was shown.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 607109e1..658dded6 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -517,6 +517,7 @@
 
 extern const char kEasyUnlockAllowed[];
 extern const char kEasyUnlockPairing[];
+extern const char kHasSeenSmartLockSignInRemovedNotification[];
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 extern const char kToolbarIconSurfacingBubbleAcknowledged[];
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index 1904d43..1356261 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -21,6 +21,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -442,6 +443,52 @@
   }
 }
 
+// Run a child process that will create/update a shortcut for an
+// install. This is done in a child process to avoid crashing the main
+// install process if we crash in Windows shell functions. For more info,
+// see crbug.com/1276348.
+void RunShortcutCreationInChildProc(
+    const InstallerState& installer_state,
+    const base::FilePath& setup_path,
+    const absl::optional<const base::FilePath>& prefs_path,
+    InstallShortcutLevel install_level,
+    InstallShortcutOperation install_operation) {
+  base::CommandLine command_line(setup_path);
+  InstallUtil::AppendModeAndChannelSwitches(&command_line);
+  if (installer_state.system_install())
+    command_line.AppendSwitch(switches::kSystemLevel);
+
+  command_line.AppendSwitch(switches::kVerboseLogging);
+  if (prefs_path.has_value())
+    command_line.AppendSwitchPath(switches::kInstallerData, prefs_path.value());
+
+  command_line.AppendSwitchASCII(switches::kCreateShortcuts,
+                                 base::NumberToString(install_operation));
+  command_line.AppendSwitchASCII(switches::kInstallLevel,
+                                 base::NumberToString(install_level));
+  base::LaunchOptions launch_options;
+  launch_options.feedback_cursor_off = true;
+
+  VLOG(1) << "Launching \"" << command_line.GetCommandLineString()
+          << "\" to create shortcuts";
+  ::SetLastError(ERROR_SUCCESS);
+  base::Process process = base::LaunchProcess(command_line, launch_options);
+  if (!process.IsValid()) {
+    PLOG(ERROR) << "Failed to launch \"" << command_line.GetCommandLineString()
+                << "\"";
+    return;
+  }
+  int exit_code = OS_ERROR;
+  process.Process::WaitForExit(&exit_code);
+
+  if (exit_code != CREATE_SHORTCUTS_SUCCESS) {
+    LOG(ERROR) << "Launch shortcut creation process failed with exit code "
+               << exit_code;
+  } else {
+    VLOG(1) << "Shortcut creation process succeeded.";
+  }
+}
+
 InstallStatus InstallOrUpdateProduct(const InstallParams& install_params,
                                      const base::FilePath& prefs_path,
                                      const InitialPreferences& prefs) {
@@ -473,22 +520,12 @@
   if (!InstallUtil::GetInstallReturnCode(result)) {
     installer_state.SetStage(COPYING_PREFERENCES_FILE);
 
-    if (result == FIRST_INSTALL_SUCCESS && !prefs_path.empty())
+    const bool use_initial_prefs =
+        result == FIRST_INSTALL_SUCCESS && !prefs_path.empty();
+    if (use_initial_prefs)
       CopyPreferenceFileForFirstRun(installer_state, prefs_path);
 
     installer_state.SetStage(CREATING_SHORTCUTS);
-
-    // Creates shortcuts for Chrome.
-    const base::FilePath chrome_exe(
-        installer_state.target_path().Append(kChromeExe));
-
-    // Install per-user shortcuts on user-level installs and all-users shortcuts
-    // on system-level installs. Note that Active Setup will take care of
-    // installing missing per-user shortcuts on system-level install (i.e.,
-    // quick launch, taskbar pin, and possibly deleted all-users shortcuts).
-    InstallShortcutLevel install_level =
-        installer_state.system_install() ? ALL_USERS : CURRENT_USER;
-
     InstallShortcutOperation install_operation =
         INSTALL_SHORTCUT_REPLACE_EXISTING;
     if (result == FIRST_INSTALL_SUCCESS || result == INSTALL_REPAIRED ||
@@ -497,9 +534,13 @@
       // when the Chrome product is being added to the current install.
       install_operation = INSTALL_SHORTCUT_CREATE_ALL;
     }
-
-    CreateOrUpdateShortcuts(chrome_exe, prefs, install_level,
-                            install_operation);
+    InstallShortcutLevel install_level =
+        installer_state.system_install() ? ALL_USERS : CURRENT_USER;
+    RunShortcutCreationInChildProc(
+        installer_state, setup_path,
+        use_initial_prefs ? absl::optional<base::FilePath>(prefs_path)
+                          : absl::nullopt,
+        install_level, install_operation);
 
     // Register Chrome and, if requested, make Chrome the default browser.
     installer_state.SetStage(REGISTERING_CHROME);
@@ -573,21 +614,19 @@
 }
 
 void HandleOsUpgradeForBrowser(const InstallerState& installer_state,
-                               const base::Version& installed_version) {
+                               const base::Version& installed_version,
+                               const base::FilePath& setup_path) {
   VLOG(1) << "Updating and registering shortcuts for --on-os-upgrade.";
 
-  // Read the initial preferences copied beside chrome.exe at install.
-  const InitialPreferences prefs(
-      installer_state.target_path().AppendASCII(kLegacyInitialPrefs));
-
   // Update shortcuts at this install level (per-user shortcuts on system-level
   // installs will be updated through Active Setup).
   const InstallShortcutLevel level =
       installer_state.system_install() ? ALL_USERS : CURRENT_USER;
-  const base::FilePath chrome_exe(
-      installer_state.target_path().Append(kChromeExe));
-  CreateOrUpdateShortcuts(chrome_exe, prefs, level,
-                          INSTALL_SHORTCUT_REPLACE_EXISTING);
+
+  RunShortcutCreationInChildProc(
+      installer_state, setup_path,
+      installer_state.target_path().AppendASCII(kLegacyInitialPrefs), level,
+      INSTALL_SHORTCUT_REPLACE_EXISTING);
 
   // Adapt Chrome registrations to this new OS.
   RegisterChromeOnMachine(installer_state, false, installed_version);
@@ -614,7 +653,8 @@
   // can be done directly; whereas it requires triggering Active Setup for each
   // user's subsequent login on system-level installs.
   if (!installer_state.system_install()) {
-    UpdateDefaultBrowserBeaconForPath(chrome_exe);
+    UpdateDefaultBrowserBeaconForPath(
+        installer_state.target_path().Append(kChromeExe));
   } else {
     UpdateActiveSetupVersionWorkItem active_setup_work_item(
         install_static::GetActiveSetupPath(),
@@ -632,6 +672,7 @@
 // install. It may also be invoked again when a system-level chrome install goes
 // through an OS upgrade.
 void HandleActiveSetupForBrowser(const InstallerState& installer_state,
+                                 const base::FilePath& setup_path,
                                  bool force) {
   std::unique_ptr<WorkItemList> cleanup_list(WorkItem::CreateWorkItemList());
   cleanup_list->set_log_message("Cleanup deprecated per-user registrations");
@@ -652,27 +693,23 @@
           ? INSTALL_SHORTCUT_REPLACE_EXISTING
           : INSTALL_SHORTCUT_CREATE_EACH_IF_NO_SYSTEM_LEVEL;
 
-  // Read the initial preferences copied beside chrome.exe at install for the
+  // Use the initial preferences copied beside chrome.exe at install for the
   // sake of creating/updating shortcuts.
   const base::FilePath installation_root = installer_state.target_path();
-  InitialPreferences prefs(installation_root.AppendASCII(kLegacyInitialPrefs));
-  base::FilePath chrome_exe(installation_root.Append(kChromeExe));
-  CreateOrUpdateShortcuts(chrome_exe, prefs, CURRENT_USER, install_operation);
+  RunShortcutCreationInChildProc(
+      installer_state, setup_path,
+      installation_root.AppendASCII(kLegacyInitialPrefs), CURRENT_USER,
+      install_operation);
 
-  UpdateDefaultBrowserBeaconForPath(chrome_exe);
+  UpdateDefaultBrowserBeaconForPath(installation_root.Append(kChromeExe));
 
   // This install may have been selected into a study for a retention
   // experiment following a successful update. In case the experiment was not
   // able to run immediately after the update (e.g., no user was logged on at
   // the time), try to run it now that the installer is running in the context
   // of a user.
-  if (ShouldRunUserExperiment(installer_state)) {
-    base::FilePath setup_exe;
-    if (!base::PathService::Get(base::FILE_EXE, &setup_exe))
-      LOG(ERROR) << "Failed to get path to setup.exe.";
-    else
-      BeginUserExperiment(installer_state, setup_exe, true /* user_context */);
-  }
+  if (ShouldRunUserExperiment(installer_state))
+    BeginUserExperiment(installer_state, setup_path, true /* user_context */);
 }
 
 }  // namespace installer
diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h
index b3a5460..721cd32f 100644
--- a/chrome/installer/setup/install.h
+++ b/chrome/installer/setup/install.h
@@ -24,6 +24,7 @@
   // Create all shortcuts (potentially skipping those explicitly stated not to
   // be installed in the InstallShortcutPreferences).
   INSTALL_SHORTCUT_CREATE_ALL,
+  INSTALL_SHORTCUT_FIRST = INSTALL_SHORTCUT_CREATE_ALL,
   // Create each per-user shortcut (potentially skipping those explicitly stated
   // not to be installed in the InstallShortcutPreferences), but only if the
   // system-level equivalent of that shortcut is not present on the system.
@@ -31,15 +32,19 @@
   // Replace all shortcuts that still exist with the most recent version of
   // each individual shortcut.
   INSTALL_SHORTCUT_REPLACE_EXISTING,
+  INSTALL_SHORTCUT_LAST = INSTALL_SHORTCUT_REPLACE_EXISTING,
 };
 
 enum InstallShortcutLevel {
   // Install shortcuts for the current user only.
-  CURRENT_USER,
+  INSTALL_SHORTCUT_LEVEL_FIRST = 0,
+  CURRENT_USER = INSTALL_SHORTCUT_LEVEL_FIRST,
   // Install global shortcuts visible to all users. Note: the Quick Launch
   // and taskbar pin shortcuts are still installed per-user (as they have no
   // all-users version).
   ALL_USERS,
+  // Update if a shortcut level is added.
+  INSTALL_SHORTCUT_LEVEL_LAST = ALL_USERS,
 };
 
 // Creates chrome.VisualElementsManifest.xml in |src_path| if
@@ -84,15 +89,18 @@
                                     const InstallerState& installer_state);
 
 // Performs installation-related tasks following an OS upgrade.
-// |chrome| The installed product (must be a browser).
-// |installed_version| the current version of this install.
+// `installer_state` The installer state.
+// `installed_version` the current version of this install.
+// `setup_path` path to setup.exe
 void HandleOsUpgradeForBrowser(const InstallerState& installer_state,
-                               const base::Version& installed_version);
+                               const base::Version& installed_version,
+                               const base::FilePath& setup_path);
 
 // Performs per-user installation-related tasks on Active Setup (ran on first
 // login for each user post system-level Chrome install). Shortcut creation is
-// skipped if the First Run beacon is present (unless |force| is set to true).
+// skipped if the First Run beacon is present (unless `force` is set to true).
 void HandleActiveSetupForBrowser(const InstallerState& installer_state,
+                                 const base::FilePath& setup_path,
                                  bool force);
 
 }  // namespace installer
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index d233e04e1..ed41241 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -896,12 +896,36 @@
   return status;
 }
 
+installer::InstallStatus CreateShortcutsInChildProc(
+    const InstallerState& installer_state,
+    const InitialPreferences& prefs,
+    installer::InstallShortcutLevel install_level,
+    installer::InstallShortcutOperation install_operation) {
+  // Create shortcut in a child process so that shell crashes don't make the
+  // install/update fail. Pass install operation on the command line since
+  // it can't be deduced by the child process;
+
+  // Creates shortcuts for Chrome.
+  const base::FilePath chrome_exe(
+      installer_state.target_path().Append(installer::kChromeExe));
+
+  // Install per-user shortcuts on user-level installs and all-users shortcuts
+  // on system-level installs. Note that Active Setup will take care of
+  // installing missing per-user shortcuts on system-level install (i.e.,
+  // quick launch, taskbar pin, and possibly deleted all-users shortcuts).
+  CreateOrUpdateShortcuts(chrome_exe, prefs, install_level, install_operation);
+  // TODO(): Plumb shortcut creation failure through and return a failure exit
+  // code.
+  return installer::CREATE_SHORTCUTS_SUCCESS;
+}
+
 // This method processes any command line options that make setup.exe do
 // various tasks other than installation (renaming chrome.exe, showing eula
 // among others). This function returns true if any such command line option
 // has been found and processed (so setup.exe should exit at that point).
 bool HandleNonInstallCmdLineOptions(installer::ModifyParams& modify_params,
                                     const base::CommandLine& cmd_line,
+                                    const InitialPreferences& prefs,
                                     int* exit_code) {
   installer::InstallerState* installer_state = &(modify_params.installer_state);
   installer::InstallationState* original_state =
@@ -970,7 +994,8 @@
     if (installer_state->system_install()) {
       bool force =
           cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
-      installer::HandleActiveSetupForBrowser(*installer_state, force);
+      installer::HandleActiveSetupForBrowser(*installer_state, setup_exe,
+                                             force);
       status = installer::INSTALL_REPAIRED;
     } else {
       LOG(DFATAL)
@@ -1080,7 +1105,8 @@
     const base::Version installed_version(
         base::UTF16ToUTF8(version_info->product_version()));
     if (installed_version.IsValid()) {
-      installer::HandleOsUpgradeForBrowser(*installer_state, installed_version);
+      installer::HandleOsUpgradeForBrowser(*installer_state, installed_version,
+                                           setup_exe);
       status = installer::INSTALL_REPAIRED;
     } else {
       LOG(DFATAL) << "Failed to extract product version from "
@@ -1181,6 +1207,29 @@
             ? installer::ROTATE_DTKEY_SUCCESS
             : installer::ROTATE_DTKEY_FAILED;
 #endif
+  } else if (cmd_line.HasSwitch(installer::switches::kCreateShortcuts)) {
+    std::string install_op_arg =
+        cmd_line.GetSwitchValueASCII(installer::switches::kCreateShortcuts);
+    std::string shortcut_level_arg =
+        cmd_line.GetSwitchValueASCII(installer::switches::kInstallLevel);
+    int install_op;
+    int install_level_op;
+    if (!base::StringToInt(install_op_arg, &install_op) ||
+        install_op < installer::INSTALL_SHORTCUT_FIRST ||
+        install_op > installer::INSTALL_SHORTCUT_LAST) {
+      LOG(ERROR) << "Invalid shortcut operation " << install_op_arg;
+      *exit_code = installer::UNSUPPORTED_OPTION;
+    } else if (!base::StringToInt(shortcut_level_arg, &install_level_op) ||
+               install_level_op < installer::INSTALL_SHORTCUT_LEVEL_FIRST ||
+               install_level_op > installer::INSTALL_SHORTCUT_LEVEL_LAST) {
+      LOG(ERROR) << "Invalid shortcut level " << shortcut_level_arg;
+      *exit_code = installer::UNSUPPORTED_OPTION;
+    } else {
+      *exit_code = CreateShortcutsInChildProc(
+          *installer_state, prefs,
+          static_cast<installer::InstallShortcutLevel>(install_level_op),
+          static_cast<installer::InstallShortcutOperation>(install_op));
+    }
   } else {
     handled = false;
   }
@@ -1577,7 +1626,8 @@
   };
 
   int exit_code = 0;
-  if (HandleNonInstallCmdLineOptions(modify_params, cmd_line, &exit_code)) {
+  if (HandleNonInstallCmdLineOptions(modify_params, cmd_line, prefs,
+                                     &exit_code)) {
     return exit_code;
   }
 
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
index 2dda2923..a05091fd 100644
--- a/chrome/installer/util/util_constants.cc
+++ b/chrome/installer/util/util_constants.cc
@@ -26,6 +26,9 @@
 // this option is not compatible with any other installer options.
 const char kConfigureUserSettings[] = "configure-user-settings";
 
+// Create shortcuts with the installer operation arg.
+const char kCreateShortcuts[] = "create-shortcuts";
+
 // The version number of an update containing critical fixes, for which an
 // in-use Chrome should be restarted ASAP.
 const char kCriticalUpdateVersion[] = "critical-update-version";
@@ -82,6 +85,9 @@
 // Specify the file path of Chrome initial preference file.
 const char kInstallerData[] = "installerdata";
 
+// What install level to create shortcuts for, if "create-shortcuts" is present.
+const char kInstallLevel[] = "install-level";
+
 // If present, specify file path to write logging info.
 const char kLogFile[] = "log-file";
 
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index d494504..ca8bbe4 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -119,8 +119,9 @@
   DOWNGRADE_CLEANUP_UNKNOWN_OPERATION = 70,
   ROTATE_DTKEY_FAILED = 71,   // Failed to rotate device trust signing key.
   ROTATE_DTKEY_SUCCESS = 72,  // Successfully rotated device trust signing key.
-  MAX_INSTALL_STATUS = 73,    // When adding a new result, bump this and update
-                              // the SetupInstallResult enum in histograms.xml.
+  CREATE_SHORTCUTS_SUCCESS = 73,  // Successfully created Chrome shortcuts.
+  MAX_INSTALL_STATUS = 74,  // When adding a new result, bump this and update
+                            // the SetupInstallResult enum in enums.xml.
 };
 
 // The type of an update archive.
@@ -158,6 +159,7 @@
 extern const char kAllowDowngrade[];
 extern const char kChannel[];
 extern const char kConfigureUserSettings[];
+extern const char kCreateShortcuts[];
 extern const char kCriticalUpdateVersion[];
 extern const char kDeleteOldVersions[];
 extern const char kDeleteProfile[];
@@ -172,6 +174,7 @@
 extern const char kInputFile[];
 extern const char kInstallArchive[];
 extern const char kInstallerData[];
+extern const char kInstallLevel[];
 extern const char kLogFile[];
 extern const char kMakeChromeDefault[];
 extern const char kMsi[];
diff --git a/chrome/renderer/loadtimes_extension_bindings.cc b/chrome/renderer/loadtimes_extension_bindings.cc
index a88aa98a..c4e18f7 100644
--- a/chrome/renderer/loadtimes_extension_bindings.cc
+++ b/chrome/renderer/loadtimes_extension_bindings.cc
@@ -135,7 +135,7 @@
     if (!document_loader) {
       return;
     }
-    const blink::WebURLResponse& response = document_loader->GetResponse();
+    const blink::WebURLResponse& response = document_loader->GetWebResponse();
     WebPerformance web_performance = frame->Performance();
     // Though request time now tends to be used to describe the time that the
     // request for the main resource was issued, when chrome.loadTimes() was
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 183a943..5d06e5b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -351,6 +351,7 @@
       "//components/ukm:test_support",
       "//components/ukm:ukm_test_helper",
       "//components/web_modal",
+      "//ui/compositor:test_support",
       "//ui/snapshot",
     ]
 
@@ -374,6 +375,8 @@
       "../browser/ui/profile_ui_test_utils.h",
       "../browser/ui/tabs/pinned_tab_test_utils.cc",
       "../browser/ui/tabs/pinned_tab_test_utils.h",
+      "../browser/ui/test/test_browser_ui.cc",
+      "../browser/ui/test/test_browser_ui.h",
       "../test/base/browser_with_test_window_test.cc",
       "../test/base/browser_with_test_window_test.h",
       "base/dialog_test_browser_window.cc",
@@ -1985,6 +1988,7 @@
       "../browser/spellchecker/spellcheck_service_browsertest.cc",
       "../browser/ssl/certificate_reporting_test_utils.cc",
       "../browser/ssl/certificate_reporting_test_utils.h",
+      "../browser/ssl/certificate_transparency_browsertest.cc",
       "../browser/ssl/chrome_expect_ct_reporter_browsertest.cc",
       "../browser/ssl/connection_help_tab_helper_browsertest.cc",
       "../browser/ssl/crlset_browsertest.cc",
@@ -2110,8 +2114,6 @@
       "../browser/ui/test/browser_ui_browsertest.cc",
       "../browser/ui/test/test_browser_dialog.cc",
       "../browser/ui/test/test_browser_dialog.h",
-      "../browser/ui/test/test_browser_ui.cc",
-      "../browser/ui/test/test_browser_ui.h",
       "../browser/ui/test/test_infobar.cc",
       "../browser/ui/test/test_infobar.h",
       "../browser/ui/thumbnails/thumbnail_readiness_tracker_browsertest.cc",
@@ -5488,6 +5490,7 @@
       "../browser/download/download_shelf_context_menu_unittest.cc",
       "../browser/enterprise/signals/client_certificate_fetcher_unittest.cc",
       "../browser/metrics/power/power_metrics_reporter_unittest.cc",
+      "../browser/metrics/power/power_metrics_unittest.cc",
       "../browser/metrics/power/usage_scenario_unittest.cc",
       "../browser/metrics/usage_scenario/system_event_provider_unittest.cc",
       "../browser/metrics/usage_scenario/tab_usage_scenario_tracker_unittest.cc",
@@ -7907,6 +7910,7 @@
       "//components/safe_browsing/content/browser:safe_browsing_blocking_page",
       "//components/safe_browsing/content/common:file_type_policies",
       "//components/safe_browsing/content/common:file_type_policies_test_support",
+      "//components/safe_browsing/core/browser:token_fetcher",
     ]
   }
 
@@ -8960,8 +8964,6 @@
       "../browser/ui/startup/startup_browser_creator_interactive_uitest.cc",
       "../browser/ui/test/test_browser_dialog.cc",
       "../browser/ui/test/test_browser_dialog.h",
-      "../browser/ui/test/test_browser_ui.cc",
-      "../browser/ui/test/test_browser_ui.h",
       "../browser/ui/thumbnails/thumbnail_tab_helper_interactive_uitest.cc",
       "../browser/ui/translate/translate_bubble_test_utils.h",
       "../browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc",
diff --git a/chrome/test/android/BUILD.gn b/chrome/test/android/BUILD.gn
index 3da168a..3666013 100644
--- a/chrome/test/android/BUILD.gn
+++ b/chrome/test/android/BUILD.gn
@@ -46,7 +46,6 @@
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_drawerlayout_drawerlayout_java",
     "//third_party/androidx:androidx_test_uiautomator_uiautomator_java",
     "//third_party/junit",
     "//third_party/ub-uiautomator:ub_uiautomator_java",
@@ -110,7 +109,6 @@
     "//third_party/android_deps:espresso_java",
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_support_test_runner:runner_java",
-    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
@@ -150,7 +148,6 @@
     "//third_party/android_deps:guava_android_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit",
@@ -363,7 +360,6 @@
     "//net/android:net_java",
     "//net/android:net_java_test_support",
     "//services/device/public/mojom:mojom_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:chromium_play_services_availability_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/android_deps:espresso_java",
@@ -373,9 +369,9 @@
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/blink/public:blink_headers_java",
     "//third_party/blink/public/mojom:mojom_platform_java",
diff --git a/chrome/test/data/web_apps/site_b/basic.json b/chrome/test/data/web_apps/site_b/basic.json
index e205f5dc..36a63d10 100644
--- a/chrome/test/data/web_apps/site_b/basic.json
+++ b/chrome/test/data/web_apps/site_b/basic.json
@@ -14,5 +14,21 @@
   ],
   "start_url": "/web_apps/site_b/basic.html",
   "scope": "/web_apps/site_b/",
-  "display": "minimal-ui"
+  "display": "minimal-ui",
+  "file_handlers": [
+    {
+      "action": "/web_apps/site_b/text_handler.html",
+      "name": "Plain Text",
+      "accept": {
+        "text/plain": [".txt", ".md", ".csv", ".text"]
+      }
+    },
+    {
+      "action": "/web_apps/site_b/image_handler.html",
+      "accept": {
+        "image/png": [".png"]
+      },
+      "launch_type": "multiple-clients"
+    }
+  ]
 }
diff --git a/chrome/test/data/web_apps/site_b/image_handler.html b/chrome/test/data/web_apps/site_b/image_handler.html
new file mode 100644
index 0000000..acdf8a9
--- /dev/null
+++ b/chrome/test/data/web_apps/site_b/image_handler.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Site B - Image Handler</title>
+    <script src="/web_apps/test_utils.js"></script>
+</head>
+<body>
+  <h1>Site B - Image Handler</h1>
+  <div>
+    <p>
+        TODO: Add code to verify the images loaded are correct.
+    </p>
+    <p>
+      This site is used for dPWA integration tests, and is subject to modification to support that framework. See
+      <a
+        href="https://chromium.googlesource.com/chromium/src/+/main/docs/webapps/integration-testing-framework.md">https://chromium.googlesource.com/chromium/src/+/main/docs/webapps/integration-testing-framework.md</a>
+    </p>
+  </div>
+</body>
+</html>
diff --git a/chrome/test/data/web_apps/site_b/text_handler.html b/chrome/test/data/web_apps/site_b/text_handler.html
new file mode 100644
index 0000000..4fac732
--- /dev/null
+++ b/chrome/test/data/web_apps/site_b/text_handler.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Site B - Text Handler</title>
+    <script src="/web_apps/test_utils.js"></script>
+</head>
+<body>
+  <h1>Site B - Text Handler</h1>
+  <div>
+    <p>
+        TODO: Add code to verify the text files loaded are correct.
+    </p>
+    <p>
+      This site is used for dPWA integration tests, and is subject to modification to support that framework. See
+      <a
+        href="https://chromium.googlesource.com/chromium/src/+/main/docs/webapps/integration-testing-framework.md">https://chromium.googlesource.com/chromium/src/+/main/docs/webapps/integration-testing-framework.md</a>
+    </p>
+  </div>
+</body>
+</html>
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_theme_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_theme_element_test.ts
index 6ba1f14..b84c2894 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_theme_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_theme_element_test.ts
@@ -97,7 +97,7 @@
     personalizationStore.setReducersEnabled(true);
     personalizationStore.expectAction(ThemeActionName.SET_DARK_MODE_ENABLED);
     radioButton.click();
-    let action =
+    const action =
         await personalizationStore.waitForAction(
             ThemeActionName.SET_DARK_MODE_ENABLED) as SetDarkModeEnabledAction;
     assertTrue(action.enabled);
@@ -120,8 +120,8 @@
     personalizationStore.expectAction(
         ThemeActionName.SET_COLOR_MODE_AUTO_SCHEDULE_ENABLED);
     radioButton.click();
-    let action = await personalizationStore.waitForAction(
-                     ThemeActionName.SET_COLOR_MODE_AUTO_SCHEDULE_ENABLED) as
+    const action = await personalizationStore.waitForAction(
+                       ThemeActionName.SET_COLOR_MODE_AUTO_SCHEDULE_ENABLED) as
         SetDarkModeEnabledAction;
     assertTrue(action.enabled);
     assertTrue(personalizationStore.data.theme.colorModeAutoScheduleEnabled);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts
index 83e9a2553..53b74f5d 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/personalization_toast_element_test.ts
@@ -86,8 +86,8 @@
 
   test('automatically dismisses after ten seconds', async () => {
     // Spy on calls to |window.setTimeout|.
-    let setTimeout = window.setTimeout;
-    let setTimeoutCalls: {handler: Function|string, delay?: number}[] = [];
+    const setTimeout = window.setTimeout;
+    const setTimeoutCalls: {handler: Function|string, delay?: number}[] = [];
     window.setTimeout =
         (handler: Function|string, delay?: number, ...args: any[]): number => {
           setTimeoutCalls.push({handler, delay});
@@ -100,7 +100,7 @@
     await waitAfterNextRender(personalizationToastElement);
 
     // Expect that a timeout will have been scheduled for 10 seconds.
-    let setTimeoutCall: {handler: Function|string, delay?: number}|undefined =
+    const setTimeoutCall: {handler: Function|string, delay?: number}|undefined =
         setTimeoutCalls.find((setTimeoutCall) => {
           return typeof setTimeoutCall.handler === 'function' &&
               setTimeoutCall.delay === 10000;
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts
index 83eb855..470806b9 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.ts
@@ -273,7 +273,7 @@
     personalizationStore.notifyObservers();
 
     // Wait for |sendCollections| to be called.
-    let [target, data] = await testProxy.whenCalled('sendCollections') as
+    const [target, data] = await testProxy.whenCalled('sendCollections') as
         Parameters<IFrameApi['sendCollections']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
 
@@ -286,7 +286,7 @@
     assertEquals(null, data);
 
     // Wait for |sendLocalImages| to be called.
-    let [imageTarget, imageData] =
+    const [imageTarget, imageData] =
         await testProxy.whenCalled('sendLocalImages') as
         Parameters<IFrameApi['sendLocalImages']>;
     await waitAfterNextRender(wallpaperCollectionsElement);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
index 70ffbce2..117f856 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_selected_element_test.ts
@@ -7,7 +7,7 @@
 import 'chrome://personalization/strings.m.js';
 import 'chrome://webui-test/mojo_webui_test_support.js';
 
-import {Paths, WallpaperLayout, WallpaperSelected, WallpaperType} from 'chrome://personalization/trusted/personalization_app.js';
+import {CurrentWallpaper, Paths, WallpaperLayout, WallpaperSelected, WallpaperType} from 'chrome://personalization/trusted/personalization_app.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/test_util.js';
 
@@ -294,4 +294,25 @@
         await wallpaperProvider.whenCalled('setCurrentWallpaperLayout'),
         WallpaperLayout.kCenter);
   });
+
+  test('shows attribution for device default wallpaper', async () => {
+    const currentSelected: CurrentWallpaper = {
+      url: {url: 'url'},
+      attribution: ['testing attribution'],
+      layout: WallpaperLayout.kStretch,
+      type: WallpaperType.kDefault,
+      key: 'key',
+    };
+    personalizationStore.data.wallpaper.currentSelected = currentSelected;
+
+    wallpaperSelectedElement =
+        initElement(WallpaperSelected, {path: Paths.CollectionImages});
+    await waitAfterNextRender(wallpaperSelectedElement);
+
+    assertEquals(
+        wallpaperSelectedElement.i18n('defaultWallpaper'),
+        wallpaperSelectedElement.shadowRoot!.getElementById(
+                                                'imageTitle')!.innerText,
+        'default wallpaper attribution is shown');
+  });
 });
diff --git a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_enter_rsu_wp_disable_code_page_test.js b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_enter_rsu_wp_disable_code_page_test.js
index 9427a6e..f751e68 100644
--- a/chrome/test/data/webui/chromeos/shimless_rma/onboarding_enter_rsu_wp_disable_code_page_test.js
+++ b/chrome/test/data/webui/chromeos/shimless_rma/onboarding_enter_rsu_wp_disable_code_page_test.js
@@ -64,6 +64,17 @@
     return flushTasks();
   }
 
+  /**
+   * @param {string} inputSelector
+   * @return {!Promise}
+   */
+  function pressEnter(inputSelector) {
+    const rsuCodeInput = component.shadowRoot.querySelector(inputSelector);
+    rsuCodeInput.value = '12345678';
+    rsuCodeInput.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
+    return flushTasks();
+  }
+
   test('EnterRsuWpDisableCodePageInitializes', async () => {
     await initializeEnterRsuWpDisableCodePage('rsu challenge', '');
     const rsuCodeComponent = component.shadowRoot.querySelector('#rsuCode');
@@ -164,4 +175,17 @@
     await flushTasks();
     assertFalse(rsuCodeInput.invalid);
   });
+
+  test('EnterRsuWpDisableCodePagePressEnterkey', async () => {
+    await initializeEnterRsuWpDisableCodePage(
+        /*challenge=*/ '', /*hwid=*/ '');
+
+    let nextButtonEventFired = false;
+    component.addEventListener('click-next-button', (e) => {
+      nextButtonEventFired = true;
+    });
+    await pressEnter('#rsuCode');
+
+    assertTrue(nextButtonEventFired);
+  });
 }
diff --git a/chrome/test/data/webui/color_provider_css_colors_test.ts b/chrome/test/data/webui/color_provider_css_colors_test.ts
index 8cbbda59..acc3f51 100644
--- a/chrome/test/data/webui/color_provider_css_colors_test.ts
+++ b/chrome/test/data/webui/color_provider_css_colors_test.ts
@@ -12,7 +12,7 @@
         link.rel = 'stylesheet';
         link.href = 'chrome://theme/colors.css';
         link.onload = function() {
-          let style = getComputedStyle(document.body);
+          const style = getComputedStyle(document.body);
           // Check that we are able to query for a ui/ side color.
           assertNotEquals('', style.getPropertyValue('--color-accent'));
           // Check that we are able to query for a chrome/ side color.
diff --git a/chrome/test/data/webui/extensions/activity_log_stream_test.ts b/chrome/test/data/webui/extensions/activity_log_stream_test.ts
index 5066d10..d79fc31 100644
--- a/chrome/test/data/webui/extensions/activity_log_stream_test.ts
+++ b/chrome/test/data/webui/extensions/activity_log_stream_test.ts
@@ -184,7 +184,7 @@
     proxyDelegate.getOnExtensionActivity().callListeners(contentScriptActivity);
 
     flush();
-    let streamItems = getStreamItems();
+    const streamItems = getStreamItems();
     assertEquals(2, streamItems.length);
 
     // We should see two items: one for every script called.
diff --git a/chrome/test/data/webui/extensions/code_section_test.ts b/chrome/test/data/webui/extensions/code_section_test.ts
index 7d8195f..49dfc551 100644
--- a/chrome/test/data/webui/extensions/code_section_test.ts
+++ b/chrome/test/data/webui/extensions/code_section_test.ts
@@ -56,7 +56,7 @@
     assertTrue(testIsVisible('#main'));
     assertFalse(testIsVisible('#no-code'));
 
-    let codeSections =
+    const codeSections =
         codeSection.shadowRoot!.querySelectorAll('#source span span');
 
     assertEquals(code.beforeHighlight, codeSections[0]!.textContent);
diff --git a/chrome/test/data/webui/extensions/detail_view_test.ts b/chrome/test/data/webui/extensions/detail_view_test.ts
index fdb6ecb..f02a58b 100644
--- a/chrome/test/data/webui/extensions/detail_view_test.ts
+++ b/chrome/test/data/webui/extensions/detail_view_test.ts
@@ -80,7 +80,7 @@
     ];
     const isChecked = (id: string) =>
         item.shadowRoot!.querySelector<CrCheckboxElement>(id)!.checked;
-    for (let option of accessOptions) {
+    for (const option of accessOptions) {
       assertTrue(isChildVisible(item, option.id));
       assertFalse(isChecked(option.id), option.id);
       item.set('data.' + option.key + '.isEnabled', false);
diff --git a/chrome/test/data/webui/extensions/error_console_test.ts b/chrome/test/data/webui/extensions/error_console_test.ts
index 5128591..4425d68 100644
--- a/chrome/test/data/webui/extensions/error_console_test.ts
+++ b/chrome/test/data/webui/extensions/error_console_test.ts
@@ -29,7 +29,7 @@
   });
 
   test('TestUpDownErrors', function() {
-    let initialFocus = findMatches(document, ACTIVE_ERROR_IN_STACK)[0];
+    const initialFocus = findMatches(document, ACTIVE_ERROR_IN_STACK)[0];
     assertTrue(!!initialFocus);
     assertEquals(1, findMatches(document, ACTIVE_ERROR_IN_STACK).length);
     assertEquals(4, findMatches(document, STACK_ERRORS).length);
diff --git a/chrome/test/data/webui/extensions/extension_options_dialog_test.ts b/chrome/test/data/webui/extensions/extension_options_dialog_test.ts
index fc1720b..72db580 100644
--- a/chrome/test/data/webui/extensions/extension_options_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/extension_options_dialog_test.ts
@@ -35,7 +35,7 @@
     const dialog =
         manager.shadowRoot!.querySelector('extensions-options-dialog');
     assertTrue(!!dialog);
-    let waitForClose = eventToPromise('close', dialog);
+    const waitForClose = eventToPromise('close', dialog);
     dialog.$.dialog.cancel();
     await waitForClose;
     const activeElement = getDeepActiveElement();
diff --git a/chrome/test/data/webui/extensions/item_test.ts b/chrome/test/data/webui/extensions/item_test.ts
index a3359d5..4e1c83147 100644
--- a/chrome/test/data/webui/extensions/item_test.ts
+++ b/chrome/test/data/webui/extensions/item_test.ts
@@ -397,7 +397,7 @@
   });
 
   test(assert(extension_item_tests.TestNames.HtmlInName), function() {
-    let name = '<HTML> in the name!';
+    const name = '<HTML> in the name!';
     item.set('data.name', name);
     flush();
     assertEquals(name, item.$.name.textContent!.trim());
diff --git a/chrome/test/data/webui/extensions/manager_unit_test.ts b/chrome/test/data/webui/extensions/manager_unit_test.ts
index 68dcb885..ca3e22ee 100644
--- a/chrome/test/data/webui/extensions/manager_unit_test.ts
+++ b/chrome/test/data/webui/extensions/manager_unit_test.ts
@@ -101,7 +101,7 @@
     assertEquals(alphaFromStore.id, getExtension(1).id);
 
     // Extensions from the same location are sorted by name.
-    let gammaUnpacked = createExtensionInfo({
+    const gammaUnpacked = createExtensionInfo({
       location: chrome.developerPrivate.Location.UNPACKED,
       name: 'Gamma',
       id: 'c'.repeat(32)
@@ -181,7 +181,7 @@
             manager.shadowRoot!.querySelector('extensions-detail-view');
         assertTrue(!!detailsView);
 
-        let extensionCopy = Object.assign({}, extension);
+        const extensionCopy = Object.assign({}, extension);
         extensionCopy.description = newDescription;
         service.itemStateChangedTarget.callListeners({
           event_type: chrome.developerPrivate.EventType.PREFS_CHANGED,
@@ -189,7 +189,7 @@
         });
 
         // Updating a different extension shouldn't have any impact.
-        let secondExtensionCopy = Object.assign({}, secondExtension);
+        const secondExtensionCopy = Object.assign({}, secondExtension);
         secondExtensionCopy.description = 'something else';
         service.itemStateChangedTarget.callListeners({
           event_type: chrome.developerPrivate.EventType.PREFS_CHANGED,
diff --git a/chrome/test/data/webui/extensions/navigation_helper_test.ts b/chrome/test/data/webui/extensions/navigation_helper_test.ts
index 9bf1d875..d2dd120 100644
--- a/chrome/test/data/webui/extensions/navigation_helper_test.ts
+++ b/chrome/test/data/webui/extensions/navigation_helper_test.ts
@@ -117,16 +117,16 @@
         };
 
         // Test url -> state.
-        for (let key in stateUrlPairs) {
-          let entry = stateUrlPairs[key];
+        for (const key in stateUrlPairs) {
+          const entry = stateUrlPairs[key];
           assertTrue(!!entry);
           history.pushState({}, '', entry.url);
           assertDeepEquals(entry.state, navigationHelper.getCurrentPage(), key);
         }
 
         // Test state -> url.
-        for (let key in stateUrlPairs) {
-          let entry = stateUrlPairs[key];
+        for (const key in stateUrlPairs) {
+          const entry = stateUrlPairs[key];
           assertTrue(!!entry);
           navigationHelper.updateHistory(entry.state, false);
           assertEquals(entry.url, location.href, key);
diff --git a/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts b/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
index 569d195..682e264 100644
--- a/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
+++ b/chrome/test/data/webui/extensions/runtime_host_permissions_test.ts
@@ -202,7 +202,7 @@
     // Canceling the dialog should reset the selectHostAccess value to ON_CLICK,
     // since no host was added.
     assertTrue(dialog.isOpen());
-    let whenClosed = eventToPromise('close', dialog);
+    const whenClosed = eventToPromise('close', dialog);
     dialog.shadowRoot!.querySelector<HTMLElement>('.cancel-button')!.click();
     await whenClosed;
 
@@ -265,7 +265,7 @@
     // Closing the dialog (as opposed to canceling) should keep the
     // selectHostAccess value at ON_SPECIFIC_SITES.
     assertTrue(dialog.isOpen());
-    let whenClosed = eventToPromise('close', dialog);
+    const whenClosed = eventToPromise('close', dialog);
     dialog.$.submit.click();
     return whenClosed.then(() => {
       flush();
diff --git a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
index 399f38e..e417b19b 100644
--- a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
+++ b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.ts
@@ -59,8 +59,8 @@
     assertFalse(submit.disabled);
     submit.click();
     return delegate.whenCalled('addRuntimeHostPermission').then((args) => {
-      let id = args[0];
-      let input = args[1];
+      const id = args[0];
+      const input = args[1];
       assertEquals(ITEM_ID, id);
       assertEquals('http://www.example.com/*', input);
     });
@@ -171,8 +171,8 @@
     assertFalse(submit.disabled);
     submit.click();
     return delegate.whenCalled('setItemHostAccess').then((args) => {
-      let id = args[0];
-      let access = args[1];
+      const id = args[0];
+      const access = args[1];
       assertEquals(ITEM_ID, id);
       assertEquals(
           chrome.developerPrivate.HostAccess.ON_SPECIFIC_SITES, access);
@@ -249,11 +249,11 @@
     assertFalse(submit.disabled);
     submit.click();
 
-    let [id, host] = await delegate.whenCalled('addRuntimeHostPermission');
+    const [id, host] = await delegate.whenCalled('addRuntimeHostPermission');
     assertEquals(ITEM_ID, id);
     assertEquals('http://*.restricted.com/*', host);
 
-    let [siteSet, removedSites] =
+    const [siteSet, removedSites] =
         await delegate.whenCalled('removeUserSpecifiedSites');
     assertEquals(chrome.developerPrivate.UserSiteSet.RESTRICTED, siteSet);
     assertDeepEquals(
diff --git a/chrome/test/data/webui/extensions/test_util.ts b/chrome/test/data/webui/extensions/test_util.ts
index 7ed0fba..5d95683 100644
--- a/chrome/test/data/webui/extensions/test_util.ts
+++ b/chrome/test/data/webui/extensions/test_util.ts
@@ -221,11 +221,11 @@
  */
 export function findMatches(
     root: HTMLElement|Document, query: string): HTMLElement[] {
-  let elements = new Set<HTMLElement>();
+  const elements = new Set<HTMLElement>();
   function doSearch(node: Node) {
     if (node.nodeType === Node.ELEMENT_NODE) {
       const matches = (node as Element).querySelectorAll<HTMLElement>(query);
-      for (let match of matches) {
+      for (const match of matches) {
         elements.add(match);
       }
     }
diff --git a/chrome/test/data/webui/metrics_reporter/metrics_reporter_test.ts b/chrome/test/data/webui/metrics_reporter/metrics_reporter_test.ts
index 506dec27..9958fd23 100644
--- a/chrome/test/data/webui/metrics_reporter/metrics_reporter_test.ts
+++ b/chrome/test/data/webui/metrics_reporter/metrics_reporter_test.ts
@@ -15,7 +15,7 @@
   let now: bigint;
 
   let callbackRouter: PageMetricsCallbackRouter;
-  let apiProxy = TestBrowserProxy.fromClass(BrowserProxyImpl);
+  const apiProxy = TestBrowserProxy.fromClass(BrowserProxyImpl);
   let metricsReporter: MetricsReporter;
 
   function forwardTime() {
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index 5d260ab..ffa269a2 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -28,7 +28,7 @@
   let backgroundManager: TestBrowserProxy;
   let moduleResolver: PromiseResolver<Module[]>;
 
-  let url: URL = new URL(location.href);
+  const url: URL = new URL(location.href);
 
   setup(async () => {
     document.body.innerHTML = '';
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts
index dcbbdd8b..1a33609 100644
--- a/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/cart_v2/module_test.ts
@@ -752,7 +752,7 @@
       // Act.
       let waitForLeftScrollEnableChange =
           eventToPromise('left-scroll-hide', moduleElement);
-      let waitForRightScrollEnableChange =
+      const waitForRightScrollEnableChange =
           eventToPromise('right-scroll-show', moduleElement);
       await waitForLeftScrollEnableChange;
       await waitForRightScrollEnableChange;
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
index 489c27c..1174826 100644
--- a/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/modules_test.ts
@@ -245,7 +245,7 @@
       // Arrange.
       const moduleArray = [];
       for (let i = 0; i < 4; ++i) {
-        let module = createElement();
+        const module = createElement();
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
@@ -314,7 +314,7 @@
       let restoreCalled = false;
       const moduleArray = [];
       for (let i = 0; i < 3; ++i) {
-        let module = createElement();
+        const module = createElement();
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
@@ -662,7 +662,7 @@
       // Arrange.
       const moduleArray = [];
       for (let i = 0; i < 3; ++i) {
-        let module = createElement();
+        const module = createElement();
         moduleArray.push(module);
       }
       const fooDescriptor = new ModuleDescriptorV2(
@@ -825,5 +825,90 @@
       assertEquals(secondPositionRect.x, firstModule.getBoundingClientRect().x);
       assertEquals(secondPositionRect.y, firstModule.getBoundingClientRect().y);
     });
+
+    test('drag tall module over short module sibling container', async () => {
+      // Arrange.
+      const moduleArray = [];
+      for (let i = 0; i < 3; ++i) {
+        const module = createElement();
+        moduleArray.push(module);
+      }
+      const fooDescriptor = new ModuleDescriptorV2(
+          'foo', 'Foo', ModuleHeight.TALL, async () => createElement());
+      const barDescriptor = new ModuleDescriptorV2(
+          'bar', 'Bar', ModuleHeight.SHORT, async () => createElement());
+      const fooBarDescriptor = new ModuleDescriptorV2(
+          'foo bar', 'Foo Baz', ModuleHeight.SHORT,
+          async () => createElement());
+
+      moduleRegistry.setResultFor(
+          'getDescriptors', [fooDescriptor, barDescriptor, fooBarDescriptor]);
+      const modulesElement = await createModulesElement([
+        {
+          descriptor: fooDescriptor,
+          element: moduleArray[0]!,
+        },
+        {
+          descriptor: barDescriptor,
+          element: moduleArray[1]!,
+        },
+        {
+          descriptor: fooBarDescriptor,
+          element: moduleArray[2]!,
+        },
+      ]);
+      callbackRouterRemote.setDisabledModules(false, []);
+      await callbackRouterRemote.$.flushForTesting();
+
+      let moduleWrappers = Array.from(
+          modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper'));
+      const tallModule = moduleWrappers[0];
+      const shortModule1 = moduleWrappers[1];
+      const shortModule2 = moduleWrappers[2];
+      assertTrue(!!tallModule);
+      assertTrue(!!shortModule1);
+      assertTrue(!!shortModule2);
+      assertStyle(tallModule, 'cursor', 'grab');
+      assertStyle(shortModule1, 'cursor', 'grab');
+      assertStyle(shortModule2, 'cursor', 'grab');
+
+      // Act.
+      tallModule.dispatchEvent(new MouseEvent('mousedown'));
+      document.dispatchEvent(new MouseEvent('mousemove'));
+
+      // Act.
+      shortModule1.dispatchEvent(new MouseEvent('mouseover'));
+
+      // Assert.
+      moduleWrappers = Array.from(
+          modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper'));
+      assertEquals(0, moduleWrappers.indexOf(shortModule1));
+      assertEquals(1, moduleWrappers.indexOf(shortModule2));
+      assertEquals(2, moduleWrappers.indexOf(tallModule));
+
+      // Act.
+      shortModule2.dispatchEvent(new MouseEvent('mouseover'));
+
+      // Assert.
+      moduleWrappers = Array.from(
+          modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper'));
+      assertEquals(0, moduleWrappers.indexOf(tallModule));
+      assertEquals(1, moduleWrappers.indexOf(shortModule1));
+      assertEquals(2, moduleWrappers.indexOf(shortModule2));
+
+      // Act.
+      shortModule1.dispatchEvent(new MouseEvent('mousedown'));
+      document.dispatchEvent(new MouseEvent('mousemove'));
+
+      // Act.
+      tallModule.dispatchEvent(new MouseEvent('mouseover'));
+
+      // Assert.
+      moduleWrappers = Array.from(
+          modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper'));
+      assertEquals(0, moduleWrappers.indexOf(shortModule1));
+      assertEquals(1, moduleWrappers.indexOf(tallModule));
+      assertEquals(2, moduleWrappers.indexOf(shortModule2));
+    });
   });
 });
diff --git a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
index 2822da6..a0c6a540 100644
--- a/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
+++ b/chrome/test/data/webui/new_tab_page/realbox/realbox_test.ts
@@ -490,8 +490,8 @@
     assertTrue(matchEls[0]!.classList.contains(Classes.SELECTED));
 
     assertEquals('      hello world', realbox.$.input.value);
-    let start = realbox.$.input.selectionStart!;
-    let end = realbox.$.input.selectionEnd!;
+    const start = realbox.$.input.selectionStart!;
+    const end = realbox.$.input.selectionEnd!;
     assertEquals('', realbox.$.input.value.substring(start, end));
   });
 
@@ -519,7 +519,7 @@
 
     assertTrue(areMatchesShowing());
 
-    let matchEls =
+    const matchEls =
         realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
     assertEquals(1, matchEls.length);
     verifyMatch(matches[0]!, matchEls[0]!);
@@ -690,8 +690,8 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
 
     assertEquals('hello world', realbox.$.input.value);
-    let start = realbox.$.input.selectionStart!;
-    let end = realbox.$.input.selectionEnd!;
+    const start = realbox.$.input.selectionStart!;
+    const end = realbox.$.input.selectionEnd!;
     assertEquals('world', realbox.$.input.value.substring(start, end));
 
     // Select the entire input.
@@ -722,8 +722,8 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
 
     assertEquals('helloworld.com', realbox.$.input.value);
-    let start = realbox.$.input.selectionStart!;
-    let end = realbox.$.input.selectionEnd!;
+    const start = realbox.$.input.selectionStart!;
+    const end = realbox.$.input.selectionEnd!;
     assertEquals('world.com', realbox.$.input.value.substring(start, end));
 
     const copyEvent = createClipboardEvent('copy');
@@ -1227,7 +1227,7 @@
     assertEquals(2, matchEls.length);
 
     // Select the second match.
-    let arrowUpEvent = new KeyboardEvent('keydown', {
+    const arrowUpEvent = new KeyboardEvent('keydown', {
       bubbles: true,
       cancelable: true,
       composed: true,  // So it propagates across shadow DOM boundary.
@@ -1429,7 +1429,7 @@
     // First match is not selected.
     assertFalse(matchEls[0]!.classList.contains(Classes.SELECTED));
 
-    let arrowDownEvent = new KeyboardEvent('keydown', {
+    const arrowDownEvent = new KeyboardEvent('keydown', {
       bubbles: true,
       cancelable: true,
       composed: true,  // So it propagates across shadow DOM boundary.
@@ -1590,7 +1590,7 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
 
     assertTrue(areMatchesShowing());
-    let matchEls =
+    const matchEls =
         realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
     assertEquals(2, matchEls.length);
 
@@ -1692,13 +1692,13 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
 
     assertTrue(areMatchesShowing());
-    let matchEls =
+    const matchEls =
         realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
 
-    let focusIndicator = matchEls[0]!.$['focus-indicator'];
+    const focusIndicator = matchEls[0]!.$['focus-indicator'];
 
     // Select the first match
-    let arrowDownEvent = new KeyboardEvent('keydown', {
+    const arrowDownEvent = new KeyboardEvent('keydown', {
       bubbles: true,
       cancelable: true,
       composed: true,  // So it propagates across shadow DOM boundary.
@@ -1778,8 +1778,8 @@
     testProxy.handler.reset();
 
     assertEquals('hello', realbox.$.input.value);
-    let start = realbox.$.input.selectionStart!;
-    let end = realbox.$.input.selectionEnd!;
+    const start = realbox.$.input.selectionStart!;
+    const end = realbox.$.input.selectionEnd!;
     assertEquals('ello', realbox.$.input.value.substring(start, end));
 
     // Type the next character of the inline autocompletion.
@@ -1842,7 +1842,7 @@
         await testProxy.callbackRouterRemote.$.flushForTesting();
 
         assertTrue(areMatchesShowing());
-        let matchEls =
+        const matchEls =
             realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
         assertEquals(2, matchEls.length);
         assertIconMaskImageUrl(matchEls[0]!.$.icon, 'clock.svg');
@@ -1906,7 +1906,7 @@
         assertBackgroundImageDataUrl(realbox.$.icon, faviconData);
 
         // Select the first match by pressing 'Escape'.
-        let escapeEvent = new KeyboardEvent('keydown', {
+        const escapeEvent = new KeyboardEvent('keydown', {
           bubbles: true,
           cancelable: true,
           composed: true,  // So it propagates across shadow DOM boundary.
@@ -1943,7 +1943,7 @@
         // Select the entire input.
         realbox.$.input.setSelectionRange(0, realbox.$.input.value.length);
 
-        let cutEvent = createClipboardEvent('cut');
+        const cutEvent = createClipboardEvent('cut');
         realbox.$.input.dispatchEvent(cutEvent);
         assertTrue(cutEvent.defaultPrevented);
 
@@ -1974,7 +1974,7 @@
         await testProxy.callbackRouterRemote.$.flushForTesting();
 
         assertTrue(areMatchesShowing());
-        let matchEls =
+        const matchEls =
             realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
         assertEquals(2, matchEls.length);
         assertIconMaskImageUrl(matchEls[0]!.$.icon, 'page.svg');
@@ -2046,7 +2046,7 @@
         assertTrue(window.getComputedStyle(realbox.$.icon).display !== 'none');
 
         // Select the first match by pressing 'Escape'.
-        let escapeEvent = new KeyboardEvent('keydown', {
+        const escapeEvent = new KeyboardEvent('keydown', {
           bubbles: true,
           cancelable: true,
           composed: true,  // So it propagates across shadow DOM boundary.
@@ -2243,7 +2243,7 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
 
     assertTrue(areMatchesShowing());
-    let matchEls =
+    const matchEls =
         realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
     assertEquals(1, matchEls.length);
 
@@ -2255,7 +2255,7 @@
     assertEquals(
         window.getComputedStyle(matchEls[0]!.$.separator).display, 'none');
 
-    let arrowDownEvent = new KeyboardEvent('keydown', {
+    const arrowDownEvent = new KeyboardEvent('keydown', {
       bubbles: true,
       cancelable: true,
       composed: true,  // So it propagates across shadow DOM boundary.
@@ -2291,7 +2291,7 @@
     });
     await testProxy.callbackRouterRemote.$.flushForTesting();
     assertTrue(areMatchesShowing());
-    let matchEls =
+    const matchEls =
         realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
     verifyMatch(matches[0]!, matchEls[0]!);
 
@@ -2299,7 +2299,7 @@
     assertEquals(
         window.getComputedStyle(matchEls[0]!.$.separator).display, 'none');
 
-    let arrowDownEvent = new KeyboardEvent('keydown', {
+    const arrowDownEvent = new KeyboardEvent('keydown', {
       bubbles: true,
       cancelable: true,
       composed: true,  // So it propagates across shadow DOM boundary.
@@ -2336,7 +2336,7 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
     assertTrue(areMatchesShowing());
 
-    let matchEl = $$(realbox.$.matches, 'ntp-realbox-match')!;
+    const matchEl = $$(realbox.$.matches, 'ntp-realbox-match')!;
     verifyMatch(matches[0]!, matchEl);
 
     const pedalEl = $$($$(matchEl, 'ntp-realbox-action')!, '.contents')!;
@@ -2346,7 +2346,7 @@
         'chrome://theme/current-channel-logo');  // Default Pedal
                                                  // Icon
 
-    let leftClick = new MouseEvent('click', {
+    const leftClick = new MouseEvent('click', {
       bubbles: true,
       button: 1,
       cancelable: true,
@@ -2388,7 +2388,7 @@
     await testProxy.callbackRouterRemote.$.flushForTesting();
     assertTrue(areMatchesShowing());
 
-    let matchEls =
+    const matchEls =
         realbox.$.matches.shadowRoot!.querySelectorAll('ntp-realbox-match');
     verifyMatch(matches[0]!, matchEls[0]!);
     verifyMatch(matches[1]!, matchEls[1]!);
@@ -2400,7 +2400,7 @@
         'chrome://theme/current-channel-logo');  // Default Pedal
                                                  // Icon
 
-    let leftClick = new MouseEvent('click', {
+    const leftClick = new MouseEvent('click', {
       bubbles: true,
       button: 0,
       cancelable: true,
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 04a936af..0b0fedf 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_subpage_tests.js
@@ -825,5 +825,129 @@
         assertFalse(!!multideviceSubpage.$$('#phoneHubNotificationsItem'));
         assertFalse(!!multideviceSubpage.$$('#phoneHubAppsItem'));
         assertTrue(!!multideviceSubpage.$$('#phoneHubCombinedSetupItem'));
+
+        // Notifications, CameraRoll and Apps features are not grant, but
+        // Notifications is prohibited. Should show Notifications and combined
+        // settings.
+        multideviceSubpage.pageContentData =
+            Object.assign({}, multideviceSubpage.pageContentData, {
+              phoneHubCameraRollState: MultiDeviceFeatureState.DISABLED_BY_USER,
+              cameraRollAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubNotificationsState:
+                  MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              notificationAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubAppsState: MultiDeviceFeatureState.DISABLED_BY_USER,
+              isPhoneHubAppsAccessGranted: false
+            });
+
+        flush();
+
+        assertTrue(!!multideviceSubpage.$$('#phoneHubItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubCameraRollItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubNotificationsItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubAppsItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubCombinedSetupItem'));
+
+        // Notifications, CameraRoll and Apps features are not grant, but
+        // cameraRoll is prohibited. Should show cameraRoll and combined
+        // settings.
+        multideviceSubpage.pageContentData =
+            Object.assign({}, multideviceSubpage.pageContentData, {
+              phoneHubCameraRollState:
+                  MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              cameraRollAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubNotificationsState:
+                  MultiDeviceFeatureState.DISABLED_BY_USER,
+              notificationAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubAppsState: MultiDeviceFeatureState.DISABLED_BY_USER,
+              isPhoneHubAppsAccessGranted: false
+            });
+
+        flush();
+
+        assertTrue(!!multideviceSubpage.$$('#phoneHubItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubCameraRollItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubNotificationsItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubAppsItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubCombinedSetupItem'));
+
+        // Notifications, CameraRoll and Apps features are not grant, but Apps
+        // is prohibited. Should show Apps and combined settings.
+        multideviceSubpage.pageContentData =
+            Object.assign({}, multideviceSubpage.pageContentData, {
+              phoneHubCameraRollState: MultiDeviceFeatureState.DISABLED_BY_USER,
+              cameraRollAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubNotificationsState:
+                  MultiDeviceFeatureState.DISABLED_BY_USER,
+              notificationAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubAppsState: MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              isPhoneHubAppsAccessGranted: false
+            });
+
+        flush();
+
+        assertTrue(!!multideviceSubpage.$$('#phoneHubItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubCameraRollItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubNotificationsItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubAppsItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubCombinedSetupItem'));
+
+        // Notifications, CameraRoll and Apps features are not grant, but
+        // Notifications and CameraRoll are prohibited. Should show
+        // Notifications, CameraRoll and Apps and hide combined settings.
+        multideviceSubpage.pageContentData =
+            Object.assign({}, multideviceSubpage.pageContentData, {
+              phoneHubCameraRollState:
+                  MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              cameraRollAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubNotificationsState:
+                  MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              notificationAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubAppsState: MultiDeviceFeatureState.DISABLED_BY_USER,
+              isPhoneHubAppsAccessGranted: false
+            });
+
+        flush();
+
+        assertTrue(!!multideviceSubpage.$$('#phoneHubItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubCameraRollItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubNotificationsItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubAppsItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubCombinedSetupItem'));
+
+
+        // Notifications, CameraRoll and Apps features are not grant, but Phone
+        // Hub (top feature) is prohibited. Should show Notifications,
+        // CameraRoll and Apps and hide combined settings.
+        multideviceSubpage.pageContentData =
+            Object.assign({}, multideviceSubpage.pageContentData, {
+              phoneHubState: MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              phoneHubCameraRollState:
+                  MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              cameraRollAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubNotificationsState:
+                  MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              notificationAccessStatus:
+                  PhoneHubFeatureAccessStatus.AVAILABLE_BUT_NOT_GRANTED,
+              phoneHubAppsState: MultiDeviceFeatureState.PROHIBITED_BY_POLICY,
+              isPhoneHubAppsAccessGranted: false
+            });
+
+        flush();
+
+        assertTrue(!!multideviceSubpage.$$('#phoneHubItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubCameraRollItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubNotificationsItem'));
+        assertTrue(!!multideviceSubpage.$$('#phoneHubAppsItem'));
+        assertFalse(!!multideviceSubpage.$$('#phoneHubCombinedSetupItem'));
       });
 });
diff --git a/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_interactive_ui_test.ts b/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_interactive_ui_test.ts
index 311b4af..df9e020 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_interactive_ui_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/bookmarks_list_interactive_ui_test.ts
@@ -144,7 +144,7 @@
     assertEquals('3', copiedId);
 
     keyDownOn(bookmarkElement, 0, ['ctrl'], 'v');
-    let [pastedId, pastedDestinationId] =
+    const [pastedId, pastedDestinationId] =
         await bookmarksApi.whenCalled('pasteToBookmark');
     assertEquals('0', pastedId);
     assertEquals('3', pastedDestinationId);
diff --git a/chrome/test/data/webui/tab_search/tab_search_app_test.ts b/chrome/test/data/webui/tab_search/tab_search_app_test.ts
index f785348..22bdfe5 100644
--- a/chrome/test/data/webui/tab_search/tab_search_app_test.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_app_test.ts
@@ -242,7 +242,7 @@
       recentlyClosedSectionExpanded: true
     }));
 
-    let tabSearchItem = tabSearchApp.$.tabsList.querySelector<HTMLElement>(
+    const tabSearchItem = tabSearchApp.$.tabsList.querySelector<HTMLElement>(
         'tab-search-item[id="100"]')!;
     tabSearchItem.click();
     const [tabId, withSearch, isTab, index] =
@@ -278,7 +278,7 @@
       recentlyClosedSectionExpanded: true
     }));
 
-    let tabSearchItem =
+    const tabSearchItem =
         tabSearchApp.$.tabsList.querySelector('tab-search-group-item')!;
     tabSearchItem.click();
     const [id, withSearch, isTab, index] =
@@ -769,7 +769,7 @@
       tabGroups: [tabGroup],
     }));
 
-    let tabSearchItem = tabSearchApp.$.tabsList.querySelector<TabSearchItem>(
+    const tabSearchItem = tabSearchApp.$.tabsList.querySelector<TabSearchItem>(
         'tab-search-item[id="1"]')!;
     assertEquals('Google', tabSearchItem.data.tab.title);
     assertEquals('Search Engines', tabSearchItem.data.tabGroup!.title);
diff --git a/chrome/test/data/webui/tab_search/tab_search_test_helper.ts b/chrome/test/data/webui/tab_search/tab_search_test_helper.ts
index ffd3448..8e6ff03 100644
--- a/chrome/test/data/webui/tab_search/tab_search_test_helper.ts
+++ b/chrome/test/data/webui/tab_search/tab_search_test_helper.ts
@@ -14,7 +14,7 @@
   klass.prototype[functionName] = function(options: any) {
     const args = [];
     if (typeof options === 'object' && options !== null) {
-      let noAnimationOptions = Object.assign({}, options);
+      const noAnimationOptions = Object.assign({}, options);
       delete noAnimationOptions.behavior;
 
       args.push(noAnimationOptions);
diff --git a/chrome/test/data/webui/tab_strip/drag_manager_test.ts b/chrome/test/data/webui/tab_strip/drag_manager_test.ts
index c555718c..5afbf42 100644
--- a/chrome/test/data/webui/tab_strip/drag_manager_test.ts
+++ b/chrome/test/data/webui/tab_strip/drag_manager_test.ts
@@ -181,7 +181,7 @@
     const eventYWithinDragImageCenter =
         eventYPercentage * dragImageCenterRect.height;
 
-    let expectedOffsetX =
+    const expectedOffsetX =
         dragImageCenterLeftMargin + eventXWithinDragImageCenter;
     let expectedOffsetY =
         dragImageCenterTopMargin + eventYWithinDragImageCenter;
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.ts b/chrome/test/data/webui/tab_strip/tab_list_test.ts
index d12917e..ca8e9107 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.ts
@@ -289,7 +289,7 @@
   async function testPlaceTabElementAnimation(
       indexToMove: number, newIndex: number, direction: number) {
     await tabList.animationPromises;
-    let unpinnedTabs = getUnpinnedTabs();
+    const unpinnedTabs = getUnpinnedTabs();
 
     const movedTab = unpinnedTabs[indexToMove]!;
     tabList.placeTabElement(movedTab, newIndex, false, undefined);
@@ -661,7 +661,7 @@
     callbackRouter.tabUpdated(updatedTab);
     await flushTasks();
 
-    let pinnedTabElements = getPinnedTabs();
+    const pinnedTabElements = getPinnedTabs();
     assertEquals(pinnedTabElements.length, 1);
     assertTrue(pinnedTabElements[0]!.tab.pinned);
     assertEquals(pinnedTabElements[0]!.tab.id, tabToPin.id);
@@ -947,7 +947,7 @@
     // The 2nd tab should be off-screen to the right, so activating it should
     // scroll so that the element's right edge is aligned with the screen's
     // right edge.
-    let activeTab = getUnpinnedTabs()[1]!;
+    const activeTab = getUnpinnedTabs()[1]!;
     assertEquals(
         tabList.scrollLeft + tabList.offsetWidth,
         activeTab.offsetLeft + activeTab.offsetWidth + scrollPadding);
diff --git a/chrome/test/data/webui/tab_strip/tab_swiper_test.ts b/chrome/test/data/webui/tab_strip/tab_swiper_test.ts
index 49246b6..7f129d01a 100644
--- a/chrome/test/data/webui/tab_strip/tab_swiper_test.ts
+++ b/chrome/test/data/webui/tab_strip/tab_swiper_test.ts
@@ -44,14 +44,14 @@
     tabElement.dispatchEvent(new PointerEvent('pointermove', pointerState));
     assertEquals(tabElStyle.maxWidth, `${tabWidth}px`);
     assertEquals(tabElStyle.opacity, '1');
-    let startTop = tabElement.getBoundingClientRect().top;
+    const startTop = tabElement.getBoundingClientRect().top;
     assertEquals(startTop, 0);
 
     // Swipe was enough to start animating the position.
     pointerState.clientY = startY - (TRANSLATE_ANIMATION_THRESHOLD_PX + 1);
     tabElement.dispatchEvent(new PointerEvent('pointermove', pointerState));
     assertEquals(tabElStyle.maxWidth, `${tabWidth}px`);
-    let top = tabElement.getBoundingClientRect().top;
+    const top = tabElement.getBoundingClientRect().top;
     assertTrue(top < startTop && top > -1 * SWIPE_FINISH_THRESHOLD_PX);
 
     // Swipe was enough to start animating max width and opacity.
diff --git a/chrome/test/interaction/interaction_sequence_browser_util.cc b/chrome/test/interaction/interaction_sequence_browser_util.cc
index 0dd0def..1059d12c 100644
--- a/chrome/test/interaction/interaction_sequence_browser_util.cc
+++ b/chrome/test/interaction/interaction_sequence_browser_util.cc
@@ -28,6 +28,9 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "chrome/browser/ui/test/test_browser_ui.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/test/pixel/browser_skia_gold_pixel_diff.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -47,6 +50,43 @@
 
 namespace {
 
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#define SUPPORTS_PIXEL_TESTS 1
+#else
+#define SUPPORTS_PIXEL_TESTS 0
+#endif
+
+#if SUPPORTS_PIXEL_TESTS
+class PixelTestUi : public TestBrowserUi {
+ public:
+  PixelTestUi(views::View* view,
+              const std::string& screenshot_name,
+              const std::string& baseline)
+      : view_(view), screenshot_name_(screenshot_name), baseline_(baseline) {}
+  ~PixelTestUi() override = default;
+
+  void ShowUi(const std::string& name) override { NOTREACHED(); }
+  void WaitForUserDismissal() override { NOTREACHED(); }
+
+  bool VerifyUi() override {
+    auto* const test_info =
+        testing::UnitTest::GetInstance()->current_test_info();
+    const std::string test_name =
+        base::StrCat({test_info->test_case_name(), "_", test_info->name()});
+    const std::string screenshot_name =
+        screenshot_name_.empty()
+            ? baseline_
+            : base::StrCat({screenshot_name_, "_", baseline_});
+    return VerifyPixelUi(view_, test_name, screenshot_name);
+  }
+
+ private:
+  base::raw_ptr<views::View> view_ = nullptr;
+  std::string screenshot_name_;
+  std::string baseline_;
+};
+#endif  // SUPPORTS_PIXEL_TESTS
+
 content::WebContents* GetWebContents(Browser* browser,
                                      absl::optional<int> tab_index) {
   auto* const model = browser->tab_strip_model();
@@ -398,6 +438,38 @@
 }
 
 // static
+bool InteractionSequenceBrowserUtil::CompareScreenshot(
+    ui::TrackedElement* element,
+    const std::string& screenshot_name,
+    const std::string& baseline) {
+#if SUPPORTS_PIXEL_TESTS
+  views::View* view = nullptr;
+  if (auto* const view_el = element->AsA<views::TrackedElementViews>()) {
+    view = view_el->view();
+  } else if (auto* const page_el = element->AsA<TrackedElementWebPage>()) {
+    auto* const util = page_el->owner();
+    if (util->web_view_data_) {
+      view = util->web_view_data_->web_view();
+    } else {
+      Browser* const browser = GetBrowserFromContext(page_el->context());
+      BrowserView* const browser_view =
+          BrowserView::GetBrowserViewForBrowser(browser);
+      CHECK(browser_view);
+      CHECK_EQ(util->web_contents(), browser_view->GetActiveWebContents());
+      view = browser_view->contents_web_view();
+    }
+  }
+
+  CHECK(view);
+
+  PixelTestUi pixel_test_ui(view, screenshot_name, baseline);
+  return pixel_test_ui.VerifyUi();
+#else  // !SUPPORTS_PIXEL_TESTS
+  return true;
+#endif
+}
+
+// static
 std::unique_ptr<InteractionSequenceBrowserUtil>
 InteractionSequenceBrowserUtil::ForExistingTabInContext(
     ui::ElementContext context,
diff --git a/chrome/test/interaction/interaction_sequence_browser_util.h b/chrome/test/interaction/interaction_sequence_browser_util.h
index d2fa2576..4c35746e 100644
--- a/chrome/test/interaction/interaction_sequence_browser_util.h
+++ b/chrome/test/interaction/interaction_sequence_browser_util.h
@@ -168,6 +168,52 @@
   // Returns whether the given value is "truthy" in the Javascript sense.
   static bool IsTruthy(const base::Value& value);
 
+  // Takes a screenshot based on the contents of `element` and compares with
+  // Skia Gold. Not all element types may be supported. On platforms where
+  // screenshots are unsupported or flaky, may trivially return true.
+  //
+  // If `element` is a TrackedElementWebPage that corresponds to a tab, the tab
+  // must be the active tab in the browser window.
+  //
+  // The name of the screenshot will be composed as follows:
+  //   TestFixture_TestName[_screenshot_name]_baseline
+  // If you are taking more than one screenshot per test, then `screenshot_name`
+  // must be specified and unique within the test; otherwise you may leave it
+  // empty.
+  //
+  // IMPORTANT USAGE NOTES:
+  //
+  // In order to actually take screenshots:
+  // - Your test must be in browser_tests rather than interactive_ui_tests
+  // - Your test must be included in pixel_browser_tests.filter
+  //
+  // Note that test in browser_tests (when not running in the
+  // pixel_browser_tests CQ task) may run at the same time as other tests, which
+  // can result in flakiness for interaction tests (especially if mouse
+  // position, window activation, or occlusion could change the behavior of a
+  // test). So if you need to both test complex interaction and take screenshots
+  // you have several options:
+  //  1. Make a detailed test for interactive_ui_tests and one or more simple
+  //     tests that just verify the UI visuals in browser_tests (downside: code
+  //     duplication)
+  //  2. Put a test in browser_tests that only runs when the command line flag
+  //     for pixel tests is set (downside: won't run on platforms that don't
+  //     support pixel tests)
+  //  3. Put the full test in browser_tests and harden it against activation and
+  //     focus changes, mouse position, etc. - e.g. by using things like
+  //     BubbleDialogDelegate::PreventCloseOnDeactivate() (downside: more
+  //     complicated test, still potential for flaking)
+  //
+  // In general, if (3) is possible and you can be sure your test won't flake,
+  // it's probably the best choice, followed by (1) if you can't guarantee
+  // stability in non-single-process tests.
+  //
+  // We are currently considering enabling pixel tests in interactive_ui_tests,
+  // which would solve the problem by providing a single safe place for both.
+  static bool CompareScreenshot(ui::TrackedElement* element,
+                                const std::string& screenshot_name,
+                                const std::string& baseline);
+
   // Allow access to the associated WebContents.
   content::WebContents* web_contents() const {
     return WebContentsObserver::web_contents();
diff --git a/chrome/test/interaction/interaction_sequence_browser_util_browsertest.cc b/chrome/test/interaction/interaction_sequence_browser_util_browsertest.cc
index de4422a..55400be6 100644
--- a/chrome/test/interaction/interaction_sequence_browser_util_browsertest.cc
+++ b/chrome/test/interaction/interaction_sequence_browser_util_browsertest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/ui/browser_element_identifiers.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/test/interaction/interaction_sequence_browser_util.h"
 
 #include <sstream>
@@ -1399,3 +1401,67 @@
 
   EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
 }
+
+IN_PROC_BROWSER_TEST_F(InteractionSequenceBrowserUtilTest,
+                       CompareScreenshot_View) {
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
+
+  auto sequence =
+      ui::InteractionSequence::Builder()
+          .SetCompletedCallback(completed.Get())
+          .SetAbortedCallback(aborted.Get())
+          .SetContext(browser()->window()->GetElementContext())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetElementID(kAppMenuButtonElementId)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence* sequence,
+                          ui::TrackedElement* element) {
+                        EXPECT_TRUE(
+                            InteractionSequenceBrowserUtil::CompareScreenshot(
+                                element, "AppMenuButton", "3600270"));
+                      }))
+                  .Build())
+          .Build();
+
+  EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
+}
+
+IN_PROC_BROWSER_TEST_F(InteractionSequenceBrowserUtilTest,
+                       CompareScreenshot_WebPage) {
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
+  UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
+
+  // Set the browser view to a consistent size.
+  BrowserView* const browser_view =
+      BrowserView::GetBrowserViewForBrowser(browser());
+  browser_view->GetWidget()->SetSize({400, 300});
+
+  auto util = InteractionSequenceBrowserUtil::ForExistingTabInBrowser(
+      browser(), kInteractionSequenceBrowserUtilTestId);
+  const GURL url = embedded_test_server()->GetURL("/title1.html");
+  util->LoadPage(url);
+
+  auto sequence =
+      ui::InteractionSequence::Builder()
+          .SetCompletedCallback(completed.Get())
+          .SetAbortedCallback(aborted.Get())
+          .SetContext(browser()->window()->GetElementContext())
+          .AddStep(
+              ui::InteractionSequence::StepBuilder()
+                  .SetType(ui::InteractionSequence::StepType::kShown)
+                  .SetElementID(kInteractionSequenceBrowserUtilTestId)
+                  .SetStartCallback(base::BindLambdaForTesting(
+                      [&](ui::InteractionSequence* sequence,
+                          ui::TrackedElement* element) {
+                        EXPECT_TRUE(
+                            InteractionSequenceBrowserUtil::CompareScreenshot(
+                                element, std::string(), "3600270"));
+                      }))
+                  .Build())
+          .Build();
+
+  EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
+}
diff --git a/chrome/test/interaction/interaction_sequence_browser_util_interactive_uitest.cc b/chrome/test/interaction/interaction_sequence_browser_util_interactive_uitest.cc
index d1bc32cb..5cc6652 100644
--- a/chrome/test/interaction/interaction_sequence_browser_util_interactive_uitest.cc
+++ b/chrome/test/interaction/interaction_sequence_browser_util_interactive_uitest.cc
@@ -100,6 +100,7 @@
           .AddStep(ui::InteractionSequence::StepBuilder()
                        .SetType(ui::InteractionSequence::StepType::kShown)
                        .SetElementID(AppMenuModel::kDownloadsMenuItem)
+                       .SetMustRemainVisible(false)
                        .SetStartCallback(base::BindLambdaForTesting(
                            [&](ui::InteractionSequence*,
                                ui::TrackedElement* element) {
diff --git a/chrome/test/webapps/coverage/coverage_cros.tsv b/chrome/test/webapps/coverage/coverage_cros.tsv
index a5f8a4de..c051842 100644
--- a/chrome/test/webapps/coverage/coverage_cros.tsv
+++ b/chrome/test/webapps/coverage/coverage_cros.tsv
@@ -1,5 +1,5 @@
 # This is a generated file.
-# Full coverage: 56%, with partial coverage: 76%
+# Full coverage: 47%, with partial coverage: 65%
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_menu_option_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_launch_icon_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_chrome_apps_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
@@ -613,3 +613,131 @@
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	check_app_in_list_windowed_SiteA🌓
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_install_icon_not_shown🌕
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_launch_icon_shown🌕
+install_create_shortcut_windowed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
diff --git a/chrome/test/webapps/coverage/coverage_linux.tsv b/chrome/test/webapps/coverage/coverage_linux.tsv
index 0c14179a..a4aba43f 100644
--- a/chrome/test/webapps/coverage/coverage_linux.tsv
+++ b/chrome/test/webapps/coverage/coverage_linux.tsv
@@ -1,5 +1,5 @@
 # This is a generated file.
-# Full coverage: 63%, with partial coverage: 85%
+# Full coverage: 55%, with partial coverage: 75%
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_menu_option_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_launch_icon_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_chrome_apps_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
@@ -782,3 +782,131 @@
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	check_app_in_list_windowed_SiteA🌓
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_install_icon_not_shown🌕
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_launch_icon_shown🌕
+install_create_shortcut_windowed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
diff --git a/chrome/test/webapps/coverage/coverage_mac.tsv b/chrome/test/webapps/coverage/coverage_mac.tsv
index 5260ae1f..f49f5bb 100644
--- a/chrome/test/webapps/coverage/coverage_mac.tsv
+++ b/chrome/test/webapps/coverage/coverage_mac.tsv
@@ -1,5 +1,5 @@
 # This is a generated file.
-# Full coverage: 59%, with partial coverage: 76%
+# Full coverage: 51%, with partial coverage: 67%
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_menu_option_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_launch_icon_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_chrome_apps_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
@@ -782,3 +782,131 @@
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	check_app_in_list_windowed_SiteA🌓
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_install_icon_not_shown🌕
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_launch_icon_shown🌕
+install_create_shortcut_windowed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
diff --git a/chrome/test/webapps/coverage/coverage_win.tsv b/chrome/test/webapps/coverage/coverage_win.tsv
index 2d8a9f6..9c5bc8db 100644
--- a/chrome/test/webapps/coverage/coverage_win.tsv
+++ b/chrome/test/webapps/coverage/coverage_win.tsv
@@ -1,5 +1,5 @@
 # This is a generated file.
-# Full coverage: 65%, with partial coverage: 87%
+# Full coverage: 56%, with partial coverage: 76%
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_menu_option_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_launch_icon_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
 install_create_shortcut_windowed_SiteA🌕	manifest_update_title_SiteA🌑	accept_app_id_update_dialog🌑	close_pwa🌑	launch_from_chrome_apps_SiteA🌑	check_app_title_site_a_is_SiteAUpdated🌑
@@ -782,3 +782,131 @@
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	check_app_in_list_windowed_SiteA🌓
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_install_icon_not_shown🌕
 install_create_shortcut_tabbed_SiteA🌕	set_open_in_window_SiteA🌓	navigate_browser_SiteA🌕	check_launch_icon_shown🌕
+install_create_shortcut_windowed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OneTextFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultipleTextFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_MultipleTextFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_omnibox_icon_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_menu_option_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OnePngFile🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_One🌑	check_files_loaded_in_site_SiteB_OnePngFile🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_omnibox_icon_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_menu_option_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_MultiplePngFiles🌑	file_handling_dialog_Allow_AskAgain🌑	check_pwa_window_created_SiteB_Two🌑	check_files_loaded_in_site_SiteB_MultiplePngFiles🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_Remember🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Allow_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	check_window_not_created🌑	check_site_handles_file_SiteB_Txt🌑	check_site_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_AskAgain🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_windowed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_omnibox_icon_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_windowed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_menu_option_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_tabbed_SiteB🌕	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	launch_file_OneTextFile🌑	file_handling_dialog_Deny_Remember🌑	check_window_not_created🌑	check_site_not_handles_file_SiteB_Txt🌑	check_site_not_handles_file_SiteB_Png🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_NotShown🌑	check_pwa_window_created_SiteB_One🌑
+install_create_shortcut_windowed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_omnibox_icon_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_windowed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_menu_option_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_create_shortcut_tabbed_SiteB🌕	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
+install_policy_app_tabbed_no_shortcut_SiteB🌓	add_file_handling_policy_approval_SiteB🌑	remove_file_handling_policy_approval_SiteB🌑	launch_file_OneTextFile🌑	check_file_handling_dialog_Shown🌑
diff --git a/chrome/test/webapps/data/actions.md b/chrome/test/webapps/data/actions.md
index f2bbedd..3a6e35d 100644
--- a/chrome/test/webapps/data/actions.md
+++ b/chrome/test/webapps/data/actions.md
@@ -20,7 +20,7 @@
 
 TODO(dmurph): Possibly this table up into markdown-header section.
 
-| # Action base name | Argument Types | Output Actions | Unique Identifier (next: 123) | Status (WIP, Implemented, Not Implemented, Parameterized) | Description | Metadata, implementation bug, etc |
+| # Action base name | Argument Types | Output Actions | Unique Identifier (next: 128) | Status (WIP, Implemented, Not Implemented, Parameterized) | Description | Metadata, implementation bug, etc |
 | --- | --- | --- | --- | --- | --- | --- |
 | # Badging |
 | check_app_badge_empty | Site |  | 2 | Not Implemented | Check that the 'badge' on the app icon is empty |  |
@@ -107,6 +107,8 @@
 | check_user_cannot_set_run_on_os_login | Site |  | 111 | Implemented | Check user can't change the app's  run_on_os_login state.  |  |
 | check_window_closed |  |  | 23 | Implemented | The window was closed |  |
 | check_window_created |  |  | 24 | Implemented | A window was created. |  |
+| check_window_not_created |  |  | 127 | Not Implemented | A window was created. | P2 |
+| check_pwa_window_created | Site, Number |  | 123 | Implemented | A given number of windows were created for the given pwa. |  |
 | check_window_display_minimal |  |  | 25 | Implemented | Check that the window is a PWA window, and has minimal browser controls. |  |
 | check_window_display_standalone |  |  | 26 | Implemented | Check that the window is a PWA window, and has no browser controls. |  |
 | close_custom_toolbar |  |  | 27 | Implemented | Press the 'x' button on the custom toolbar that is towards the top of the WebApp window. |  |
@@ -142,12 +144,15 @@
 | sync_turn_off |  |  | 41 | Implemented | Turn chrome sync off for "Apps": chrome://settings/syncSetup/advanced |  |
 | sync_turn_on |  |  | 42 | Implemented | Turn chrome sync on for "Apps": chrome://settings/syncSetup/advanced |  |
 | switch_incognito_profile |  |  | 73 | Not Implemented | Switch to using incognito mode | P2 |
-| check_site_handles_file | Site, FileExtension |  | 118 | Not Implemented |  |  |
 | # File handling |
+| check_site_handles_file | Site, FileExtension |  | 118 | Not Implemented |  |  |
+| check_site_not_handles_file | Site, FileExtension |  | 122 | Not Implemented |  |  |
 | check_file_handling_dialog | IsShown |  | 119 | Not Implemented |  |  |
 | launch_file | FilesOptions |  | 120 | Not Implemented |  |  |
-| accept_file_handling_dialog |  |  | 121 | Not Implemented |  |  |
-| deny_file_handling_dialog |  |  | 122 | Not Implemented |  |  |
+| file_handling_dialog | AllowDenyOptions, AskAgainOptions |  | 121 | Not Implemented |  |  |
+| check_files_loaded_in_site | Site, FilesOptions |  | 126 | Not Implemented | Check that the appropriate file contents have loaded in in PWA windows. |  |
+| add_file_handling_policy_approval | Site |  | 124 | Not Implemented |  |  |
+| remove_file_handling_policy_approval | Site |  | 125 | Not Implemented |  |  |
 | # Window Controls Overlay
 | check_window_controls_overlay_toggle | Site, IsShown |  | 112 | WIP |  |  |
 | check_window_controls_overlay | Site, IsOn |  | 113 | WIP |  |  |
diff --git a/chrome/test/webapps/data/critical_user_journeys.md b/chrome/test/webapps/data/critical_user_journeys.md
index 7b29194..748220ed8 100644
--- a/chrome/test/webapps/data/critical_user_journeys.md
+++ b/chrome/test/webapps/data/critical_user_journeys.md
@@ -6,6 +6,8 @@
 
 TODO(dmurph): Move more documentation here. https://crbug.com/1314822
 
+[[TOC]]
+
 ## How this file is parsed
 
 The tables are parsed in this file as critical user journeys. Lines are considered a CUJ if:
@@ -259,3 +261,27 @@
 | #WMLC | install_windowed(SiteB) | manifest_update_display(SiteB, WCO) | await_manifest_update(SiteB) | launch(SiteB) | enable_window_controls_overlay(SiteB) | check_window_controls_overlay_toggle(SiteB, Shown) |
 | #WMLC | install_windowed(SiteWCO) | manifest_update_display(SiteWCO, Standalone) | await_manifest_update(SiteWCO) | launch(SiteWCO) | check_window_controls_overlay_toggle(SiteWCO, NotShown) | 
 | #WMLC | install_windowed(SiteWCO) | manifest_update_display(SiteWCO, Standalone) | await_manifest_update(SiteWCO) | launch(SiteWCO) | check_window_controls_overlay(SiteWCO, Off) | 
+
+## File Handling
+
+| #Platforms | Test -> | | | | | | | | | | | | | | | | |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| WMLC | install(SiteB) | check_site_handles_file(SiteB, Txt) | check_site_handles_file(SiteB, Png) |
+| # Single open & multiple open behavior |
+| WMLC | install(SiteB) | launch_file(OneTextFile) | check_file_handling_dialog(Shown) |
+| WMLC | install(SiteB) | launch_file(OneTextFile) | file_handling_dialog(Allow, AskAgain) | check_pwa_window_created(SiteB, One) | check_files_loaded_in_site(SiteB, OneTextFile) |
+| WMLC | install(SiteB) | launch_file(MultipleTextFiles) | check_file_handling_dialog(Shown)
+| WMLC | install(SiteB) | launch_file(MultipleTextFiles) | file_handling_dialog(Allow, AskAgain) | check_pwa_window_created(SiteB, One) | check_files_loaded_in_site(SiteB, MultipleTextFiles) |
+| WMLC | install(SiteB) | launch_file(OnePngFile) | check_file_handling_dialog(Shown) |
+| WMLC | install(SiteB) | launch_file(OnePngFile) | file_handling_dialog(Allow, AskAgain) | check_pwa_window_created(SiteB, One) | check_files_loaded_in_site(SiteB, OnePngFile) |
+| WMLC | install(SiteB) | launch_file(MultiplePngFiles) | check_file_handling_dialog(Shown) |
+| WMLC | install(SiteB) | launch_file(MultiplePngFiles) | file_handling_dialog(Allow, AskAgain) | check_pwa_window_created(SiteB, Two) | check_files_loaded_in_site(SiteB, MultiplePngFiles) |
+| # Dialog options |
+| WMLC | install(SiteB) | launch_file(OneTextFile) | file_handling_dialog(Allow, Remember) | launch_file(OneTextFile) | check_file_handling_dialog(NotShown) | check_pwa_window_created(SiteB, One) | 
+| WMLC | install(SiteB) | launch_file(OneTextFile) | file_handling_dialog(Allow, AskAgain) | launch_file(OneTextFile) | check_file_handling_dialog(Shown) |
+| WMLC | install(SiteB) | launch_file(OneTextFile) | file_handling_dialog(Deny, AskAgain) | check_window_not_created | check_site_handles_file(SiteB, Txt) | check_site_handles_file(SiteB, Png) | 
+| WMLC | install(SiteB) | launch_file(OneTextFile) | file_handling_dialog(Deny, AskAgain) | launch_file(OneTextFile) | check_file_handling_dialog(Shown) |
+| WMLC | install(SiteB) | launch_file(OneTextFile) | file_handling_dialog(Deny, Remember) | check_window_not_created | check_site_not_handles_file(SiteB, Txt) | check_site_not_handles_file(SiteB, Png) |
+| # Policy approval |
+| WMLC | install(SiteB) | add_file_handling_policy_approval(SiteB) | launch_file(OneTextFile) | check_file_handling_dialog(NotShown) | check_pwa_window_created(SiteB, One) | 
+| WMLC | install(SiteB) | add_file_handling_policy_approval(SiteB) | remove_file_handling_policy_approval(SiteB) | launch_file(OneTextFile) | check_file_handling_dialog(Shown) |
diff --git a/chrome/test/webapps/data/enums.md b/chrome/test/webapps/data/enums.md
index a55361f7..fbb24ef 100644
--- a/chrome/test/webapps/data/enums.md
+++ b/chrome/test/webapps/data/enums.md
@@ -20,7 +20,7 @@
 
 | #Enum Name | Values (* = default) |  |  |  |  |  |  |
 | --- | --- | --- | --- | --- | --- | --- | --- |
-| Site | SiteA* | SiteB | SiteC | SiteAFoo | SiteABar | SiteWco | kSiteIsolatedApp |  |
+| Site | SiteA* | SiteB | SiteC | SiteAFoo | SiteABar | SiteWco | SiteIsolatedApp |  |
 | InstallableSite | SiteA* | SiteB | SiteAFoo | SiteABar | SiteWco |  |  |
 | Scope | SiteARoot |  |  |  |
 | Title | SiteA | SiteAUpdated |  |  |  |  |  |
@@ -34,3 +34,5 @@
 | Location | StartUrl | FileHandleUrlForTxt | FileHandleUrlForPng |  |  |  |  |
 | Number | One | Two |  |  |  |  |  |
 | FilesOptions | OneTextFile | MultipleTextFiles | OnePngFile | MultiplePngFiles | AllTestAndPngFiles |  |  |
+| AllowDenyOptions | Allow | Deny |
+| AskAgainOptions | AskAgain | Remember |
diff --git a/chrome/utility/services.cc b/chrome/utility/services.cc
index e27ddfac..97aaeac 100644
--- a/chrome/utility/services.cc
+++ b/chrome/utility/services.cc
@@ -303,7 +303,7 @@
 auto RunImeService(
     mojo::PendingReceiver<ash::ime::mojom::ImeService> receiver) {
   return std::make_unique<ash::ime::ImeService>(
-      std::move(receiver), ash::ime::ImeSharedLibImpl::GetInstance(),
+      std::move(receiver), ash::ime::ImeDecoderImpl::GetInstance(),
       std::make_unique<ash::ime::FieldTrialParamsRetrieverImpl>());
 }
 
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 71de70a..54d7b5f 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2075,6 +2075,9 @@
       <message name="IDS_PERSONALIZATION_APP_WALLPAPER_LABEL" desc="Label for wallpaper element in personalization hub">
         Wallpaper
       </message>
+      <message name="IDS_PERSONALIZATION_APP_DEFAULT_WALLPAPER" desc="Label for the device default wallpaper">
+        Default Wallpaper
+      </message>
       <message name="IDS_PERSONALIZATION_APP_BACK_BUTTON" desc="Aria label for the back button to return to a prior page">
         Back to <ph name="PAGE_NAME">$1<ex>Wallpaper</ex></ph>
       </message>
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_DEFAULT_WALLPAPER.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_DEFAULT_WALLPAPER.png.sha1
new file mode 100644
index 0000000..e4169309
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_DEFAULT_WALLPAPER.png.sha1
@@ -0,0 +1 @@
+b5ec9f9fbe71ca0fccc083668bc7651ec7cc36ea
\ No newline at end of file
diff --git a/chromeos/components/test/data/onc/policy/policy_allow_cellular_sim_lock.onc b/chromeos/components/test/data/onc/policy/policy_allow_cellular_sim_lock.onc
index 4740612..05e08862 100644
--- a/chromeos/components/test/data/onc/policy/policy_allow_cellular_sim_lock.onc
+++ b/chromeos/components/test/data/onc/policy/policy_allow_cellular_sim_lock.onc
@@ -1,8 +1,16 @@
 {
   "NetworkConfigurations": [
+    {
+      "GUID": "policy_cellular",
+      "Type": "Cellular",
+      "Name": "Managed cellular",
+      "Cellular": {
+        "SMDPAddress": "1$SMDP.GSMA.COM$00000-00000-00000-000-0001"
+      }
+    }
   ],
   "GlobalNetworkConfiguration": {
     "AllowCellularSimLock": false
   },
   "Type": "UnencryptedConfiguration"
-}
\ No newline at end of file
+}
diff --git a/chromeos/components/test/data/onc/policy/policy_allow_only_policy_cellular_networks.onc b/chromeos/components/test/data/onc/policy/policy_allow_only_policy_cellular_networks.onc
index 2a95100..3ceb493 100644
--- a/chromeos/components/test/data/onc/policy/policy_allow_only_policy_cellular_networks.onc
+++ b/chromeos/components/test/data/onc/policy/policy_allow_only_policy_cellular_networks.onc
@@ -1,8 +1,16 @@
 {
   "NetworkConfigurations": [
+    {
+      "GUID": "policy_cellular",
+      "Type": "Cellular",
+      "Name": "Managed cellular",
+      "Cellular": {
+        "SMDPAddress": "1$SMDP.GSMA.COM$00000-00000-00000-000-0001"
+      }
+    }
   ],
   "GlobalNetworkConfiguration": {
     "AllowOnlyPolicyCellularNetworks": true
   },
   "Type": "UnencryptedConfiguration"
-}
\ No newline at end of file
+}
diff --git a/chromeos/components/test/data/onc/policy/policy_empty_global_network_configuration.onc b/chromeos/components/test/data/onc/policy/policy_empty_global_network_configuration.onc
new file mode 100644
index 0000000..35d96164
--- /dev/null
+++ b/chromeos/components/test/data/onc/policy/policy_empty_global_network_configuration.onc
@@ -0,0 +1,6 @@
+{
+  "NetworkConfigurations": [ ],
+  "GlobalNetworkConfiguration": { },
+  "Type": "UnencryptedConfiguration"
+}
+
diff --git a/chromeos/components/test/data/onc/policy/policy_exhaustive_global_network_configuration.onc b/chromeos/components/test/data/onc/policy/policy_exhaustive_global_network_configuration.onc
new file mode 100644
index 0000000..6ded3612
--- /dev/null
+++ b/chromeos/components/test/data/onc/policy/policy_exhaustive_global_network_configuration.onc
@@ -0,0 +1,13 @@
+{
+  "NetworkConfigurations": [ ],
+  "GlobalNetworkConfiguration": {
+    "AllowOnlyPolicyCellularNetworks": true,
+    "AllowOnlyPolicyNetworksToAutoconnect": true,
+    "AllowOnlyPolicyNetworksToConnect": true,
+    "AllowOnlyPolicyNetworksToConnectIfAvailable": true,
+    "BlockedHexSSIDs": [ "blocked_ssid" ],
+    "DisableNetworkTypes": [ "VPN" ]
+  },
+  "Type": "UnencryptedConfiguration"
+}
+
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 5db8560..c39717fc 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -535,7 +535,7 @@
     policies_by_user_[userhash] = base::WrapUnique(policies);
   }
 
-  policies->global_network_config.MergeDictionary(&global_network_config);
+  policies->global_network_config = global_network_config.Clone();
 
   // Update prohibited technologies.
   if (prohibited_technologies_handler_) {
diff --git a/chromeos/network/managed_network_configuration_handler_unittest.cc b/chromeos/network/managed_network_configuration_handler_unittest.cc
index 8f492bd..ff2999e 100644
--- a/chromeos/network/managed_network_configuration_handler_unittest.cc
+++ b/chromeos/network/managed_network_configuration_handler_unittest.cc
@@ -1065,12 +1065,9 @@
   EXPECT_CALL(*network_state_handler_, UpdateBlockedCellularNetworks(true))
       .Times(2);
 
-  // Set 'AllowOnlyPolicyCellularNetworks' policy and another arbitrary cellular
-  // policy.
+  // Set 'AllowOnlyPolicyCellularNetworks' policy.
   SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
             "policy/policy_allow_only_policy_cellular_networks.onc");
-  SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
-            "policy/policy_cellular.onc");
   FastForwardProfileRefreshDelay();
   base::RunLoop().RunUntilIdle();
 
@@ -1087,12 +1084,9 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(ash::features::kSimLockPolicy);
 
-  // Set 'AllowCellularSimLock' policy and another arbitrary cellular
-  // policy.
+  // Set 'AllowCellularSimLock' policy.
   SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
             "policy/policy_allow_cellular_sim_lock.onc");
-  SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
-            "policy/policy_cellular.onc");
   base::RunLoop().RunUntilIdle();
 
   // Check ManagedNetworkConfigurationHandler policy accessors.
@@ -1153,6 +1147,54 @@
   EXPECT_EQ(blocked, managed_handler()->GetBlockedHexSSIDs());
 }
 
+TEST_F(ManagedNetworkConfigurationHandlerTest, WipeGlobalNetworkConfiguration) {
+  InitializeStandardProfiles();
+
+  // A user policy must be present to apply some global config, e.g. blocked
+  // SSIDs, even though they are actually given in device policy. It does not
+  // really matter which user policy is configured for this test.
+  SetPolicy(::onc::ONC_SOURCE_USER_POLICY, kUser1, "policy/policy_wifi1.onc");
+
+  // Step 1: Apply a device policy which sets all possible entries in
+  // GlobalNetworkConfiguration.
+  EXPECT_CALL(
+      *network_state_handler_,
+      UpdateBlockedWifiNetworks(/*only_managed=*/true, /*available_only=*/true,
+                                std::vector<std::string>({"blocked_ssid"})))
+      .Times(1);
+
+  SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+            "policy/policy_exhaustive_global_network_configuration.onc");
+  base::RunLoop().RunUntilIdle();
+
+  testing::Mock::VerifyAndClearExpectations(network_state_handler_.get());
+  EXPECT_TRUE(managed_handler()->AllowOnlyPolicyCellularNetworks());
+  EXPECT_TRUE(managed_handler()->AllowOnlyPolicyNetworksToAutoconnect());
+  EXPECT_TRUE(managed_handler()->AllowOnlyPolicyWiFiToConnect());
+  EXPECT_TRUE(managed_handler()->AllowOnlyPolicyWiFiToConnectIfAvailable());
+  EXPECT_THAT(managed_handler()->GetBlockedHexSSIDs(),
+              testing::ElementsAre("blocked_ssid"));
+  // TODO(b/219568567): Also test that DisableNetworkTypes are propagated to
+  // ProhibitedTechnologiesHandler.
+
+  // Step 2: Now apply a device policy with an empty GlobalNetworkConfiguration.
+  EXPECT_CALL(*network_state_handler_,
+              UpdateBlockedWifiNetworks(
+                  /*only_managed=*/false, /*available_only=*/false,
+                  std::vector<std::string>()))
+      .Times(1);
+  SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+            "policy/policy_empty_global_network_configuration.onc");
+  base::RunLoop().RunUntilIdle();
+
+  testing::Mock::VerifyAndClearExpectations(network_state_handler_.get());
+  EXPECT_FALSE(managed_handler()->AllowOnlyPolicyCellularNetworks());
+  EXPECT_FALSE(managed_handler()->AllowOnlyPolicyNetworksToAutoconnect());
+  EXPECT_FALSE(managed_handler()->AllowOnlyPolicyWiFiToConnect());
+  EXPECT_FALSE(managed_handler()->AllowOnlyPolicyWiFiToConnectIfAvailable());
+  EXPECT_THAT(managed_handler()->GetBlockedHexSSIDs(), testing::IsEmpty());
+}
+
 // Proxy settings can come from different sources. Proxy enforced by user policy
 // (provided by kProxy prefence) should have precedence over configurations set
 // by ONC policy. This test verifies that the order of preference is respected.
diff --git a/chromeos/services/federated/public/cpp/federated_example_util.cc b/chromeos/services/federated/public/cpp/federated_example_util.cc
index a46318bf..a4fa636 100644
--- a/chromeos/services/federated/public/cpp/federated_example_util.cc
+++ b/chromeos/services/federated/public/cpp/federated_example_util.cc
@@ -8,24 +8,15 @@
 namespace federated {
 
 mojom::ValueListPtr CreateInt64List(const std::vector<int64_t>& values) {
-  mojom::ValueListPtr value_list = mojom::ValueList::New();
-  value_list->set_int64_list(mojom::Int64List::New());
-  value_list->get_int64_list()->value = values;
-  return value_list;
+  return mojom::ValueList::NewInt64List(mojom::Int64List::New(values));
 }
 
 mojom::ValueListPtr CreateFloatList(const std::vector<double>& values) {
-  mojom::ValueListPtr value_list = mojom::ValueList::New();
-  value_list->set_float_list(mojom::FloatList::New());
-  value_list->get_float_list()->value = values;
-  return value_list;
+  return mojom::ValueList::NewFloatList(mojom::FloatList::New(values));
 }
 
 mojom::ValueListPtr CreateStringList(const std::vector<std::string>& values) {
-  mojom::ValueListPtr value_list = mojom::ValueList::New();
-  value_list->set_string_list(mojom::StringList::New());
-  value_list->get_string_list()->value = values;
-  return value_list;
+  return mojom::ValueList::NewStringList(mojom::StringList::New(values));
 }
 
 }  // namespace federated
diff --git a/components/autofill_assistant/android/BUILD.gn b/components/autofill_assistant/android/BUILD.gn
index c9a0152..293abf2ed 100644
--- a/components/autofill_assistant/android/BUILD.gn
+++ b/components/autofill_assistant/android/BUILD.gn
@@ -36,16 +36,15 @@
     "//components/version_info/android:version_constants_java",
     "//content/public/android:content_java",
     "//mojo/public/java:bindings_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_android_support_support_annotations_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_collection_collection_java",
     "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_gridlayout_gridlayout_java",
-    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
     "//third_party/androidx:androidx_lifecycle_lifecycle_runtime_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
@@ -335,7 +334,6 @@
   deps = [
     ":animated_poodle_resources",
     "//base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//ui/android:ui_java",
   ]
@@ -359,7 +357,6 @@
   deps = [
     ":animated_poodle_resources",
     "//base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//ui/android:ui_java",
   ]
diff --git a/components/browser_ui/modaldialog/android/BUILD.gn b/components/browser_ui/modaldialog/android/BUILD.gn
index 3d6dd2e..7ec923b 100644
--- a/components/browser_ui/modaldialog/android/BUILD.gn
+++ b/components/browser_ui/modaldialog/android/BUILD.gn
@@ -18,8 +18,8 @@
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/widget/android:java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_core_core_java",
     "//ui/android:ui_java",
   ]
   resources_package = "org.chromium.components.browser_ui.modaldialog"
@@ -63,7 +63,6 @@
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/hamcrest:hamcrest_java",
     "//third_party/junit:junit",
diff --git a/components/browser_ui/settings/android/BUILD.gn b/components/browser_ui/settings/android/BUILD.gn
index ccc442a..967a91bc 100644
--- a/components/browser_ui/settings/android/BUILD.gn
+++ b/components/browser_ui/settings/android/BUILD.gn
@@ -29,9 +29,11 @@
     ":java_resources",
     "//base:base_java",
     "//components/browser_ui/util/android:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_preference_preference_java",
     "//ui/android:ui_java",
   ]
diff --git a/components/browser_ui/site_settings/android/BUILD.gn b/components/browser_ui/site_settings/android/BUILD.gn
index 7967d1d..c7fe615c 100644
--- a/components/browser_ui/site_settings/android/BUILD.gn
+++ b/components/browser_ui/site_settings/android/BUILD.gn
@@ -87,9 +87,14 @@
     "//components/user_prefs/android:java",
     "//content/public/android:content_java",
     "//services/device/public/java:device_feature_list_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_preference_preference_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
     "//ui/android:ui_full_java",
     "//ui/android:ui_utils_java",
     "//url:gurl_java",
@@ -169,6 +174,5 @@
     "//components/browser_ui/strings/android:browser_ui_strings_grd",
     "//components/browser_ui/styles/android:java_resources",
     "//components/permissions/android:java_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
   ]
 }
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index 4793c03..960c2dbc 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -13,9 +13,9 @@
   deps = [
     ":java_resources",
     "//base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
   ]
   resources_package = "org.chromium.components.browser_ui.styles"
diff --git a/components/browser_ui/widget/android/BUILD.gn b/components/browser_ui/widget/android/BUILD.gn
index 684c407b..a0e8693 100644
--- a/components/browser_ui/widget/android/BUILD.gn
+++ b/components/browser_ui/widget/android/BUILD.gn
@@ -121,11 +121,16 @@
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/util/android:java",
     "//components/embedder_support/android:util_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:material_design_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_coordinatorlayout_coordinatorlayout_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_interpolator_interpolator_java",
+    "//third_party/androidx:androidx_recyclerview_recyclerview_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
     "//ui/android:ui_java",
     "//url:gurl_java",
   ]
@@ -332,7 +337,6 @@
     "//third_party/android_support_test_runner:rules_java",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_test_runner_java",
diff --git a/components/crash/core/app/crashpad.cc b/components/crash/core/app/crashpad.cc
index 6da6be4..1ff1011 100644
--- a/components/crash/core/app/crashpad.cc
+++ b/components/crash/core/app/crashpad.cc
@@ -209,7 +209,7 @@
     g_database =
         crashpad::CrashReportDatabase::Initialize(database_path).release();
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_IOS)
     CrashReporterClient* crash_reporter_client = GetCrashReporterClient();
     SetUploadConsent(crash_reporter_client->GetCollectStatsConsent());
 #endif
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index b0dbdbb6..5a0dfe94d 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -434,7 +434,16 @@
     "sample/res/values/strings.xml",
   ]
   android_manifest = "sample/AndroidManifest.xml"
-  deps = [ "//third_party/android_deps:android_support_v7_appcompat_java" ]
+  deps = [
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_drawerlayout_drawerlayout_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/androidx:androidx_interpolator_interpolator_java",
+    "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
+  ]
 }
 
 android_library("cronet_sample_apk_java") {
@@ -448,8 +457,9 @@
     ":cronet_sample_apk_resources",
     ":package_api_java",
     ":package_impl_native_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_java",
   ]
 }
 
@@ -786,7 +796,6 @@
       "//third_party/junit",
     ]
 
-    enable_multidex = false
     if (!is_java_debug) {
       proguard_enabled = true
       proguard_configs = [ "sample/javatests/proguard.cfg" ]
diff --git a/components/exo/wayland/clients/client_base.cc b/components/exo/wayland/clients/client_base.cc
index 637026b..7b97fe0 100644
--- a/components/exo/wayland/clients/client_base.cc
+++ b/components/exo/wayland/clients/client_base.cc
@@ -28,6 +28,7 @@
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/shared_memory_mapper.h"
 #include "base/memory/unsafe_shared_memory_region.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/string_number_conversions.h"
@@ -123,9 +124,11 @@
 class MemfdMemoryMapping : public base::SharedMemoryMapping {
  public:
   MemfdMemoryMapping(base::span<uint8_t> mapped_span)
-      : base::SharedMemoryMapping(mapped_span,
-                                  mapped_span.size(),
-                                  base::UnguessableToken::Create()) {}
+      : base::SharedMemoryMapping(
+            mapped_span,
+            mapped_span.size(),
+            base::UnguessableToken::Create(),
+            base::SharedMemoryMapper::GetDefaultInstance()) {}
 };
 
 void RegistryHandler(void* data,
diff --git a/components/external_intents/android/BUILD.gn b/components/external_intents/android/BUILD.gn
index 44c5877..c5a8bd0 100644
--- a/components/external_intents/android/BUILD.gn
+++ b/components/external_intents/android/BUILD.gn
@@ -7,7 +7,6 @@
 android_library("java") {
   sources = [
     "java/src/org/chromium/components/external_intents/AuthenticatorNavigationInterceptor.java",
-    "java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java",
     "java/src/org/chromium/components/external_intents/ExternalIntentsSwitches.java",
     "java/src/org/chromium/components/external_intents/ExternalNavigationDelegate.java",
     "java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java",
@@ -45,18 +44,11 @@
 }
 
 generate_jni("jni_headers") {
-  sources = [
-    "java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java",
-    "java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java",
-  ]
+  sources = [ "java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java" ]
 }
 
 static_library("android") {
-  sources = [
-    "external_intents_features.cc",
-    "external_intents_features.h",
-    "intercept_navigation_delegate_impl.cc",
-  ]
+  sources = [ "intercept_navigation_delegate_impl.cc" ]
 
   deps = [
     ":jni_headers",
diff --git a/components/external_intents/android/external_intents_features.cc b/components/external_intents/android/external_intents_features.cc
deleted file mode 100644
index dca311e..0000000
--- a/components/external_intents/android/external_intents_features.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/external_intents/android/external_intents_features.h"
-
-#include <jni.h>
-#include <stddef.h>
-#include <string>
-
-#include "base/android/jni_string.h"
-#include "base/notreached.h"
-#include "components/external_intents/android/jni_headers/ExternalIntentsFeatures_jni.h"
-
-namespace external_intents {
-
-namespace {
-
-// Array of features exposed through the Java ExternalIntentsFeatures API.
-const base::Feature* kFeaturesExposedToJava[] = {
-    &kIntentBlockExternalFormRedirectsNoGesture,
-};
-
-}  // namespace
-
-// Alphabetical:
-const base::Feature kIntentBlockExternalFormRedirectsNoGesture{
-    "IntentBlockExternalFormRedirectsNoGesture",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
-static jlong JNI_ExternalIntentsFeatures_GetFeature(JNIEnv* env, jint ordinal) {
-  return reinterpret_cast<jlong>(kFeaturesExposedToJava[ordinal]);
-}
-
-}  // namespace external_intents
diff --git a/components/external_intents/android/external_intents_features.h b/components/external_intents/android/external_intents_features.h
deleted file mode 100644
index e6c0d54..0000000
--- a/components/external_intents/android/external_intents_features.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
-#define COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
-
-#include "base/feature_list.h"
-
-namespace external_intents {
-
-// Alphabetical:
-extern const base::Feature kIntentBlockExternalFormRedirectsNoGesture;
-
-}  // namespace external_intents
-
-#endif  // COMPONENTS_EXTERNAL_INTENTS_ANDROID_EXTERNAL_INTENTS_FEATURES_H_
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
deleted file mode 100644
index 2e1f1a3b..0000000
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalIntentsFeatures.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.external_intents;
-
-import org.chromium.base.Features;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.base.annotations.NativeMethods;
-
-/**
- * Java accessor for base/feature_list.h state.
- *
- * This class provides methods to access values of feature flags registered in
- * |kFeaturesExposedToJava| in components/external_intents/android/external_intents_feature_list.cc.
- *
- */
-@JNINamespace("external_intents")
-public class ExternalIntentsFeatures extends Features {
-    public static final String INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE_NAME =
-            "IntentBlockExternalFormRedirectsNoGesture";
-
-    public static final ExternalIntentsFeatures INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE =
-            new ExternalIntentsFeatures(0, INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE_NAME);
-
-    private final int mOrdinal;
-
-    private ExternalIntentsFeatures(int ordinal, String name) {
-        super(name);
-        mOrdinal = ordinal;
-    }
-
-    @Override
-    protected long getFeaturePointer() {
-        return ExternalIntentsFeaturesJni.get().getFeature(mOrdinal);
-    }
-
-    @NativeMethods
-    interface Natives {
-        long getFeature(int ordinal);
-    }
-}
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
index a962249..8b4ef37 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java
@@ -657,12 +657,6 @@
         return false;
     }
 
-    /** Wrapper of check against the feature to support overriding for testing. */
-    @VisibleForTesting
-    boolean blockExternalFormRedirectsWithoutGesture() {
-        return ExternalIntentsFeatures.INTENT_BLOCK_EXTERNAL_FORM_REDIRECT_NO_GESTURE.isEnabled();
-    }
-
     /**
      * http://crbug.com/149218: We want to show the intent picker for ordinary links, providing
      * the link is not an incoming intent from another application, unless it's a redirect.
@@ -708,19 +702,6 @@
             return false;
         }
 
-        // http://crbug.com/839751: Require user gestures for form submits to external
-        //                          protocols.
-        // TODO(tedchoc): Turn this on by default once we verify this change does
-        //                not break the world.
-        if (isRedirectFromFormSubmit && !incomingIntentRedirect && !params.hasUserGesture()
-                && blockExternalFormRedirectsWithoutGesture()) {
-            if (DEBUG) {
-                Log.i(TAG,
-                        "Incoming form intent attempting to redirect without "
-                                + "user gesture");
-            }
-            return false;
-        }
         // http://crbug/331571 : Do not override a navigation started from user typing.
         if (params.getRedirectHandler() != null
                 && params.getRedirectHandler().isNavigationFromUserTyping()) {
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
index aaca6c0..391ac6f 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
@@ -4,8 +4,11 @@
 
 package org.chromium.components.external_intents;
 
+import android.util.Pair;
+
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
@@ -35,7 +38,7 @@
 public class InterceptNavigationDelegateImpl extends InterceptNavigationDelegate {
     private final AuthenticatorNavigationInterceptor mAuthenticatorHelper;
     private InterceptNavigationDelegateClient mClient;
-    private OverrideUrlLoadingResult mLastOverrideUrlLoadingResultForTesting;
+    private Callback<Pair<GURL, OverrideUrlLoadingResult>> mResultCallbackForTesting;
     private WebContents mWebContents;
     private ExternalNavigationHandler mExternalNavHandler;
 
@@ -91,21 +94,13 @@
                                                   .setIsMainFrame(true)
                                                   .build();
         OverrideUrlLoadingResult result = mExternalNavHandler.shouldOverrideUrlLoading(params);
-        mLastOverrideUrlLoadingResultForTesting = result;
+        if (mResultCallbackForTesting != null) {
+            mResultCallbackForTesting.onResult(Pair.create(url, result));
+        }
         return result.getResultType()
                 != ExternalNavigationHandler.OverrideUrlLoadingResultType.NO_OVERRIDE;
     }
 
-    @VisibleForTesting
-    public void clearLastOverrideUrlLoadingResultForTests() {
-        mLastOverrideUrlLoadingResultForTesting = null;
-    }
-
-    @VisibleForTesting
-    public OverrideUrlLoadingResult getLastOverrideUrlLoadingResultForTests() {
-        return mLastOverrideUrlLoadingResultForTesting;
-    }
-
     @Override
     public boolean shouldIgnoreNavigation(NavigationHandle navigationHandle, GURL escapedUrl) {
         mClient.onNavigationStarted(navigationHandle);
@@ -146,7 +141,9 @@
                 navigationHandle, redirectHandler, shouldCloseTab, escapedUrl)
                                                   .build();
         OverrideUrlLoadingResult result = mExternalNavHandler.shouldOverrideUrlLoading(params);
-        mLastOverrideUrlLoadingResultForTesting = result;
+        if (mResultCallbackForTesting != null) {
+            mResultCallbackForTesting.onResult(Pair.create(url, result));
+        }
 
         mClient.onDecisionReachedForNavigation(navigationHandle, result);
 
@@ -325,6 +322,12 @@
                 ContextUtils.getApplicationContext().getString(resId, url.getSpec()));
     }
 
+    @VisibleForTesting
+    public void setResultCallbackForTesting(
+            Callback<Pair<GURL, OverrideUrlLoadingResult>> callback) {
+        mResultCallbackForTesting = callback;
+    }
+
     @NativeMethods
     interface Natives {
         void associateWithWebContents(
diff --git a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
index 0fb8f96e..1eda2ba0 100644
--- a/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
+++ b/components/external_intents/android/javatests/src/org/chromium/components/external_intents/ExternalNavigationHandlerTest.java
@@ -329,12 +329,14 @@
                 .withPageTransition(PageTransition.FORM_SUBMIT)
                 .withIsRedirect(true)
                 .withHasUserGesture(false)
-                .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
         checkUrl("http://youtube.com://")
                 .withPageTransition(PageTransition.FORM_SUBMIT)
                 .withIsRedirect(true)
                 .withHasUserGesture(false)
-                .expecting(OverrideUrlLoadingResultType.NO_OVERRIDE, IGNORE);
+                .expecting(OverrideUrlLoadingResultType.OVERRIDE_WITH_EXTERNAL_INTENT,
+                        START_OTHER_ACTIVITY);
     }
 
     @Test
@@ -2590,11 +2592,6 @@
         }
 
         @Override
-        public boolean blockExternalFormRedirectsWithoutGesture() {
-            return true;
-        }
-
-        @Override
         protected boolean canLaunchIncognitoIntent(Intent intent, Context context) {
             mStartActivityInIncognitoIntent = intent;
             mStartIncognitoIntentCalled = true;
diff --git a/components/history_clusters/core/history_clusters_debug_jsons.cc b/components/history_clusters/core/history_clusters_debug_jsons.cc
index c3e6ba6d..51848a5 100644
--- a/components/history_clusters/core/history_clusters_debug_jsons.cc
+++ b/components/history_clusters/core/history_clusters_debug_jsons.cc
@@ -87,6 +87,11 @@
         debug_entities.Append(std::move(debug_entity));
       }
       debug_visit.SetKey("entities", std::move(debug_entities));
+      if (!visit.annotated_visit.content_annotations.search_terms.empty()) {
+        debug_visit.SetStringKey(
+            "search_terms",
+            visit.annotated_visit.content_annotations.search_terms);
+      }
       debug_visit.SetDoubleKey("site_engagement_score", visit.engagement_score);
 
       base::ListValue debug_duplicate_visits;
diff --git a/components/media_router/browser/android/BUILD.gn b/components/media_router/browser/android/BUILD.gn
index 3a028f66..31f7cda 100644
--- a/components/media_router/browser/android/BUILD.gn
+++ b/components/media_router/browser/android/BUILD.gn
@@ -19,12 +19,11 @@
     "//components/browser_ui/media/android:java",
     "//content/public/android:content_java",
     "//services/media_session/public/cpp/android:media_session_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_media:android_media_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_collection_collection_java",
-    "//third_party/androidx:androidx_core_core_java",
     "//third_party/androidx:androidx_fragment_fragment_java",
+    "//third_party/androidx:androidx_media_media_java",
     "//third_party/androidx:androidx_mediarouter_mediarouter_java",
   ]
   sources = [
diff --git a/components/media_router/test/android/cast_emulator/BUILD.gn b/components/media_router/test/android/cast_emulator/BUILD.gn
index 14be15c..62f60b3e 100644
--- a/components/media_router/test/android/cast_emulator/BUILD.gn
+++ b/components/media_router/test/android/cast_emulator/BUILD.gn
@@ -22,7 +22,6 @@
   deps = [
     "$google_play_services_package:google_play_services_cast_java",
     "//base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_mediarouter_mediarouter_java",
   ]
 }
diff --git a/components/page_info/android/BUILD.gn b/components/page_info/android/BUILD.gn
index c5e0d7f..31fe42d2c 100644
--- a/components/page_info/android/BUILD.gn
+++ b/components/page_info/android/BUILD.gn
@@ -110,8 +110,11 @@
     "//components/url_formatter/android:url_formatter_java",
     "//content/public/android:content_java",
     "//services/device/public/java:device_feature_list_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_core_core_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
     "//third_party/androidx:androidx_preference_preference_java",
     "//ui/android:ui_java",
     "//url:gurl_java",
diff --git a/components/policy/core/common/features.cc b/components/policy/core/common/features.cc
index e1cfb4ed..02518d33 100644
--- a/components/policy/core/common/features.cc
+++ b/components/policy/core/common/features.cc
@@ -22,9 +22,6 @@
 const base::Feature kPasswordBreachEventReporting{
     "PasswordBreachEventReporting", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kChromeManagementPageAndroid{
-    "ChromeManagementPageAndroid", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kEnableUserCloudSigninRestrictionPolicyFetcher{
     "UserCloudSigninRestrictionPolicyFetcher",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/policy/core/common/features.h b/components/policy/core/common/features.h
index 6ce74123..accdfb8 100644
--- a/components/policy/core/common/features.h
+++ b/components/policy/core/common/features.h
@@ -14,9 +14,6 @@
 namespace policy {
 namespace features {
 
-// Enable chrome://management page on Android.
-POLICY_EXPORT extern const base::Feature kChromeManagementPageAndroid;
-
 // Enable force installed Chrome apps policy migration.
 POLICY_EXPORT extern const base::Feature kDefaultChromeAppsMigration;
 
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc
index 02049e1..d242fee 100644
--- a/components/printing/test/print_render_frame_helper_browsertest.cc
+++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -508,18 +508,6 @@
   mojo::AssociatedReceiver<mojom::PrintManagerHost> receiver_{this};
 };
 
-class ScopedGenerateTaggedPDF {
- public:
-  ScopedGenerateTaggedPDF() {
-    PrintTestContentRendererClient::SetGenerateTaggedPDFs(true);
-  }
-  ScopedGenerateTaggedPDF(const ScopedGenerateTaggedPDF&) = delete;
-  ScopedGenerateTaggedPDF& operator=(const ScopedGenerateTaggedPDF&) = delete;
-  ~ScopedGenerateTaggedPDF() {
-    PrintTestContentRendererClient::SetGenerateTaggedPDFs(false);
-  }
-};
-
 }  // namespace
 
 class PrintRenderFrameHelperTestBase : public content::RenderViewTest {
@@ -534,7 +522,7 @@
  protected:
   // content::RenderViewTest:
   content::ContentRendererClient* CreateContentRendererClient() override {
-    return new PrintTestContentRendererClient();
+    return new PrintTestContentRendererClient(/*generate_tagged_pdfs=*/false);
   }
 
   void SetUp() override {
@@ -1801,44 +1789,28 @@
   ExpectOneBeforeOneAfterPrintEvent();
 }
 
-TEST_F(PrintRenderFrameHelperPreviewTest,
-       PrintPreviewRerenderWithNoTaggedPDFGeneration) {
-  LoadHTML(kHelloWorldHTML);
+class PrintRenderFrameHelperTaggedPreviewTest
+    : public PrintRenderFrameHelperPreviewTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  PrintRenderFrameHelperTaggedPreviewTest() = default;
+  PrintRenderFrameHelperTaggedPreviewTest(
+      const PrintRenderFrameHelperTaggedPreviewTest&) = delete;
+  PrintRenderFrameHelperTaggedPreviewTest& operator=(
+      const PrintRenderFrameHelperTaggedPreviewTest&) = delete;
+  ~PrintRenderFrameHelperTaggedPreviewTest() override = default;
 
-  OnPrintPreview();
+  // content::RenderViewTest:
+  content::ContentRendererClient* CreateContentRendererClient() override {
+    return new PrintTestContentRendererClient(GenerateTaggedPDFs());
+  }
 
-  EXPECT_EQ(0u, preview_ui()->print_preview_pages_remaining());
-  VerifyDidPreviewPage(true, 0);
-  VerifyPreviewPageCount(1);
-  VerifyDefaultPageLayout(540, 720, 36, 36, 36, 36, false);
-  VerifyPrintPreviewCancelled(false);
-  VerifyPrintPreviewFailed(false);
-  VerifyPrintPreviewGenerated(true);
-  VerifyPagesPrinted(false);
-#if BUILDFLAG(ENABLE_TAGGED_PDF)
-  EXPECT_EQ(0, print_manager()->accessibility_tree_set_count());
-#endif
+  bool GenerateTaggedPDFs() const { return GetParam(); }
+  bool ExpectsSetAccessibilityTreeCalls() const { return GenerateTaggedPDFs(); }
+};
 
-  print_settings().SetIntKey(kSettingScaleFactor, 200);
-  OnPrintPreviewRerender();
-
-  EXPECT_EQ(0u, preview_ui()->print_preview_pages_remaining());
-  VerifyDidPreviewPage(true, 0);
-  VerifyPreviewPageCount(1);
-  VerifyDefaultPageLayout(540, 720, 36, 36, 36, 36, false);
-  VerifyPrintPreviewCancelled(false);
-  VerifyPrintPreviewFailed(false);
-  VerifyPrintPreviewGenerated(true);
-  VerifyPagesPrinted(false);
-#if BUILDFLAG(ENABLE_TAGGED_PDF)
-  EXPECT_EQ(0, print_manager()->accessibility_tree_set_count());
-#endif
-}
-
-TEST_F(PrintRenderFrameHelperPreviewTest,
+TEST_P(PrintRenderFrameHelperTaggedPreviewTest,
        PrintPreviewRerenderGeneratesTaggedPDF) {
-  ScopedGenerateTaggedPDF generate_tagged_pdf;
-
   LoadHTML(kHelloWorldHTML);
 
   OnPrintPreview();
@@ -1852,7 +1824,10 @@
   VerifyPrintPreviewGenerated(true);
   VerifyPagesPrinted(false);
 #if BUILDFLAG(ENABLE_TAGGED_PDF)
-  EXPECT_EQ(1, print_manager()->accessibility_tree_set_count());
+  int expected_accessibility_tree_set_count =
+      ExpectsSetAccessibilityTreeCalls() ? 1 : 0;
+  EXPECT_EQ(expected_accessibility_tree_set_count,
+            print_manager()->accessibility_tree_set_count());
 #endif
 
   print_settings().SetIntKey(kSettingScaleFactor, 200);
@@ -1867,10 +1842,17 @@
   VerifyPrintPreviewGenerated(true);
   VerifyPagesPrinted(false);
 #if BUILDFLAG(ENABLE_TAGGED_PDF)
-  EXPECT_EQ(2, print_manager()->accessibility_tree_set_count());
+  expected_accessibility_tree_set_count =
+      ExpectsSetAccessibilityTreeCalls() ? 2 : 0;
+  EXPECT_EQ(expected_accessibility_tree_set_count,
+            print_manager()->accessibility_tree_set_count());
 #endif
 }
 
+INSTANTIATE_TEST_SUITE_P(All,
+                         PrintRenderFrameHelperTaggedPreviewTest,
+                         testing::Bool());
+
 #endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
 
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/printing/test/print_test_content_renderer_client.cc b/components/printing/test/print_test_content_renderer_client.cc
index f6afe16..f53a11a 100644
--- a/components/printing/test/print_test_content_renderer_client.cc
+++ b/components/printing/test/print_test_content_renderer_client.cc
@@ -14,10 +14,10 @@
 
 namespace {
 
-bool g_generate_tagged_pdfs = false;
-
 class PrintRenderFrameHelperDelegate : public PrintRenderFrameHelper::Delegate {
  public:
+  explicit PrintRenderFrameHelperDelegate(bool generate_tagged_pdfs)
+      : generate_tagged_pdfs_(generate_tagged_pdfs) {}
   ~PrintRenderFrameHelperDelegate() override = default;
 
   blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override {
@@ -30,25 +30,26 @@
     return false;
 #endif
   }
-  bool ShouldGenerateTaggedPDF() override { return g_generate_tagged_pdfs; }
+  bool ShouldGenerateTaggedPDF() override { return generate_tagged_pdfs_; }
   bool OverridePrint(blink::WebLocalFrame* frame) override { return false; }
+
+ private:
+  const bool generate_tagged_pdfs_;
 };
 
 }  // namespace
 
-PrintTestContentRendererClient::PrintTestContentRendererClient() = default;
+PrintTestContentRendererClient::PrintTestContentRendererClient(
+    bool generate_tagged_pdfs)
+    : generate_tagged_pdfs_(generate_tagged_pdfs) {}
 
 PrintTestContentRendererClient::~PrintTestContentRendererClient() = default;
 
-// static
-void PrintTestContentRendererClient::SetGenerateTaggedPDFs(bool generate) {
-  g_generate_tagged_pdfs = generate;
-}
-
 void PrintTestContentRendererClient::RenderFrameCreated(
     content::RenderFrame* render_frame) {
   new PrintRenderFrameHelper(
-      render_frame, std::make_unique<PrintRenderFrameHelperDelegate>());
+      render_frame,
+      std::make_unique<PrintRenderFrameHelperDelegate>(generate_tagged_pdfs_));
 }
 
 }  // namespace printing
diff --git a/components/printing/test/print_test_content_renderer_client.h b/components/printing/test/print_test_content_renderer_client.h
index e1ed43a..e040f3f3 100644
--- a/components/printing/test/print_test_content_renderer_client.h
+++ b/components/printing/test/print_test_content_renderer_client.h
@@ -11,13 +11,14 @@
 
 class PrintTestContentRendererClient : public content::ContentRendererClient {
  public:
-  PrintTestContentRendererClient();
+  explicit PrintTestContentRendererClient(bool generate_tagged_pdfs);
   ~PrintTestContentRendererClient() override;
 
-  static void SetGenerateTaggedPDFs(bool generate);
-
   // content::ContentRendererClient:
   void RenderFrameCreated(content::RenderFrame* render_frame) override;
+
+ private:
+  const bool generate_tagged_pdfs_;
 };
 
 }  // namespace printing
diff --git a/components/search_engines/template_url.cc b/components/search_engines/template_url.cc
index a81c8ac..0532d6c 100644
--- a/components/search_engines/template_url.cc
+++ b/components/search_engines/template_url.cc
@@ -1446,6 +1446,8 @@
         engine->created_from_play_api(),
         // Favor prepopulated engines over other auto-generated engines.
         engine->prepopulate_id() > 0,
+        // Favor starter pack engines over other auto-generated engines.
+        engine->starter_pack_id() > 0,
         // Favor engines derived from OpenSearch descriptions over
         // autogenerated engines heuristically generated from searchable forms.
         engine->originating_url().is_valid(),
diff --git a/components/search_engines/template_url_data.cc b/components/search_engines/template_url_data.cc
index 96241fa..f3c8a57 100644
--- a/components/search_engines/template_url_data.cc
+++ b/components/search_engines/template_url_data.cc
@@ -16,18 +16,24 @@
 
 namespace {
 
-// Returns a GUID used for sync, which is random except for prepopulated search
+// Returns a GUID used for sync, which is random except for built-in search
 // engines. The latter benefit from using a deterministic GUID, to make sure
 // sync doesn't incur in duplicates for prepopulated engines.
-std::string GenerateGUID(int prepopulate_id) {
+std::string GenerateGUID(int prepopulate_id, int starter_pack_id) {
+  // We compute a GUID deterministically given |prepopulate_id| or
+  // |starter_pack_id|, using an arbitrary base GUID.
+  std::string guid;
   // IDs above 1000 are reserved for distribution custom engines.
-  if (prepopulate_id <= 0 || prepopulate_id > 1000)
-    return base::GenerateGUID();
+  if (prepopulate_id > 0 && prepopulate_id <= 1000) {
+    guid = base::StringPrintf("485bf7d3-0215-45af-87dc-538868%06d",
+                              prepopulate_id);
+  } else if (starter_pack_id > 0) {
+    guid = base::StringPrintf("ec205736-edd7-4022-a9a3-b431fc%06d",
+                              starter_pack_id);
+  } else {
+    guid = base::GenerateGUID();
+  }
 
-  // We compute a GUID deterministically given |prepopulate_id|, using an
-  // arbitrary base GUID.
-  std::string guid =
-      base::StringPrintf("485bf7d3-0215-45af-87dc-538868%06d", prepopulate_id);
   DCHECK(base::IsValidGUID(guid));
   return guid;
 }
@@ -91,7 +97,7 @@
       created_from_play_api(false),
       usage_count(0),
       prepopulate_id(prepopulate_id),
-      sync_guid(GenerateGUID(prepopulate_id)),
+      sync_guid(GenerateGUID(prepopulate_id, 0)),
       preconnect_to_search_url(preconnect_to_search_url),
       prefetch_likely_navigations(prefetch_likely_navigations) {
   SetShortName(name);
@@ -135,7 +141,7 @@
 }
 
 void TemplateURLData::GenerateSyncGUID() {
-  sync_guid = GenerateGUID(prepopulate_id);
+  sync_guid = GenerateGUID(prepopulate_id, starter_pack_id);
 }
 
 size_t TemplateURLData::EstimateMemoryUsage() const {
diff --git a/components/search_engines/template_url_data.h b/components/search_engines/template_url_data.h
index 5b76e09..069cfd8 100644
--- a/components/search_engines/template_url_data.h
+++ b/components/search_engines/template_url_data.h
@@ -65,8 +65,9 @@
   const std::string& url() const { return url_; }
 
   // Recomputes |sync_guid| using the same logic as in the constructor. This
-  // means a random GUID is generated, except for prepopulated search engines,
-  // which generate GUIDs deterministically based on |prepopulate_id|.
+  // means a random GUID is generated, except for built-in search engines,
+  // which generate GUIDs deterministically based on |prepopulate_id| or
+  // |starter_pack_id|.
   void GenerateSyncGUID();
 
   // Estimates dynamic memory usage.
diff --git a/components/search_engines/template_url_data_util.cc b/components/search_engines/template_url_data_util.cc
index 18c2316..39cded9 100644
--- a/components/search_engines/template_url_data_util.cc
+++ b/components/search_engines/template_url_data_util.cc
@@ -409,6 +409,7 @@
   turl->SetURL(engine.search_url);
   turl->favicon_url = GURL(ToStringPiece(engine.favicon_url));
   turl->starter_pack_id = engine.id;
+  turl->GenerateSyncGUID();
   turl->is_active = TemplateURLData::ActiveStatus::kTrue;
 
   return turl;
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 5970294e..1c5a390 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -1400,6 +1400,7 @@
   for (size_t i = 0; i < turl.alternate_urls().size(); ++i)
     se_specifics->add_alternate_urls(turl.alternate_urls()[i]);
   se_specifics->set_is_active(ActiveStatusToSync(turl.is_active()));
+  se_specifics->set_starter_pack_id(turl.starter_pack_id());
 
   return syncer::SyncData::CreateLocalData(se_specifics->sync_guid(),
                                            se_specifics->keyword(),
@@ -1472,6 +1473,7 @@
   for (int i = 0; i < specifics.alternate_urls_size(); ++i)
     data.alternate_urls.push_back(specifics.alternate_urls(i));
   data.is_active = ActiveStatusFromSync(specifics.is_active());
+  data.starter_pack_id = specifics.starter_pack_id();
 
   std::unique_ptr<TemplateURL> turl(new TemplateURL(data));
   // If this TemplateURL matches a built-in prepopulated template URL, it's
@@ -2092,16 +2094,19 @@
     local_data->erase(guid);
   }
 
-  // Try to take over a local prepopulated entry, assuming we haven't already
-  // run into a keyword conflict.
-  if (local_duplicates.empty() && sync_turl->prepopulate_id() != 0) {
+  // Try to take over a local built-in (prepopulated or starter pack) entry,
+  // assuming we haven't already run into a keyword conflict.
+  if (local_duplicates.empty() &&
+      (sync_turl->prepopulate_id() != 0 || sync_turl->starter_pack_id() != 0)) {
     // Check for a turl with a conflicting prepopulate_id. This detects the case
     // where the user changes a prepopulated engine's keyword on one client,
     // then begins syncing on another client.  We want to reflect this keyword
     // change to that prepopulated URL on other clients instead of assuming that
     // the modified TemplateURL is a new entity.
-    TemplateURL* conflicting_prepopulated_turl =
-        FindPrepopulatedTemplateURL(sync_turl->prepopulate_id());
+    TemplateURL* conflicting_built_in_turl =
+        (sync_turl->prepopulate_id() != 0)
+            ? FindPrepopulatedTemplateURL(sync_turl->prepopulate_id())
+            : FindStarterPackTemplateURL(sync_turl->starter_pack_id());
 
     // If we found a conflict, and the sync entity is better, apply the remote
     // changes locally. We consider |sync_turl| better if it's been modified
@@ -2113,12 +2118,12 @@
     // be applied to other clients.
     // If we can't safely replace the local entry with the synced one, or merge
     // the relevant changes in, we give up and leave both intact.
-    if (conflicting_prepopulated_turl &&
-        !IsFromSync(conflicting_prepopulated_turl, sync_data) &&
+    if (conflicting_built_in_turl &&
+        !IsFromSync(conflicting_built_in_turl, sync_data) &&
         sync_turl->IsBetterThanEngineWithConflictingKeyword(
-            conflicting_prepopulated_turl)) {
-      std::string guid = conflicting_prepopulated_turl->sync_guid();
-      if (conflicting_prepopulated_turl == default_search_provider_) {
+            conflicting_built_in_turl)) {
+      std::string guid = conflicting_built_in_turl->sync_guid();
+      if (conflicting_built_in_turl == default_search_provider_) {
         bool pref_matched =
             prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID) ==
             default_search_provider_->sync_guid();
@@ -2136,7 +2141,7 @@
 
         should_add_sync_turl = false;
       } else {
-        Remove(conflicting_prepopulated_turl);
+        Remove(conflicting_built_in_turl);
       }
       // Remove the local data so it isn't written to sync.
       local_data->erase(guid);
@@ -2242,6 +2247,16 @@
   return nullptr;
 }
 
+TemplateURL* TemplateURLService::FindStarterPackTemplateURL(
+    int starter_pack_id) {
+  DCHECK(starter_pack_id);
+  for (const auto& turl : template_urls_) {
+    if (turl->starter_pack_id() == starter_pack_id)
+      return turl.get();
+  }
+  return nullptr;
+}
+
 TemplateURL* TemplateURLService::FindTemplateURLForExtension(
     const std::string& extension_id,
     TemplateURL::Type type) {
diff --git a/components/search_engines/template_url_service.h b/components/search_engines/template_url_service.h
index 56399fb..0e17278 100644
--- a/components/search_engines/template_url_service.h
+++ b/components/search_engines/template_url_service.h
@@ -684,6 +684,9 @@
   // Returns the TemplateURL corresponding to |prepopulated_id|, if any.
   TemplateURL* FindPrepopulatedTemplateURL(int prepopulated_id);
 
+  // Returns the TemplateURL corresponding to |starter_pack_id|, if any.
+  TemplateURL* FindStarterPackTemplateURL(int starter_pack_id);
+
   // Returns the TemplateURL associated with |extension_id|, if any.
   TemplateURL* FindTemplateURLForExtension(const std::string& extension_id,
                                            TemplateURL::Type type);
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 e3c413bc..6637a74 100644
--- a/components/services/storage/public/mojom/indexed_db_control_test.mojom
+++ b/components/services/storage/public/mojom/indexed_db_control_test.mojom
@@ -4,7 +4,6 @@
 
 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";
@@ -61,19 +60,19 @@
       (V2SchemaCorruptionStatus status);
 
   // Does a direct write to a database with a given key/value pair.
-  WriteToIndexedDBForTesting(storage.mojom.BucketLocator bucket_locator,
-                             string key, string value) => ();
+  WriteToIndexedDBForTesting(blink.mojom.StorageKey storage_key, 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(storage.mojom.BucketLocator bucket_locator,
+  GetNextBlobNumberForTesting(blink.mojom.StorageKey storage_key,
                               int64 database_id) => (int64 next_blob_number);
 
   // Returns the path for a given key/database/blob.
-  GetPathForBlobForTesting(storage.mojom.BucketLocator bucket_locator,
+  GetPathForBlobForTesting(blink.mojom.StorageKey storage_key,
                            int64 database_id, int64 blob_number) =>
       (mojo_base.mojom.FilePath path);
 
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index f1d21e7..75153808 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -836,6 +836,7 @@
   VISIT(image_url_post_params);
   VISIT(new_tab_url);
   VISIT_ENUM(is_active);
+  VISIT(starter_pack_id);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::SendTabToSelfSpecifics& proto) {
diff --git a/components/sync/protocol/search_engine_specifics.proto b/components/sync/protocol/search_engine_specifics.proto
index 29f5448..7b07e74 100644
--- a/components/sync/protocol/search_engine_specifics.proto
+++ b/components/sync/protocol/search_engine_specifics.proto
@@ -92,4 +92,7 @@
   // Whether a search engine is 'active' and can be triggered via the omnibox by
   // typing in the relevant keyword.
   optional ActiveStatus is_active = 27;
+  // The ID associated with the starter pack engine.  This is set to 0 if not a
+  // starter pack engine.
+  optional int32 starter_pack_id = 28;
 }
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index ecc4a7960..88119df 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -2029,6 +2029,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityFencedFrameScrollable) {
+  RunHtmlTest(FILE_PATH_LITERAL("fencedframe-scrollable.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        AccessibilityIframeScrollable) {
   RunHtmlTest(FILE_PATH_LITERAL("iframe-scrollable.html"));
 }
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index c7788dd..5d38713 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -90,6 +90,11 @@
 
 constexpr size_t kMaxDelayedTriggers = 30;
 
+void ReportBadMessageInsecureReportingOrigin() {
+  mojo::ReportBadMessage(
+      "AttributionDataHost: Reporting origin must be secure.");
+}
+
 }  // namespace
 
 struct AttributionDataHostManagerImpl::FrozenContext {
@@ -223,10 +228,16 @@
 
 void AttributionDataHostManagerImpl::SourceDataAvailable(
     blink::mojom::AttributionSourceDataPtr data) {
-  // The API is only allowed in secure contexts.
-  if (!network::IsOriginPotentiallyTrustworthy(data->reporting_origin) ||
-      !network::IsOriginPotentiallyTrustworthy(data->destination)) {
+  if (!network::IsOriginPotentiallyTrustworthy(data->reporting_origin)) {
     RecordSourceDataHandleStatus(DataHandleStatus::kUntrustworthyOrigin);
+    ReportBadMessageInsecureReportingOrigin();
+    return;
+  }
+
+  if (!network::IsOriginPotentiallyTrustworthy(data->destination)) {
+    RecordSourceDataHandleStatus(DataHandleStatus::kUntrustworthyOrigin);
+    mojo::ReportBadMessage(
+        "AttributionDataHost: Destination origin must be secure.");
     return;
   }
 
@@ -237,8 +248,6 @@
     case AttributionSourceType::kNavigation:
       DCHECK(context.destination.has_value());
 
-      // For navigation sources verify the destination matches the final
-      // navigation origin.
       if (net::SchemefulSite(data->destination) !=
           net::SchemefulSite(*context.destination)) {
         RecordSourceDataHandleStatus(DataHandleStatus::kContextError);
@@ -246,11 +255,15 @@
       }
       break;
     case AttributionSourceType::kEvent:
-      // For event sources verify that all sources are consistent.
       if (!context.destination.has_value()) {
         context.destination = data->destination;
       } else if (data->destination != *context.destination) {
         RecordSourceDataHandleStatus(DataHandleStatus::kContextError);
+        if (context.destination->opaque()) {
+          mojo::ReportBadMessage(
+              "AttributionDataHost: Cannot register sources after registering "
+              "a trigger.");
+        }
         return;
       }
       break;
@@ -258,11 +271,16 @@
 
   base::Time source_time = base::Time::Now();
 
+  // When converting mojo values to the browser process equivalents, it should
+  // not be possible for there to be an error except in the case of a bad
+  // renderer. All of the validation here is also performed renderer-side.
+
   absl::optional<AttributionFilterData> filter_data =
       AttributionFilterData::FromSourceFilterValues(
           std::move(data->filter_data->filter_values));
   if (!filter_data.has_value()) {
     RecordSourceDataHandleStatus(DataHandleStatus::kInvalidData);
+    mojo::ReportBadMessage("AttributionDataHost: Invalid filter data.");
     return;
   }
 
@@ -271,6 +289,7 @@
           std::move(data->aggregatable_source->keys));
   if (!aggregatable_source.has_value()) {
     RecordSourceDataHandleStatus(DataHandleStatus::kInvalidData);
+    mojo::ReportBadMessage("AttributionDataHost: Invalid aggregatable source.");
     return;
   }
 
@@ -294,19 +313,20 @@
 
 void AttributionDataHostManagerImpl::TriggerDataAvailable(
     blink::mojom::AttributionTriggerDataPtr data) {
-  // The API is only allowed in secure contexts.
   if (!network::IsOriginPotentiallyTrustworthy(data->reporting_origin)) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kUntrustworthyOrigin);
+    ReportBadMessageInsecureReportingOrigin();
     return;
   }
 
   FrozenContext& context = receivers_.current_context();
   DCHECK(network::IsOriginPotentiallyTrustworthy(context.context_origin));
 
-  // Only possible in the case of a bad renderer, navigation bound data hosts
-  // cannot register triggers.
   if (context.source_type == AttributionSourceType::kNavigation) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kContextError);
+    mojo::ReportBadMessage(
+        "AttributionDataHost: Navigation-bound data hosts cannot register "
+        "triggers.");
     return;
   }
 
@@ -315,6 +335,9 @@
     OnSourceEligibleDataHostFinished(context.register_time);
   } else if (!context.destination->opaque()) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kContextError);
+    mojo::ReportBadMessage(
+        "AttributionDataHost: Cannot register triggers after registering a "
+        "source.");
     return;
   }
 
@@ -323,11 +346,13 @@
           std::move(data->filters->filter_values));
   if (!filters.has_value()) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
+    mojo::ReportBadMessage("AttributionDataHost: Invalid top-level filters.");
     return;
   }
 
   if (data->event_triggers.size() > blink::kMaxAttributionEventTriggerData) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
+    mojo::ReportBadMessage("AttributionDataHost: Too many event triggers.");
     return;
   }
 
@@ -340,6 +365,8 @@
             std::move(event_trigger->filters->filter_values));
     if (!filters.has_value()) {
       RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
+      mojo::ReportBadMessage(
+          "AttributionDataHost: Invalid event-trigger filters.");
       return;
     }
 
@@ -348,6 +375,8 @@
             std::move(event_trigger->not_filters->filter_values));
     if (!not_filters.has_value()) {
       RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
+      mojo::ReportBadMessage(
+          "AttributionDataHost: Invalid event-trigger not_filters.");
       return;
     }
 
@@ -364,6 +393,8 @@
           std::move(data->aggregatable_trigger));
   if (!aggregatable_trigger.has_value()) {
     RecordTriggerDataHandleStatus(DataHandleStatus::kInvalidData);
+    mojo::ReportBadMessage(
+        "AttributionDataHost: Invalid aggregatable trigger.");
     return;
   }
 
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index b2122db..8aeeca6 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -28,6 +28,7 @@
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_utils.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
@@ -97,8 +98,6 @@
   }
 };
 
-}  // namespace
-
 class AttributionDataHostManagerImplTest : public testing::Test {
  public:
   AttributionDataHostManagerImplTest()
@@ -169,6 +168,7 @@
     const char* destination_origin;
     const char* reporting_origin;
     bool source_expected;
+    const char* bad_message = nullptr;
   } kTestCases[] = {
       {.source_origin = kLocalHost,
        .destination_origin = kLocalHost,
@@ -181,11 +181,14 @@
       {.source_origin = kLocalHost,
        .destination_origin = kLocalHost,
        .reporting_origin = "http://insecure.com",
-       .source_expected = false},
+       .source_expected = false,
+       .bad_message = "AttributionDataHost: Reporting origin must be secure."},
       {.source_origin = kLocalHost,
        .destination_origin = "http://insecure.com",
        .reporting_origin = kLocalHost,
-       .source_expected = false},
+       .source_expected = false,
+       .bad_message =
+           "AttributionDataHost: Destination origin must be secure."},
       {.source_origin = "http://insecure.com",
        .destination_origin = kLocalHost,
        .reporting_origin = kLocalHost,
@@ -199,6 +202,8 @@
   for (const auto& test_case : kTestCases) {
     EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.source_expected);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     RemoteDataHost data_host_remote{.task_environment = task_environment_};
     data_host_manager_.RegisterDataHost(
         data_host_remote.data_host.BindNewPipeAndPassReceiver(),
@@ -216,6 +221,13 @@
     data_host_remote.data_host.FlushForTesting();
 
     Mock::VerifyAndClear(&mock_manager_);
+
+    EXPECT_EQ(bad_message_observer.got_bad_message(), !!test_case.bad_message);
+
+    if (test_case.bad_message) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                test_case.bad_message);
+    }
   }
 
   histograms.ExpectUniqueSample("Conversions.RegisteredSourcesPerDataHost", 1,
@@ -239,6 +251,8 @@
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://page.example")));
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     auto source_data = blink::mojom::AttributionSourceData::New();
     source_data->destination =
         url::Origin::Create(GURL("https://trigger.example"));
@@ -253,6 +267,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.valid);
+
+    if (!test_case.valid) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid filter data.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kSourceDataHandleStatusMetric,
                                   test_case.valid ? 0 : 3, 1);
@@ -289,6 +310,8 @@
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://page.example")));
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     auto source_data = blink::mojom::AttributionSourceData::New();
     source_data->destination =
         url::Origin::Create(GURL("https://trigger.example"));
@@ -303,6 +326,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.valid);
+
+    if (!test_case.valid) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid filter data.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kSourceDataHandleStatusMetric,
                                   test_case.valid ? 0 : 3, 1);
@@ -409,6 +439,8 @@
         test_case.description);  // Since EXPECT_CALL doesn't support <<
     EXPECT_CALL(mock_manager_, HandleSource).Times(test_case.valid);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
     data_host_manager_.RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
@@ -426,6 +458,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.valid);
+
+    if (!test_case.valid) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid aggregatable source.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kSourceDataHandleStatusMetric,
                                   test_case.valid ? 0 : 3, 1);
@@ -522,6 +561,7 @@
     const char* destination_origin;
     const char* reporting_origin;
     bool trigger_expected;
+    const char* bad_message = nullptr;
   } kTestCases[] = {
       {.destination_origin = kLocalHost,
        .reporting_origin = kLocalHost,
@@ -531,7 +571,8 @@
        .trigger_expected = true},
       {.destination_origin = kLocalHost,
        .reporting_origin = "http://insecure.com",
-       .trigger_expected = false},
+       .trigger_expected = false,
+       .bad_message = "AttributionDataHost: Reporting origin must be secure."},
       {.destination_origin = "http://insecure.com",
        .reporting_origin = kLocalHost,
        .trigger_expected = false},
@@ -543,6 +584,8 @@
   for (const auto& test_case : kTestCases) {
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.trigger_expected);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     RemoteDataHost data_host_remote{.task_environment = task_environment_};
     data_host_manager_.RegisterDataHost(
         data_host_remote.data_host.BindNewPipeAndPassReceiver(),
@@ -559,6 +602,13 @@
     data_host_remote.data_host->TriggerDataAvailable(std::move(trigger_data));
     data_host_remote.data_host.FlushForTesting();
 
+    EXPECT_EQ(bad_message_observer.got_bad_message(), !!test_case.bad_message);
+
+    if (test_case.bad_message) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                test_case.bad_message);
+    }
+
     Mock::VerifyAndClear(&mock_manager_);
   }
 
@@ -578,6 +628,8 @@
     SCOPED_TRACE(test_case.description);  // EXPECT_CALL doesn't support <<
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
     data_host_manager_.RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
@@ -598,6 +650,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.valid);
+
+    if (!test_case.valid) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid top-level filters.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric,
                                   test_case.valid ? 0 : 3, 1);
@@ -612,6 +671,8 @@
     SCOPED_TRACE(test_case.description);  // EXPECT_CALL doesn't support <<
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
     data_host_manager_.RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
@@ -638,6 +699,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.valid);
+
+    if (!test_case.valid) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid event-trigger filters.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric,
                                   test_case.valid ? 0 : 3, 1);
@@ -652,6 +720,8 @@
     SCOPED_TRACE(test_case.description);  // EXPECT_CALL doesn't support <<
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.valid);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
     data_host_manager_.RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
@@ -679,6 +749,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.valid);
+
+    if (!test_case.valid) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid event-trigger not_filters.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric,
                                   test_case.valid ? 0 : 3, 1);
@@ -700,6 +777,8 @@
 
     EXPECT_CALL(mock_manager_, HandleTrigger).Times(test_case.expected);
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     mojo::Remote<blink::mojom::AttributionDataHost> data_host_remote;
     data_host_manager_.RegisterDataHost(
         data_host_remote.BindNewPipeAndPassReceiver(),
@@ -728,6 +807,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.expected);
+
+    if (!test_case.expected) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Too many event triggers.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric,
                                   test_case.expected ? 0 : 3, 1);
@@ -754,6 +840,8 @@
         data_host_remote.BindNewPipeAndPassReceiver(),
         url::Origin::Create(GURL("https://trigger.example")));
 
+    mojo::test::BadMessageObserver bad_message_observer;
+
     auto trigger_data = blink::mojom::AttributionTriggerData::New();
     trigger_data->reporting_origin =
         url::Origin::Create(GURL("https://reporter.example"));
@@ -772,6 +860,13 @@
 
     Mock::VerifyAndClear(&mock_manager_);
 
+    EXPECT_NE(bad_message_observer.got_bad_message(), test_case.expected);
+
+    if (!test_case.expected) {
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Invalid aggregatable trigger.");
+    }
+
     // kSuccess = 0, kInvalidData = 3.
     histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric,
                                   test_case.expected ? 0 : 3, 1);
@@ -821,15 +916,23 @@
 
     checkpoint.Call(2);
 
-    auto source_data = blink::mojom::AttributionSourceData::New();
-    source_data->destination = destination_origin;
-    source_data->reporting_origin = reporting_origin;
-    source_data->filter_data = blink::mojom::AttributionFilterData::New();
-    source_data->aggregatable_source =
-        blink::mojom::AttributionAggregatableSource::New();
+    {
+      mojo::test::BadMessageObserver bad_message_observer;
 
-    data_host_remote.data_host->SourceDataAvailable(std::move(source_data));
-    data_host_remote.data_host.FlushForTesting();
+      auto source_data = blink::mojom::AttributionSourceData::New();
+      source_data->destination = destination_origin;
+      source_data->reporting_origin = reporting_origin;
+      source_data->filter_data = blink::mojom::AttributionFilterData::New();
+      source_data->aggregatable_source =
+          blink::mojom::AttributionAggregatableSource::New();
+
+      data_host_remote.data_host->SourceDataAvailable(std::move(source_data));
+      data_host_remote.data_host.FlushForTesting();
+
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Cannot register sources after "
+                "registering a trigger.");
+    }
 
     checkpoint.Call(3);
 
@@ -890,14 +993,22 @@
 
     checkpoint.Call(2);
 
-    auto trigger_data = blink::mojom::AttributionTriggerData::New();
-    trigger_data->reporting_origin = reporting_origin;
-    trigger_data->filters = blink::mojom::AttributionFilterData::New();
-    trigger_data->aggregatable_trigger =
-        blink::mojom::AttributionAggregatableTrigger::New();
+    {
+      mojo::test::BadMessageObserver bad_message_observer;
 
-    data_host_remote.data_host->TriggerDataAvailable(std::move(trigger_data));
-    data_host_remote.data_host.FlushForTesting();
+      auto trigger_data = blink::mojom::AttributionTriggerData::New();
+      trigger_data->reporting_origin = reporting_origin;
+      trigger_data->filters = blink::mojom::AttributionFilterData::New();
+      trigger_data->aggregatable_trigger =
+          blink::mojom::AttributionAggregatableTrigger::New();
+
+      data_host_remote.data_host->TriggerDataAvailable(std::move(trigger_data));
+      data_host_remote.data_host.FlushForTesting();
+
+      EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+                "AttributionDataHost: Cannot register triggers after "
+                "registering a source.");
+    }
 
     checkpoint.Call(3);
 
@@ -1596,6 +1707,8 @@
       attribution_src_token, url::Origin::Create(GURL("https://s.test")),
       url::Origin::Create(GURL("https://d.test")));
 
+  mojo::test::BadMessageObserver bad_message_observer;
+
   auto trigger_data = blink::mojom::AttributionTriggerData::New();
   trigger_data->reporting_origin = url::Origin::Create(GURL("https://r.test"));
   trigger_data->filters = blink::mojom::AttributionFilterData::New();
@@ -1605,6 +1718,10 @@
   data_host_remote->TriggerDataAvailable(std::move(trigger_data));
   data_host_remote.FlushForTesting();
 
+  EXPECT_EQ(bad_message_observer.WaitForBadMessage(),
+            "AttributionDataHost: Navigation-bound data hosts cannot register "
+            "triggers.");
+
   // kContextError = 2.
   histograms.ExpectUniqueSample(kTriggerDataHandleStatusMetric, 2, 1);
 }
@@ -1656,4 +1773,5 @@
   data_host_remote2.FlushForTesting();
 }
 
+}  // namespace
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc
index e5422cb..61fe4625 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc
@@ -133,10 +133,8 @@
   return path;
 }
 
-std::string ComputeOriginIdentifier(
-    const storage::BucketLocator& bucket_locator) {
-  return storage::GetIdentifierFromOrigin(bucket_locator.storage_key.origin()) +
-         "@1";
+std::string ComputeOriginIdentifier(const blink::StorageKey& storage_key) {
+  return storage::GetIdentifierFromOrigin(storage_key.origin()) + "@1";
 }
 
 // TODO(ericu): Error recovery. If we persistently can't read the
@@ -628,7 +626,7 @@
 IndexedDBBackingStore::IndexedDBBackingStore(
     Mode backing_store_mode,
     TransactionalLevelDBFactory* transactional_leveldb_factory,
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const base::FilePath& blob_path,
     std::unique_ptr<TransactionalLevelDBDatabase> db,
     storage::mojom::BlobStorageContext* blob_storage_context,
@@ -639,13 +637,13 @@
     scoped_refptr<base::SequencedTaskRunner> idb_task_runner)
     : backing_store_mode_(backing_store_mode),
       transactional_leveldb_factory_(transactional_leveldb_factory),
-      bucket_locator_(bucket_locator),
+      storage_key_(storage_key),
       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(bucket_locator)),
+      origin_identifier_(ComputeOriginIdentifier(storage_key)),
       idb_task_runner_(std::move(idb_task_runner)),
       db_(std::move(db)),
       blob_files_cleaned_(std::move(blob_files_cleaned)) {
@@ -711,9 +709,7 @@
     INTERNAL_READ_ERROR(SET_UP_METADATA);
     return s;
   }
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  indexed_db::ReportSchemaVersion(db_schema_version,
-                                  bucket_locator_.storage_key);
+  indexed_db::ReportSchemaVersion(db_schema_version, storage_key_);
   if (!found) {
     // Initialize new backing store.
     db_schema_version = indexed_db::kLatestKnownSchemaVersion;
@@ -793,10 +789,9 @@
   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,
-        bucket_locator_.storage_key);
+        storage_key_);
     INTERNAL_WRITE_ERROR(SET_UP_METADATA);
     return s;
   }
@@ -804,11 +799,10 @@
   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,
-          bucket_locator_.storage_key);
+          storage_key_);
     }
   }
 #if DCHECK_IS_ON()
@@ -3194,7 +3188,7 @@
   const std::string schema_version_key = SchemaVersionKey::Encode();
   Status s;
 
-  if (bucket_locator_.storage_key.origin().host() != "docs.google.com") {
+  if (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 53563f18..bc73164d 100644
--- a/content/browser/indexed_db/indexed_db_backing_store.h
+++ b/content/browser/indexed_db/indexed_db_backing_store.h
@@ -27,7 +27,6 @@
 #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"
@@ -39,18 +38,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 {
@@ -389,7 +388,7 @@
   IndexedDBBackingStore(
       Mode backing_store_mode,
       TransactionalLevelDBFactory* transactional_leveldb_factory,
-      const storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext* blob_storage_context,
@@ -408,9 +407,7 @@
   // operations or method calls on this object.
   leveldb::Status Initialize(bool clean_active_blob_journal);
 
-  const storage::BucketLocator& bucket_locator() const {
-    return bucket_locator_;
-  }
+  const blink::StorageKey& storage_key() const { return storage_key_; }
   base::SequencedTaskRunner* idb_task_runner() const {
     return idb_task_runner_.get();
   }
@@ -666,7 +663,7 @@
 
   const Mode backing_store_mode_;
   const raw_ptr<TransactionalLevelDBFactory> transactional_leveldb_factory_;
-  const storage::BucketLocator bucket_locator_;
+  const blink::StorageKey storage_key_;
   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 aec1bcc5..fea887f 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -35,7 +35,6 @@
 #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"
@@ -72,7 +71,7 @@
   TestableIndexedDBBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       TransactionalLevelDBFactory* leveldb_factory,
-      const storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext* blob_storage_context,
@@ -83,7 +82,7 @@
       scoped_refptr<base::SequencedTaskRunner> idb_task_runner)
       : IndexedDBBackingStore(backing_store_mode,
                               leveldb_factory,
-                              bucket_locator,
+                              storage_key,
                               blob_path,
                               std::move(db),
                               blob_storage_context,
@@ -141,7 +140,7 @@
   std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       TransactionalLevelDBFactory* leveldb_factory,
-      const storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext*,
@@ -155,7 +154,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, bucket_locator, blob_path,
+        backing_store_mode, 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));
@@ -337,8 +336,6 @@
   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());
@@ -346,7 +343,7 @@
     leveldb::Status s;
     std::tie(bucket_state_handle_, s, std::ignore, data_loss_info_,
              std::ignore) =
-        idb_factory_->GetOrOpenBucketFactory(bucket_locator,
+        idb_factory_->GetOrOpenBucketFactory(storage_key,
                                              idb_context_->data_path(),
                                              /*create_if_missing=*/true);
     if (!bucket_state_handle_.IsHeld()) {
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc
index b323b3d..67fce13 100644
--- a/content/browser/indexed_db/indexed_db_browsertest.cc
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc
@@ -28,8 +28,6 @@
 #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_id.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"
@@ -275,13 +273,13 @@
   }
 
   // Synchronously writes to the IndexedDB database at the given storage_key.
-  void WriteToIndexedDB(const storage::BucketLocator& bucket_locator,
+  void WriteToIndexedDB(const blink::StorageKey& storage_key,
                         std::string key,
                         std::string value) {
     auto control_test = GetControlTest();
     base::RunLoop loop;
     control_test->WriteToIndexedDBForTesting(
-        bucket_locator, std::move(key), std::move(value), loop.QuitClosure());
+        storage_key, std::move(key), std::move(value), loop.QuitClosure());
     loop.Run();
   }
 
@@ -380,9 +378,6 @@
   const GURL database_open_url = GetTestUrl("indexeddb", "database_test.html");
   const blink::StorageKey kTestStorageKey =
       blink::StorageKey(url::Origin::Create(database_open_url));
-  auto bucket_locator = storage::BucketLocator();
-  bucket_locator.id = storage::BucketId::FromUnsafeValue(1);
-  bucket_locator.storage_key = kTestStorageKey;
   // Create the database.
   SimpleTest(database_open_url);
   // -10, little endian.
@@ -399,7 +394,7 @@
       }));
   loop.Run();
 
-  WriteToIndexedDB(bucket_locator, key, value);
+  WriteToIndexedDB(kTestStorageKey, key, value);
   // Crash the tab to ensure no old navigations are picked up.
   CrashTab(shell()->web_contents());
   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
@@ -409,9 +404,6 @@
   const GURL database_open_url = GetTestUrl("indexeddb", "database_test.html");
   const blink::StorageKey kTestStorageKey =
       blink::StorageKey(url::Origin::Create(database_open_url));
-  auto bucket_locator = storage::BucketLocator();
-  bucket_locator.id = storage::BucketId::FromUnsafeValue(1);
-  bucket_locator.storage_key = kTestStorageKey;
   // Create the database.
   SimpleTest(database_open_url);
   // -10, little endian.
@@ -428,7 +420,7 @@
       }));
   loop.Run();
 
-  WriteToIndexedDB(bucket_locator, key, value);
+  WriteToIndexedDB(kTestStorageKey, key, value);
   // Crash the tab to ensure no old navigations are picked up.
   CrashTab(shell()->web_contents());
   SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
@@ -1200,14 +1192,14 @@
 // This test is for https://crbug.com/1039446.
 class IndexedDBBrowserTestBlobKeyCorruption : public IndexedDBBrowserTest {
  public:
-  int64_t GetNextBlobNumber(const storage::BucketLocator& bucket_locator,
+  int64_t GetNextBlobNumber(const blink::StorageKey& storage_key,
                             int64_t database_id) {
     int64_t number;
 
     base::RunLoop loop;
     auto control_test = GetControlTest();
     control_test->GetNextBlobNumberForTesting(
-        bucket_locator, database_id,
+        storage_key, database_id,
         base::BindLambdaForTesting([&](int64_t next_blob_number) {
           number = next_blob_number;
           loop.Quit();
@@ -1216,14 +1208,14 @@
     return number;
   }
 
-  base::FilePath PathForBlob(const storage::BucketLocator& bucket_locator,
+  base::FilePath PathForBlob(const blink::StorageKey& storage_key,
                              int64_t database_id,
                              int64_t blob_number) {
     base::FilePath path;
     base::RunLoop loop;
     auto control_test = GetControlTest();
     control_test->GetPathForBlobForTesting(
-        bucket_locator, database_id, blob_number,
+        storage_key, database_id, blob_number,
         base::BindLambdaForTesting([&](const base::FilePath& blob_path) {
           path = blob_path;
           loop.Quit();
@@ -1242,9 +1234,6 @@
               embedded_test_server()->InitializeAndListen());
   const blink::StorageKey kTestStorageKey = blink::StorageKey(
       url::Origin::Create(embedded_test_server()->base_url()));
-  auto bucket_locator = storage::BucketLocator();
-  bucket_locator.id = storage::BucketId::FromUnsafeValue(1);
-  bucket_locator.storage_key = kTestStorageKey;
   embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
       &StaticFileRequestHandler, s_indexeddb_test_prefix, this));
   embedded_test_server()->StartAcceptingConnections();
@@ -1253,12 +1242,12 @@
   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(bucket_locator, 1);
+  int64_t next_blob_number = GetNextBlobNumber(kTestStorageKey, 1);
 
   base::FilePath first_blob =
-      PathForBlob(bucket_locator, 1, next_blob_number - 1);
+      PathForBlob(kTestStorageKey, 1, next_blob_number - 1);
   base::FilePath corrupt_blob =
-      PathForBlob(bucket_locator, 1, next_blob_number);
+      PathForBlob(kTestStorageKey, 1, next_blob_number);
   {
     base::ScopedAllowBlockingForTesting allow_blocking;
     EXPECT_TRUE(base::PathExists(first_blob));
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 6836257..66fcf57 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,7 +16,6 @@
 #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"
@@ -37,8 +36,6 @@
   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();
@@ -48,7 +45,7 @@
   std::unique_ptr<IndexedDBBackingStore> backing_store = std::make_unique<
       IndexedDBBackingStore>(
       IndexedDBBackingStore::Mode::kInMemory, &transactional_leveldb_factory,
-      bucket_locator, path,
+      storage_key, path,
       transactional_leveldb_factory.CreateLevelDBDatabase(
           FakeLevelDBFactory::GetBrokenLevelDB(
               leveldb::Status::IOError("It's broken!"), path),
@@ -68,8 +65,6 @@
   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();
@@ -89,7 +84,7 @@
     std::unique_ptr<IndexedDBBackingStore> backing_store = std::make_unique<
         IndexedDBBackingStore>(
         IndexedDBBackingStore::Mode::kInMemory, &transactional_leveldb_factory,
-        bucket_locator, path,
+        storage_key, 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 08ad665..f0378553 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.cc
+++ b/content/browser/indexed_db/indexed_db_context_impl.cc
@@ -30,7 +30,6 @@
 #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"
@@ -580,14 +579,14 @@
 }
 
 void IndexedDBContextImpl::WriteToIndexedDBForTesting(
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     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(bucket_locator, data_path(),
+      GetIDBFactory()->GetOrOpenBucketFactory(storage_key, data_path(),
                                               /*create_if_missing=*/true);
   CHECK(s.ok()) << s.ToString();
   CHECK(handle.IsHeld());
@@ -598,8 +597,7 @@
   s = db->Put(key, &value_copy);
   CHECK(s.ok()) << s.ToString();
   handle.Release();
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  GetIDBFactory()->ForceClose(bucket_locator.storage_key, true);
+  GetIDBFactory()->ForceClose(storage_key, true);
   std::move(callback).Run();
 }
 
@@ -610,13 +608,13 @@
 }
 
 void IndexedDBContextImpl::GetNextBlobNumberForTesting(
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     int64_t database_id,
     GetNextBlobNumberForTestingCallback callback) {
   IndexedDBBucketStateHandle handle;
   leveldb::Status s;
   std::tie(handle, s, std::ignore, std::ignore, std::ignore) =
-      GetIDBFactory()->GetOrOpenBucketFactory(bucket_locator, data_path(),
+      GetIDBFactory()->GetOrOpenBucketFactory(storage_key, data_path(),
                                               /*create_if_missing=*/true);
   CHECK(s.ok()) << s.ToString();
   CHECK(handle.IsHeld());
@@ -640,14 +638,14 @@
 }
 
 void IndexedDBContextImpl::GetPathForBlobForTesting(
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     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(bucket_locator, data_path(),
+      GetIDBFactory()->GetOrOpenBucketFactory(storage_key, 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 663da87a..6aff990 100644
--- a/content/browser/indexed_db/indexed_db_context_impl.h
+++ b/content/browser/indexed_db/indexed_db_context_impl.h
@@ -40,16 +40,15 @@
 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;
@@ -123,18 +122,18 @@
   void HasV2SchemaCorruptionForTesting(
       const blink::StorageKey& storage_key,
       HasV2SchemaCorruptionForTestingCallback callback) override;
-  void WriteToIndexedDBForTesting(const storage::BucketLocator& bucket_locator,
+  void WriteToIndexedDBForTesting(const blink::StorageKey& storage_key,
                                   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 storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       int64_t database_id,
       GetNextBlobNumberForTestingCallback callback) override;
   void GetPathForBlobForTesting(
-      const storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       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 d8bec47..eb606e18 100644
--- a/content/browser/indexed_db/indexed_db_context_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_context_unittest.cc
@@ -13,7 +13,6 @@
 #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"
@@ -92,14 +91,10 @@
   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_bucket_locator, indexed_db_context_->data_path());
-  auto google_bucket_locator = storage::BucketLocator();
-  google_bucket_locator.storage_key = google_storage_key_;
+      callbacks, example_storage_key_, indexed_db_context_->data_path());
   indexed_db_context_->GetIDBFactory()->GetDatabaseInfo(
-      callbacks, google_bucket_locator, indexed_db_context_->data_path());
+      callbacks, google_storage_key_, 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 7af4a72..aa395181 100644
--- a/content/browser/indexed_db/indexed_db_cursor.cc
+++ b/content/browser/indexed_db/indexed_db_cursor.cc
@@ -12,7 +12,6 @@
 #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"
@@ -52,9 +51,9 @@
     indexed_db::CursorType cursor_type,
     blink::mojom::IDBTaskType task_type,
     base::WeakPtr<IndexedDBTransaction> transaction)
-    : bucket_locator_(transaction->BackingStoreTransaction()
-                          ->backing_store()
-                          ->bucket_locator()),
+    : storage_key_(transaction->BackingStoreTransaction()
+                       ->backing_store()
+                       ->storage_key()),
       task_type_(task_type),
       cursor_type_(cursor_type),
       transaction_(std::move(transaction)),
@@ -129,7 +128,7 @@
   if (value) {
     mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
     external_objects.swap(value->external_objects);
-    dispatcher_host->CreateAllExternalObjects(bucket_locator_, external_objects,
+    dispatcher_host->CreateAllExternalObjects(storage_key_, external_objects,
                                               &mojo_value->external_objects);
   } else {
     mojo_value = blink::mojom::IDBValue::New();
@@ -210,7 +209,7 @@
   if (value) {
     mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
     external_objects.swap(value->external_objects);
-    dispatcher_host->CreateAllExternalObjects(bucket_locator_, external_objects,
+    dispatcher_host->CreateAllExternalObjects(storage_key_, external_objects,
                                               &mojo_value->external_objects);
   } else {
     mojo_value = blink::mojom::IDBValue::New();
@@ -338,7 +337,7 @@
     mojo_values.push_back(
         IndexedDBValue::ConvertAndEraseValue(&found_values[i]));
     dispatcher_host->CreateAllExternalObjects(
-        bucket_locator_, found_values[i].external_objects,
+        storage_key_, 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 08d5f30..2ec5598 100644
--- a/content/browser/indexed_db/indexed_db_cursor.h
+++ b/content/browser/indexed_db/indexed_db_cursor.h
@@ -15,12 +15,9 @@
 #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 {
@@ -81,7 +78,7 @@
       IndexedDBTransaction* transaction);
 
  private:
-  const storage::BucketLocator bucket_locator_;
+  const blink::StorageKey storage_key_;
   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 912be1d..58c59f3 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 storage::BucketLocator& bucket_locator) {
+    const blink::StorageKey& storage_key) {
   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(
-        bucket_locator, found_values[i].external_objects,
+        storage_key, 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(
-        bucket_locator(), value.external_objects,
+        storage_key(), 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(
-      bucket_locator(), value.external_objects,
+      storage_key(), 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(), bucket_locator()));
+            found_values, dispatcher_host.get(), storage_key()));
         found_values.clear();
       }
     }
@@ -1075,8 +1075,8 @@
     }
   } else {
     if (!found_values.empty()) {
-      result_sink->ReceiveValues(CreateMojoValues(
-          found_values, dispatcher_host.get(), bucket_locator()));
+      result_sink->ReceiveValues(
+          CreateMojoValues(found_values, dispatcher_host.get(), storage_key()));
     }
   }
   return s;
@@ -1209,9 +1209,8 @@
     std::move(params->callback)
         .Run(blink::mojom::IDBTransactionPutResult::NewKey(*key));
   }
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   factory_->NotifyIndexedDBContentChanged(
-      bucket_locator().storage_key, metadata_.name,
+      storage_key(), metadata_.name,
       metadata_.object_stores[params->object_store_id].name);
   return s;
 }
@@ -1402,7 +1401,7 @@
       mojo_values.push_back(
           IndexedDBReturnValue::ConvertReturnValue(&found_values[j]));
       dispatcher_host->CreateAllExternalObjects(
-          bucket_locator(), found_values[j].external_objects,
+          storage_key(), found_values[j].external_objects,
           &mojo_values[j]->value->external_objects);
     }
     all_mojo_values.push_back(std::move(mojo_values));
@@ -1506,7 +1505,9 @@
   }
 
   if (mojo_value) {
-    dispatcher_host->CreateAllExternalObjects(bucket_locator, external_objects,
+    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+    dispatcher_host->CreateAllExternalObjects(bucket_locator.storage_key,
+                                              external_objects,
                                               &mojo_value->external_objects);
   }
 
@@ -1581,9 +1582,8 @@
   if (!s.ok())
     return s;
   callbacks->OnSuccess();
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   factory_->NotifyIndexedDBContentChanged(
-      bucket_locator().storage_key, metadata_.name,
+      storage_key(), metadata_.name,
       metadata_.object_stores[object_store_id].name);
   return s;
 }
@@ -1628,9 +1628,8 @@
     return s;
   callbacks->OnSuccess();
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   factory_->NotifyIndexedDBContentChanged(
-      bucket_locator().storage_key, metadata_.name,
+      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 00e81bf0..56e5687 100644
--- a/content/browser/indexed_db/indexed_db_database.h
+++ b/content/browser/indexed_db/indexed_db_database.h
@@ -34,6 +34,7 @@
 #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 {
@@ -56,8 +57,8 @@
 
 class CONTENT_EXPORT IndexedDBDatabase {
  public:
-  // Identifier is pair of (bucket_locator, database name).
-  using Identifier = std::pair<storage::BucketLocator, std::u16string>;
+  // Identifier is pair of (storage_key, database name).
+  using Identifier = std::pair<blink::StorageKey, std::u16string>;
   // Used to report irrecoverable backend errors. The second argument can be
   // null.
   using ErrorCallback =
@@ -76,9 +77,7 @@
 
   int64_t id() const { return metadata_.id; }
   const std::u16string& name() const { return metadata_.name; }
-  const storage::BucketLocator& bucket_locator() const {
-    return identifier_.first;
-  }
+  const blink::StorageKey& storage_key() 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 beaa2c8..79df6b3 100644
--- a/content/browser/indexed_db/indexed_db_database_callbacks.cc
+++ b/content/browser/indexed_db/indexed_db_database_callbacks.cc
@@ -79,9 +79,8 @@
   if (complete_)
     return;
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   indexed_db_context_->TransactionComplete(
-      transaction.database()->bucket_locator().storage_key);
+      transaction.database()->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 f020c385..d84539b7 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
@@ -291,8 +291,9 @@
       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, indexed_db_path);
+      std::move(callbacks), bucket_locator.storage_key, indexed_db_path);
 }
 
 void IndexedDBDispatcherHost::Open(
@@ -336,8 +337,9 @@
 
   // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
   // created) if this origin is already over quota.
-  indexed_db_context_->GetIDBFactory()->Open(name, std::move(connection),
-                                             bucket_locator, indexed_db_path);
+  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
+  indexed_db_context_->GetIDBFactory()->Open(
+      name, std::move(connection), bucket_locator.storage_key, indexed_db_path);
 }
 
 void IndexedDBDispatcherHost::DeleteDatabase(
@@ -363,8 +365,10 @@
       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, indexed_db_path, force_close);
+      name, std::move(callbacks), bucket_locator.storage_key, indexed_db_path,
+      force_close);
 }
 
 void IndexedDBDispatcherHost::AbortTransactionsAndCompactDatabase(
@@ -444,7 +448,7 @@
 }
 
 void IndexedDBDispatcherHost::CreateAllExternalObjects(
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const std::vector<IndexedDBExternalObject>& objects,
     std::vector<blink::mojom::IDBExternalObjectPtr>* mojo_objects) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -509,7 +513,7 @@
         } else {
           DCHECK(!blob_info.file_system_access_token().empty());
           file_system_access_context()->DeserializeHandle(
-              bucket_locator.storage_key, blob_info.file_system_access_token(),
+              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 5a85a8c..bec07d6 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -28,7 +28,11 @@
 namespace base {
 class SequencedTaskRunner;
 class TaskRunner;
-}  // namespace base
+}
+
+namespace blink {
+class StorageKey;
+}
 
 namespace content {
 class IndexedDBContextImpl;
@@ -93,7 +97,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 storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       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 a500bf5..c325be9 100644
--- a/content/browser/indexed_db/indexed_db_factory.h
+++ b/content/browser/indexed_db/indexed_db_factory.h
@@ -24,10 +24,6 @@
 #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;
@@ -42,16 +38,16 @@
   virtual ~IndexedDBFactory() = default;
 
   virtual void GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,
-                               const storage::BucketLocator& bucket_locator,
+                               const blink::StorageKey& storage_key,
                                const base::FilePath& data_directory) = 0;
   virtual void Open(const std::u16string& name,
                     std::unique_ptr<IndexedDBPendingConnection> connection,
-                    const storage::BucketLocator& bucket_locator,
+                    const blink::StorageKey& storage_key,
                     const base::FilePath& data_directory) = 0;
 
   virtual void DeleteDatabase(const std::u16string& name,
                               scoped_refptr<IndexedDBCallbacks> callbacks,
-                              const storage::BucketLocator& bucket_locator,
+                              const blink::StorageKey& storage_key,
                               const base::FilePath& data_directory,
                               bool force_close) = 0;
 
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.cc b/content/browser/indexed_db/indexed_db_factory_impl.cc
index 231b6f93..fcd6f8f 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.cc
+++ b/content/browser/indexed_db/indexed_db_factory_impl.cc
@@ -39,7 +39,6 @@
 #include "components/services/storage/indexed_db/scopes/leveldb_scopes_factory.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 "components/services/storage/public/cpp/filesystem/filesystem_proxy.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "components/services/storage/public/mojom/indexed_db_control.mojom.h"
@@ -189,7 +188,7 @@
 
 void IndexedDBFactoryImpl::GetDatabaseInfo(
     scoped_refptr<IndexedDBCallbacks> callbacks,
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const base::FilePath& data_directory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBFactoryImpl::GetDatabaseInfo");
@@ -200,7 +199,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(bucket_locator, data_directory,
+      GetOrOpenBucketFactory(storage_key, data_directory,
                              /*create_if_missing=*/false);
   if (!bucket_state_handle.IsHeld() || !bucket_state_handle.bucket_state()) {
     if (s.IsNotFound()) {
@@ -208,10 +207,8 @@
     } else {
       callbacks->OnError(error);
     }
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(storage_key, error);
     return;
   }
   IndexedDBBucketState* factory = bucket_state_handle.bucket_state();
@@ -225,10 +222,8 @@
                                    "Internal error opening backing store for "
                                    "indexedDB.databases().");
     callbacks->OnError(error);
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(storage_key, error);
     return;
   }
   callbacks->OnSuccess(std::move(names_and_versions));
@@ -237,24 +232,22 @@
 void IndexedDBFactoryImpl::Open(
     const std::u16string& name,
     std::unique_ptr<IndexedDBPendingConnection> connection,
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const base::FilePath& data_directory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBFactoryImpl::Open");
-  IndexedDBDatabase::Identifier unique_identifier(bucket_locator, name);
+  IndexedDBDatabase::Identifier unique_identifier(storage_key, 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(bucket_locator, data_directory,
+      GetOrOpenBucketFactory(storage_key, data_directory,
                              /*create_if_missing=*/true);
   if (!bucket_state_handle.IsHeld() || !bucket_state_handle.bucket_state()) {
     connection->callbacks->OnError(error);
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(storage_key, error);
     return;
   }
   IndexedDBBucketState* factory = bucket_state_handle.bucket_state();
@@ -265,12 +258,11 @@
     return;
   }
   std::unique_ptr<IndexedDBDatabase> database;
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   std::tie(database, s) = class_factory_->CreateIndexedDBDatabase(
       name, factory->backing_store(), this,
       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForBucket,
                           bucket_state_destruction_weak_factory_.GetWeakPtr(),
-                          bucket_locator.storage_key),
+                          storage_key),
       std::make_unique<IndexedDBMetadataCoding>(), std::move(unique_identifier),
       factory->lock_manager());
   if (!database.get()) {
@@ -278,10 +270,8 @@
         blink::mojom::IDBException::kUnknownError,
         u"Internal error creating database backend for indexedDB.open.");
     connection->callbacks->OnError(error);
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(storage_key, error);
     return;
   }
 
@@ -296,26 +286,24 @@
 void IndexedDBFactoryImpl::DeleteDatabase(
     const std::u16string& name,
     scoped_refptr<IndexedDBCallbacks> callbacks,
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const base::FilePath& data_directory,
     bool force_close) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   TRACE_EVENT0("IndexedDB", "IndexedDBFactoryImpl::DeleteDatabase");
-  IndexedDBDatabase::Identifier unique_identifier(bucket_locator, name);
+  IndexedDBDatabase::Identifier unique_identifier(storage_key, 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(bucket_locator, data_directory,
+      GetOrOpenBucketFactory(storage_key, data_directory,
                              /*create_if_missing=*/true);
   if (!bucket_state_handle.IsHeld() || !bucket_state_handle.bucket_state()) {
     callbacks->OnError(error);
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(storage_key, error);
     return;
   }
   IndexedDBBucketState* factory = bucket_state_handle.bucket_state();
@@ -323,18 +311,14 @@
   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(), bucket_locator.storage_key));
+                       weak_factory_.GetWeakPtr(), storage_key));
     if (force_close) {
       leveldb::Status status = database->ForceCloseAndRunTasks();
-      if (!status.ok()) {
-        // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-        OnDatabaseError(bucket_locator.storage_key, status,
-                        "Error aborting transactions.");
-      }
+      if (!status.ok())
+        OnDatabaseError(storage_key, status, "Error aborting transactions.");
     }
     return;
   }
@@ -351,10 +335,8 @@
                                    "Internal error opening backing store for "
                                    "indexedDB.deleteDatabase.");
     callbacks->OnError(error);
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(storage_key, error);
     return;
   }
 
@@ -365,12 +347,11 @@
   }
 
   std::unique_ptr<IndexedDBDatabase> database;
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   std::tie(database, s) = class_factory_->CreateIndexedDBDatabase(
       name, factory->backing_store(), this,
       base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForBucket,
                           bucket_state_destruction_weak_factory_.GetWeakPtr(),
-                          bucket_locator.storage_key),
+                          storage_key),
       std::make_unique<IndexedDBMetadataCoding>(), unique_identifier,
       factory->lock_manager());
   if (!database.get()) {
@@ -378,27 +359,21 @@
                                    u"Internal error creating database backend "
                                    u"for indexedDB.deleteDatabase.");
     callbacks->OnError(error);
-    if (s.IsCorruption()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      HandleBackingStoreCorruption(bucket_locator.storage_key, error);
-    }
+    if (s.IsCorruption())
+      HandleBackingStoreCorruption(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(), bucket_locator.storage_key));
+                     weak_factory_.GetWeakPtr(), storage_key));
   if (force_close) {
     leveldb::Status status = database_ptr->ForceCloseAndRunTasks();
-    if (!status.ok()) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      OnDatabaseError(bucket_locator.storage_key, status,
-                      "Error aborting transactions.");
-    }
+    if (!status.ok())
+      OnDatabaseError(storage_key, status, "Error aborting transactions.");
   }
 }
 
@@ -649,7 +624,7 @@
            IndexedDBDataLossInfo,
            /*is_cold_open=*/bool>
 IndexedDBFactoryImpl::GetOrOpenBucketFactory(
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const base::FilePath& data_directory,
     bool create_if_missing) {
   TRACE_EVENT0("IndexedDB", "indexed_db::GetOrOpenBucketFactory");
@@ -659,8 +634,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_);
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  auto it = factories_per_bucket_.find(bucket_locator.storage_key);
+  auto it = factories_per_bucket_.find(storage_key);
   if (it != factories_per_bucket_.end()) {
     return {it->second->CreateHandle(), leveldb::Status::OK(),
             IndexedDBDatabaseError(), IndexedDBDataLossInfo(),
@@ -677,9 +651,8 @@
   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, bucket_locator.storage_key);
+        filesystem_proxy.get(), data_directory, storage_key);
     if (!s.ok())
       return {IndexedDBBucketStateHandle(), s, CreateDefaultError(),
               IndexedDBDataLossInfo(), /*was_cold_open=*/true};
@@ -700,21 +673,20 @@
     scopes_options.lock_manager = lock_manager.get();
     scopes_options.metadata_key_prefix = ScopesPrefix::Encode();
     scopes_options.failure_callback = base::BindRepeating(
-        [](const storage::BucketLocator& bucket_locator,
+        [](const blink::StorageKey& storage_key,
            base::WeakPtr<IndexedDBFactoryImpl> factory, leveldb::Status s) {
           if (!factory)
             return;
-          // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-          factory->OnDatabaseError(bucket_locator.storage_key, s, nullptr);
+          factory->OnDatabaseError(storage_key, s, nullptr);
         },
-        bucket_locator, weak_factory_.GetWeakPtr());
+        storage_key, 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(
-            bucket_locator, data_directory, database_path, blob_path,
+            storage_key, 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))
@@ -729,13 +701,10 @@
       std::string sanitized_message = leveldb_env::GetCorruptionMessage(s);
       base::ReplaceSubstringsAfterOffset(&sanitized_message, 0u,
                                          data_directory.AsUTF8Unsafe(), "...");
-      // 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);
+      LOG(ERROR) << "Got corruption for " << storage_key.GetDebugString()
+                 << ", " << sanitized_message;
+      IndexedDBBackingStore::RecordCorruptionInfo(data_directory, storage_key,
+                                                  sanitized_message);
     }
   }
 
@@ -751,14 +720,11 @@
   }
 
   if (UNLIKELY(!s.ok())) {
-    // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
-                     bucket_locator.storage_key);
+                     storage_key);
 
     if (disk_full) {
-      // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-      context_->quota_manager_proxy()->NotifyWriteFailed(
-          bucket_locator.storage_key);
+      context_->quota_manager_proxy()->NotifyWriteFailed(storage_key);
       return {IndexedDBBucketStateHandle(), s,
               IndexedDBDatabaseError(blink::mojom::IDBException::kQuotaError,
                                      u"Encountered full disk while opening "
@@ -777,9 +743,8 @@
       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,
-                     bucket_locator.storage_key);
+                     storage_key);
 
     return {IndexedDBBucketStateHandle(), s, CreateDefaultError(),
             data_loss_info, /*was_cold_open=*/true};
@@ -787,39 +752,32 @@
 
   if (!is_incognito_and_in_memory)
     ReportOpenStatus(indexed_db::INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
-                     bucket_locator.storage_key);
+                     storage_key);
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  auto run_tasks_callback =
-      base::BindRepeating(&IndexedDBFactoryImpl::MaybeRunTasksForBucket,
-                          bucket_state_destruction_weak_factory_.GetWeakPtr(),
-                          bucket_locator.storage_key);
+  auto run_tasks_callback = base::BindRepeating(
+      &IndexedDBFactoryImpl::MaybeRunTasksForBucket,
+      bucket_state_destruction_weak_factory_.GetWeakPtr(), storage_key);
 
   auto tear_down_callback = base::BindRepeating(
-      [](const storage::BucketLocator& bucket_locator,
+      [](const blink::StorageKey& storage_key,
          base::WeakPtr<IndexedDBFactoryImpl> factory, leveldb::Status s) {
         if (!factory)
           return;
-        // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-        factory->OnDatabaseError(bucket_locator.storage_key, s, nullptr);
+        factory->OnDatabaseError(storage_key, s, nullptr);
       },
-      bucket_locator, weak_factory_.GetWeakPtr());
+      storage_key, weak_factory_.GetWeakPtr());
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   auto bucket_state = std::make_unique<IndexedDBBucketState>(
-      bucket_locator.storage_key,
+      storage_key,
       /*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));
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  it = factories_per_bucket_
-           .emplace(bucket_locator.storage_key, std::move(bucket_state))
-           .first;
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
-  context_->FactoryOpened(bucket_locator.storage_key);
+  it =
+      factories_per_bucket_.emplace(storage_key, std::move(bucket_state)).first;
+  context_->FactoryOpened(storage_key);
   return {it->second->CreateHandle(), s, IndexedDBDatabaseError(),
           data_loss_info, /*was_cold_open=*/true};
 }
@@ -827,7 +785,7 @@
 std::unique_ptr<IndexedDBBackingStore> IndexedDBFactoryImpl::CreateBackingStore(
     IndexedDBBackingStore::Mode backing_store_mode,
     TransactionalLevelDBFactory* transactional_leveldb_factory,
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     const base::FilePath& blob_path,
     std::unique_ptr<TransactionalLevelDBDatabase> db,
     storage::mojom::BlobStorageContext* blob_storage_context,
@@ -838,18 +796,17 @@
         report_outstanding_blobs,
     scoped_refptr<base::SequencedTaskRunner> idb_task_runner) {
   return std::make_unique<IndexedDBBackingStore>(
-      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));
+      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));
 }
 std::tuple<std::unique_ptr<IndexedDBBackingStore>,
            leveldb::Status,
            IndexedDBDataLossInfo,
            bool /* is_disk_full */>
 IndexedDBFactoryImpl::OpenAndVerifyIndexedDBBackingStore(
-    const storage::BucketLocator& bucket_locator,
+    const blink::StorageKey& storage_key,
     base::FilePath data_directory,
     base::FilePath database_path,
     base::FilePath blob_path,
@@ -874,17 +831,15 @@
   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, bucket_locator.storage_key);
+        filesystem_proxy.get(), data_directory, 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,
-            bucket_locator.storage_key);
+            storage_key);
       }
       data_loss_info.status = blink::mojom::IDBDataLoss::Total;
       data_loss_info.message = base::StrCat(
@@ -950,41 +905,35 @@
     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,
-        bucket_locator.storage_key);
+        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,
-        bucket_locator.storage_key);
+        storage_key);
     return {nullptr, leveldb::Status::Corruption("Unknown IndexedDB schema"),
             std::move(data_loss_info), /*is_disk_full=*/false};
   }
 
-  // TODO(crbug.com/1218100): Propagate BucketLocator to callee.
   bool first_open_since_startup =
-      backends_opened_since_startup_.insert(bucket_locator.storage_key).second;
+      backends_opened_since_startup_.insert(storage_key).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(),
-      bucket_locator, blob_path, std::move(database),
+      storage_key, 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(),
-                          bucket_locator.storage_key),
+                          weak_factory_.GetWeakPtr(), storage_key),
       base::BindRepeating(&IndexedDBFactoryImpl::ReportOutstandingBlobs,
-                          weak_factory_.GetWeakPtr(),
-                          bucket_locator.storage_key),
+                          weak_factory_.GetWeakPtr(), storage_key),
       context_->IDBTaskRunner());
   status = backing_store->Initialize(
       /*clean_active_blob_journal=*/(!is_incognito_and_in_memory &&
diff --git a/content/browser/indexed_db/indexed_db_factory_impl.h b/content/browser/indexed_db/indexed_db_factory_impl.h
index 1c5857fe..72d6f91 100644
--- a/content/browser/indexed_db/indexed_db_factory_impl.h
+++ b/content/browser/indexed_db/indexed_db_factory_impl.h
@@ -38,11 +38,7 @@
 namespace base {
 class FilePath;
 class SequencedTaskRunner;
-}  // namespace base
-
-namespace storage {
-struct BucketLocator;
-}  // namespace storage
+}
 
 namespace content {
 class IndexedDBBucketState;
@@ -68,16 +64,16 @@
 
   // content::IndexedDBFactory overrides:
   void GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,
-                       const storage::BucketLocator& bucket_locator,
+                       const blink::StorageKey& storage_key,
                        const base::FilePath& data_directory) override;
   void Open(const std::u16string& name,
             std::unique_ptr<IndexedDBPendingConnection> connection,
-            const storage::BucketLocator& bucket_locator,
+            const blink::StorageKey& storage_key,
             const base::FilePath& data_directory) override;
 
   void DeleteDatabase(const std::u16string& name,
                       scoped_refptr<IndexedDBCallbacks> callbacks,
-                      const storage::BucketLocator& bucket_locator,
+                      const blink::StorageKey& storage_key,
                       const base::FilePath& data_directory,
                       bool force_close) override;
 
@@ -142,7 +138,7 @@
              IndexedDBDatabaseError,
              IndexedDBDataLossInfo,
              /*was_cold_open=*/bool>
-  GetOrOpenBucketFactory(const storage::BucketLocator& bucket_locator,
+  GetOrOpenBucketFactory(const blink::StorageKey& storage_key,
                          const base::FilePath& data_directory,
                          bool create_if_missing);
 
@@ -159,7 +155,7 @@
   virtual std::unique_ptr<IndexedDBBackingStore> CreateBackingStore(
       IndexedDBBackingStore::Mode backing_store_mode,
       TransactionalLevelDBFactory* leveldb_factory,
-      const storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       const base::FilePath& blob_path,
       std::unique_ptr<TransactionalLevelDBDatabase> db,
       storage::mojom::BlobStorageContext* blob_storage_context,
@@ -201,7 +197,7 @@
              IndexedDBDataLossInfo,
              bool /* is_disk_full */>
   OpenAndVerifyIndexedDBBackingStore(
-      const storage::BucketLocator& bucket_locator,
+      const blink::StorageKey& storage_key,
       base::FilePath data_directory,
       base::FilePath database_path,
       base::FilePath blob_path,
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 2728d490..c992285 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -144,7 +144,7 @@
   // is no actual data in the database.
   std::tuple<std::unique_ptr<IndexedDBConnection>,
              scoped_refptr<MockIndexedDBDatabaseCallbacks>>
-  CreateConnectionForDatatabase(const storage::BucketLocator& bucket_locator,
+  CreateConnectionForDatatabase(const blink::StorageKey& storage_key,
                                 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), bucket_locator,
+      factory()->Open(name, std::move(connection), storage_key,
                       context()->data_path());
       loop.Run();
     }
@@ -236,27 +236,21 @@
 
   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(bucket_locator_1,
-                                        context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key_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(bucket_locator_2,
-                                        context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key_2, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state2_handle.IsHeld()) << s.ToString();
   EXPECT_TRUE(s.ok()) << s.ToString();
@@ -274,13 +268,12 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
@@ -300,13 +293,12 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
@@ -324,15 +316,14 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
@@ -356,7 +347,7 @@
   // 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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   bucket_state_handle.Release();
@@ -377,7 +368,7 @@
 
   // Stop sweep by opening a connection.
   std::tie(bucket_state_handle, s, std::ignore, std::ignore, std::ignore) =
-      factory()->GetOrOpenBucketFactory(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   EXPECT_FALSE(
@@ -403,7 +394,7 @@
   //  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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   bucket_state_handle.Release();
   factory()->GetBucketFactory(storage_key)->close_timer()->FireNow();
@@ -425,15 +416,14 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -464,15 +454,14 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -506,15 +495,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;
 
   // 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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -527,13 +514,12 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
   EXPECT_TRUE(StorageBucketFromHandle(bucket_state_handle)
@@ -562,13 +548,12 @@
   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_bucket_locator,
+      factory()->GetOrOpenBucketFactory(too_long_storage_key,
                                         context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_FALSE(bucket_state_handle.IsHeld());
@@ -579,8 +564,6 @@
   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>();
@@ -592,7 +575,7 @@
       callbacks, db_callbacks,
       transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
       std::move(create_transaction_callback));
-  factory()->Open(u"db", std::move(connection), bucket_locator,
+  factory()->Open(u"db", std::move(connection), storage_key,
                   context()->data_path());
   RunPostedTasks();
 
@@ -605,13 +588,12 @@
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -625,13 +607,12 @@
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
@@ -647,8 +628,6 @@
   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>();
@@ -660,7 +639,7 @@
       callbacks, db_callbacks,
       transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
       std::move(create_transaction_callback));
-  factory()->Open(u"db", std::move(connection), bucket_locator,
+  factory()->Open(u"db", std::move(connection), storage_key,
                   context()->data_path());
   EXPECT_FALSE(callbacks->connection());
   RunPostedTasks();
@@ -681,8 +660,6 @@
   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>();
@@ -700,7 +677,7 @@
     base::RunLoop loop;
     callbacks->CallOnUpgradeNeeded(
         base::BindLambdaForTesting([&]() { loop.Quit(); }));
-    factory()->Open(u"db", std::move(connection), bucket_locator,
+    factory()->Open(u"db", std::move(connection), storage_key,
                     context()->data_path());
     loop.Run();
   }
@@ -721,8 +698,6 @@
   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>();
@@ -740,7 +715,7 @@
     base::RunLoop loop;
     callbacks->CallOnUpgradeNeeded(
         base::BindLambdaForTesting([&]() { loop.Quit(); }));
-    factory()->Open(u"db", std::move(connection), bucket_locator,
+    factory()->Open(u"db", std::move(connection), storage_key,
                     context()->data_path());
     loop.Run();
   }
@@ -761,13 +736,11 @@
   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(bucket_locator, u"db");
+      CreateConnectionForDatatabase(storage_key, u"db");
 
   // Force close the database.
   connection->database()->ForceCloseAndRunTasks();
@@ -786,10 +759,8 @@
 
   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, bucket_locator,
+  factory()->DeleteDatabase(u"db", callbacks, storage_key,
                             context()->data_path(),
                             /*force_close=*/false);
 
@@ -803,14 +774,12 @@
 
   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(bucket_locator, name);
+      CreateConnectionForDatatabase(storage_key, name);
 
   base::RunLoop run_loop;
   factory()->CallOnDatabaseDeletedForTesting(base::BindLambdaForTesting(
@@ -822,7 +791,7 @@
   auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
       /*expect_connection=*/false);
 
-  factory()->DeleteDatabase(name, callbacks, bucket_locator,
+  factory()->DeleteDatabase(name, callbacks, storage_key,
                             context()->data_path(),
                             /*force_close=*/true);
 
@@ -846,10 +815,8 @@
 
   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, bucket_locator, context()->data_path());
+  factory()->GetDatabaseInfo(callbacks, storage_key, context()->data_path());
 
   EXPECT_TRUE(callbacks->info_called());
   // Don't create a factory if one doesn't exist.
@@ -864,17 +831,16 @@
 
   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(bucket_locator, context()->data_path(),
+      factory()->GetOrOpenBucketFactory(storage_key, context()->data_path(),
                                         /*create_if_missing=*/true);
   EXPECT_TRUE(bucket_state_handle.IsHeld()) << s.ToString();
 
-  factory()->GetDatabaseInfo(callbacks, bucket_locator, context()->data_path());
+  factory()->GetDatabaseInfo(callbacks, storage_key, context()->data_path());
 
   EXPECT_TRUE(callbacks->info_called());
   EXPECT_TRUE(factory()->GetBucketFactory(storage_key));
@@ -920,8 +886,6 @@
           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);
@@ -929,7 +893,7 @@
       callbacks, dummy_database_callbacks,
       /*transaction_id=*/1, /*version=*/1,
       std::move(create_transaction_callback));
-  factory()->Open(name, std::move(connection), bucket_locator,
+  factory()->Open(name, std::move(connection), storage_key,
                   context()->data_path());
   EXPECT_TRUE(callbacks->error_called());
   base::RunLoop().RunUntilIdle();
@@ -979,8 +943,6 @@
   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;
 
@@ -1002,7 +964,7 @@
       base::RunLoop loop;
       callbacks->CallOnUpgradeNeeded(
           base::BindLambdaForTesting([&]() { loop.Quit(); }));
-      factory()->Open(db_name, std::move(connection), bucket_locator,
+      factory()->Open(db_name, std::move(connection), storage_key,
                       context()->data_path());
       loop.Run();
     }
@@ -1033,7 +995,7 @@
     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), bucket_locator,
+    factory()->Open(db_name, std::move(connection), storage_key,
                     context()->data_path());
     EXPECT_TRUE(factory()->IsDatabaseOpen(storage_key, db_name));
     RunPostedTasks();
@@ -1067,7 +1029,7 @@
 
 TEST_F(IndexedDBFactoryTest, DataFormatVersion) {
   SetupContext();
-  auto try_open = [this](const storage::BucketLocator& bucket_locator,
+  auto try_open = [this](const blink::StorageKey& storage_key,
                          const IndexedDBDataFormatVersion& version) {
     base::AutoReset<IndexedDBDataFormatVersion> override_version(
         &IndexedDBDataFormatVersion::GetMutableCurrentForTesting(), version);
@@ -1095,7 +1057,7 @@
           base::BindLambdaForTesting([&]() { loop.Quit(); }));
 
       this->factory()->Open(u"test_db", std::move(pending_connection),
-                            bucket_locator, context()->data_path());
+                            storage_key, context()->data_path());
       loop.Run();
 
       // If an upgrade was requested, then commit the upgrade transaction.
@@ -1116,7 +1078,7 @@
       }
     }
     RunPostedTasks();
-    factory()->ForceClose(bucket_locator.storage_key, false);
+    factory()->ForceClose(storage_key, false);
     RunPostedTasks();
     return callbacks->data_loss();
   };
@@ -1134,12 +1096,10 @@
     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(bucket_locator, test.open_version_1));
+              try_open(storage_key, test.open_version_1));
     EXPECT_EQ(test.expected_data_loss,
-              try_open(bucket_locator, test.open_version_2));
+              try_open(storage_key, 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 18a72cfe..3a53cb1 100644
--- a/content/browser/indexed_db/indexed_db_fake_backing_store.cc
+++ b/content/browser/indexed_db/indexed_db_fake_backing_store.cc
@@ -11,7 +11,6 @@
 #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"
 
@@ -26,49 +25,41 @@
   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(),
-                            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()) {}
+    : 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()) {}
 IndexedDBFakeBackingStore::IndexedDBFakeBackingStore(
     BlobFilesCleanedCallback blob_files_cleaned,
     ReportOutstandingBlobsCallback report_outstanding_blobs,
     scoped_refptr<base::SequencedTaskRunner> 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) {}
+    : 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) {}
 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 ff0129e..b778a11 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -266,7 +266,7 @@
                 std::make_unique<IndexedDBPendingConnection>(
                     open_callbacks, open_db_callbacks, host_transaction_id,
                     version, std::move(create_transaction_callback1)),
-                bucket_locator, context()->data_path());
+                kTestStorageKey, 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)),
-                bucket_locator, context()->data_path());
+                kTestStorageKey, context()->data_path());
   RunPostedTasks();
   ASSERT_TRUE(closed_callbacks->connection());
   closed_callbacks->connection()->AbortTransactionsAndClose(
@@ -325,8 +325,6 @@
 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());
@@ -341,7 +339,7 @@
       callbacks, db_callbacks,
       transaction_id, IndexedDBDatabaseMetadata::DEFAULT_VERSION,
       std::move(create_transaction_callback1));
-  factory->Open(u"db", std::move(connection), bucket_locator,
+  factory->Open(u"db", std::move(connection), kTestStorageKey,
                 context()->data_path());
   RunPostedTasks();
 
diff --git a/content/browser/indexed_db/mock_indexed_db_factory.h b/content/browser/indexed_db/mock_indexed_db_factory.h
index f744c893..0f0b369 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 storage::BucketLocator& bucket_locator,
+                    const blink::StorageKey& storage_key,
                     const base::FilePath& data_directory));
   MOCK_METHOD4(OpenProxy,
                void(const std::u16string& name,
@@ -39,15 +39,14 @@
   // 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 storage::BucketLocator& bucket_locator,
+            const blink::StorageKey& storage_key,
             const base::FilePath& data_directory) override {
-    OpenProxy(name, connection.get(), bucket_locator.storage_key,
-              data_directory);
+    OpenProxy(name, connection.get(), storage_key, data_directory);
   }
   MOCK_METHOD5(DeleteDatabase,
                void(const std::u16string& name,
                     scoped_refptr<IndexedDBCallbacks> callbacks,
-                    const storage::BucketLocator& bucket_locator,
+                    const blink::StorageKey& storage_key,
                     const base::FilePath& data_directory,
                     bool force_close));
   MOCK_METHOD2(AbortTransactionsAndCompactDatabaseProxy,
diff --git a/content/browser/media/media_license_manager.cc b/content/browser/media/media_license_manager.cc
index 380bb5a..3ee40f6 100644
--- a/content/browser/media/media_license_manager.cc
+++ b/content/browser/media/media_license_manager.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/callback_forward.h"
 #include "base/containers/flat_map.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/memory/scoped_refptr.h"
@@ -33,6 +34,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/cdm_info.h"
+#include "content/public/common/content_features.h"
 #include "media/cdm/cdm_type.h"
 #include "media/media_buildflags.h"
 #include "net/base/io_buffer.h"
@@ -299,7 +301,13 @@
         {blink::mojom::StorageType::kTemporary});
   }
 
-  // TODO(crbug.com/1231162): Consider migrating media licenses here.
+  if (base::FeatureList::IsEnabled(features::kMediaLicenseBackend)) {
+    // Ensure the file system context is kept alive until we're done migrating
+    // media license data from the Plugin Private File System to this backend.
+    MigrateMediaLicenses(
+        base::BindOnce([](scoped_refptr<storage::FileSystemContext>) {},
+                       base::WrapRefCounted(context().get())));
+  }
 }
 
 MediaLicenseManager::~MediaLicenseManager() = default;
@@ -620,6 +628,13 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(host);
 
+  if (in_memory()) {
+    // Don't delete `host` for an in-memory profile, since the data is not safe
+    // to delete yet. For example, a site may be re-visited within the same
+    // incognito session. `host` will be destroyed when `this` is destroyed.
+    return;
+  }
+
   DCHECK_GT(hosts_.count(host->storage_key()), 0ul);
   DCHECK_EQ(hosts_[host->storage_key()].get(), host);
 
diff --git a/content/browser/renderer_host/navigator.cc b/content/browser/renderer_host/navigator.cc
index 5d1e0c0..7aba6be0 100644
--- a/content/browser/renderer_host/navigator.cc
+++ b/content/browser/renderer_host/navigator.cc
@@ -844,6 +844,16 @@
       frame_tree_node_id = render_frame_host->GetOutermostMainFrame()
                                ->frame_tree_node()
                                ->frame_tree_node_id();
+
+      // Fenced frames are enforced to have a history of length 1. Because the
+      // renderer thinks this navigation is to the fenced frame root, it sets
+      // `should_replace_current_entry` to true, but we do not want this
+      // restriction for navigations outside the fenced frame.
+      // TODO(crbug.com/1315802): Make sure that the browser doesn't rely on
+      // whether the renderer says we should replace the current entry, i.e.
+      // make sure there are no situations where we should actually replace the
+      // current entry but don't, due to this line.
+      should_replace_current_entry = false;
     } else {
       // Otherwise, proceed normally.
       frame_tree_node_id =
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 26870cf..94e3690 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -2822,21 +2822,16 @@
   EXPECT_EQ(0, process->get_media_stream_count_for_testing());
 }
 
-#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
-// ChromeOS and Linux failures are tracked in https://crbug.com/954217
-#define MAYBE_VisibilityScrolledOutOfView DISABLED_VisibilityScrolledOutOfView
-#else
-#define MAYBE_VisibilityScrolledOutOfView VisibilityScrolledOutOfView
-#endif
 // Test that a frame is visible/hidden depending on its WebContents visibility
 // state.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       MAYBE_VisibilityScrolledOutOfView) {
+                       DISABLED_VisibilityScrolledOutOfView) {
   GURL main_frame(embedded_test_server()->GetURL("/iframe_out_of_view.html"));
   GURL child_url(embedded_test_server()->GetURL("/hello.html"));
 
   // This will set up the page frame tree as A(A1()).
   ASSERT_TRUE(NavigateToURL(shell(), main_frame));
+  // TODO(crbug.com/954217): Re-enable this test
   FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
   FrameTreeNode* nested_iframe_node = root->child_at(0);
   EXPECT_TRUE(NavigateToURLFromRenderer(nested_iframe_node, child_url));
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 158a585..add6707 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -12132,7 +12132,7 @@
 // //mojo/core/channel_fuchsia.cc
 #if BUILDFLAG(IS_FUCHSIA)
 #define MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown \
-  RenderFrameProxyNotRecreatedDuringProcessShutdown
+  DISABLED_RenderFrameProxyNotRecreatedDuringProcessShutdown
 #else
 #define MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown \
   RenderFrameProxyNotRecreatedDuringProcessShutdown
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 6dbbdd8..935636ec 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -539,7 +539,7 @@
 // Use a custom backend to store media licenses in lieu of the
 // Plugin Private File System.
 const base::Feature kMediaLicenseBackend{"MediaLicenseBackend",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
+                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Allow cross-context transfer of MediaStreamTracks.
 const base::Feature kMediaStreamTrackTransfer{
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index f217de19..abcd95a 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -415,7 +415,7 @@
   // URL". This case should never reach this point as it's handled above, where
   // we return the original data: URL instead.
   if (document_loader->HasUnreachableURL()) {
-    *output = document_loader->UnreachableURL();
+    *output = document_loader->UnreachableWebURL();
     return true;
   }
 
@@ -4667,7 +4667,7 @@
     const blink::DocumentPolicyFeatureState& document_policy_header,
     const absl::optional<base::UnguessableToken>& embedding_token) {
   WebDocumentLoader* document_loader = frame_->GetDocumentLoader();
-  const WebURLResponse& response = document_loader->GetResponse();
+  const WebURLResponse& response = document_loader->GetWebResponse();
 
   DocumentState* document_state =
       DocumentState::FromDocumentLoader(frame_->GetDocumentLoader());
@@ -4767,7 +4767,7 @@
 
     // Update contents MIME type for main frame.
     params->contents_mime_type =
-        document_loader->GetResponse().MimeType().Utf8();
+        document_loader->GetWebResponse().MimeType().Utf8();
 
     params->transition = transition;
     // Check that if we are in a fenced frame tree then we must have
diff --git a/content/test/data/accessibility/html/fencedframe-scrollable-expected-blink.txt b/content/test/data/accessibility/html/fencedframe-scrollable-expected-blink.txt
new file mode 100644
index 0000000..f651bc4
--- /dev/null
+++ b/content/test/data/accessibility/html/fencedframe-scrollable-expected-blink.txt
@@ -0,0 +1,14 @@
+rootWebArea scrollable=true
+++genericContainer ignored
+++++genericContainer
+++++++iframe name='Scrollable iframe'
+++++++++rootWebArea scrollable=true
+++++++++++genericContainer ignored
+++++++++++++genericContainer ignored
+++++++++++++++genericContainer
+++++++++++++++++staticText name='visible text'
+++++++++++++++++++inlineTextBox name='visible text'
+++++++++++++++genericContainer ignored
+++++++++++++++genericContainer
+++++++++++++++++staticText name='hidden from viewport'
+++++++++++++++++++inlineTextBox name='hidden from viewport'
diff --git a/content/test/data/accessibility/html/fencedframe-scrollable.html b/content/test/data/accessibility/html/fencedframe-scrollable.html
new file mode 100644
index 0000000..885957a
--- /dev/null
+++ b/content/test/data/accessibility/html/fencedframe-scrollable.html
@@ -0,0 +1,10 @@
+<!--
+@BLINK-ALLOW:scrollable=*
+-->
+<!DOCTYPE html>
+<html style="width:100px; height:100px;">
+<body>
+  <iframe style="width:200px; height: 200px;" aria-label="Scrollable iframe" src="frame/visible_text.html">
+  </iframe>
+</body>
+</html>
diff --git a/content/test/gpu/gpu_tests/test_expectations/OWNERS b/content/test/gpu/gpu_tests/test_expectations/OWNERS
new file mode 100644
index 0000000..56ad5561
--- /dev/null
+++ b/content/test/gpu/gpu_tests/test_expectations/OWNERS
@@ -0,0 +1,2 @@
+# The entire Chrome Graphics team is allowed to administer these test expectations.
+file://gpu/GRAPHICS_TEAM_OWNERS
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 4becb4a..bc2fc6c 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -339,11 +339,11 @@
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] WebglExtension_WEBGL_multi_draw_instanced_base_vertex_base_instance [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance/ogles/GL/build/build_009_to_016.html [ Failure ]
 crbug.com/angleproject/6430 [ mac passthrough angle-metal ] conformance2/rendering/fs-color-type-mismatch-color-buffer-type.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/sub.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/whole.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.2_samples.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.4_samples.html [ Failure ]
-crbug.com/angleproject/6430 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.8_samples.html [ Failure ]
+crbug.com/angleproject/7079 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/sub.html [ Failure ]
+crbug.com/angleproject/7079 [ mac passthrough angle-metal ] deqp/functional/gles3/fboinvalidate/whole.html [ Failure ]
+crbug.com/angleproject/7079 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.2_samples.html [ Failure ]
+crbug.com/angleproject/7079 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.4_samples.html [ Failure ]
+crbug.com/angleproject/7079 [ mac passthrough angle-metal ] deqp/functional/gles3/fbomultisample.8_samples.html [ Failure ]
 crbug.com/angleproject/7234 [ mac passthrough angle-metal ] conformance2/textures/misc/integer-cubemap-texture-sampling.html [ Failure ]
 
 # Metal AMD
diff --git a/content/web_test/renderer/test_runner.cc b/content/web_test/renderer/test_runner.cc
index 1193618..2fc1dc8 100644
--- a/content/web_test/renderer/test_runner.cc
+++ b/content/web_test/renderer/test_runner.cc
@@ -3364,7 +3364,7 @@
       spec = spec.substr(path_start);
 
     std::string mime_type =
-        web_frame->GetDocumentLoader()->GetResponse().MimeType().Utf8();
+        web_frame->GetDocumentLoader()->GetWebResponse().MimeType().Utf8();
 
     // In a text/plain document, and in a dumpAsText/ subdirectory, we generate
     // text results no matter what the test may previously have requested.
diff --git a/device/vr/openxr/openxr_anchor_manager.cc b/device/vr/openxr/openxr_anchor_manager.cc
index 843308bd..6989b0d 100644
--- a/device/vr/openxr/openxr_anchor_manager.cc
+++ b/device/vr/openxr/openxr_anchor_manager.cc
@@ -193,20 +193,20 @@
     const gfx::Transform& native_origin_from_anchor,
     const std::vector<mojom::XRInputSourceStatePtr>& input_state) const {
   switch (native_origin_information.which()) {
-    case mojom::XRNativeOriginInformation::Tag::INPUT_SOURCE_SPACE_INFO:
+    case mojom::XRNativeOriginInformation::Tag::kInputSourceSpaceInfo:
       // Currently unimplemented as only anchors are supported and are never
       // created relative to input sources
       return absl::nullopt;
-    case mojom::XRNativeOriginInformation::Tag::REFERENCE_SPACE_TYPE:
+    case mojom::XRNativeOriginInformation::Tag::kReferenceSpaceType:
       return GetXrLocationFromReferenceSpace(openxr, current_stage_parameters,
                                              native_origin_information,
                                              native_origin_from_anchor);
-    case mojom::XRNativeOriginInformation::Tag::PLANE_ID:
-    case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
-    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
+    case mojom::XRNativeOriginInformation::Tag::kPlaneId:
+    case mojom::XRNativeOriginInformation::Tag::kHandJointSpaceInfo:
+    case mojom::XRNativeOriginInformation::Tag::kImageIndex:
       // Unsupported for now
       return absl::nullopt;
-    case mojom::XRNativeOriginInformation::Tag::ANCHOR_ID:
+    case mojom::XRNativeOriginInformation::Tag::kAnchorId:
       return XrLocation{
           GfxTransformToXrPose(native_origin_from_anchor),
           GetAnchorSpace(AnchorId(native_origin_information.get_anchor_id()))};
diff --git a/device/vr/openxr/openxr_scene_understanding_manager.cc b/device/vr/openxr/openxr_scene_understanding_manager.cc
index 1860309..39cedc6 100644
--- a/device/vr/openxr/openxr_scene_understanding_manager.cc
+++ b/device/vr/openxr/openxr_scene_understanding_manager.cc
@@ -358,7 +358,7 @@
     const gfx::Transform& mojo_from_viewer,
     const std::vector<mojom::XRInputSourceStatePtr>& input_state) {
   switch (native_origin_information.which()) {
-    case mojom::XRNativeOriginInformation::Tag::INPUT_SOURCE_SPACE_INFO:
+    case mojom::XRNativeOriginInformation::Tag::kInputSourceSpaceInfo:
       for (auto& input_source_state : input_state) {
         mojom::XRInputSourceSpaceInfo* input_source_space_info =
             native_origin_information.get_input_source_space_info().get();
@@ -368,17 +368,17 @@
         }
       }
       return absl::nullopt;
-    case mojom::XRNativeOriginInformation::Tag::REFERENCE_SPACE_TYPE:
+    case mojom::XRNativeOriginInformation::Tag::kReferenceSpaceType:
       return GetMojoFromReferenceSpace(
           native_origin_information.get_reference_space_type(),
           mojo_from_viewer);
-    case mojom::XRNativeOriginInformation::Tag::PLANE_ID:
+    case mojom::XRNativeOriginInformation::Tag::kPlaneId:
       return absl::nullopt;
-    case mojom::XRNativeOriginInformation::Tag::ANCHOR_ID:
+    case mojom::XRNativeOriginInformation::Tag::kAnchorId:
       return absl::nullopt;
-    case mojom::XRNativeOriginInformation::Tag::HAND_JOINT_SPACE_INFO:
+    case mojom::XRNativeOriginInformation::Tag::kHandJointSpaceInfo:
       return absl::nullopt;
-    case mojom::XRNativeOriginInformation::Tag::IMAGE_INDEX:
+    case mojom::XRNativeOriginInformation::Tag::kImageIndex:
       return absl::nullopt;
   }
 }
@@ -447,4 +447,4 @@
   return mojo_from_input * input_from_pointer;
 }
 
-}  // namespace device
\ No newline at end of file
+}  // namespace device
diff --git a/extensions/browser/api/storage/settings_test_util.cc b/extensions/browser/api/storage/settings_test_util.cc
index 5d66d96..ab26359 100644
--- a/extensions/browser/api/storage/settings_test_util.cc
+++ b/extensions/browser/api/storage/settings_test_util.cc
@@ -23,18 +23,18 @@
 namespace settings_test_util {
 
 // Creates a kilobyte of data.
-std::unique_ptr<base::Value> CreateKilobyte() {
+base::Value CreateKilobyte() {
   std::string kilobyte_string(1024u, 'a');
-  return std::make_unique<base::Value>(std::move(kilobyte_string));
+  return base::Value(std::move(kilobyte_string));
 }
 
 // Creates a megabyte of data.
-std::unique_ptr<base::Value> CreateMegabyte() {
-  base::ListValue* megabyte = new base::ListValue();
+base::Value CreateMegabyte() {
+  base::Value::List megabyte;
   for (int i = 0; i < 1000; ++i) {
-    megabyte->Append(CreateKilobyte());
+    megabyte.Append(CreateKilobyte());
   }
-  return std::unique_ptr<base::Value>(megabyte);
+  return base::Value(std::move(megabyte));
 }
 
 // Intended as a StorageCallback from GetStorage.
diff --git a/extensions/browser/api/storage/settings_test_util.h b/extensions/browser/api/storage/settings_test_util.h
index 071a3f1..b7dcb0a 100644
--- a/extensions/browser/api/storage/settings_test_util.h
+++ b/extensions/browser/api/storage/settings_test_util.h
@@ -17,6 +17,10 @@
 #include "extensions/browser/mock_extension_system.h"
 #include "extensions/common/extension.h"
 
+namespace base {
+class Value;
+}
+
 namespace value_store {
 class ValueStore;
 }
@@ -28,10 +32,10 @@
 namespace settings_test_util {
 
 // Creates a kilobyte of data.
-std::unique_ptr<base::Value> CreateKilobyte();
+base::Value CreateKilobyte();
 
 // Creates a megabyte of data.
-std::unique_ptr<base::Value> CreateMegabyte();
+base::Value CreateMegabyte();
 
 // Synchronously gets the storage area for an extension from |frontend|.
 value_store::ValueStore* GetStorage(
diff --git a/extensions/browser/api/storage/storage_frontend_unittest.cc b/extensions/browser/api/storage/storage_frontend_unittest.cc
index 35481ba..fed2841 100644
--- a/extensions/browser/api/storage/storage_frontend_unittest.cc
+++ b/extensions/browser/api/storage/storage_frontend_unittest.cc
@@ -194,30 +194,30 @@
       extension, settings::LOCAL, frontend_.get());
 
   // Sync storage should run out after ~100K.
-  std::unique_ptr<base::Value> kilobyte = settings_test_util::CreateKilobyte();
+  base::Value kilobyte = settings_test_util::CreateKilobyte();
   for (int i = 0; i < 100; ++i) {
-    sync_storage->Set(DEFAULTS, base::NumberToString(i), *kilobyte);
+    sync_storage->Set(DEFAULTS, base::NumberToString(i), kilobyte);
   }
 
   EXPECT_FALSE(
-      sync_storage->Set(DEFAULTS, "WillError", *kilobyte).status().ok());
+      sync_storage->Set(DEFAULTS, "WillError", kilobyte).status().ok());
 
   // Local storage shouldn't run out after ~100K.
   for (int i = 0; i < 100; ++i) {
-    local_storage->Set(DEFAULTS, base::NumberToString(i), *kilobyte);
+    local_storage->Set(DEFAULTS, base::NumberToString(i), kilobyte);
   }
 
   EXPECT_TRUE(
-      local_storage->Set(DEFAULTS, "WontError", *kilobyte).status().ok());
+      local_storage->Set(DEFAULTS, "WontError", kilobyte).status().ok());
 
   // Local storage should run out after ~5MB.
-  std::unique_ptr<base::Value> megabyte = settings_test_util::CreateMegabyte();
+  base::Value megabyte = settings_test_util::CreateMegabyte();
   for (int i = 0; i < 5; ++i) {
-    local_storage->Set(DEFAULTS, base::NumberToString(i), *megabyte);
+    local_storage->Set(DEFAULTS, base::NumberToString(i), megabyte);
   }
 
   EXPECT_FALSE(
-      local_storage->Set(DEFAULTS, "WillError", *megabyte).status().ok());
+      local_storage->Set(DEFAULTS, "WillError", megabyte).status().ok());
 }
 
 }  // namespace extensions
diff --git a/gin/array_buffer.cc b/gin/array_buffer.cc
index 2e78aa6..917ab23 100644
--- a/gin/array_buffer.cc
+++ b/gin/array_buffer.cc
@@ -8,9 +8,11 @@
 #include <stdlib.h>
 
 #include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/bits.h"
 #include "base/check_op.h"
 #include "build/build_config.h"
 #include "gin/per_isolate_data.h"
+#include "v8/include/v8-initialization.h"
 
 #if BUILDFLAG(IS_POSIX)
 #include <sys/mman.h>
@@ -96,4 +98,91 @@
   return true;
 }
 
+// ArrayBufferSharedMemoryMapper ---------------------------------------------
+
+namespace {
+#ifdef V8_SANDBOX
+// When the V8 sandbox is enabled, shared memory backing ArrayBuffers must be
+// mapped into the sandbox address space. This custom SharedMemoryMapper
+// implements this.
+
+class ArrayBufferSharedMemoryMapper : public base::SharedMemoryMapper {
+ public:
+  absl::optional<base::span<uint8_t>> Map(
+      base::subtle::PlatformSharedMemoryHandle handle,
+      bool write_allowed,
+      uint64_t offset,
+      size_t size) override {
+    v8::VirtualAddressSpace* address_space = v8::V8::GetSandboxAddressSpace();
+    size_t allocation_granularity = address_space->allocation_granularity();
+
+    v8::PlatformSharedMemoryHandle v8_handle;
+#if BUILDFLAG(IS_MAC)
+    v8_handle = v8::SharedMemoryHandleFromMachMemoryEntry(handle);
+#elif BUILDFLAG(IS_FUCHSIA)
+    v8_handle = v8::SharedMemoryHandleFromVMO(handle->get());
+#elif BUILDFLAG(IS_WIN)
+    v8_handle = v8::SharedMemoryHandleFromFileMapping(handle);
+#elif BUILDFLAG(IS_ANDROID)
+    v8_handle = v8::SharedMemoryHandleFromFileDescriptor(handle);
+#elif BUILDFLAG(IS_POSIX)
+    v8_handle = v8::SharedMemoryHandleFromFileDescriptor(handle.fd);
+#else
+#error "Unknown platform"
+#endif
+
+    // Size and offset must be a multiple of the page allocation granularity.
+    // The caller already ensures that the offset is a multiple of the
+    // allocation granularity though.
+    CHECK_EQ(0UL, offset % allocation_granularity);
+    size_t mapping_size = base::bits::AlignUp(size, allocation_granularity);
+
+    v8::PagePermissions permissions = write_allowed
+                                          ? v8::PagePermissions::kReadWrite
+                                          : v8::PagePermissions::kRead;
+    uintptr_t mapping = v8::V8::GetSandboxAddressSpace()->AllocateSharedPages(
+        0, mapping_size, permissions, v8_handle, offset);
+    if (!mapping)
+      return absl::nullopt;
+
+    return base::make_span(reinterpret_cast<uint8_t*>(mapping), size);
+  }
+
+  void Unmap(base::span<uint8_t> mapping) override {
+    v8::VirtualAddressSpace* address_space = v8::V8::GetSandboxAddressSpace();
+    size_t allocation_granularity = address_space->allocation_granularity();
+
+    uintptr_t address = reinterpret_cast<uintptr_t>(mapping.data());
+    CHECK_EQ(0UL, address % allocation_granularity);
+    size_t mapping_size =
+        base::bits::AlignUp(mapping.size(), allocation_granularity);
+
+    address_space->FreeSharedPages(address, mapping_size);
+  }
+};
+#endif  // V8_SANDBOX
+
+base::SharedMemoryMapper* CreateSharedMemoryMapperForArrayBuffers() {
+#if V8_SANDBOX
+  static ArrayBufferSharedMemoryMapper instance;
+  // Currently, it is still possible for the sandbox to be disabled at runtime
+  // (by not initializing it), in which case the default shared memory mapper
+  // must be used. In the future, this will no longer be allowed and this helper
+  // function can then be removed entirely.
+  // TODO(saelo) remove once sandbox initialization is mandatory.
+  if (v8::V8::GetSandboxSizeInBytes() > 0)
+    return &instance;
+  else
+#endif
+    // Use nullptr here and let //base select the default mapper.
+    return nullptr;
+}
+}  // namespace
+
+base::SharedMemoryMapper* GetSharedMemoryMapperForArrayBuffers() {
+  static base::SharedMemoryMapper* mapper =
+      CreateSharedMemoryMapperForArrayBuffers();
+  return mapper;
+}
+
 }  // namespace gin
diff --git a/gin/array_buffer.h b/gin/array_buffer.h
index 8dfcf65b..cf8a650 100644
--- a/gin/array_buffer.h
+++ b/gin/array_buffer.h
@@ -10,6 +10,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_mapper.h"
 #include "gin/converter.h"
 #include "gin/gin_export.h"
 #include "v8/include/v8-array-buffer.h"
@@ -76,6 +77,8 @@
                      ArrayBufferView* out);
 };
 
+GIN_EXPORT base::SharedMemoryMapper* GetSharedMemoryMapperForArrayBuffers();
+
 }  // namespace gin
 
 #endif  // GIN_ARRAY_BUFFER_H_
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index 9dddcdf8..0723877 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -477,6 +477,13 @@
     base::internal::PartitionAddressSpace::InitConfigurablePool(pool_base,
                                                                 pool_size);
     // TODO(saelo) maybe record the size of the Pool into UMA.
+
+    // If this CHECK fails, it means that something used the array buffer
+    // shared memory mapper before the sandbox was initialized, which may then
+    // cause crashes later on as array buffers may have been mapped outside the
+    // sandbox. See GetSharedMemoryMapperForArrayBuffers(). TODO(saelo) remove
+    // once sandbox initialization is mandatory.
+    CHECK_NE(nullptr, GetSharedMemoryMapperForArrayBuffers());
   }
 #endif  // V8_SANDBOX
 }
diff --git a/gpu/GRAPHICS_TEAM_OWNERS b/gpu/GRAPHICS_TEAM_OWNERS
new file mode 100644
index 0000000..4cfead6
--- /dev/null
+++ b/gpu/GRAPHICS_TEAM_OWNERS
@@ -0,0 +1,101 @@
+## File enumerating all Chrome Graphics team members who should have
+## ownership over common files.
+
+# Chrome GPU Overall team
+vmiura@chromium.org
+geofflang@chromium.org
+fserb@chromium.org
+sadrul@chromium.org
+rockot@google.com
+rjkroege@chromium.org
+zmo@chromium.org
+
+# ANGLE team
+capn@chromium.org
+jmadill@chromium.org
+jonahr@chromium.org
+srisser@chromium.org
+sugoi@chromium.org
+syoussefi@chromium.org
+ynovikov@chromium.org
+
+# Canvas team
+aaronhk@chromium.org
+jpgravel@google.com
+juanmihd@chromium.org
+junov@chromium.org
+yiyix@chromium.org
+
+# Metrics team
+behdadb@chromium.org
+jonross@chromium.org
+mjzhang@chromium.org
+mohsen@chromium.org
+
+# Platforms Team
+backer@chromium.org
+boliu@chromium.org
+ccameron@chromium.org
+fangzhoug@chromium.org
+hitawala@chromium.org
+khaslett@chromium.org
+kylechar@chromium.org
+magchen@chromium.org
+penghuang@chromium.org
+petermcneeley@chromium.org
+rivr@chromium.org
+sunnyps@chromium.org
+vasilyt@chromium.org
+vikassoni@chromium.org
+
+# 3D Web team
+alanbaker@google.com
+amaiorano@google.com
+bajones@chromium.org
+bclayton@chromium.org
+cwallez@chromium.org
+dneto@google.com
+dsinclair@chromium.org
+enga@chromium.org
+gman@chromium.org
+jrprice@google.com
+kainino@chromium.org
+kbr@chromium.org
+lokokung@google.com
+rharrison@chromium.org
+ricardocabello@google.com
+shrekshao@google.com
+senorblanco@chromium.org
+
+# Skia
+brianosman@google.com
+bsalomon@google.com
+djsollen@google.com
+egdaniel@google.com
+fmalita@chromium.org
+jcgregorio@chromium.org
+
+# Skia Core / eSkia
+bungeman@chromium.org
+jlavrova@google.com
+scroggo@google.com
+tdenniston@google.com
+wrightgeorge@google.com
+
+# Skia GPU
+jvanverth@chromium.org
+michaelludwig@google.com
+robertphillips@google.com
+
+# Skia Infra
+borenet@google.com
+erikrose@google.com
+kjlubick@chromium.org
+lovisolo@google.com
+rmistry@chromium.org
+
+# SkSL
+armansito@chromium.org
+ethannicholas@chromium.org
+herb@google.com
+johnstiles@google.com
diff --git a/gpu/OWNERS b/gpu/OWNERS
index b2e87eeb..83d4d26 100644
--- a/gpu/OWNERS
+++ b/gpu/OWNERS
@@ -18,3 +18,6 @@
 
 # D3D, dcomp, Windows platform integration, etc.
 rafael.cintron@microsoft.com
+
+# The extended Chrome Graphics team should be able to self-administer.
+per-file GRAPHICS_TEAM_OWNERS=file://gpu/GRAPHICS_TEAM_OWNERS
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index 2a43b28..d3a7613 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -43,7 +43,6 @@
 #include "gpu/command_buffer/client/shared_memory_limits.h"
 #include "gpu/command_buffer/client/transfer_buffer.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
-#include "ui/base/ui_base_features.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -506,8 +505,7 @@
       lost_(false),
       max_inlined_entry_size_(kMaxTransferCacheEntrySizeForTransferBuffer),
       transfer_cache_(this),
-      image_decode_accelerator_(image_decode_accelerator),
-      raw_draw_(features::IsUsingRawDraw()) {
+      image_decode_accelerator_(image_decode_accelerator) {
   DCHECK(helper);
   DCHECK(transfer_buffer);
   DCHECK(gpu_control);
@@ -1410,7 +1408,7 @@
           raster_properties_->color_space, &skottie_serialization_history_,
           raster_properties_->can_use_lcd_text,
           capabilities().context_supports_distance_field_text,
-          capabilities().max_texture_size, raw_draw_));
+          capabilities().max_texture_size, /*raw_draw=*/true));
   if (preserve_recording) {
     serializer.Serialize(&list->paint_op_buffer_, &temp_raster_offsets_,
                          preamble);
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index fdd547e..d936b83 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -458,7 +458,6 @@
   cc::SkottieSerializationHistory skottie_serialization_history_;
 
   raw_ptr<ImageDecodeAcceleratorInterface> image_decode_accelerator_;
-  const bool raw_draw_;
 
   // Tracing helpers.
   int raster_chromium_id_ = 0;
diff --git a/ios/build/bots/scripts/PRESUBMIT.py b/ios/build/bots/scripts/PRESUBMIT.py
index 488b9c8..beda9baf04 100644
--- a/ios/build/bots/scripts/PRESUBMIT.py
+++ b/ios/build/bots/scripts/PRESUBMIT.py
@@ -11,6 +11,9 @@
 
 
 def _RunTestRunnerUnitTests(input_api, output_api):
+  # Don't run iOS tests on Windows.
+  if input_api.is_windows:
+    return []
   """ Runs iOS test runner unit tests """
   files = ['.*_test.py$']
 
diff --git a/media/gpu/v4l2/test/av1_decoder.cc b/media/gpu/v4l2/test/av1_decoder.cc
index 3ced9cf..2844280 100644
--- a/media/gpu/v4l2/test/av1_decoder.cc
+++ b/media/gpu/v4l2/test/av1_decoder.cc
@@ -104,6 +104,20 @@
   return ParsingResult::kOk;
 }
 
+void Av1Decoder::CopyFrameData(const libgav1::ObuFrameHeader& frame_hdr,
+                               std::unique_ptr<V4L2Queue>& queue) {
+  CHECK_EQ(queue->num_buffers(), 1u)
+      << "Only 1 buffer is expected to be used for OUTPUT queue for now.";
+
+  CHECK_EQ(queue->num_planes(), 1u)
+      << "Number of planes is expected to be 1 for OUTPUT queue.";
+
+  scoped_refptr<MmapedBuffer> buffer = queue->GetBuffer(0);
+
+  memcpy(static_cast<uint8_t*>(buffer->mmaped_planes()[0].start_addr),
+         ivf_frame_data_, ivf_frame_header_.frame_size);
+}
+
 VideoDecoder::Result Av1Decoder::DecodeNextFrame(std::vector<char>& y_plane,
                                                  std::vector<char>& u_plane,
                                                  std::vector<char>& v_plane,
@@ -118,17 +132,46 @@
     return VideoDecoder::kEOStream;
   }
 
+  libgav1::ObuFrameHeader current_frame_header = obu_parser_->frame_header();
+
   if (obu_parser_->sequence_header_changed())
     current_sequence_header_.emplace(obu_parser_->sequence_header());
 
   LOG_ASSERT(current_sequence_header_)
       << "Sequence header missing for decoding.";
 
+  CopyFrameData(current_frame_header, OUTPUT_queue_);
+
+  LOG_ASSERT(OUTPUT_queue_->num_buffers() == 1)
+      << "Too many buffers in OUTPUT queue. It is currently designed to "
+         "support only 1 request at a time.";
+
+  OUTPUT_queue_->GetBuffer(0)->set_frame_number(frame_number);
+
+  if (!v4l2_ioctl_->QBuf(OUTPUT_queue_, 0))
+    LOG(FATAL) << "VIDIOC_QBUF failed for OUTPUT queue.";
+
   // TODO(b/228534725): add changes to support reference frames management
 
   // TODO(b/228534730): add changes to prepare parameters for V4L2 AV1 stateless
   // decoding
 
+  if (!v4l2_ioctl_->MediaRequestIocQueue(OUTPUT_queue_))
+    LOG(FATAL) << "MEDIA_REQUEST_IOC_QUEUE failed.";
+
+  uint32_t index;
+
+  if (!v4l2_ioctl_->DQBuf(CAPTURE_queue_, &index))
+    LOG(FATAL) << "VIDIOC_DQBUF failed for CAPTURE queue.";
+
+  scoped_refptr<MmapedBuffer> buffer = CAPTURE_queue_->GetBuffer(index);
+
+  if (!v4l2_ioctl_->DQBuf(OUTPUT_queue_, &index))
+    LOG(FATAL) << "VIDIOC_DQBUF failed for OUTPUT queue.";
+
+  if (!v4l2_ioctl_->MediaRequestIocReinit(OUTPUT_queue_))
+    LOG(FATAL) << "MEDIA_REQUEST_IOC_REINIT failed.";
+
   return VideoDecoder::kOk;
 }
 
diff --git a/media/gpu/v4l2/test/av1_decoder.h b/media/gpu/v4l2/test/av1_decoder.h
index b7fc74e..074dab8 100644
--- a/media/gpu/v4l2/test/av1_decoder.h
+++ b/media/gpu/v4l2/test/av1_decoder.h
@@ -76,6 +76,10 @@
   // set upon completion.
   ParsingResult ReadNextFrame(libgav1::RefCountedBufferPtr& current_frame);
 
+  // Copies the frame data into the V4L2 buffer of OUTPUT |queue|.
+  void CopyFrameData(const libgav1::ObuFrameHeader& frame_hdr,
+                     std::unique_ptr<V4L2Queue>& queue);
+
   IvfFrameHeader ivf_frame_header_{};
   const uint8_t* ivf_frame_data_ = nullptr;
 
diff --git a/media/gpu/v4l2/test/vp9_decoder.cc b/media/gpu/v4l2/test/vp9_decoder.cc
index 2c7ae41..1dd48d8 100644
--- a/media/gpu/v4l2/test/vp9_decoder.cc
+++ b/media/gpu/v4l2/test/vp9_decoder.cc
@@ -482,7 +482,7 @@
   FillV4L2VP9SegmentationParams(segm_params, &v4l2_frame_params->seg);
 }
 
-bool Vp9Decoder::CopyFrameData(const Vp9FrameHeader& frame_hdr,
+void Vp9Decoder::CopyFrameData(const Vp9FrameHeader& frame_hdr,
                                std::unique_ptr<V4L2Queue>& queue) {
   LOG_ASSERT(queue->num_buffers() == 1)
       << "Only 1 buffer is expected to be used for OUTPUT queue for now.";
@@ -492,8 +492,8 @@
 
   scoped_refptr<MmapedBuffer> buffer = queue->GetBuffer(0);
 
-  return memcpy(static_cast<uint8_t*>(buffer->mmaped_planes()[0].start_addr),
-                frame_hdr.data, frame_hdr.frame_size);
+  memcpy(static_cast<uint8_t*>(buffer->mmaped_planes()[0].start_addr),
+         frame_hdr.data, frame_hdr.frame_size);
 }
 
 VideoDecoder::Result Vp9Decoder::DecodeNextFrame(std::vector<char>& y_plane,
@@ -520,8 +520,7 @@
   VLOG_IF(2, !frame_hdr.show_frame) << "not displaying frame";
   last_decoded_frame_visible_ = frame_hdr.show_frame;
 
-  if (!CopyFrameData(frame_hdr, OUTPUT_queue_))
-    LOG(FATAL) << "Failed to copy the frame data into the V4L2 buffer.";
+  CopyFrameData(frame_hdr, OUTPUT_queue_);
 
   LOG_ASSERT(OUTPUT_queue_->num_buffers() == 1)
       << "Too many buffers in OUTPUT queue. It is currently designed to "
diff --git a/media/gpu/v4l2/test/vp9_decoder.h b/media/gpu/v4l2/test/vp9_decoder.h
index 03fe5d3..d5144cb 100644
--- a/media/gpu/v4l2/test/vp9_decoder.h
+++ b/media/gpu/v4l2/test/vp9_decoder.h
@@ -54,7 +54,7 @@
                                   gfx::Size& size);
 
   // Copies the frame data into the V4L2 buffer of OUTPUT |queue|.
-  bool CopyFrameData(const Vp9FrameHeader& frame_hdr,
+  void CopyFrameData(const Vp9FrameHeader& frame_hdr,
                      std::unique_ptr<V4L2Queue>& queue);
 
   // Sets up per frame parameters |v4l2_frame_params| needed for VP9 decoding
diff --git a/mojo/public/cpp/base/shared_memory_utils.cc b/mojo/public/cpp/base/shared_memory_utils.cc
index 172f28c..e88d5f8 100644
--- a/mojo/public/cpp/base/shared_memory_utils.cc
+++ b/mojo/public/cpp/base/shared_memory_utils.cc
@@ -25,12 +25,14 @@
   return mojo::UnwrapWritableSharedMemoryRegion(std::move(handle));
 }
 
-base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size) {
+base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(
+    size_t size,
+    base::SharedMemoryMapper* mapper) {
   auto writable_region = CreateWritableSharedMemoryRegion(size);
   if (!writable_region.IsValid())
     return {};
 
-  base::WritableSharedMemoryMapping mapping = writable_region.Map();
+  base::WritableSharedMemoryMapping mapping = writable_region.Map(mapper);
   return {base::WritableSharedMemoryRegion::ConvertToReadOnly(
               std::move(writable_region)),
           std::move(mapping)};
diff --git a/ppapi/PRESUBMIT.py b/ppapi/PRESUBMIT.py
index b3a11d6a..f518d31 100644
--- a/ppapi/PRESUBMIT.py
+++ b/ppapi/PRESUBMIT.py
@@ -86,7 +86,7 @@
         todo.append(filename)
 
   if todo:
-    return [output_api.PresubmitError(
+    return [output_api.PresubmitPromptWarning(
         'TODOs found in stable public PPAPI files:',
         long_text='\n'.join(todo))]
   return []
diff --git a/remoting/android/BUILD.gn b/remoting/android/BUILD.gn
index 939dd7a..d19b08c 100644
--- a/remoting/android/BUILD.gn
+++ b/remoting/android/BUILD.gn
@@ -133,7 +133,6 @@
   deps = [
     ":credits_resources_raw",
     "//remoting/resources:strings_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
   ]
 }
 
diff --git a/remoting/android/client_java_tmpl.gni b/remoting/android/client_java_tmpl.gni
index 373788f..bffc012 100644
--- a/remoting/android/client_java_tmpl.gni
+++ b/remoting/android/client_java_tmpl.gni
@@ -87,11 +87,11 @@
       "//remoting/android:remoting_android_client_java_resources",
       "//remoting/android:remoting_apk_manifest",
       "//remoting/proto/remoting/v1:directory_proto_java",
-      "//third_party/android_deps:android_support_v7_appcompat_java",
       "//third_party/android_deps:protobuf_lite_runtime_java",
       "//third_party/androidx:androidx_annotation_annotation_java",
+      "//third_party/androidx:androidx_appcompat_appcompat_java",
+      "//third_party/androidx:androidx_core_core_java",
       "//third_party/androidx:androidx_drawerlayout_drawerlayout_java",
-      "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
       "//third_party/androidx:androidx_mediarouter_mediarouter_java",
       "//ui/android:ui_utils_java",
     ]
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 6086503..289671a1 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -205,9 +205,6 @@
 // Temporarily insulate Chrome pixel tests from Skia LOD bias change on GPU.
 #define SK_USE_LEGACY_MIPMAP_LOD_BIAS
 
-// Temporarily insulate Chrome layout tests from change to HW stroking
-#define SK_LEGACY_LINE_TESSELLATION
-
 // Max. verb count for paths rendered by the edge-AA tessellating path renderer.
 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 100
 
diff --git a/skia/public/mojom/image_info_mojom_traits.cc b/skia/public/mojom/image_info_mojom_traits.cc
index de7b7b6..fe5b162 100644
--- a/skia/public/mojom/image_info_mojom_traits.cc
+++ b/skia/public/mojom/image_info_mojom_traits.cc
@@ -4,6 +4,7 @@
 
 #include "skia/public/mojom/image_info_mojom_traits.h"
 
+#include "base/numerics/checked_math.h"
 #include "base/numerics/safe_conversions.h"
 #include "mojo/public/cpp/bindings/array_data_view.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -20,6 +21,8 @@
                             int height,
                             mojo::ArrayDataView<float> color_transfer_function,
                             mojo::ArrayDataView<float> color_to_xyz_matrix) {
+  CHECK_GE(width, 0);
+  CHECK_GE(height, 0);
   sk_sp<SkColorSpace> color_space;
   if (!color_transfer_function.is_null() && !color_to_xyz_matrix.is_null()) {
     const float* data = color_transfer_function.data();
@@ -208,9 +211,16 @@
   mojo::ArrayDataView<float> color_to_xyz_matrix;
   data.GetColorToXyzMatrixDataView(&color_to_xyz_matrix);
 
-  *info = MakeSkImageInfo(color_type, alpha_type, data.width(), data.height(),
-                          std::move(color_transfer_function),
-                          std::move(color_to_xyz_matrix));
+  // The ImageInfo wire types are uint32_t, but the Skia type uses int, and the
+  // values can't be negative.
+  auto width = base::MakeCheckedNum(data.width()).Cast<int>();
+  auto height = base::MakeCheckedNum(data.height()).Cast<int>();
+  if (!width.IsValid() || !height.IsValid())
+    return false;
+
+  *info = MakeSkImageInfo(
+      color_type, alpha_type, width.ValueOrDie(), height.ValueOrDie(),
+      std::move(color_transfer_function), std::move(color_to_xyz_matrix));
   return true;
 }
 
@@ -227,9 +237,16 @@
   mojo::ArrayDataView<float> color_to_xyz_matrix;
   data.GetColorToXyzMatrixDataView(&color_to_xyz_matrix);
 
-  *info = MakeSkImageInfo(kN32_SkColorType, alpha_type, data.width(),
-                          data.height(), std::move(color_transfer_function),
-                          std::move(color_to_xyz_matrix));
+  // The ImageInfo wire types are uint32_t, but the Skia type uses int, and the
+  // values can't be negative.
+  auto width = base::MakeCheckedNum(data.width()).Cast<int>();
+  auto height = base::MakeCheckedNum(data.height()).Cast<int>();
+  if (!width.IsValid() || !height.IsValid())
+    return false;
+
+  *info = MakeSkImageInfo(
+      kN32_SkColorType, alpha_type, width.ValueOrDie(), height.ValueOrDie(),
+      std::move(color_transfer_function), std::move(color_to_xyz_matrix));
   return true;
 }
 
diff --git a/skia/public/mojom/test/mojom_traits_unittest.cc b/skia/public/mojom/test/mojom_traits_unittest.cc
index 279f297f..4850da4 100644
--- a/skia/public/mojom/test/mojom_traits_unittest.cc
+++ b/skia/public/mojom/test/mojom_traits_unittest.cc
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits>
 #include "mojo/public/cpp/test_support/test_utils.h"
 #include "skia/public/mojom/bitmap.mojom.h"
 #include "skia/public/mojom/bitmap_skbitmap_mojom_traits.h"
+#include "skia/public/mojom/image_info.mojom-shared.h"
 #include "skia/public/mojom/image_info.mojom.h"
 #include "skia/public/mojom/tile_mode.mojom.h"
 #include "skia/public/mojom/tile_mode_mojom_traits.h"
@@ -69,6 +71,21 @@
   return mojom_bitmap;
 }
 
+// A helper to construct a skia.mojom.ImageInfo without using StructTraits
+// to bypass checks on the sending/serialization side.
+mojo::StructPtr<skia::mojom::ImageInfo> ConstructImageInfo(
+    SkColorType color_type,
+    SkAlphaType alpha_type,
+    uint32_t width,
+    uint32_t height) {
+  auto mojom_info = skia::mojom::ImageInfo::New();
+  mojom_info->color_type = color_type;
+  mojom_info->alpha_type = alpha_type;
+  mojom_info->width = width;
+  mojom_info->height = height;
+  return mojom_info;
+}
+
 TEST(StructTraitsTest, ImageInfo) {
   SkImageInfo input = SkImageInfo::Make(
       34, 56, SkColorType::kGray_8_SkColorType,
@@ -88,6 +105,31 @@
   EXPECT_EQ(another_input_with_null_color_space, output);
 }
 
+// We catch negative integers on the sending side and crash, when struct traits
+// are used.
+TEST(StructTraitsDeathTest, ImageInfoOverflowSizeWithStructTrait) {
+  SkImageInfo input = SkImageInfo::Make(
+      std::numeric_limits<uint32_t>::max(),
+      std::numeric_limits<uint32_t>::max(), SkColorType::kGray_8_SkColorType,
+      SkAlphaType::kUnpremul_SkAlphaType,
+      SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kAdobeRGB));
+  SkImageInfo output;
+  EXPECT_DEATH(skia::mojom::ImageInfo::SerializeAsMessage(&input), "");
+}
+
+// We must reject sizes that would cause integer overflow on the receiving side.
+// The wire format is `uint32_t`, but Skia needs us to convert that to an `int`
+// for the SkImageInfo type.
+TEST(StructTraitsTest, ImageInfoOverflowSizeWithoutStructTrait) {
+  SkImageInfo output;
+  mojo::StructPtr<skia::mojom::ImageInfo> input = ConstructImageInfo(
+      SkColorType::kGray_8_SkColorType, SkAlphaType::kUnpremul_SkAlphaType,
+      std::numeric_limits<uint32_t>::max(),
+      std::numeric_limits<uint32_t>::max());
+  EXPECT_FALSE(mojo::test::SerializeAndDeserialize<skia::mojom::ImageInfo>(
+      input, output));
+}
+
 TEST(StructTraitsTest, ImageInfoCustomColorSpace) {
   skcms_TransferFunction transfer{0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f};
   skcms_Matrix3x3 gamut{
diff --git a/testing/PRESUBMIT.py b/testing/PRESUBMIT.py
index 6a5524c..61b4fc1 100644
--- a/testing/PRESUBMIT.py
+++ b/testing/PRESUBMIT.py
@@ -23,9 +23,7 @@
       input_api,
       output_api,
       '.',
-      [r'^.+_unittest\.py$'],
-      run_on_python3=USE_PYTHON3,
-      skip_shebang_check=True))
+      [r'^.+_unittest\.py$']))
   output.extend(input_api.canned_checks.RunUnitTestsInDirectory(
       input_api,
       output_api,
@@ -38,6 +36,21 @@
   output.extend(input_api.canned_checks.RunPylint(
       input_api,
       output_api,
+      files_to_skip=[r'gmock.*', r'gtest.*',
+          r'buildbot.*', r'merge_scripts.*', r'trigger_scripts.*',
+          r'unexpected_passes_common.*',
+          r'clusterfuzz.*',
+          r'libfuzzer.*']))
+  # Pylint2.7 is run on subdirs whose presubmit checks are migrated to Python3
+  output.extend(input_api.canned_checks.RunPylint(
+      input_api,
+      output_api,
+      files_to_check=[r'buildbot.*\.py$',
+          r'merge_scripts.*\.py$',
+          r'trigger_scripts.*\.py$',
+          r'unexpected_passes_common.*\.py$',
+          r'clusterfuzz.*\.py$',
+          r'libfuzzer.*\.py$'],
       version='2.7'))
 
   return output
diff --git a/testing/__init__.py b/testing/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/testing/__init__.py
+++ /dev/null
diff --git a/testing/buildbot/PRESUBMIT_test.py b/testing/buildbot/PRESUBMIT_test.py
index 38e97fc..0ef25a4 100755
--- a/testing/buildbot/PRESUBMIT_test.py
+++ b/testing/buildbot/PRESUBMIT_test.py
@@ -3,14 +3,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
-import sys
 import time
 import unittest
 
-# Add src/testing/ into sys.path for importing PRESUBMIT without pylint errors.
-sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
-from buildbot import PRESUBMIT
+import PRESUBMIT
 
 
 class PresubmitError:
diff --git a/testing/buildbot/__init__.py b/testing/buildbot/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/testing/buildbot/__init__.py
+++ /dev/null
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index ab7c726..bd656cf1e 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -8414,7 +8414,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8498,7 +8498,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -8918,7 +8918,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 6c930398..400e5948 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -46015,7 +46015,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46099,7 +46099,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M102",
-              "revision": "version:102.0.5005.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -46519,7 +46519,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47027,7 +47027,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -47531,7 +47531,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48106,7 +48106,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -48610,7 +48610,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49185,7 +49185,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -49689,7 +49689,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M101",
-              "revision": "version:101.0.4951.49"
+              "revision": "version:101.0.4951.50"
             },
             {
               "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.22"
+              "revision": "version:102.0.5005.23"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index c13fc3d..f5826d3 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -9428,7 +9428,7 @@
         "args": [
           "--browser-ui-tests-verify-pixels",
           "--enable-pixel-output-in-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_browser_tests.filter",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter",
           "--git-revision=${got_revision}"
         ],
         "isolate_profile_data": true,
@@ -10187,6 +10187,37 @@
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
       },
       {
+        "args": [
+          "--browser-ui-tests-verify-pixels",
+          "--enable-pixel-output-in-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "pixel_interactive_ui_tests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-11-22000"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
+      },
+      {
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -104417,7 +104448,7 @@
         "args": [
           "--browser-ui-tests-verify-pixels",
           "--enable-pixel-output-in-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_browser_tests.filter",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter",
           "--git-revision=${got_revision}"
         ],
         "merge": {
@@ -104444,6 +104475,38 @@
         },
         "test": "browser_tests",
         "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--browser-ui-tests-verify-pixels",
+          "--enable-pixel-output-in-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter",
+          "--git-revision=${got_revision}"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "pixel_interactive_ui_tests",
+        "non_precommit_args": [
+          "--test-launcher-retry-limit=0"
+        ],
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Windows-10-19042"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
       }
     ]
   },
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index 5d892790..781e2fa 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -2378,7 +2378,7 @@
         "args": [
           "--browser-ui-tests-verify-pixels",
           "--enable-pixel-output-in-tests",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_browser_tests.filter",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter",
           "--git-revision=${got_revision}"
         ],
         "isolate_profile_data": true,
@@ -3139,6 +3139,37 @@
         "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
       },
       {
+        "args": [
+          "--browser-ui-tests-verify-pixels",
+          "--enable-pixel-output-in-tests",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter",
+          "--git-revision=${got_revision}"
+        ],
+        "isolate_profile_data": true,
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "pixel_interactive_ui_tests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-19042"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "interactive_ui_tests",
+        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
+      },
+      {
         "isolate_profile_data": true,
         "merge": {
           "args": [],
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 25a91f5f..5020f65 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -72,7 +72,7 @@
     "//testing/buildbot/filters/mac.mac11-arm64-rel.browser_tests.filter",
     "//testing/buildbot/filters/mac.mac-rel.browser_tests.filter",
     "//testing/buildbot/filters/ozone-linux.wayland_browser_tests.filter",
-    "//testing/buildbot/filters/pixel_browser_tests.filter",
+    "//testing/buildbot/filters/pixel_tests.filter",
     "//testing/buildbot/filters/webrtc_functional.browser_tests.filter",
     "//testing/buildbot/filters/win_backuprefptr_fyi.browser_tests.filter",
   ]
@@ -300,6 +300,7 @@
   data = [
     "//testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter",
     "//testing/buildbot/filters/ozone-linux.interactive_ui_tests_wayland.filter",
+    "//testing/buildbot/filters/pixel_tests.filter",
   ]
 }
 
diff --git a/testing/buildbot/filters/pixel_browser_tests.filter b/testing/buildbot/filters/pixel_tests.filter
similarity index 77%
rename from testing/buildbot/filters/pixel_browser_tests.filter
rename to testing/buildbot/filters/pixel_tests.filter
index bc3b0d9..9354c96 100644
--- a/testing/buildbot/filters/pixel_browser_tests.filter
+++ b/testing/buildbot/filters/pixel_tests.filter
@@ -1,67 +1,80 @@
-# This is the filter for browser_tests that supports pixel tests.

-AccuracyTipBubbleViewDialogBrowserTest.*

-AppInfoDialogBrowserTest.*

-AskGoogleForSuggestionsDialogTest.*

-BookmarkBubbleViewBrowserTest.*

-BookmarkEditorViewBrowserTest.*

-ChromeLabsUiTest.*

-ConfirmBubbleTest.*

-ContentAnalysysDialogUiTest.*

-ContentSettingBubbleDialogTest.*

-CookieControlsBubbleViewTest.*

-CryptoModulePasswordDialogTest.*

-DeepScanningFailureModalDialogTest.*

-*DiceWebSigninInterceptionBubblePixelTest.InvokeUi_*

-*DiceWebSigninInterceptionBubbleSyncPromoPixelTest.InvokeUi_*

-ExtensionInstallDialogViewInteractiveBrowserTest.*

-ExtensionUninstallDialogViewInteractiveBrowserTest.*

-*EyeDropperBrowserTest.*

-FeaturePromoDialogTest.*

-FileSystemAccessUsageBubbleViewTest.*

-FirstRunDialogTest.*

-GlobalErrorBubbleTest.*

-HatsBubbleTest.*

-HungRendererDialogViewBrowserTest.*

-ImportLockDialogViewBrowserTest.*

-InlineLoginHelperBrowserTest.InvokeUi_*

-LocalCardMigrationBrowserUiTest.*

-NewTabPageTest.*

-OneTimePermissionPromptBubbleViewBrowserTest.*

-OutdatedUpgradeBubbleTest.*

-PageInfoBubbleViewAboutThisSiteDialogBrowserTest.*

-PageInfoBubbleViewDialogBrowserTest.*

-PageInfoBubbleViewPrivacySandboxDialogBrowserTest.*

-PageInfoBubbleViewHistoryDialogBrowserTest.*

-*PasswordBubbleBrowserTest.*

-PasswordReuseModalWarningTest.*

-PermissionRequestChipDialogBrowserTest.*

-*PermissionPromptBubbleViewBrowserTest.*

-PrivacySandboxDialogViewBrowserTest.*

-PromptForScanningModalDialogTest.*

-QRCodeGeneratorBubbleBrowserTest.*

-RelaunchRecommendedBubbleViewDialogTest.*

-RelaunchRequiredDialogViewDialogTest.*

-ReopenTabPromoControllerDialogBrowserTest.*

-SafetyTipPageInfoBubbleViewDialogTest.*

-ScreenCaptureNotificationUiBrowserTest.*

-SecurePaymentConfirmationDialogViewTest.InvokeUi_*

-*SendTabToSelfBubbleTest.*

-SessionCrashedBubbleViewTest.*

-TabGroupEditorBubbleViewDialogBrowserTest.*

-TabHoverCardBubbleViewBrowserTest.*

-UpdateRecommendedDialogTest.*

-WebAppConfirmViewBrowserTest.*

-ZoomBubbleDialogTest.*

-

-# This test uses random network port and shows it on ui.

--ContentSettingBubbleDialogTest.InvokeUi_popups

--OutdatedUpgradeBubbleTest.InvokeUi_Critical

-# TODO(crbug.com/1108988): Fix this flakiness test.

--BookmarkBubbleViewBrowserTest.InvokeUi_bookmark_details_signed_in

-

-# TODO(crbug.com/2666206): Flaky

--ContentSettingBubbleDialogTest.InvokeUi_mediastream_camera

--ContentSettingBubbleDialogTest.InvokeUi_mediastream_mic_and_camera

-

-# TODO(crbug.com/1164574): Flaky

--ContentSettingBubbleDialogTest.InvokeUi_mediastream_mic

+# This is the filter for browser_tests and interactive_ui_tests that support
+# pixel tests. In order for pixel tests to work, you *must* include your test
+# here.
+#
+# Since these tests will all run in both browser_tests (or interactive_ui_tests)
+# and pixel_browser_tests (or pixel_interactive_ui_tests), avoid including
+# non-pixel tests here.
+#
+# Prefer browser_tests to interactive_ui_tests, as they are less expensive to
+# run; only use interactive_ui_tests if running your test in browser_tests
+# causes flakes (due to widget activation, occlusion, etc.)
+
+AccuracyTipBubbleViewDialogBrowserTest.*
+AppInfoDialogBrowserTest.*
+AskGoogleForSuggestionsDialogTest.*
+BookmarkBubbleViewBrowserTest.*
+BookmarkEditorViewBrowserTest.*
+ChromeLabsUiTest.*
+ConfirmBubbleTest.*
+ContentAnalysysDialogUiTest.*
+ContentSettingBubbleDialogTest.*
+CookieControlsBubbleViewTest.*
+CryptoModulePasswordDialogTest.*
+DeepScanningFailureModalDialogTest.*
+*DiceWebSigninInterceptionBubblePixelTest.InvokeUi_*
+*DiceWebSigninInterceptionBubbleSyncPromoPixelTest.InvokeUi_*
+ExtensionInstallDialogViewInteractiveBrowserTest.*
+ExtensionUninstallDialogViewInteractiveBrowserTest.*
+*EyeDropperBrowserTest.*
+FeaturePromoDialogIntentChipTest.InvokeUi_*
+FeaturePromoDialogTest.InvokeUi_*
+FileSystemAccessUsageBubbleViewTest.*
+FirstRunDialogTest.*
+GlobalErrorBubbleTest.*
+HatsBubbleTest.*
+HungRendererDialogViewBrowserTest.*
+ImportLockDialogViewBrowserTest.*
+InlineLoginHelperBrowserTest.InvokeUi_*
+InteractionSequenceBrowserUtilTest.CompareScreenshot_*
+LocalCardMigrationBrowserUiTest.*
+NewTabPageTest.*
+OneTimePermissionPromptBubbleViewBrowserTest.*
+OutdatedUpgradeBubbleTest.*
+PageInfoBubbleViewAboutThisSiteDialogBrowserTest.*
+PageInfoBubbleViewDialogBrowserTest.*
+PageInfoBubbleViewPrivacySandboxDialogBrowserTest.*
+PageInfoBubbleViewHistoryDialogBrowserTest.*
+*PasswordBubbleBrowserTest.*
+PasswordReuseModalWarningTest.*
+PermissionRequestChipDialogBrowserTest.*
+*PermissionPromptBubbleViewBrowserTest.*
+PrivacySandboxDialogViewBrowserTest.*
+PromptForScanningModalDialogTest.*
+QRCodeGeneratorBubbleBrowserTest.*
+RelaunchRecommendedBubbleViewDialogTest.*
+RelaunchRequiredDialogViewDialogTest.*
+ReopenTabPromoControllerDialogBrowserTest.*
+SafetyTipPageInfoBubbleViewDialogTest.*
+ScreenCaptureNotificationUiBrowserTest.*
+SecurePaymentConfirmationDialogViewTest.InvokeUi_*
+*SendTabToSelfBubbleTest.*
+SessionCrashedBubbleViewTest.*
+TabGroupEditorBubbleViewDialogBrowserTest.*
+TabHoverCardBubbleViewBrowserTest.*
+UpdateRecommendedDialogTest.*
+WebAppConfirmViewBrowserTest.*
+ZoomBubbleDialogTest.*
+
+# This test uses random network port and shows it on ui.
+-ContentSettingBubbleDialogTest.InvokeUi_popups
+-OutdatedUpgradeBubbleTest.InvokeUi_Critical
+# TODO(crbug.com/1108988): Fix this flakiness test.
+-BookmarkBubbleViewBrowserTest.InvokeUi_bookmark_details_signed_in
+
+# TODO(crbug.com/2666206): Flaky
+-ContentSettingBubbleDialogTest.InvokeUi_mediastream_camera
+-ContentSettingBubbleDialogTest.InvokeUi_mediastream_mic_and_camera
+
+# TODO(crbug.com/1164574): Flaky
+-ContentSettingBubbleDialogTest.InvokeUi_mediastream_mic
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 2723558..45b5cddc 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -2581,6 +2581,19 @@
       },
     },
   },
+  'pixel_interactive_ui_tests': {
+    'modifications': {
+      'win-pixel-tester-rel': {
+        'non_precommit_args': [
+          # Do not allow retry or it will break the bulk approval process.
+          # When retry with fail-pass pattern, the passing test will overwrite the previously
+          # seen flaky version on the trace. We can triage the image using the printed link,
+          # but it will not show on the Skia Gold search page.
+          '--test-launcher-retry-limit=0',
+        ],
+      },
+    },
+  },
   'pixel_skia_gold_passthrough_test': {
     'modifications': {
       'Android FYI Release (Pixel 4)': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index a0bd72c..ad08652 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4615,13 +4615,25 @@
         'args': [
           '--browser-ui-tests-verify-pixels',
           '--enable-pixel-output-in-tests',
-          '--test-launcher-filter-file=../../testing/buildbot/filters/pixel_browser_tests.filter',
+          '--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter',
         ],
         'test': 'browser_tests',
         'mixins': [
           'skia_gold_test',
         ],
       },
+      'pixel_interactive_ui_tests': {
+        'name': 'pixel_interactive_ui_tests',
+        'args': [
+          '--browser-ui-tests-verify-pixels',
+          '--enable-pixel-output-in-tests',
+          '--test-launcher-filter-file=../../testing/buildbot/filters/pixel_tests.filter',
+        ],
+        'test': 'interactive_ui_tests',
+        'mixins': [
+          'skia_gold_test',
+        ],
+      },
     },
 
     # TODO(dpranke): These are run on the p/chromium waterfall; they should
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 4592b93..07fd18fc 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.22',
+          'revision': 'version:102.0.5005.23',
         }
       ],
     },
@@ -473,7 +473,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M101',
-          'revision': 'version:101.0.4951.49',
+          'revision': 'version:101.0.4951.50',
         }
       ],
     },
@@ -593,7 +593,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.22',
+          'revision': 'version:102.0.5005.23',
         }
       ],
     },
@@ -617,7 +617,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M101',
-          'revision': 'version:101.0.4951.49',
+          'revision': 'version:101.0.4951.50',
         }
       ],
     },
@@ -737,7 +737,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M102',
-          'revision': 'version:102.0.5005.22',
+          'revision': 'version:102.0.5005.23',
         }
       ],
     },
@@ -761,7 +761,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M101',
-          'revision': 'version:101.0.4951.49',
+          'revision': 'version:101.0.4951.50',
         }
       ],
     },
diff --git a/testing/chromoting/browser_tests_launcher.py b/testing/chromoting/browser_tests_launcher.py
index c0dd849..6f0ba62 100644
--- a/testing/chromoting/browser_tests_launcher.py
+++ b/testing/chromoting/browser_tests_launcher.py
@@ -85,7 +85,7 @@
       retries += 1
       time.sleep(30)
       continue
-    if jids_used:
+    elif jids_used:
       print('JID used by test matched me2me host JID: %s' % host_jid)
     else:
       # There wasn't a mismatch and no JIDs were returned. If no JIDs were
diff --git a/testing/merge_scripts/code_coverage/merge_lib.py b/testing/merge_scripts/code_coverage/merge_lib.py
index 15a86e50..1699e958 100644
--- a/testing/merge_scripts/code_coverage/merge_lib.py
+++ b/testing/merge_scripts/code_coverage/merge_lib.py
@@ -70,7 +70,8 @@
   paths = []
   for dir_path, _sub_dirs, file_names in os.walk(input_dir):
     paths.extend([
-        os.path.join(dir_path, fn)
+        # Normalize to POSIX style paths for consistent results.
+        os.path.join(dir_path, fn).replace('\\', '/')
         for fn in file_names
         if fn.endswith(input_extension) and re.search(input_filename_pattern,fn)
     ])
diff --git a/testing/scripts/__init__.py b/testing/scripts/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/testing/scripts/__init__.py
+++ /dev/null
diff --git a/testing/scripts/blink_lint_expectations.py b/testing/scripts/blink_lint_expectations.py
index c1f2ad9..bb26b91 100755
--- a/testing/scripts/blink_lint_expectations.py
+++ b/testing/scripts/blink_lint_expectations.py
@@ -7,10 +7,8 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/blink_python_tests.py b/testing/scripts/blink_python_tests.py
index a9c854b..c7f14e68 100755
--- a/testing/scripts/blink_python_tests.py
+++ b/testing/scripts/blink_python_tests.py
@@ -7,10 +7,8 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/check_gn_headers.py b/testing/scripts/check_gn_headers.py
index ccf0ba21..6530ada7 100755
--- a/testing/scripts/check_gn_headers.py
+++ b/testing/scripts/check_gn_headers.py
@@ -7,10 +7,8 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/check_network_annotations.py b/testing/scripts/check_network_annotations.py
index 7d05b49a..6e2875d8 100755
--- a/testing/scripts/check_network_annotations.py
+++ b/testing/scripts/check_network_annotations.py
@@ -12,10 +12,8 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/check_static_initializers.py b/testing/scripts/check_static_initializers.py
index dc9b1d27..3afd112 100755
--- a/testing/scripts/check_static_initializers.py
+++ b/testing/scripts/check_static_initializers.py
@@ -11,10 +11,7 @@
 import subprocess
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 # A list of files that are allowed to have static initializers.
 # If something adds a static initializer, revert it. We don't accept regressions
diff --git a/testing/scripts/checkbins.py b/testing/scripts/checkbins.py
index af8208dc..f66c8b1 100755
--- a/testing/scripts/checkbins.py
+++ b/testing/scripts/checkbins.py
@@ -21,14 +21,11 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 WIN_PY3_TARGETS = ['python3.exe', 'python3.bat']
 
-
 def with_python3():
   if sys.version_info.major >= 3:
     return sys.executable
diff --git a/testing/scripts/checkdeps.py b/testing/scripts/checkdeps.py
index 9c49d82..e772ae87 100755
--- a/testing/scripts/checkdeps.py
+++ b/testing/scripts/checkdeps.py
@@ -8,10 +8,7 @@
 import sys
 
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/checklicenses.py b/testing/scripts/checklicenses.py
index 3a51fcf6..43342d0 100755
--- a/testing/scripts/checklicenses.py
+++ b/testing/scripts/checklicenses.py
@@ -8,10 +8,7 @@
 import sys
 
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/checkperms.py b/testing/scripts/checkperms.py
index e635aa1..8adb025 100755
--- a/testing/scripts/checkperms.py
+++ b/testing/scripts/checkperms.py
@@ -8,10 +8,7 @@
 import sys
 
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/common.py b/testing/scripts/common.py
index d57595c..1c31066d 100644
--- a/testing/scripts/common.py
+++ b/testing/scripts/common.py
@@ -18,8 +18,7 @@
 logging.basicConfig(level=logging.INFO)
 
 # Add src/testing/ into sys.path for importing xvfb and test_env.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
 import test_env
 if sys.platform.startswith('linux'):
   import xvfb
@@ -66,8 +65,6 @@
     '\\ALL RESTRICTED APPLICATION PACKAGES:(I)(OI)(CI)(RX)'
 ]
 
-# pylint: disable=useless-object-inheritance
-
 
 def set_lpac_acls(acl_dir, is_test_script=False):
   """Sets LPAC ACLs on a directory. Windows 10 only."""
@@ -234,7 +231,7 @@
   passing_statuses = ('PASS', 'SLOW', 'NEEDSREBASELINE')
 
   for test, result in convert_trie_to_flat_paths(
-      json_results['tests']).items():
+      json_results['tests']).iteritems():
     key = 'unexpected_' if result.get('is_unexpected') else ''
     data = result['actual']
     actual_results = data.split()
diff --git a/testing/scripts/content_shell_crash_test.py b/testing/scripts/content_shell_crash_test.py
index 60f76a75..fe3515a7 100755
--- a/testing/scripts/content_shell_crash_test.py
+++ b/testing/scripts/content_shell_crash_test.py
@@ -9,11 +9,11 @@
 import sys
 
 
-# Add src/testing/ into sys.path for importing xvfb and common.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
+import common
+
+# Add src/testing/ into sys.path for importing xvfb.
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
 import xvfb
-from scripts import common
 
 
 # Unfortunately we need to copy these variables from ../test_env.py.
diff --git a/testing/scripts/count_filtered_tests.py b/testing/scripts/count_filtered_tests.py
index 588c87f..97bb8f54 100644
--- a/testing/scripts/count_filtered_tests.py
+++ b/testing/scripts/count_filtered_tests.py
@@ -9,10 +9,7 @@
 import sys
 
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def ParseTestList(test_list_contents):
diff --git a/testing/scripts/get_compile_targets.py b/testing/scripts/get_compile_targets.py
index 75e15dc9d..3772d50a 100755
--- a/testing/scripts/get_compile_targets.py
+++ b/testing/scripts/get_compile_targets.py
@@ -8,10 +8,7 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def main(argv):
@@ -54,11 +51,7 @@
         return rc
 
       with open(tempfile_path) as f:
-        # json.load() throws a ValueError for empty files
-        try:
-          results[filename] = json.load(f)
-        except ValueError:
-          pass
+        results[filename] = json.load(f)
 
   with open(args.output, 'w') as f:
     json.dump(results, f)
diff --git a/testing/scripts/gpu_integration_test_adapter.py b/testing/scripts/gpu_integration_test_adapter.py
index 6aae983e..8c12571 100644
--- a/testing/scripts/gpu_integration_test_adapter.py
+++ b/testing/scripts/gpu_integration_test_adapter.py
@@ -2,14 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os
-import sys
-
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
-
+import common
 
 class GpuIntegrationTestAdapater(common.BaseIsolatedScriptArgsAdapter):
 
diff --git a/testing/scripts/grit_python_unittests.py b/testing/scripts/grit_python_unittests.py
index 9a2218e..532e4b1 100755
--- a/testing/scripts/grit_python_unittests.py
+++ b/testing/scripts/grit_python_unittests.py
@@ -11,10 +11,7 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/headless_python_unittests.py b/testing/scripts/headless_python_unittests.py
index 4e32907..881cff4a 100755
--- a/testing/scripts/headless_python_unittests.py
+++ b/testing/scripts/headless_python_unittests.py
@@ -7,10 +7,8 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 
 def main_run(args):
@@ -18,7 +16,7 @@
       os.path.dirname(__file__), os.path.pardir, os.path.pardir,
       'third_party', 'catapult', 'third_party', 'typ'))
   _AddToPathIfNeeded(typ_path)
-  import typ #pylint: disable=import-outside-toplevel
+  import typ
 
   top_level_dir = os.path.join(
       common.SRC_DIR, 'headless', 'lib', 'browser', 'devtools_api')
diff --git a/testing/scripts/host_info.py b/testing/scripts/host_info.py
index 86c8514..63e3b9ba 100755
--- a/testing/scripts/host_info.py
+++ b/testing/scripts/host_info.py
@@ -9,10 +9,7 @@
 import platform
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 def is_linux():
@@ -91,7 +88,7 @@
       v['ro.build.fingerprint'] for v in device_info if not v['denylisted']]
 
   def unique_build_details(index):
-    return sorted(list({v.split(':')[index] for v in details}))
+    return sorted(list(set([v.split(':')[index] for v in details])))
 
   parsed_details = {
     'device_names': unique_build_details(0),
diff --git a/testing/scripts/metrics_python_tests.py b/testing/scripts/metrics_python_tests.py
index 2746430..70770f41 100755
--- a/testing/scripts/metrics_python_tests.py
+++ b/testing/scripts/metrics_python_tests.py
@@ -9,10 +9,8 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+
+import common
 
 
 def main_run(args):
diff --git a/testing/scripts/run_android_wpt.py b/testing/scripts/run_android_wpt.py
index 0417a5775..db183b2 100755
--- a/testing/scripts/run_android_wpt.py
+++ b/testing/scripts/run_android_wpt.py
@@ -26,16 +26,12 @@
 # run_wpt_tests.py script.
 
 import json
-import os
 import sys
 
+import common
 
 from run_wpt_tests import WPTAdapter
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
 
 # This is not really a "script test" so does not need to manually add
 # any additional compile targets.
diff --git a/testing/scripts/run_android_wpt.pydeps b/testing/scripts/run_android_wpt.pydeps
index cba4c8d8..ac6fdf5 100644
--- a/testing/scripts/run_android_wpt.pydeps
+++ b/testing/scripts/run_android_wpt.pydeps
@@ -20,7 +20,6 @@
 //build/util/lib/results/__init__.py
 //build/util/lib/results/result_sink.py
 //build/util/lib/results/result_types.py
-//testing/scripts/__init__.py
 //testing/scripts/common.py
 //testing/scripts/run_android_wpt.py
 //testing/scripts/run_wpt_tests.py
diff --git a/testing/scripts/run_cast_core_tests.py b/testing/scripts/run_cast_core_tests.py
index 7d5e9e9..f94e106 100755
--- a/testing/scripts/run_cast_core_tests.py
+++ b/testing/scripts/run_cast_core_tests.py
@@ -19,13 +19,9 @@
 """
 
 import json
-import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 class CastCoreIntegrationTestAdapter(common.BaseIsolatedScriptArgsAdapter):
@@ -51,4 +47,4 @@
       'compile_targets': main_compile_targets,
     }
     sys.exit(common.run_script(sys.argv[1:], funcs))
-  sys.exit(main())
+  sys.exit(main())
\ No newline at end of file
diff --git a/testing/scripts/run_chromedriver_tests.py b/testing/scripts/run_chromedriver_tests.py
index 203ae32..b1f67ce 100755
--- a/testing/scripts/run_chromedriver_tests.py
+++ b/testing/scripts/run_chromedriver_tests.py
@@ -25,10 +25,7 @@
 import tempfile
 import traceback
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 class ChromeDriverAdapter(common.BaseIsolatedScriptArgsAdapter):
diff --git a/testing/scripts/run_devtools_check.py b/testing/scripts/run_devtools_check.py
index 9f9be63..104f5f72 100755
--- a/testing/scripts/run_devtools_check.py
+++ b/testing/scripts/run_devtools_check.py
@@ -21,11 +21,11 @@
 import sys
 
 
-# Add src/testing/ into sys.path for importing xvfb and common.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
+import common
+
+# Add src/testing/ into sys.path for importing xvfb.
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
 import xvfb
-from scripts import common
 
 
 def main():
diff --git a/testing/scripts/run_finch_smoke_tests_android.py b/testing/scripts/run_finch_smoke_tests_android.py
index cd12b7e..e3d91220 100755
--- a/testing/scripts/run_finch_smoke_tests_android.py
+++ b/testing/scripts/run_finch_smoke_tests_android.py
@@ -19,7 +19,6 @@
 
 SRC_DIR = os.path.abspath(
     os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
-PAR_DIR = os.path.join(SRC_DIR, 'testing')
 OUT_DIR = os.path.join(SRC_DIR, 'out', 'Release')
 BLINK_TOOLS = os.path.join(
     SRC_DIR, 'third_party', 'blink', 'tools')
@@ -47,11 +46,10 @@
 if WEBVIEW_VARIATIONS_PROTO not in sys.path:
   sys.path.append(WEBVIEW_VARIATIONS_PROTO)
 
-sys.path.append(PAR_DIR)
-
 if 'compile_targets' not in sys.argv:
   import aw_variations_seed_pb2
 
+import common
 import devil_chromium
 import wpt_common
 
@@ -68,7 +66,6 @@
 from devil.utils import logging_common
 from pylib.local.emulator import avd
 from py_utils.tempfile_ext import NamedTemporaryDirectory
-from scripts import common
 from skia_gold_infra.finch_skia_gold_properties import FinchSkiaGoldProperties
 from skia_gold_infra import finch_skia_gold_session_manager
 from skia_gold_infra import finch_skia_gold_utils
@@ -85,8 +82,6 @@
 logger.setLevel(logging.INFO)
 TEST_CASES = {}
 
-# pylint: disable=super-with-arguments
-
 
 class FinchTestCase(wpt_common.BaseWptScriptAdapter):
 
@@ -100,7 +95,6 @@
     self.browser_activity_name = (self.options.browser_activity_name or
                                   self.default_browser_activity_name)
     self.log_mon = None
-    self.layout_test_results_subdir = None
     self.test_specific_browser_args = []
     if self.options.webview_provider_apk:
       self.webview_provider_package_name = (
diff --git a/testing/scripts/run_flatbuffers_unittests.py b/testing/scripts/run_flatbuffers_unittests.py
index daea0f5..c9b8fde9 100755
--- a/testing/scripts/run_flatbuffers_unittests.py
+++ b/testing/scripts/run_flatbuffers_unittests.py
@@ -20,14 +20,11 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing xvfb and common.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
+import common
+
+# Add src/testing/ into sys.path for importing xvfb and test_env.
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
 import xvfb
-from scripts import common
-
-# pylint: disable=super-with-arguments
-
 
 def main():
   parser = argparse.ArgumentParser()
diff --git a/testing/scripts/run_gpu_integration_test_as_googletest.py b/testing/scripts/run_gpu_integration_test_as_googletest.py
index 3456a7e..dc5e128 100755
--- a/testing/scripts/run_gpu_integration_test_as_googletest.py
+++ b/testing/scripts/run_gpu_integration_test_as_googletest.py
@@ -19,16 +19,11 @@
 """
 
 import json
-import os
 import sys
 
+import common
 import gpu_integration_test_adapter
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
-
 
 def main():
   adapter = gpu_integration_test_adapter.GpuIntegrationTestAdapater()
diff --git a/testing/scripts/run_isolated_script_test.py b/testing/scripts/run_isolated_script_test.py
index 0e963d6..fed58832 100755
--- a/testing/scripts/run_isolated_script_test.py
+++ b/testing/scripts/run_isolated_script_test.py
@@ -26,11 +26,7 @@
 import tempfile
 
 
-# Add src/testing/ into sys.path for importing xvfb and common.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-import xvfb
-from scripts import common
+import common
 
 
 # Some harnesses understand the --isolated-script-test arguments
@@ -56,9 +52,10 @@
     'test_suite_all.py',  # //tools/grit:grit_python_unittests
 }
 
-# pylint: disable=super-with-arguments
-
 class IsolatedScriptTestAdapter(common.BaseIsolatedScriptArgsAdapter):
+  def __init__(self):
+    super(IsolatedScriptTestAdapter, self).__init__()
+
   def generate_sharding_args(self, total_shards, shard_index):
     # This script only uses environment variable for sharding.
     del total_shards, shard_index  # unused
@@ -121,6 +118,9 @@
       return 'vpython3.bat' if sys.platform == 'win32' else 'vpython3'
     return super(TypUnittestAdapter, self).select_python_executable()
 
+  def run_test(self):
+    return super(TypUnittestAdapter, self).run_test()
+
 
 def main():
   if any(r in sys.argv[1] for r in KNOWN_ISOLATED_SCRIPT_TEST_RUNNERS):
diff --git a/testing/scripts/run_isolated_script_test.pydeps b/testing/scripts/run_isolated_script_test.pydeps
index df35f82..bc8ff58 100644
--- a/testing/scripts/run_isolated_script_test.pydeps
+++ b/testing/scripts/run_isolated_script_test.pydeps
@@ -4,7 +4,6 @@
 //build/util/lib/results/__init__.py
 //build/util/lib/results/result_sink.py
 //build/util/lib/results/result_types.py
-//testing/scripts/__init__.py
 //testing/scripts/common.py
 //testing/scripts/run_isolated_script_test.py
 //testing/test_env.py
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index a46efbe5..b687be8 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -48,11 +48,11 @@
 import traceback
 import six
 
+import common
 from collections import OrderedDict
 
 CHROMIUM_SRC_DIR = os.path.abspath(
-    os.path.join(os.path.dirname(__file__),
-                 os.path.pardir, os.path.pardir))
+    os.path.join(os.path.dirname(__file__), '..', '..'))
 
 PERF_DIR = os.path.join(CHROMIUM_SRC_DIR, 'tools', 'perf')
 sys.path.append(PERF_DIR)
@@ -63,21 +63,19 @@
 sys.path.append(PERF_CORE_DIR)
 import results_merger
 
-# Add src/testing/ into sys.path for importing xvfb, test_env, and common.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
+# Add src/testing/ into sys.path for importing xvfb and test_env.
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
 import xvfb
 import test_env
-from scripts import common
 
 # Unfortunately we need to copy these variables from ../test_env.py.
 # Importing it and using its get_sandbox_env breaks test runs on Linux
 # (it seems to unset DISPLAY).
 CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX'
 CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox'
-SHARD_MAPS_DIRECTORY = os.path.abspath(
-    os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir,
-                 'tools', 'perf', 'core', 'shard_maps'))
+SHARD_MAPS_DIRECTORY = os.path.join(
+    os.path.dirname(__file__), '..', '..', 'tools', 'perf', 'core',
+    'shard_maps')
 
 # See https://crbug.com/923564.
 # We want to switch over to using histograms for everything, but converting from
@@ -108,8 +106,6 @@
   'xr.vr.common_perftests',
 ]
 
-# pylint: disable=useless-object-inheritance
-
 
 class OutputFilePaths(object):
   """Provide paths to where results outputs should be written.
@@ -192,7 +188,8 @@
     executable = str(self.executable_name)
     if IsWindows():
       return r'.\%s.exe' % executable
-    return './%s' % executable
+    else:
+      return './%s' % executable
 
   def _get_additional_flags(self):
     return self._additional_flags
@@ -249,7 +246,7 @@
           benchmark_name: {
               'expected': 'PASS',
               'actual': 'FAIL' if return_code else 'PASS',
-              'is_unexpected': bool(return_code),
+              'is_unexpected': True if return_code else False,
           },
       },
       'interrupted': False,
@@ -356,9 +353,9 @@
   if os.path.exists(output_paths.perf_results):
     if command_generator.executable_name in GTEST_CONVERSION_WHITELIST:
       with path_util.SysPath(path_util.GetTracingDir()):
-        # pylint: disable=no-name-in-module,import-outside-toplevel
+        # pylint: disable=no-name-in-module
         from tracing.value import gtest_json_converter
-        # pylint: enable=no-name-in-module,import-outside-toplevel
+        # pylint: enable=no-name-in-module
       gtest_json_converter.ConvertGtestJsonFile(output_paths.perf_results)
   else:
     print('ERROR: gtest perf test %s did not generate perf output' %
diff --git a/testing/scripts/run_rendering_benchmark_with_gated_performance.py b/testing/scripts/run_rendering_benchmark_with_gated_performance.py
index 39537a0..1011ac3 100755
--- a/testing/scripts/run_rendering_benchmark_with_gated_performance.py
+++ b/testing/scripts/run_rendering_benchmark_with_gated_performance.py
@@ -26,13 +26,9 @@
 
 import numpy as np
 
+import common
 import run_performance_tests
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
-
 # AVG_ERROR_MARGIN determines how much more the value of frame times can be
 # compared to the recorded value (multiplier of upper limit).
 AVG_ERROR_MARGIN = 1.1
@@ -44,9 +40,6 @@
 
 METRIC_NAME = 'frame_times'
 
-# pylint: disable=useless-object-inheritance
-
-
 class ResultRecorder(object):
   def __init__(self):
     self.fails = 0
@@ -159,7 +152,7 @@
     # least by 10 [AVG_ERROR_MARGIN] percent of upper limit, that would be
     # considered a failure. crbug.com/953895
     with open(
-      os.path.join(os.path.dirname(os.path.abspath(__file__)),
+      os.path.join(os.path.dirname(__file__),
       'representative_perf_test_data',
       'representatives_frame_times_upper_limit.json')
     ) as bound_data:
diff --git a/testing/scripts/run_telemetry_as_googletest.py b/testing/scripts/run_telemetry_as_googletest.py
index b67063bb..e05f733 100755
--- a/testing/scripts/run_telemetry_as_googletest.py
+++ b/testing/scripts/run_telemetry_as_googletest.py
@@ -23,10 +23,7 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 
 class TelemetryUnittestAdapter(common.BaseIsolatedScriptArgsAdapter):
diff --git a/testing/scripts/run_variations_smoke_tests.py b/testing/scripts/run_variations_smoke_tests.py
index 1bba02f..b681e54a 100755
--- a/testing/scripts/run_variations_smoke_tests.py
+++ b/testing/scripts/run_variations_smoke_tests.py
@@ -21,12 +21,12 @@
 from skia_gold_infra.finch_skia_gold_properties import FinchSkiaGoldProperties
 from skia_gold_infra import finch_skia_gold_utils
 
+import common
 import variations_seed_access_helper as seed_helper
 
 _THIS_DIR = os.path.abspath(os.path.dirname(__file__))
 _VARIATIONS_TEST_DATA = 'variations_smoke_test_data'
 
-from . import common
 from selenium import webdriver
 from selenium.webdriver import ChromeOptions
 from selenium.common.exceptions import NoSuchElementException
@@ -84,7 +84,7 @@
     'Windows (win32 or cygwin) are supported' % sys.platform)
 
 
-def _find_chrome_binary(): #pylint: disable=inconsistent-return-statements
+def _find_chrome_binary():
   """Finds and returns the relative path to the Chrome binary.
 
   This function assumes that the CWD is the build directory.
@@ -95,11 +95,11 @@
   platform = _get_platform()
   if platform == 'linux':
     return os.path.join('.', 'chrome')
-  if platform == 'mac':
+  elif platform == 'mac':
     chrome_name = 'Google Chrome'
     return os.path.join('.', chrome_name + '.app', 'Contents', 'MacOS',
                             chrome_name)
-  if platform == 'win':
+  elif platform == 'win':
     return os.path.join('.', 'chrome.exe')
 
 
diff --git a/testing/scripts/run_wpt_tests.py b/testing/scripts/run_wpt_tests.py
index 2e7c0ff..82bc4b0cc 100755
--- a/testing/scripts/run_wpt_tests.py
+++ b/testing/scripts/run_wpt_tests.py
@@ -12,13 +12,9 @@
 import os
 import sys
 
+import common
 import wpt_common
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
-
 logger = logging.getLogger(__name__)
 
 SRC_DIR = os.path.abspath(
@@ -58,7 +54,6 @@
     _ANDROID_ENABLED = False
 
 
-# pylint: disable=super-with-arguments
 def _make_pass_through_action(dest, map_arg=lambda arg: arg):
     class PassThroughAction(argparse.Action):
         def __init__(self, option_strings, dest, nargs=None, **kwargs):
@@ -179,7 +174,7 @@
   def log_level(self):
     if self.options.verbose >= 2:
       return logging.DEBUG
-    if self.options.verbose >= 1:
+    elif self.options.verbose >= 1:
       return logging.INFO
     return logging.WARNING
 
@@ -345,26 +340,26 @@
           'Options for configuring Android devices and tooling.')
       add_emulator_args(group)
       group.add_argument(
-          '--apk',
+          '--browser-apk',
           # Aliases for backwards compatibility.
           '--chrome-apk',
-          '--weblayer-support',
-          action='append',
-          default=[],
-          help='Path to an APK to install.')
-      group.add_argument(
-          '--shell-apk',
-          # Aliases for backwards compatibility.
           '--system-webview-shell',
           '--weblayer-shell',
-          help=('Path to a shell APK to install. '
-                '(WebView and WebLayer only. '
+          help=('Path to the browser APK to install and run. '
+                '(For WebView and WebLayer, this value is the shell. '
                 'Defaults to an on-device APK if not provided.)'))
       group.add_argument(
           '--webview-provider',
           help=('Path to a WebView provider APK to install. '
                 '(WebView only.)'))
       group.add_argument(
+          '--additional-apk',
+          # Aliases for backwards compatibility.
+          '--weblayer-support',
+          action='append',
+          default=[],
+          help='Paths to additional APKs to install.')
+      group.add_argument(
           '--release-channel',
           help='Install WebView from release channel. (WebView only.)')
       group.add_argument(
@@ -436,7 +431,7 @@
     def wpt_args(self):
         """list[str]: Arguments to add to a 'wpt run' command."""
         args = []
-        version = self.get_version() # pylint: disable=assignment-from-none
+        version = self.get_version()
         if version:
             args.append('--browser-version=%s' % version)
         webdriver = self.webdriver_binary
@@ -617,7 +612,12 @@
         See Also:
             https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/browser.py#L867-L924
         """
-        return self._options.package_name
+        if self._options.package_name:
+            return self._options.package_name
+        if self._options.browser_apk:
+            with contextlib.suppress(apk_helper.ApkHelperError):
+                return apk_helper.GetPackageName(self._options.browser_apk)
+        return None
 
     def get_version_provider_package_name(self):
         """Get the name of the package containing the product version.
@@ -639,7 +639,10 @@
 
     def provision_device(self, device):
         """Provision an Android device for a test."""
-        for apk in self._options.apk:
+        if self._options.browser_apk:
+            self._tasks.enter_context(
+                _install_apk(device, self._options.browser_apk))
+        for apk in self._options.additional_apk:
             self._tasks.enter_context(_install_apk(device, apk))
         logger.info('Provisioned device (serial: %s)', device.serial)
 
@@ -663,25 +666,7 @@
     yield
 
 
-class ChromeAndroidShellBase(ChromeAndroidBase):
-    def get_browser_package_name(self):
-        package_name = super(ChromeAndroidShellBase,
-                             self).get_browser_package_name()
-        if package_name:
-            return package_name
-        if self._options.shell_apk:
-            with contextlib.suppress(apk_helper.ApkHelperError):
-                return apk_helper.GetPackageName(self._options.shell_apk)
-        return None
-
-    def provision_device(self, device):
-        if self._options.shell_apk:
-            self._tasks.enter_context(_install_apk(device,
-                                                   self._options.shell_apk))
-        super(ChromeAndroidShellBase, self).provision_device(device)
-
-
-class WebLayer(ChromeAndroidShellBase):
+class WebLayer(ChromeAndroidBase):
     name = ANDROID_WEBLAYER
     aliases = ['weblayer']
 
@@ -696,14 +681,14 @@
                 or 'org.chromium.weblayer.shell')
 
     def get_version_provider_package_name(self):
-        # Read version from support APK.
-        if self._options.apk:
+        if self._options.additional_apk:
+            support_apk = self._options.additional_apk[0]
             with contextlib.suppress(apk_helper.ApkHelperError):
-                return apk_helper.GetPackageName(self._options.apk[0])
+                return apk_helper.GetPackageName(support_apk)
         return super(WebLayer, self).get_version_provider_package_name()
 
 
-class WebView(ChromeAndroidShellBase):
+class WebView(ChromeAndroidBase):
     name = ANDROID_WEBVIEW
     aliases = ['webview']
 
@@ -713,11 +698,12 @@
             return webview_app.UseWebViewProvider(
                 device,
                 self._options.webview_provider)
-        assert self._options.release_channel, 'no webview install method'
-        return _install_webview_from_release(
-            device,
-            self._options.release_channel,
-            self._python_executable)
+        else:
+            assert self._options.release_channel, 'no webview install method'
+            return _install_webview_from_release(
+                device,
+                self._options.release_channel,
+                self._python_executable)
 
     def _validate_options(self):
         super(WebView, self)._validate_options()
@@ -751,20 +737,11 @@
 
     def _validate_options(self):
         super(ChromeAndroid, self)._validate_options()
-        if not self._options.package_name and not self._options.apk:
+        if not self._options.package_name and not self._options.browser_apk:
             raise ValueError(
-                "Must provide either '--package-name' or '--apk' "
+                "Must provide either '--package-name' or '--browser-apk' "
                 'for %r.' % self.name)
 
-    def get_browser_package_name(self):
-        package_name = super(ChromeAndroid, self).get_browser_package_name()
-        if package_name:
-            return package_name
-        if self._options.apk:
-            with contextlib.suppress(apk_helper.ApkHelperError):
-                return apk_helper.GetPackageName(self._options.apk[0])
-        return None
-
 
 def add_emulator_args(parser):
   parser.add_argument(
diff --git a/testing/scripts/rust/rust_main_program.py b/testing/scripts/rust/rust_main_program.py
index a1f95202..080dd2b 100644
--- a/testing/scripts/rust/rust_main_program.py
+++ b/testing/scripts/rust/rust_main_program.py
@@ -11,7 +11,7 @@
 import subprocess
 import sys
 
-sys.path.append(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.dirname(__file__))
 import exe_util
 import main_program
 import test_results
diff --git a/testing/scripts/rust/test_filtering.py b/testing/scripts/rust/test_filtering.py
index bc9c7eea..a386dd0 100644
--- a/testing/scripts/rust/test_filtering.py
+++ b/testing/scripts/rust/test_filtering.py
@@ -56,7 +56,8 @@
         """
         if self._is_prefix_match:
             return test_name.startswith(self._filter_text)
-        return test_name == self._filter_text
+        else:
+            return test_name == self._filter_text
 
     def is_exclusion_filter(self):
         """Rreturns whether this filter excludes (rather than includes) matching
diff --git a/testing/scripts/skia_gold_infra/finch_skia_gold_utils.py b/testing/scripts/skia_gold_infra/finch_skia_gold_utils.py
index 8d6d3d3..a6784bfb 100644
--- a/testing/scripts/skia_gold_infra/finch_skia_gold_utils.py
+++ b/testing/scripts/skia_gold_infra/finch_skia_gold_utils.py
@@ -134,3 +134,4 @@
         'Given unhandled SkiaGoldSession StatusCode %s with error %s', status,
         error)
   return triage_link
+
diff --git a/testing/scripts/test_buildbucket_api_gpu_use_cases.py b/testing/scripts/test_buildbucket_api_gpu_use_cases.py
index b4652a6c..af2b7f8 100755
--- a/testing/scripts/test_buildbucket_api_gpu_use_cases.py
+++ b/testing/scripts/test_buildbucket_api_gpu_use_cases.py
@@ -10,15 +10,11 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 # Add src/content/test/gpu into sys.path for importing common.
-sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
-                                os.path.pardir, os.path.pardir, 'content',
-                                'test', 'gpu')))
+sys.path.append(os.path.join(os.path.dirname(__file__),
+                             '..', '..', 'content', 'test', 'gpu'))
 
 import gather_power_measurement_results
 import gather_swarming_json_results
diff --git a/testing/scripts/test_traffic_annotation_auditor.py b/testing/scripts/test_traffic_annotation_auditor.py
index bcd0fb1..01f6399 100755
--- a/testing/scripts/test_traffic_annotation_auditor.py
+++ b/testing/scripts/test_traffic_annotation_auditor.py
@@ -15,10 +15,7 @@
 import tempfile
 import traceback
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 WINDOWS_SHEET_CONFIG = {
   "spreadsheet_id": "1TmBr9jnf1-hrjntiVBzT9EtkINGrtoBYFMWad2MBeaY",
diff --git a/testing/scripts/wpt_common.py b/testing/scripts/wpt_common.py
index 33cb0c3..4a86345 100644
--- a/testing/scripts/wpt_common.py
+++ b/testing/scripts/wpt_common.py
@@ -8,10 +8,7 @@
 import os
 import sys
 
-# Add src/testing/ into sys.path for importing common without pylint errors.
-sys.path.append(
-    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from scripts import common
+import common
 
 BLINK_TOOLS_DIR = os.path.join(common.SRC_DIR, 'third_party', 'blink', 'tools')
 CATAPULT_DIR = os.path.join(common.SRC_DIR, 'third_party', 'catapult')
@@ -33,7 +30,6 @@
 logger = logging.getLogger(__name__)
 
 
-# pylint: disable=super-with-arguments
 class BaseWptScriptAdapter(common.BaseIsolatedScriptArgsAdapter):
     """The base class for script adapters that use wptrunner to execute web
     platform tests. This contains any code shared between these scripts, such
@@ -214,7 +210,6 @@
     def generate_test_repeat_args(self, repeat_count):
         return ['--repeat=%d' % repeat_count]
 
-    # pylint: disable=unused-argument
     def generate_test_launcher_retry_limit_args(self, retry_limit):
         # TODO(crbug/1306222): wptrunner currently cannot rerun individual
         # failed tests, so this flag is accepted but not used.
diff --git a/testing/test_env.py b/testing/test_env.py
index 43083fc..fa8df4c 100755
--- a/testing/test_env.py
+++ b/testing/test_env.py
@@ -289,15 +289,13 @@
       if p.poll() is not None:
         continue
       # SIGBREAK is defined only for win32.
-      # pylint: disable=no-member
       if sys.platform == 'win32' and sig == signal.SIGBREAK:
         p.send_signal(signal.CTRL_BREAK_EVENT)
       else:
         print("Forwarding signal(%d) to process %d" % (sig, p.pid))
         p.send_signal(sig)
-      # pylint: enable=no-member
   if sys.platform == 'win32':
-    signal.signal(signal.SIGBREAK, _sig_handler) # pylint: disable=no-member
+    signal.signal(signal.SIGBREAK, _sig_handler)
   else:
     signal.signal(signal.SIGTERM, _sig_handler)
     signal.signal(signal.SIGINT, _sig_handler)
@@ -351,13 +349,11 @@
   if '--coverage-continuous-mode=1' in cmd:
     extra_env.update(get_coverage_continuous_mode_env(env))
 
-  # pylint: disable=import-outside-toplevel
   if '--skip-set-lpac-acls=1' not in cmd and sys.platform == 'win32':
     sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
         'scripts'))
-    from scripts import common
+    import common
     common.set_lpac_acls(ROOT_DIR, is_test_script=True)
-  # pylint: enable=import-outside-toplevel
 
   cmd = trim_cmd(cmd)
 
@@ -383,7 +379,7 @@
     if stdoutfile:
       # Write to stdoutfile and poll to produce terminal output.
       return run_command_with_output(cmd, env=env, stdoutfile=stdoutfile)
-    if use_symbolization_script:
+    elif use_symbolization_script:
       # See above comment regarding offline symbolization.
       # Need to pipe to the symbolizer script.
       p1 = _popen(cmd, env=env, stdout=subprocess.PIPE,
@@ -398,7 +394,8 @@
       # Also feed the out-of-band JSON output to the symbolizer script.
       symbolize_snippets_in_json(cmd, env)
       return p1.returncode
-    return run_command(cmd, env=env, log=False)
+    else:
+      return run_command(cmd, env=env, log=False)
   except OSError:
     print('Failed to start %s' % cmd, file=sys.stderr)
     raise
diff --git a/testing/test_env_test_script.py b/testing/test_env_test_script.py
index e318141..4957ee65 100755
--- a/testing/test_env_test_script.py
+++ b/testing/test_env_test_script.py
@@ -19,5 +19,5 @@
   signal.signal(signal.SIGTERM, print_signal)
   signal.signal(signal.SIGINT, print_signal)
   if sys.platform == 'win32':
-    signal.signal(signal.SIGBREAK, print_signal) # pylint: disable=no-member
+    signal.signal(signal.SIGBREAK, print_signal)
   time.sleep(2)  # gives process time to receive signal.
diff --git a/testing/test_env_unittest.py b/testing/test_env_unittest.py
index 8ec6194..707c65e 100755
--- a/testing/test_env_unittest.py
+++ b/testing/test_env_unittest.py
@@ -20,8 +20,6 @@
 HERE = os.path.dirname(os.path.abspath(__file__))
 TEST_SCRIPT = os.path.join(HERE, 'test_env_user_script.py')
 
-# pylint: disable=super-with-arguments
-
 
 def launch_process_windows(args):
   # The `universal_newlines` option is equivalent to `text` in Python 3.
@@ -44,13 +42,11 @@
       universal_newlines=True)
 
 
-# pylint: disable=inconsistent-return-statements
 def read_subprocess_message(proc, starts_with):
   """Finds the value after first line prefix condition."""
   for line in proc.stdout:
     if line.startswith(starts_with):
       return line.rstrip().replace(starts_with, '')
-# pylint: enable=inconsistent-return-statements
 
 
 def send_and_wait(proc, sig, sleep_time=0.3):
@@ -69,9 +65,9 @@
 
   def test_send_ctrl_break_event(self):
     proc = launch_process_windows([])
-    send_and_wait(proc, signal.CTRL_BREAK_EVENT) # pylint: disable=no-member
+    send_and_wait(proc, signal.CTRL_BREAK_EVENT)
     sig = read_subprocess_message(proc, 'Signal :')
-    self.assertEqual(sig, str(int(signal.SIGBREAK))) # pylint: disable=no-member
+    self.assertEqual(sig, str(int(signal.SIGBREAK)))
 
 
 class SignalingNonWindowsTest(unittest.TestCase):
diff --git a/testing/unexpected_passes_common/unittest_utils.py b/testing/unexpected_passes_common/unittest_utils.py
index bfc3b405..2df2f251 100644
--- a/testing/unexpected_passes_common/unittest_utils.py
+++ b/testing/unexpected_passes_common/unittest_utils.py
@@ -141,15 +141,11 @@
 
 
 class GenericBuilders(builders.Builders):
-  #pylint: disable=useless-super-delegation
   def __init__(self, include_internal_builders=False):
     super(GenericBuilders, self).__init__(include_internal_builders)
-  #pylint: enable=useless-super-delegation
 
-  #pylint: disable=unused-argument
   def _BuilderRunsTestOfInterest(self, test_map, suite):
     return True
-  #pylint: enable=unused-argument
 
   def GetIsolateNames(self):
     return {}
diff --git a/testing/variations/PRESUBMIT.py b/testing/variations/PRESUBMIT.py
index fe53483..e854e162 100644
--- a/testing/variations/PRESUBMIT.py
+++ b/testing/variations/PRESUBMIT.py
@@ -257,10 +257,10 @@
           json_data,
           f.AbsoluteLocalPath(),
           output_api.PresubmitError)
-      if result:
+      if len(result):
         return result
       result = CheckPretty(contents, f.LocalPath(), output_api.PresubmitError)
-      if result:
+      if len(result):
         return result
     except ValueError:
       return [
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f253e92..100274b 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -458,6 +458,45 @@
             ]
         }
     ],
+    "ArcVmInitThrottle2": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "throttle_to_70_percent",
+                    "enable_features": [
+                        "CrOSLateBootArcVmInitial70Throttle"
+                    ],
+                    "disable_features": [
+                        "CrOSLateBootArcVmInitial30Throttle",
+                        "CrOSLateBootArcVmInitial50Throttle"
+                    ]
+                },
+                {
+                    "name": "throttle_to_50_percent",
+                    "enable_features": [
+                        "CrOSLateBootArcVmInitial50Throttle"
+                    ],
+                    "disable_features": [
+                        "CrOSLateBootArcVmInitial30Throttle",
+                        "CrOSLateBootArcVmInitial70Throttle"
+                    ]
+                },
+                {
+                    "name": "throttle_to_30_percent",
+                    "enable_features": [
+                        "CrOSLateBootArcVmInitial30Throttle"
+                    ],
+                    "disable_features": [
+                        "CrOSLateBootArcVmInitial50Throttle",
+                        "CrOSLateBootArcVmInitial70Throttle"
+                    ]
+                }
+            ]
+        }
+    ],
     "ArcVmMemorySize": [
         {
             "platforms": [
diff --git a/testing/xvfb.py b/testing/xvfb.py
index a301e55a..c1f5f76 100755
--- a/testing/xvfb.py
+++ b/testing/xvfb.py
@@ -23,15 +23,15 @@
 
 import test_env
 
-# pylint: disable=useless-object-inheritance
-
 
 class _XvfbProcessError(Exception):
   """Exception raised when Xvfb cannot start."""
+  pass
 
 
 class _WestonProcessError(Exception):
   """Exception raised when Weston cannot start."""
+  pass
 
 
 def kill(proc, name, timeout_in_seconds=10):
@@ -54,7 +54,7 @@
           file=sys.stderr)
 
 
-def launch_dbus(env): # pylint: disable=inconsistent-return-statements
+def launch_dbus(env):
   """Starts a DBus session.
 
   Works around a bug in GLib where it performs operations which aren't
@@ -136,9 +136,10 @@
 
   if sys.platform.startswith('linux') and use_xvfb:
     return _run_with_xvfb(cmd, env, stdoutfile, use_openbox, use_xcompmgr)
-  if use_weston:
+  elif use_weston:
     return _run_with_weston(cmd, env, stdoutfile)
-  return test_env.run_executable(cmd, env, stdoutfile)
+  else:
+    return test_env.run_executable(cmd, env, stdoutfile)
 
 
 def _run_with_xvfb(cmd, env, stdoutfile, use_openbox, use_xcompmgr):
diff --git a/testing/xvfb_unittest.py b/testing/xvfb_unittest.py
index 47b78f8..f28b6d2 100755
--- a/testing/xvfb_unittest.py
+++ b/testing/xvfb_unittest.py
@@ -16,8 +16,6 @@
 import time
 import unittest
 
-# pylint: disable=super-with-arguments
-
 
 TEST_FILE = __file__.replace('.pyc', '.py')
 XVFB = TEST_FILE.replace('_unittest', '')
@@ -31,13 +29,11 @@
       stderr=subprocess.STDOUT, env=os.environ.copy())
 
 
-# pylint: disable=inconsistent-return-statements
 def read_subprocess_message(proc, starts_with):
   """Finds the value after first line prefix condition."""
-  for line in proc.stdout.read().decode('utf-8').splitlines(True):
+  for line in proc.stdout:
     if line.startswith(starts_with):
       return line.rstrip().replace(starts_with, '')
-# pylint: enable=inconsistent-return-statements
 
 
 def send_signal(proc, sig, sleep_time=0.3):
@@ -51,7 +47,7 @@
 
   def setUp(self):
     super(XvfbLinuxTest, self).setUp()
-    if sys.platform != 'linux':
+    if sys.platform != 'linux2':
       self.skipTest('linux only test')
 
   def test_no_xvfb_display(self):
@@ -96,13 +92,13 @@
     proc = launch_process(['--sleep'])
     send_signal(proc, signal.SIGINT, 1)
     sig = read_subprocess_message(proc, 'Signal :')
-    self.assertEqual(int(sig), int(signal.SIGINT))
+    self.assertEqual(sig, str(signal.SIGINT))
 
   def test_send_sigterm(self):
     proc = launch_process(['--sleep'])
     send_signal(proc, signal.SIGTERM, 1)
     sig = read_subprocess_message(proc, 'Signal :')
-    self.assertEqual(int(sig), int(signal.SIGTERM))
+    self.assertEqual(sig, str(signal.SIGTERM))
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index b45bdcd..855fbb3a 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -6,40 +6,6 @@
 
 # Library groups
 if (!limit_android_deps) {
-  java_group("android_support_v4_java") {
-    deps = [
-      "//third_party/androidx:androidx_core_core_java",
-      "//third_party/androidx:androidx_drawerlayout_drawerlayout_java",
-      "//third_party/androidx:androidx_fragment_fragment_java",
-      "//third_party/androidx:androidx_interpolator_interpolator_java",
-      "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
-      "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_java",
-      "//third_party/androidx:androidx_media_media_java",
-      "//third_party/androidx:androidx_recyclerview_recyclerview_java",
-    ]
-
-    # TODO(crbug.com/1315379): Remove this dep when inlined in chromecast
-    #                          internal code.
-    visibility = [ "//chromecast/*" ]
-  }
-
-  java_group("android_support_v7_appcompat_java") {
-    deps = [
-      "//third_party/androidx:androidx_appcompat_appcompat_java",
-      "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
-      "//third_party/androidx:androidx_core_core_java",
-      "//third_party/androidx:androidx_drawerlayout_drawerlayout_java",
-      "//third_party/androidx:androidx_fragment_fragment_java",
-      "//third_party/androidx:androidx_interpolator_interpolator_java",
-      "//third_party/androidx:androidx_lifecycle_lifecycle_common_java",
-      "//third_party/androidx:androidx_lifecycle_lifecycle_viewmodel_java",
-      "//third_party/androidx:androidx_media_media_java",
-      "//third_party/androidx:androidx_recyclerview_recyclerview_java",
-      "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
-      "//third_party/androidx:androidx_vectordrawable_vectordrawable_java",
-    ]
-  }
-
   java_group("dagger_java") {
     deps = [ dagger_java_target ]
   }
diff --git a/third_party/android_media/BUILD.gn b/third_party/android_media/BUILD.gn
index d8326b63..6ccaba9 100644
--- a/third_party/android_media/BUILD.gn
+++ b/third_party/android_media/BUILD.gn
@@ -11,10 +11,7 @@
     "java/res/layout/media_controller.xml",
     "java/res/values/colors.xml",
   ]
-  deps = [
-    "//third_party/android_deps:android_support_v7_appcompat_java",
-    "//third_party/androidx:androidx_mediarouter_mediarouter_java",
-  ]
+  deps = [ "//third_party/androidx:androidx_mediarouter_mediarouter_java" ]
 }
 
 android_library("android_media_java") {
@@ -23,7 +20,7 @@
       [ "java/src/org/chromium/third_party/android/media/MediaController.java" ]
   deps = [
     ":android_media_resources",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/androidx:androidx_media_media_java",
     "//third_party/androidx:androidx_mediarouter_mediarouter_java",
   ]
 }
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 224cdcd..ac714ce 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2698,7 +2698,7 @@
   kDOMWindowOpenPositioningFeaturesCrossScreen = 3392,
   kDOMWindowSetWindowRectCrossScreen = 3393,
   kFullscreenCrossScreen = 3394,
-  kBatterySavingsMeta = 3395,
+  kOBSOLETE_BatterySavingsMeta = 3395,
   kDigitalGoodsGetDigitalGoodsService = 3396,
   kDigitalGoodsGetDetails = 3397,
   kDigitalGoodsAcknowledge = 3398,
diff --git a/third_party/blink/public/web/web_document_loader.h b/third_party/blink/public/web/web_document_loader.h
index 32a7dd0..73addd8 100644
--- a/third_party/blink/public/web/web_document_loader.h
+++ b/third_party/blink/public/web/web_document_loader.h
@@ -77,12 +77,12 @@
   virtual WebString Referrer() const = 0;
 
   // Returns the response associated with this datasource.
-  virtual const WebURLResponse& GetResponse() const = 0;
+  virtual const WebURLResponse& GetWebResponse() const = 0;
 
   // When this datasource was created as a result of WebFrame::loadData,
   // there may be an associated unreachableURL.
   virtual bool HasUnreachableURL() const = 0;
-  virtual WebURL UnreachableURL() const = 0;
+  virtual WebURL UnreachableWebURL() const = 0;
 
   // Returns whether the navigation associated with this datasource is a
   // client redirect.
diff --git a/third_party/blink/renderer/core/css/remote_font_face_source.cc b/third_party/blink/renderer/core/css/remote_font_face_source.cc
index 1d2f960..c162b1f 100644
--- a/third_party/blink/renderer/core/css/remote_font_face_source.cc
+++ b/third_party/blink/renderer/core/css/remote_font_face_source.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/loader/subresource_integrity_helper.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
@@ -184,7 +185,27 @@
   auto* font = To<FontResource>(resource);
   histograms_.RecordRemoteFont(font);
 
-  custom_font_data_ = font->GetCustomFontData();
+  // Refer to the comments in classic_pending_script.cc for the reason why
+  // SRI checks should be done here in ResourceClient instead of
+  // ResourceFetcher. SRI failure should behave as network error
+  // (ErrorOccurred()). PreloadCache even caches network errors.
+  // Font fetch itself doesn't support SRI but font preload does.
+  // So, if the resource was preloaded we need to check
+  // SRI failure and simulate network error if it happens.
+
+  if (resource->IsLinkPreload()) {
+    SubresourceIntegrityHelper::DoReport(*execution_context,
+                                         resource->IntegrityReportInfo());
+  }
+
+  DCHECK(!custom_font_data_);
+  // font->GetCustomFontData() returns nullptr if network error happened
+  // (ErrorOccurred() is true). To simulate network error we don't update
+  // custom_font_data_ to keep the nullptr value in case of SRI failures.
+  if (!resource->IsLinkPreload() || resource->IntegrityDisposition() !=
+                                        ResourceIntegrityDisposition::kFailed) {
+    custom_font_data_ = font->GetCustomFontData();
+  }
   url_ = resource->Url().GetString();
 
   // FIXME: Provide more useful message such as OTS rejection reason.
@@ -221,12 +242,9 @@
   if (face_->FontLoaded(this)) {
     font_selector_->FontFaceInvalidated(
         FontInvalidationReason::kFontFaceLoaded);
-
-    const scoped_refptr<FontCustomPlatformData> customFontData =
-        font->GetCustomFontData();
-    if (customFontData) {
+    if (custom_font_data_) {
       probe::FontsUpdated(execution_context, face_->GetFontFace(),
-                          resource->Url().GetString(), customFontData.get());
+                          resource->Url().GetString(), custom_font_data_.get());
     }
   }
 }
diff --git a/third_party/blink/renderer/core/css/style_element.cc b/third_party/blink/renderer/core/css/style_element.cc
index 17eaf63..a367ffda 100644
--- a/third_party/blink/renderer/core/css/style_element.cc
+++ b/third_party/blink/renderer/core/css/style_element.cc
@@ -120,7 +120,7 @@
   if (sheet_->IsLoading()) {
     DCHECK(IsSameObject(owner_element));
     if (pending_sheet_type_ != PendingSheetType::kNonBlocking) {
-      owner_element.GetDocument().GetStyleEngine().RemovePendingSheet(
+      owner_element.GetDocument().GetStyleEngine().RemovePendingBlockingSheet(
           owner_element, pending_sheet_type_);
     }
     pending_sheet_type_ = PendingSheetType::kNone;
@@ -211,8 +211,8 @@
 
   DCHECK(IsSameObject(*sheet_->ownerNode()));
   if (pending_sheet_type_ != PendingSheetType::kNonBlocking) {
-    document.GetStyleEngine().RemovePendingSheet(*sheet_->ownerNode(),
-                                                 pending_sheet_type_);
+    document.GetStyleEngine().RemovePendingBlockingSheet(*sheet_->ownerNode(),
+                                                         pending_sheet_type_);
   }
   pending_sheet_type_ = PendingSheetType::kNone;
   return true;
@@ -222,7 +222,8 @@
   DCHECK(IsSameObject(element));
   DCHECK_LT(pending_sheet_type_, PendingSheetType::kBlocking);
   pending_sheet_type_ = PendingSheetType::kBlocking;
-  document.GetStyleEngine().AddPendingSheet(element, pending_sheet_type_);
+  document.GetStyleEngine().AddPendingBlockingSheet(element,
+                                                    pending_sheet_type_);
 }
 
 void StyleElement::BlockingAttributeChanged(Element& element) {
@@ -235,7 +236,7 @@
     return;
   if (blocking() && blocking()->IsRenderBlocking())
     return;
-  element.GetDocument().GetStyleEngine().RemovePendingSheet(
+  element.GetDocument().GetStyleEngine().RemovePendingBlockingSheet(
       element, pending_sheet_type_);
   pending_sheet_type_ = PendingSheetType::kNonBlocking;
 }
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index fddb5554..8488715 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -243,8 +243,8 @@
   return *inspector_style_sheet_;
 }
 
-void StyleEngine::AddPendingSheet(Node& style_sheet_candidate_node,
-                                  PendingSheetType type) {
+void StyleEngine::AddPendingBlockingSheet(Node& style_sheet_candidate_node,
+                                          PendingSheetType type) {
   DCHECK(type == PendingSheetType::kBlocking ||
          type == PendingSheetType::kDynamicRenderBlocking);
 
@@ -268,8 +268,8 @@
 }
 
 // This method is called whenever a top-level stylesheet has finished loading.
-void StyleEngine::RemovePendingSheet(Node& style_sheet_candidate_node,
-                                     PendingSheetType type) {
+void StyleEngine::RemovePendingBlockingSheet(Node& style_sheet_candidate_node,
+                                             PendingSheetType type) {
   DCHECK(type == PendingSheetType::kBlocking ||
          type == PendingSheetType::kDynamicRenderBlocking);
 
@@ -766,7 +766,7 @@
   CSSStyleSheet* style_sheet = nullptr;
 
   if (type != PendingSheetType::kNonBlocking)
-    AddPendingSheet(element, type);
+    AddPendingBlockingSheet(element, type);
 
   AtomicString text_content(text);
 
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index f2cc341..95705bd 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -246,10 +246,10 @@
   void SetPreferredStylesheetSetNameIfNotSet(const String&);
   void SetHttpDefaultStyle(const String&);
 
-  // TODO(xiaochengh): Rename them to Add/RemovePendingBlockingSheet.
-  void AddPendingSheet(Node& style_sheet_candidate_node, PendingSheetType type);
-  void RemovePendingSheet(Node& style_sheet_candidate_node,
-                          PendingSheetType type);
+  void AddPendingBlockingSheet(Node& style_sheet_candidate_node,
+                               PendingSheetType type);
+  void RemovePendingBlockingSheet(Node& style_sheet_candidate_node,
+                                  PendingSheetType type);
 
   bool HasPendingScriptBlockingSheets() const {
     return pending_script_blocking_stylesheets_ > 0;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 0313b01b..9ef772e 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -195,7 +195,6 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/viewport_data.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
-#include "third_party/blink/renderer/core/html/battery_savings.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_font_cache.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/html_canvas_element.h"
@@ -6881,34 +6880,6 @@
   GetStyleEngine().SetPageColorSchemes(color_scheme);
 }
 
-void Document::BatterySavingsMetaChanged() {
-  if (!RuntimeEnabledFeatures::BatterySavingsMetaEnabled(GetExecutionContext()))
-    return;
-
-  if (!IsInOutermostMainFrame())
-    return;
-
-  UseCounter::Count(GetExecutionContext(), WebFeature::kBatterySavingsMeta);
-  auto* root_element = documentElement();
-  if (!root_element)
-    return;
-
-  BatterySavingsFlags savings = 0;
-  for (HTMLMetaElement& meta_element :
-       Traversal<HTMLMetaElement>::DescendantsOf(*root_element)) {
-    if (EqualIgnoringASCIICase(meta_element.GetName(), "battery-savings")) {
-      SpaceSplitString split_content(
-          AtomicString(meta_element.Content().GetString().LowerASCII()));
-      if (split_content.Contains("allow-reduced-framerate"))
-        savings |= kAllowReducedFrameRate;
-      if (split_content.Contains("allow-reduced-script-speed"))
-        savings |= kAllowReducedScriptSpeed;
-      break;
-    }
-  }
-  GetPage()->GetChromeClient().BatterySavingsChanged(*GetFrame(), savings);
-}
-
 void Document::SupportsReducedMotionMetaChanged() {
   if (!IsInOutermostMainFrame())
     return;
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 16aae322..ad02450 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1718,10 +1718,6 @@
   // Update the presentation level color-scheme property for the root element.
   void ColorSchemeMetaChanged();
 
-  // A META element with name=battery-savings was added, removed, or modified.
-  // Re-collect the META values that apply and pass to LayerTreeHost.
-  void BatterySavingsMetaChanged();
-
   // A META element with name=supports-reduced-motion was added, removed, or
   // modified. Re-collect the META values.
   void SupportsReducedMotionMetaChanged();
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 1ce49f93..9533b2d7 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -73,7 +73,6 @@
 #include "third_party/blink/renderer/core/html/html_link_element.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/loader/document_loader.h"
-#include "third_party/blink/renderer/core/loader/empty_clients.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/validation_message_client.h"
 #include "third_party/blink/renderer/core/testing/color_scheme_helper.h"
@@ -1372,72 +1371,6 @@
         ViewportTestCase("cover", mojom::ViewportFit::kCover),
         ViewportTestCase("invalid", mojom::ViewportFit::kAuto)));
 
-class BatterySavingsChromeClient : public EmptyChromeClient {
- public:
-  MOCK_METHOD2(BatterySavingsChanged, void(LocalFrame&, BatterySavingsFlags));
-};
-
-class DocumentBatterySavingsTest : public PageTestBase,
-                                   private ScopedBatterySavingsMetaForTest {
- protected:
-  DocumentBatterySavingsTest() : ScopedBatterySavingsMetaForTest(true) {}
-
-  void SetUp() override {
-    chrome_client_ = MakeGarbageCollected<BatterySavingsChromeClient>();
-    SetupPageWithClients(chrome_client_);
-  }
-
-  Persistent<BatterySavingsChromeClient> chrome_client_;
-};
-
-TEST_F(DocumentBatterySavingsTest, ChromeClientCalls) {
-  testing::InSequence s;
-  // The client is called twice, once for each meta element, but is called with
-  // the same parameter both times because the first meta in DOM order takes
-  // precedence.
-  EXPECT_CALL(*chrome_client_,
-              BatterySavingsChanged(testing::_, kAllowReducedFrameRate))
-      .Times(2);
-
-  EXPECT_FALSE(GetDocument().Loader()->GetUseCounter().IsCounted(
-      WebFeature::kBatterySavingsMeta));
-
-  SetHtmlInnerHTML(R"HTML(
-    <meta id="first" name="battery-savings" content="allow-reduced-framerate">
-    <meta id="second" name="battery-savings" content="allow-reduced-script-speed">
-  )HTML");
-
-  EXPECT_TRUE(GetDocument().Loader()->GetUseCounter().IsCounted(
-      WebFeature::kBatterySavingsMeta));
-
-  // Remove the first meta causing the second to apply.
-  EXPECT_CALL(*chrome_client_,
-              BatterySavingsChanged(testing::_, kAllowReducedScriptSpeed))
-      .Times(1);
-
-  GetDocument().getElementById("first")->remove();
-
-  // Change the content attribute to an unsupported value.
-  EXPECT_CALL(*chrome_client_, BatterySavingsChanged(testing::_, 0)).Times(1);
-
-  Element* second = GetDocument().getElementById("second");
-  second->setAttribute(html_names::kContentAttr, "allow-blah");
-
-  // Change the content attribute to both supported values.
-  EXPECT_CALL(*chrome_client_,
-              BatterySavingsChanged(testing::_, kAllowReducedFrameRate |
-                                                    kAllowReducedScriptSpeed))
-      .Times(1);
-
-  second->setAttribute(html_names::kContentAttr,
-                       "allow-reduced-framerate allow-reduced-script-speed");
-
-  // Change the meta name to "viewport".
-  EXPECT_CALL(*chrome_client_, BatterySavingsChanged(testing::_, 0)).Times(1);
-
-  second->setAttribute(html_names::kNameAttr, "viewport");
-}
-
 namespace {
 class MockReportingContext final : public ReportingContext {
  public:
diff --git a/third_party/blink/renderer/core/dom/processing_instruction.cc b/third_party/blink/renderer/core/dom/processing_instruction.cc
index 6a026f9..b595e29 100644
--- a/third_party/blink/renderer/core/dom/processing_instruction.cc
+++ b/third_party/blink/renderer/core/dom/processing_instruction.cc
@@ -170,8 +170,8 @@
   } else {
     params.SetCharset(charset.IsEmpty() ? GetDocument().Encoding()
                                         : WTF::TextEncoding(charset));
-    GetDocument().GetStyleEngine().AddPendingSheet(*this,
-                                                   PendingSheetType::kBlocking);
+    GetDocument().GetStyleEngine().AddPendingBlockingSheet(
+        *this, PendingSheetType::kBlocking);
     CSSStyleSheetResource::Fetch(params, GetDocument().Fetcher(), this);
   }
 }
@@ -297,7 +297,7 @@
 void ProcessingInstruction::RemovePendingSheet() {
   if (is_xsl_)
     return;
-  GetDocument().GetStyleEngine().RemovePendingSheet(
+  GetDocument().GetStyleEngine().RemovePendingBlockingSheet(
       *this, PendingSheetType::kBlocking);
 }
 
diff --git a/third_party/blink/renderer/core/exported/BUILD.gn b/third_party/blink/renderer/core/exported/BUILD.gn
index eeb0726..fc55b0d 100644
--- a/third_party/blink/renderer/core/exported/BUILD.gn
+++ b/third_party/blink/renderer/core/exported/BUILD.gn
@@ -29,8 +29,6 @@
     "web_dev_tools_agent_impl.cc",
     "web_dev_tools_agent_impl.h",
     "web_document.cc",
-    "web_document_loader_impl.cc",
-    "web_document_loader_impl.h",
     "web_dom_activity_logger.cc",
     "web_dom_event.cc",
     "web_dom_message_event.cc",
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc b/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
deleted file mode 100644
index ee569d1..0000000
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/memory/ptr_util.h"
-#include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/mojom/loader/mhtml_load_result.mojom-blink.h"
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
-#include "third_party/blink/public/platform/web_document_subresource_filter.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_url_error.h"
-#include "third_party/blink/public/platform/web_vector.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/loader/subresource_filter.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
-#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
-#include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
-
-namespace blink {
-
-// static
-bool WebDocumentLoader::WillLoadUrlAsEmpty(const WebURL& url) {
-  return DocumentLoader::WillLoadUrlAsEmpty(url);
-}
-
-WebString WebDocumentLoaderImpl::OriginalReferrer() const {
-  return DocumentLoader::OriginalReferrer();
-}
-
-WebURL WebDocumentLoaderImpl::GetUrl() const {
-  return DocumentLoader::Url();
-}
-
-WebString WebDocumentLoaderImpl::HttpMethod() const {
-  return DocumentLoader::HttpMethod();
-}
-
-WebString WebDocumentLoaderImpl::Referrer() const {
-  return DocumentLoader::GetReferrer();
-}
-
-const WebURLResponse& WebDocumentLoaderImpl::GetResponse() const {
-  return response_wrapper_;
-}
-
-bool WebDocumentLoaderImpl::HasUnreachableURL() const {
-  return !DocumentLoader::UnreachableURL().IsEmpty();
-}
-
-WebURL WebDocumentLoaderImpl::UnreachableURL() const {
-  return DocumentLoader::UnreachableURL();
-}
-
-bool WebDocumentLoaderImpl::IsClientRedirect() const {
-  return DocumentLoader::IsClientRedirect();
-}
-
-bool WebDocumentLoaderImpl::ReplacesCurrentHistoryItem() const {
-  return DocumentLoader::ReplacesCurrentHistoryItem();
-}
-
-WebNavigationType WebDocumentLoaderImpl::GetNavigationType() const {
-  return DocumentLoader::GetNavigationType();
-}
-
-WebDocumentLoader::ExtraData* WebDocumentLoaderImpl::GetExtraData() const {
-  return extra_data_.get();
-}
-
-std::unique_ptr<WebDocumentLoader::ExtraData>
-WebDocumentLoaderImpl::TakeExtraData() {
-  return std::move(extra_data_);
-}
-
-void WebDocumentLoaderImpl::SetExtraData(
-    std::unique_ptr<ExtraData> extra_data) {
-  extra_data_ = std::move(extra_data);
-}
-
-WebDocumentLoaderImpl::WebDocumentLoaderImpl(
-    LocalFrame* frame,
-    WebNavigationType navigation_type,
-    std::unique_ptr<WebNavigationParams> navigation_params,
-    std::unique_ptr<PolicyContainer> policy_container,
-    std::unique_ptr<ExtraData> extra_data)
-    : DocumentLoader(base::PassKey<WebDocumentLoaderImpl>(),
-                     frame,
-                     navigation_type,
-                     std::move(navigation_params),
-                     std::move(policy_container)),
-      response_wrapper_(DocumentLoader::GetResponse()),
-      extra_data_(std::move(extra_data)) {}
-
-WebDocumentLoaderImpl::~WebDocumentLoaderImpl() {
-  // Verify that detachFromFrame() has been called.
-  DCHECK(!extra_data_);
-}
-
-void WebDocumentLoaderImpl::DetachFromFrame(bool flush_microtask_queue) {
-  DocumentLoader::DetachFromFrame(flush_microtask_queue);
-  extra_data_.reset();
-}
-
-void WebDocumentLoaderImpl::SetSubresourceFilter(
-    WebDocumentSubresourceFilter* subresource_filter) {
-  DocumentLoader::SetSubresourceFilter(MakeGarbageCollected<SubresourceFilter>(
-      GetFrame()->DomWindow(), base::WrapUnique(subresource_filter)));
-}
-
-void WebDocumentLoaderImpl::SetServiceWorkerNetworkProvider(
-    std::unique_ptr<WebServiceWorkerNetworkProvider> provider) {
-  DocumentLoader::SetServiceWorkerNetworkProvider(std::move(provider));
-}
-
-WebServiceWorkerNetworkProvider*
-WebDocumentLoaderImpl::GetServiceWorkerNetworkProvider() {
-  return DocumentLoader::GetServiceWorkerNetworkProvider();
-}
-
-void WebDocumentLoaderImpl::BlockParser() {
-  DocumentLoader::BlockParser();
-}
-
-void WebDocumentLoaderImpl::ResumeParser() {
-  DocumentLoader::ResumeParser();
-}
-
-bool WebDocumentLoaderImpl::HasBeenLoadedAsWebArchive() const {
-  return archive_;
-}
-
-WebArchiveInfo WebDocumentLoaderImpl::GetArchiveInfo() const {
-  if (archive_ &&
-      archive_->LoadResult() == mojom::blink::MHTMLLoadResult::kSuccess) {
-    return {
-        archive_->LoadResult(),
-        archive_->MainResource()->Url(),
-        archive_->Date(),
-    };
-  }
-
-  // TODO(arthursonzogni): Returning MHTMLLoadResult::kSuccess when there are no
-  // archive is very misleading. Consider adding a new enum value to
-  // discriminate success versus no archive.
-  return {
-      archive_ ? archive_->LoadResult()
-               : mojom::blink::MHTMLLoadResult::kSuccess,
-      WebURL(),
-      base::Time(),
-  };
-}
-
-bool WebDocumentLoaderImpl::LastNavigationHadTransientUserActivation() const {
-  return DocumentLoader::LastNavigationHadTransientUserActivation();
-}
-
-void WebDocumentLoaderImpl::SetCodeCacheHost(
-    mojo::PendingRemote<mojom::CodeCacheHost> code_cache_host) {
-  DocumentLoader::SetCodeCacheHost(std::move(code_cache_host));
-}
-
-void WebDocumentLoaderImpl::Trace(Visitor* visitor) const {
-  DocumentLoader::Trace(visitor);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_document_loader_impl.h b/third_party/blink/renderer/core/exported/web_document_loader_impl.h
deleted file mode 100644
index 5328b03..0000000
--- a/third_party/blink/renderer/core/exported/web_document_loader_impl.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_DOCUMENT_LOADER_IMPL_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_DOCUMENT_LOADER_IMPL_H_
-
-#include <memory>
-#include "third_party/blink/public/web/web_document_loader.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/frame/frame_types.h"
-#include "third_party/blink/renderer/core/loader/document_loader.h"
-#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
-#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/weborigin/kurl.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace blink {
-
-class PolicyContainer;
-
-// Extends blink::DocumentLoader to attach |extra_data_| to store data that can
-// be set/get via the WebDocumentLoader interface.
-class CORE_EXPORT WebDocumentLoaderImpl final : public DocumentLoader,
-                                                public WebDocumentLoader {
- public:
-  WebDocumentLoaderImpl(LocalFrame*,
-                        WebNavigationType navigation_type,
-                        std::unique_ptr<WebNavigationParams> navigation_params,
-                        std::unique_ptr<PolicyContainer> policy_container,
-                        std::unique_ptr<ExtraData> extra_data);
-  ~WebDocumentLoaderImpl() override;
-
-  static WebDocumentLoaderImpl* FromDocumentLoader(DocumentLoader* loader) {
-    return static_cast<WebDocumentLoaderImpl*>(loader);
-  }
-
-  // WebDocumentLoader methods:
-  WebString OriginalReferrer() const override;
-  WebURL GetUrl() const override;
-  WebString HttpMethod() const override;
-  WebString Referrer() const override;
-  const WebURLResponse& GetResponse() const override;
-  bool HasUnreachableURL() const override;
-  WebURL UnreachableURL() const override;
-  bool IsClientRedirect() const override;
-  bool ReplacesCurrentHistoryItem() const override;
-  WebNavigationType GetNavigationType() const override;
-  ExtraData* GetExtraData() const override;
-  std::unique_ptr<ExtraData> TakeExtraData() override;
-  void SetExtraData(std::unique_ptr<ExtraData>) override;
-  void SetSubresourceFilter(WebDocumentSubresourceFilter*) override;
-  void SetServiceWorkerNetworkProvider(
-      std::unique_ptr<WebServiceWorkerNetworkProvider>) override;
-  WebServiceWorkerNetworkProvider* GetServiceWorkerNetworkProvider() override;
-  void BlockParser() override;
-  void ResumeParser() override;
-  bool HasBeenLoadedAsWebArchive() const override;
-  WebArchiveInfo GetArchiveInfo() const override;
-  bool LastNavigationHadTransientUserActivation() const override;
-  void SetCodeCacheHost(
-      mojo::PendingRemote<mojom::CodeCacheHost> code_cache_host) override;
-
-  void Trace(Visitor*) const override;
-
- private:
-  void DetachFromFrame(bool flush_microtask_queue) override;
-
-  // Mutable because the const getters will magically sync these to the
-  // latest version from WebKit.
-  mutable WrappedResourceResponse response_wrapper_;
-
-  std::unique_ptr<ExtraData> extra_data_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_DOCUMENT_LOADER_IMPL_H_
diff --git a/third_party/blink/renderer/core/exported/web_navigation_params.cc b/third_party/blink/renderer/core/exported/web_navigation_params.cc
index c2839e3..4d2ef0f 100644
--- a/third_party/blink/renderer/core/exported/web_navigation_params.cc
+++ b/third_party/blink/renderer/core/exported/web_navigation_params.cc
@@ -5,7 +5,6 @@
 #include "third_party/blink/public/web/web_navigation_params.h"
 
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
 #include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h"
 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
 #include "third_party/blink/renderer/platform/network/http_names.h"
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index 27fbc0f8..ca58f631 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -67,7 +67,6 @@
 #include "third_party/blink/renderer/core/events/web_input_event_conversion.h"
 #include "third_party/blink/renderer/core/events/wheel_event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/event_handler_registry.h"
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index b8a6c6e..b12e5c0 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -296,8 +296,8 @@
       mojom::blink::PermissionsPolicyFeature::kAttributionReporting);
 
   if (!feature_policy_enabled) {
-    LogAuditIssue(AttributionReportingIssueType::kPermissionPolicyDisabled, "",
-                  element, request_id);
+    LogAuditIssue(AttributionReportingIssueType::kPermissionPolicyDisabled,
+                  /*string=*/absl::nullopt, element, request_id);
     return RegisterResult::kNotAllowed;
   }
 
@@ -491,7 +491,7 @@
 
 void AttributionSrcLoader::LogAuditIssue(
     AttributionReportingIssueType issue_type,
-    const String& string,
+    const absl::optional<String>& string,
     HTMLElement* element,
     const absl::optional<String>& request_id) {
   if (!local_frame_->IsAttached())
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.h b/third_party/blink/renderer/core/frame/attribution_src_loader.h
index 9ebfa448..36ee871 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.h
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.h
@@ -102,7 +102,7 @@
                                        RegisterResult& out_register_result);
 
   void LogAuditIssue(AttributionReportingIssueType issue_type,
-                     const String& string,
+                     const absl::optional<String>& string,
                      HTMLElement* element,
                      const absl::optional<String>& request_id);
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index f36d04f..8107267 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -231,12 +231,7 @@
       const Vector<String>& added_selectors,
       const Vector<String>& removed_selectors) = 0;
 
-  virtual DocumentLoader* CreateDocumentLoader(
-      LocalFrame*,
-      WebNavigationType,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<PolicyContainer> policy_container,
-      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) = 0;
+  virtual void DidCreateDocumentLoader(DocumentLoader*) = 0;
 
   virtual String UserAgentOverride() = 0;
   virtual String UserAgent() = 0;
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
index d10dce9f6..7727486 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
@@ -66,7 +66,6 @@
 #include "third_party/blink/renderer/core/events/message_event.h"
 #include "third_party/blink/renderer/core/events/mouse_event.h"
 #include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/fileapi/public_url_manager.h"
@@ -188,8 +187,7 @@
 void LocalFrameClientImpl::DidCommitDocumentReplacementNavigation(
     DocumentLoader* loader) {
   if (web_frame_->Client()) {
-    web_frame_->Client()->DidCommitDocumentReplacementNavigation(
-        WebDocumentLoaderImpl::FromDocumentLoader(loader));
+    web_frame_->Client()->DidCommitDocumentReplacementNavigation(loader);
   }
 }
 
@@ -770,20 +768,9 @@
   }
 }
 
-DocumentLoader* LocalFrameClientImpl::CreateDocumentLoader(
-    LocalFrame* frame,
-    WebNavigationType navigation_type,
-    std::unique_ptr<WebNavigationParams> navigation_params,
-    std::unique_ptr<PolicyContainer> policy_container,
-    std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
-  DCHECK(frame);
-  WebDocumentLoaderImpl* document_loader =
-      MakeGarbageCollected<WebDocumentLoaderImpl>(
-          frame, navigation_type, std::move(navigation_params),
-          std::move(policy_container), std::move(extra_data));
-  if (web_frame_->Client())
-    web_frame_->Client()->DidCreateDocumentLoader(document_loader);
-  return document_loader;
+void LocalFrameClientImpl::DidCreateDocumentLoader(
+    DocumentLoader* document_loader) {
+  web_frame_->Client()->DidCreateDocumentLoader(document_loader);
 }
 
 String LocalFrameClientImpl::UserAgentOverride() {
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.h b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
index ac81fdc..200f0c5 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
@@ -158,16 +158,7 @@
   void SelectorMatchChanged(const Vector<String>& added_selectors,
                             const Vector<String>& removed_selectors) override;
 
-  // Creates a WebDocumentLoaderImpl that is a DocumentLoader but also has:
-  // - storage to store an extra data that can be used by the content layer
-  // - wrapper methods to expose DocumentLoader's variables to the content
-  //   layer
-  DocumentLoader* CreateDocumentLoader(
-      LocalFrame*,
-      WebNavigationType,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<PolicyContainer> policy_container,
-      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) override;
+  void DidCreateDocumentLoader(DocumentLoader*) override;
 
   String UserAgentOverride() override;
   WTF::String UserAgent() override;
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 9b586de..011b852 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -86,7 +86,6 @@
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
-#include "third_party/blink/renderer/core/html/battery_savings.h"
 #include "third_party/blink/renderer/core/html/fenced_frame/document_fenced_frames.h"
 #include "third_party/blink/renderer/core/html/fenced_frame/html_fenced_frame_element.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
@@ -3859,11 +3858,6 @@
   }
 }
 
-void WebFrameWidgetImpl::BatterySavingsChanged(BatterySavingsFlags savings) {
-  widget_base_->LayerTreeHost()->SetEnableFrameRateThrottling(
-      savings & kAllowReducedFrameRate);
-}
-
 const viz::LocalSurfaceId& WebFrameWidgetImpl::LocalSurfaceIdFromParent() {
   return widget_base_->local_surface_id_from_parent();
 }
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index 8e1d49e3..e06403f5 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -56,7 +56,6 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/exported/web_page_popup_impl.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
-#include "third_party/blink/renderer/core/html/battery_savings.h"
 #include "third_party/blink/renderer/core/page/event_with_hit_test_results.h"
 #include "third_party/blink/renderer/core/page/page_widget_delegate.h"
 #include "third_party/blink/renderer/core/page/viewport_description.h"
@@ -569,10 +568,6 @@
   void UpdateViewportDescription(
       const ViewportDescription& viewport_description);
 
-  // The value of the applied battery-savings META element in the document
-  // changed.
-  void BatterySavingsChanged(BatterySavingsFlags savings);
-
   const viz::LocalSurfaceId& LocalSurfaceIdFromParent();
 
   ScreenMetricsEmulator* DeviceEmulator();
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 7316260..a0c658da 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -176,7 +176,6 @@
 #include "third_party/blink/renderer/core/events/after_print_event.h"
 #include "third_party/blink/renderer/core/events/before_print_event.h"
 #include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h"
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
@@ -590,10 +589,6 @@
   }
 };
 
-static WebDocumentLoader* DocumentLoaderForDocLoader(DocumentLoader* loader) {
-  return loader ? WebDocumentLoaderImpl::FromDocumentLoader(loader) : nullptr;
-}
-
 // WebFrame -------------------------------------------------------------------
 
 static CreateWebFrameWidgetCallback* g_create_web_frame_widget = nullptr;
@@ -1140,7 +1135,7 @@
 
 WebDocumentLoader* WebLocalFrameImpl::GetDocumentLoader() const {
   DCHECK(GetFrame());
-  return DocumentLoaderForDocLoader(GetFrame()->Loader().GetDocumentLoader());
+  return GetFrame()->Loader().GetDocumentLoader();
 }
 
 void WebLocalFrameImpl::EnableViewSourceMode(bool enable) {
diff --git a/third_party/blink/renderer/core/html/battery_savings.h b/third_party/blink/renderer/core/html/battery_savings.h
deleted file mode 100644
index 5519b37..0000000
--- a/third_party/blink/renderer/core/html/battery_savings.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2020 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 THIRD_PARTY_BLINK_RENDERER_CORE_HTML_BATTERY_SAVINGS_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_BATTERY_SAVINGS_H_
-
-namespace blink {
-
-// These are constants for the various keywords allowed for the battery-savings
-// meta element. For instance:
-//
-// <meta name="battery-savings" content="allow-reduced-framerate">
-// These constants are bits which can be combined.
-enum BatterySavings {
-  kAllowReducedFrameRate = 1 << 0,
-  kAllowReducedScriptSpeed = 1 << 1,
-};
-
-using BatterySavingsFlags = unsigned;
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_BATTERY_SAVINGS_H_
diff --git a/third_party/blink/renderer/core/html/build.gni b/third_party/blink/renderer/core/html/build.gni
index bf2f872..abd6f7f 100644
--- a/third_party/blink/renderer/core/html/build.gni
+++ b/third_party/blink/renderer/core/html/build.gni
@@ -7,7 +7,6 @@
   "anchor_element_metrics.h",
   "anchor_element_metrics_sender.cc",
   "anchor_element_metrics_sender.h",
-  "battery_savings.h",
   "blocking_attribute.cc",
   "blocking_attribute.h",
   "canvas/canvas_async_blob_creator.cc",
diff --git a/third_party/blink/renderer/core/html/html_dialog_element.cc b/third_party/blink/renderer/core/html/html_dialog_element.cc
index 10e9dfa8..3b1ed08 100644
--- a/third_party/blink/renderer/core/html/html_dialog_element.cc
+++ b/third_party/blink/renderer/core/html/html_dialog_element.cc
@@ -199,7 +199,8 @@
       : dialog_(dialog) {}
 
   void Invoke(ExecutionContext*, Event* event) override {
-    DCHECK(dialog_);
+    if (!dialog_)
+      return;
     if (event->type() == event_type_names::kCancel)
       dialog_->CloseWatcherFiredCancel();
     if (event->type() == event_type_names::kClose)
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 5a646f9..71bae267 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -152,6 +152,29 @@
   if (is_loading_attr_lazy)
     return true;
 
+  // When LazyAds and LazyEmbeds cause problems, the user might reload the page
+  // to fix the problem. Also, the user is likely to avoid reloading the page
+  // when they submit forms. So skips LazyEmbeds and LazyAds in the following
+  // conditions.
+  // - Reload a page.
+  // - Submit a form.
+  // - Resubmit a form.
+  // The reason why we use DocumentLoader::GetNavigationType() instead of
+  // DocumentLoader::LoadType() is that DocumentLoader::LoadType() is reset to
+  // WebFrameLoadType::kStandard on DidFinishNavigation(). When JavaScript adds
+  // iframes after navigation, DocumentLoader::LoadType() always returns
+  // WebFrameLoadType::kStandard. DocumentLoader::GetNavigationType() doesn't
+  // have this problem.
+  Document& top_document = document.TopDocument();
+  WebNavigationType navigation_type =
+      top_document.Loader() ? top_document.Loader()->GetNavigationType()
+                            : WebNavigationType::kWebNavigationTypeOther;
+  if (navigation_type == WebNavigationType::kWebNavigationTypeReload ||
+      navigation_type == WebNavigationType::kWebNavigationTypeFormSubmitted ||
+      navigation_type == WebNavigationType::kWebNavigationTypeFormResubmitted) {
+    return false;
+  }
+
   if (record_uma) {
     base::UmaHistogramEnumeration(
         "Blink.AutomaticLazyLoadFrame",
diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc
index e6d5585..1aae2c0 100644
--- a/third_party/blink/renderer/core/html/html_meta_element.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element.cc
@@ -496,8 +496,6 @@
         /*update_theme_color_cache=*/true);
   } else if (EqualIgnoringASCIICase(name_value, "color-scheme")) {
     GetDocument().ColorSchemeMetaChanged();
-  } else if (EqualIgnoringASCIICase(name_value, "battery-savings")) {
-    GetDocument().BatterySavingsMetaChanged();
   } else if (EqualIgnoringASCIICase(name_value, "supports-reduced-motion")) {
     GetDocument().SupportsReducedMotionMetaChanged();
   }
@@ -584,10 +582,6 @@
     GetDocument().ColorSchemeMetaChanged();
     return;
   }
-  if (EqualIgnoringASCIICase(name_value, "battery-savings")) {
-    GetDocument().BatterySavingsMetaChanged();
-    return;
-  }
 
   if (EqualIgnoringASCIICase(name_value, "supports-reduced-motion")) {
     GetDocument().SupportsReducedMotionMetaChanged();
diff --git a/third_party/blink/renderer/core/html/link_style.cc b/third_party/blink/renderer/core/html/link_style.cc
index 36908ccb..19898c4 100644
--- a/third_party/blink/renderer/core/html/link_style.cc
+++ b/third_party/blink/renderer/core/html/link_style.cc
@@ -177,7 +177,8 @@
 
   if (pending_sheet_type_ == PendingSheetType::kNonBlocking)
     return;
-  GetDocument().GetStyleEngine().AddPendingSheet(*owner_, pending_sheet_type_);
+  GetDocument().GetStyleEngine().AddPendingBlockingSheet(*owner_,
+                                                         pending_sheet_type_);
 }
 
 void LinkStyle::RemovePendingSheet() {
@@ -193,7 +194,7 @@
     return;
   }
 
-  GetDocument().GetStyleEngine().RemovePendingSheet(*owner_, type);
+  GetDocument().GetStyleEngine().RemovePendingBlockingSheet(*owner_, type);
 }
 
 void LinkStyle::SetDisabledState(bool disabled) {
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index f4de206..7a0b2ea 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -337,7 +337,8 @@
     // TODO(crbug.com/981419): Honor the integrity attribute value for all
     // supported preload destinations, not just the destinations that support
     // SRI in the first place.
-    if (type == ResourceType::kScript || type == ResourceType::kCSSStyleSheet) {
+    if (type == ResourceType::kScript || type == ResourceType::kCSSStyleSheet ||
+        type == ResourceType::kFont) {
       request->SetIntegrityMetadata(integrity_metadata_);
     }
 
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 4e0ddfe6..25fa9b4f 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -248,6 +248,7 @@
 
 struct SameSizeAsDocumentLoader
     : public GarbageCollected<SameSizeAsDocumentLoader>,
+      public WebDocumentLoader,
       public UseCounter,
       public WebNavigationBodyLoader::Client {
   Member<MHTMLArchive> archive;
@@ -272,6 +273,7 @@
   Member<SubresourceFilter> subresource_filter;
   AtomicString original_referrer;
   ResourceResponse response;
+  mutable WrappedResourceResponse response_wrapper;
   WebFrameLoadType load_type;
   bool is_client_redirect;
   bool replaces_current_history_item;
@@ -337,6 +339,7 @@
   bool anonymous;
   bool waiting_for_document_loader;
   bool waiting_for_code_cache;
+  std::unique_ptr<ExtraData> extra_data;
 };
 
 // Asserts size of DocumentLoader, so that whenever a new attribute is added to
@@ -348,11 +351,11 @@
 }  // namespace
 
 DocumentLoader::DocumentLoader(
-    base::PassKey<WebDocumentLoaderImpl>,
     LocalFrame* frame,
     WebNavigationType navigation_type,
     std::unique_ptr<WebNavigationParams> navigation_params,
-    std::unique_ptr<PolicyContainer> policy_container)
+    std::unique_ptr<PolicyContainer> policy_container,
+    std::unique_ptr<ExtraData> extra_data)
     : params_(std::move(navigation_params)),
       policy_container_(std::move(policy_container)),
       url_(params_->url),
@@ -378,6 +381,7 @@
                         : nullptr),
       original_referrer_(referrer_),
       response_(params_->response.ToResourceResponse()),
+      response_wrapper_(response_),
       load_type_(params_->frame_load_type),
       is_client_redirect_(params_->is_client_redirect),
       data_received_(false),
@@ -434,7 +438,8 @@
           params_->is_cross_site_cross_browsing_context_group),
       navigation_api_back_entries_(params_->navigation_api_back_entries),
       navigation_api_forward_entries_(params_->navigation_api_forward_entries),
-      anonymous_(params_->anonymous) {
+      anonymous_(params_->anonymous),
+      extra_data_(std::move(extra_data)) {
   DCHECK(frame_);
 
   // TODO(dgozman): we should get rid of this boolean field, and make client
@@ -512,6 +517,8 @@
       fenced_frame_reporting_->metadata.insert(destination, std::move(data));
     }
   }
+
+  frame_->Client()->DidCreateDocumentLoader(this);
 }
 
 std::unique_ptr<WebNavigationParams>
@@ -645,20 +652,15 @@
   return navigation_timing_info_.get();
 }
 
-const AtomicString& DocumentLoader::OriginalReferrer() const {
+WebString DocumentLoader::OriginalReferrer() const {
   return original_referrer_;
 }
 
-void DocumentLoader::SetSubresourceFilter(
-    SubresourceFilter* subresource_filter) {
-  subresource_filter_ = subresource_filter;
-}
-
 const KURL& DocumentLoader::Url() const {
   return url_;
 }
 
-const AtomicString& DocumentLoader::HttpMethod() const {
+WebString DocumentLoader::HttpMethod() const {
   return http_method_;
 }
 
@@ -1583,6 +1585,7 @@
   if (!frame_)
     return;
 
+  extra_data_.reset();
   service_worker_network_provider_ = nullptr;
   WeakIdentifierMap<DocumentLoader>::NotifyObjectDestroyed(this);
   frame_ = nullptr;
@@ -1609,6 +1612,10 @@
   return SchemeRegistry::ShouldLoadURLSchemeAsEmptyDocument(url.Protocol());
 }
 
+bool WebDocumentLoader::WillLoadUrlAsEmpty(const WebURL& url) {
+  return DocumentLoader::WillLoadUrlAsEmpty(url);
+}
+
 void DocumentLoader::InitializeEmptyResponse() {
   response_ = ResourceResponse(url_);
   response_.SetMimeType("text/html");
@@ -2993,6 +3000,46 @@
   }
 }
 
+void DocumentLoader::SetSubresourceFilter(
+    WebDocumentSubresourceFilter* subresource_filter) {
+  DCHECK(subresource_filter);
+  subresource_filter_ = MakeGarbageCollected<SubresourceFilter>(
+      frame_->DomWindow(), base::WrapUnique(subresource_filter));
+}
+
+WebDocumentLoader::ExtraData* DocumentLoader::GetExtraData() const {
+  return extra_data_.get();
+}
+
+std::unique_ptr<WebDocumentLoader::ExtraData> DocumentLoader::TakeExtraData() {
+  return std::move(extra_data_);
+}
+
+void DocumentLoader::SetExtraData(std::unique_ptr<ExtraData> extra_data) {
+  extra_data_ = std::move(extra_data);
+}
+
+WebArchiveInfo DocumentLoader::GetArchiveInfo() const {
+  if (archive_ &&
+      archive_->LoadResult() == mojom::blink::MHTMLLoadResult::kSuccess) {
+    return {
+        archive_->LoadResult(),
+        archive_->MainResource()->Url(),
+        archive_->Date(),
+    };
+  }
+
+  // TODO(arthursonzogni): Returning MHTMLLoadResult::kSuccess when there are no
+  // archive is very misleading. Consider adding a new enum value to
+  // discriminate success versus no archive.
+  return {
+      archive_ ? archive_->LoadResult()
+               : mojom::blink::MHTMLLoadResult::kSuccess,
+      WebURL(),
+      base::Time(),
+  };
+}
+
 // static
 void DocumentLoader::DisableCodeCacheForTesting() {
   GetDisableCodeCacheForTesting() = true;
diff --git a/third_party/blink/renderer/core/loader/document_loader.h b/third_party/blink/renderer/core/loader/document_loader.h
index 2dbe95f..eb8bda77 100644
--- a/third_party/blink/renderer/core/loader/document_loader.h
+++ b/third_party/blink/renderer/core/loader/document_loader.h
@@ -71,6 +71,7 @@
 #include "third_party/blink/renderer/core/loader/preload_helper.h"
 #include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/core/permissions_policy/policy_helper.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
 #include "third_party/blink/renderer/platform/loader/fetch/code_cache_host.h"
@@ -106,7 +107,6 @@
 class ResourceTimingInfo;
 class SerializedScriptValue;
 class SubresourceFilter;
-class WebDocumentLoaderImpl;
 class WebServiceWorkerNetworkProvider;
 
 namespace mojom {
@@ -121,18 +121,15 @@
 // provisional load, then commit but that is no longer necessary and this class
 // can be simplified.
 class CORE_EXPORT DocumentLoader : public GarbageCollected<DocumentLoader>,
+                                   public WebDocumentLoader,
                                    public UseCounter,
                                    public WebNavigationBodyLoader::Client {
  public:
-  // Do not create DocumentLoader directly.
-  // Use WebDocumentLoaderImpl's constructor to create it so that
-  // DocumentLoader can always be static_cast to WebDocumentLoaderImpl.
-  DocumentLoader(base::PassKey<WebDocumentLoaderImpl>,
-                 LocalFrame*,
+  DocumentLoader(LocalFrame*,
                  WebNavigationType navigation_type,
                  std::unique_ptr<WebNavigationParams> navigation_params,
-                 std::unique_ptr<PolicyContainer> policy_container);
-
+                 std::unique_ptr<PolicyContainer> policy_container,
+                 std::unique_ptr<ExtraData> extra_data);
   ~DocumentLoader() override;
 
   // Returns WebNavigationParams that can be used to clone DocumentLoader. Used
@@ -147,17 +144,58 @@
 
   ResourceTimingInfo* GetNavigationTimingInfo() const;
 
-  virtual void DetachFromFrame(bool flush_microtask_queue);
+  void DetachFromFrame(bool flush_microtask_queue);
 
   uint64_t MainResourceIdentifier() const;
 
   const AtomicString& MimeType() const;
 
-  const AtomicString& OriginalReferrer() const;
+  // WebDocumentLoader overrides:
+  WebString OriginalReferrer() const override;
+  WebURL GetUrl() const override { return Url(); }
+  WebString HttpMethod() const override;
+  WebString Referrer() const override { return GetReferrer(); }
+  bool HasUnreachableURL() const override {
+    return !UnreachableURL().IsEmpty();
+  }
+  WebURL UnreachableWebURL() const override { return UnreachableURL(); }
+  const WebURLResponse& GetWebResponse() const override {
+    return response_wrapper_;
+  }
+  bool IsClientRedirect() const override { return is_client_redirect_; }
+  bool ReplacesCurrentHistoryItem() const override {
+    return replaces_current_history_item_;
+  }
+  WebNavigationType GetNavigationType() const override {
+    return navigation_type_;
+  }
+  ExtraData* GetExtraData() const override;
+  std::unique_ptr<ExtraData> TakeExtraData() override;
+  void SetExtraData(std::unique_ptr<ExtraData>) override;
+  void SetSubresourceFilter(WebDocumentSubresourceFilter*) override;
+  void SetServiceWorkerNetworkProvider(
+      std::unique_ptr<WebServiceWorkerNetworkProvider>) override;
+  // May return null before the first HTML tag is inserted by the
+  // parser (before didCreateDataSource is called), after the document
+  // is detached from frame, or in tests.
+  WebServiceWorkerNetworkProvider* GetServiceWorkerNetworkProvider() override {
+    return service_worker_network_provider_.get();
+  }
+  // Can be used to temporarily suspend feeding the parser with new data. The
+  // parser will be allowed to read new data when ResumeParser() is called the
+  // same number of time than BlockParser().
+  void BlockParser() override;
+  void ResumeParser() override;
+  bool HasBeenLoadedAsWebArchive() const override { return archive_; }
+  WebArchiveInfo GetArchiveInfo() const override;
+  bool LastNavigationHadTransientUserActivation() const override {
+    return last_navigation_had_transient_user_activation_;
+  }
+  void SetCodeCacheHost(
+      mojo::PendingRemote<mojom::CodeCacheHost> code_cache_host) override;
 
   MHTMLArchive* Archive() const { return archive_.Get(); }
 
-  void SetSubresourceFilter(SubresourceFilter*);
   SubresourceFilter* GetSubresourceFilter() const {
     return subresource_filter_.Get();
   }
@@ -168,7 +206,6 @@
   const KURL& Url() const;
 
   const KURL& UrlForHistory() const;
-  const AtomicString& HttpMethod() const;
   const AtomicString& GetReferrer() const;
   const SecurityOrigin* GetRequestorOrigin() const;
   const KURL& UnreachableURL() const;
@@ -200,10 +237,6 @@
                                        bool is_synchronously_committed);
 
   const ResourceResponse& GetResponse() const { return response_; }
-  bool IsClientRedirect() const { return is_client_redirect_; }
-  bool ReplacesCurrentHistoryItem() const {
-    return replaces_current_history_item_;
-  }
 
   bool IsCommittedButEmpty() const {
     return state_ >= kCommitted && !data_received_;
@@ -215,7 +248,6 @@
   WebFrameLoadType LoadType() const { return load_type_; }
   void SetLoadType(WebFrameLoadType load_type) { load_type_ = load_type; }
 
-  WebNavigationType GetNavigationType() const { return navigation_type_; }
   void SetNavigationType(WebNavigationType navigation_type) {
     navigation_type_ = navigation_type;
   }
@@ -271,16 +303,6 @@
   void DispatchLinkHeaderPreloads(const ViewportDescription*,
                                   PreloadHelper::MediaPreloadPolicy);
 
-  void SetServiceWorkerNetworkProvider(
-      std::unique_ptr<WebServiceWorkerNetworkProvider>);
-
-  // May return null before the first HTML tag is inserted by the
-  // parser (before didCreateDataSource is called), after the document
-  // is detached from frame, or in tests.
-  WebServiceWorkerNetworkProvider* GetServiceWorkerNetworkProvider() {
-    return service_worker_network_provider_.get();
-  }
-
   void LoadFailed(const ResourceError&);
 
   void Trace(Visitor*) const override;
@@ -299,12 +321,6 @@
     return devtools_navigation_token_;
   }
 
-  // Can be used to temporarily suspend feeding the parser with new data. The
-  // parser will be allowed to read new data when ResumeParser() is called the
-  // same number of time than BlockParser().
-  void BlockParser();
-  void ResumeParser();
-
   UseCounterImpl& GetUseCounter() { return use_counter_; }
   Dactyloscoper& GetDactyloscoper() { return dactyloscoper_; }
 
@@ -316,10 +332,6 @@
 
   void SetCommitReason(CommitReason reason) { commit_reason_ = reason; }
 
-  bool LastNavigationHadTransientUserActivation() const {
-    return last_navigation_had_transient_user_activation_;
-  }
-
   // Whether the navigation originated from the browser process. Note: history
   // navigation is always considered to be browser initiated, even if the
   // navigation was started using the history API in the renderer.
@@ -367,8 +379,6 @@
       const mojom::blink::PrerenderPageActivationParams& params);
 
   CodeCacheHost* GetCodeCacheHost();
-  void SetCodeCacheHost(
-      mojo::PendingRemote<mojom::CodeCacheHost> code_cache_host);
   static void DisableCodeCacheForTesting();
 
   mojo::PendingRemote<blink::mojom::CodeCacheHost> CreateWorkerCodeCacheHost();
@@ -552,6 +562,9 @@
   const AtomicString original_referrer_;
 
   ResourceResponse response_;
+  // Mutable because the const getters will magically sync these to the
+  // latest version of |response_|.
+  mutable WrappedResourceResponse response_wrapper_;
 
   WebFrameLoadType load_type_;
 
@@ -695,6 +708,8 @@
   // features::kEarlyBodyLoad is enabled.
   bool waiting_for_document_loader_ = false;
   bool waiting_for_code_cache_ = false;
+
+  std::unique_ptr<ExtraData> extra_data_;
 };
 
 DECLARE_WEAK_IDENTIFIER_MAP(DocumentLoader);
diff --git a/third_party/blink/renderer/core/loader/empty_clients.cc b/third_party/blink/renderer/core/loader/empty_clients.cc
index a7957a04..0dfbe43 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.cc
+++ b/third_party/blink/renderer/core/loader/empty_clients.cc
@@ -36,14 +36,11 @@
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_media_player.h"
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
 #include "third_party/blink/renderer/core/html/forms/color_chooser.h"
 #include "third_party/blink/renderer/core/html/forms/date_time_chooser.h"
 #include "third_party/blink/renderer/core/html/forms/file_chooser.h"
 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
-#include "third_party/blink/renderer/core/loader/document_loader.h"
 
 namespace blink {
 
@@ -120,20 +117,6 @@
 
 void EmptyLocalFrameClient::DispatchWillSendSubmitEvent(HTMLFormElement*) {}
 
-DocumentLoader* EmptyLocalFrameClient::CreateDocumentLoader(
-    LocalFrame* frame,
-    WebNavigationType navigation_type,
-    std::unique_ptr<WebNavigationParams> navigation_params,
-    std::unique_ptr<PolicyContainer> policy_container,
-    std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
-  DCHECK(frame);
-  WebDocumentLoaderImpl* document_loader =
-      MakeGarbageCollected<WebDocumentLoaderImpl>(
-          frame, navigation_type, std::move(navigation_params),
-          std::move(policy_container), std::move(extra_data));
-  return document_loader;
-}
-
 LocalFrame* EmptyLocalFrameClient::CreateFrame(const AtomicString&,
                                                HTMLFrameOwnerElement*) {
   return nullptr;
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index aab6a77..acd3b33c 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -234,8 +234,6 @@
   void SetCursorForPlugin(const ui::Cursor&, LocalFrame*) override {}
   void InstallSupplements(LocalFrame&) override {}
   void MainFrameScrollOffsetChanged(LocalFrame& main_frame) const override {}
-  void BatterySavingsChanged(LocalFrame& main_frame,
-                             BatterySavingsFlags savings) override {}
 
  private:
   const display::ScreenInfos empty_screen_infos_{display::ScreenInfo()};
@@ -302,12 +300,7 @@
   void DidStartLoading() override {}
   void DidStopLoading() override {}
 
-  DocumentLoader* CreateDocumentLoader(
-      LocalFrame*,
-      WebNavigationType,
-      std::unique_ptr<WebNavigationParams> navigation_params,
-      std::unique_ptr<PolicyContainer> policy_container,
-      std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) override;
+  void DidCreateDocumentLoader(DocumentLoader*) override {}
 
   String UserAgentOverride() override { return ""; }
   String UserAgent() override { return ""; }
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index b007819..5a74c0ba 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -176,23 +176,15 @@
     filtered_load_callback_counter_ = 0;
   }
 
-  void TearDown() override {
-    document->Loader()->SetSubresourceFilter(nullptr);
-    FrameFetchContextTest::TearDown();
-  }
-
   int GetFilteredLoadCallCount() const {
     return filtered_load_callback_counter_;
   }
 
   void SetFilterPolicy(WebDocumentSubresourceFilter::LoadPolicy policy,
                        bool is_associated_with_ad_subframe = false) {
-    document->Loader()->SetSubresourceFilter(
-        MakeGarbageCollected<SubresourceFilter>(
-            document->GetExecutionContext(),
-            std::make_unique<FixedPolicySubresourceFilter>(
-                policy, &filtered_load_callback_counter_,
-                is_associated_with_ad_subframe)));
+    document->Loader()->SetSubresourceFilter(new FixedPolicySubresourceFilter(
+        policy, &filtered_load_callback_counter_,
+        is_associated_with_ad_subframe));
   }
 
   absl::optional<ResourceRequestBlockedReason> CanRequest() {
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index e700b68..bb0593ba 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -66,7 +66,6 @@
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/ignore_opens_during_unload_count_incrementer.h"
 #include "third_party/blink/renderer/core/events/page_transition_event.h"
-#include "third_party/blink/renderer/core/exported/web_document_loader_impl.h"
 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
 #include "third_party/blink/renderer/core/fragment_directive/text_fragment_anchor.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
@@ -271,7 +270,7 @@
     navigation_params->sandbox_flags |= csp->sandbox;
   }
 
-  DocumentLoader* new_document_loader = Client()->CreateDocumentLoader(
+  DocumentLoader* new_document_loader = MakeGarbageCollected<DocumentLoader>(
       frame_, kWebNavigationTypeOther, std::move(navigation_params),
       std::move(policy_container), nullptr /* extra_data */);
 
@@ -1051,10 +1050,7 @@
   if (commit_reason == CommitReason::kXSLT ||
       commit_reason == CommitReason::kJavascriptUrl) {
     DCHECK(!extra_data);
-    if (auto* old_document_loader =
-            static_cast<WebDocumentLoaderImpl*>(document_loader_.Get())) {
-      extra_data = old_document_loader->TakeExtraData();
-    }
+    extra_data = document_loader_->TakeExtraData();
   }
 
   // Create the OldDocumentInfoForCommit for the old document (that might be in
@@ -1150,7 +1146,7 @@
   }
   // TODO(dgozman): get rid of provisional document loader and most of the code
   // below. We should probably call DocumentLoader::CommitNavigation directly.
-  DocumentLoader* new_document_loader = Client()->CreateDocumentLoader(
+  DocumentLoader* new_document_loader = MakeGarbageCollected<DocumentLoader>(
       frame_, navigation_type, std::move(navigation_params),
       std::move(policy_container), std::move(extra_data));
 
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index 12f6aa4..d66cdd1 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -350,7 +350,8 @@
   // supported preload destinations, not just the destinations that support SRI
   // in the first place.
   if (resource_type == ResourceType::kScript ||
-      resource_type == ResourceType::kCSSStyleSheet) {
+      resource_type == ResourceType::kCSSStyleSheet ||
+      resource_type == ResourceType::kFont) {
     if (!integrity_attr.IsEmpty()) {
       IntegrityMetadataSet metadata_set;
       SubresourceIntegrity::ParseIntegrityAttribute(
diff --git a/third_party/blink/renderer/core/mojo/mojo_handle.cc b/third_party/blink/renderer/core/mojo/mojo_handle.cc
index acb1b04e..97140df 100644
--- a/third_party/blink/renderer/core/mojo/mojo_handle.cc
+++ b/third_party/blink/renderer/core/mojo/mojo_handle.cc
@@ -259,18 +259,31 @@
 MojoMapBufferResult* MojoHandle::mapBuffer(unsigned offset,
                                            unsigned num_bytes) {
   MojoMapBufferResult* result_dict = MojoMapBufferResult::Create();
-  void* data = nullptr;
-  MojoResult result =
-      MojoMapBuffer(handle_.get().value(), offset, num_bytes, nullptr, &data);
-  result_dict->setResult(result);
-  if (result == MOJO_RESULT_OK) {
-    ArrayBufferContents contents(
-        data, num_bytes, [](void* buffer, size_t length, void* alloc_data) {
-          MojoResult result = MojoUnmapBuffer(buffer);
-          DCHECK_EQ(result, MOJO_RESULT_OK);
-        });
+
+  // We need to extract the underlying shared memory region to map it as array
+  // buffer contents. However, as we don't know what kind of shared memory
+  // region is currently backing the buffer, we unwrap it to the underlying
+  // platform shared memory region. We also don't want to duplicate the region,
+  // and so need to perform a small dance here to first unwrap, and later
+  // re-wrap the MojoSharedBuffer to/from a //base shared memory region.
+  mojo::SharedBufferHandle buffer_handle(handle_.release().value());
+  auto region = mojo::UnwrapPlatformSharedMemoryRegion(
+      mojo::ScopedSharedBufferHandle(buffer_handle));
+
+  if (region.IsValid()) {
+    ArrayBufferContents contents(region, offset, num_bytes);
+    result_dict->setResult(MOJO_RESULT_OK);
     result_dict->setBuffer(DOMArrayBuffer::Create(contents));
+  } else {
+    result_dict->setResult(MOJO_RESULT_UNKNOWN);
   }
+
+  // 2nd part of the dance: we now need to wrap the shared memory region into a
+  // mojo handle again.
+  mojo::ScopedSharedBufferHandle mojo_buffer =
+      mojo::WrapPlatformSharedMemoryRegion(std::move(region));
+  handle_.reset(mojo::Handle(mojo_buffer.release().value()));
+
   return result_dict;
 }
 
diff --git a/third_party/blink/renderer/core/page/chrome_client.h b/third_party/blink/renderer/core/page/chrome_client.h
index 0325565..48a77bf 100644
--- a/third_party/blink/renderer/core/page/chrome_client.h
+++ b/third_party/blink/renderer/core/page/chrome_client.h
@@ -41,7 +41,6 @@
 #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/html/battery_savings.h"
 #include "third_party/blink/renderer/core/html/forms/external_date_time_chooser.h"
 #include "third_party/blink/renderer/core/html/forms/popup_menu.h"
 #include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
@@ -534,9 +533,6 @@
       LocalFrame* frame,
       std::unique_ptr<gfx::DelegatedInkMetadata> metadata) {}
 
-  virtual void BatterySavingsChanged(LocalFrame& main_frame,
-                                     BatterySavingsFlags savings) = 0;
-
   virtual void FormElementReset(HTMLFormElement& element) {}
 
   virtual void PasswordFieldReset(HTMLInputElement& element) {}
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.cc b/third_party/blink/renderer/core/page/chrome_client_impl.cc
index 6286f81c..bb3e05b 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.cc
@@ -1276,14 +1276,6 @@
   frame->GetWidgetForLocalRoot()->SetDelegatedInkMetadata(std::move(metadata));
 }
 
-void ChromeClientImpl::BatterySavingsChanged(LocalFrame& main_frame,
-                                             BatterySavingsFlags savings) {
-  DCHECK(main_frame.IsMainFrame());
-  WebLocalFrameImpl::FromFrame(main_frame)
-      ->FrameWidgetImpl()
-      ->BatterySavingsChanged(savings);
-}
-
 void ChromeClientImpl::FormElementReset(HTMLFormElement& element) {
   Document& doc = element.GetDocument();
   if (auto* fill_client = AutofillClientFromFrame(doc.GetFrame()))
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl.h b/third_party/blink/renderer/core/page/chrome_client_impl.h
index bac84fa..807567c 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl.h
+++ b/third_party/blink/renderer/core/page/chrome_client_impl.h
@@ -290,9 +290,6 @@
 
   double UserZoomFactor() const override;
 
-  void BatterySavingsChanged(LocalFrame& main_frame,
-                             BatterySavingsFlags savings) override;
-
   void FormElementReset(HTMLFormElement& element) override;
 
   void PasswordFieldReset(HTMLInputElement& element) override;
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
index f68b659..2697bbf 100644
--- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
+++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -74,12 +74,16 @@
     return;
 
   has_pending_update_ = true;
-  auto task_runner = GetSupplementable()->GetExecutionContext()->GetTaskRunner(
-      TaskType::kIdleTask);
-  task_runner->PostTask(
-      base::Location::Current(),
-      WTF::Bind(&DocumentSpeculationRules::UpdateSpeculationCandidates,
-                WrapWeakPersistent(this)));
+  ExecutionContext* execution_context =
+      GetSupplementable()->GetExecutionContext();
+  if (!execution_context)
+    return;
+
+  execution_context->GetTaskRunner(TaskType::kIdleTask)
+      ->PostTask(
+          base::Location::Current(),
+          WTF::Bind(&DocumentSpeculationRules::UpdateSpeculationCandidates,
+                    WrapWeakPersistent(this)));
 }
 
 void DocumentSpeculationRules::UpdateSpeculationCandidates() {
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/DEPS b/third_party/blink/renderer/core/typed_arrays/array_buffer/DEPS
index 8ab5ed4..1927cf464 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/DEPS
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
     "+base/allocator/partition_allocator/oom.h",
     "+base/allocator/partition_allocator/page_allocator.h",
+    "+base/memory/page_size.h",
+    "+base/memory/platform_shared_memory_region.h",
+    "+gin/array_buffer.h"
 ]
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
index 60bfe8d..457e6298 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.cc
@@ -30,6 +30,8 @@
 
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/bits.h"
+#include "base/system/sys_info.h"
+#include "gin/array_buffer.h"
 #include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 
@@ -45,6 +47,35 @@
 }
 
 ArrayBufferContents::ArrayBufferContents(
+    const base::subtle::PlatformSharedMemoryRegion& region,
+    uint64_t offset,
+    size_t length) {
+  DCHECK(region.IsValid());
+
+  // The offset must be a multiples of |SysInfo::VMAllocationGranularity()|.
+  size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+  uint64_t real_offset = offset - offset_rounding;
+  size_t real_length = length + offset_rounding;
+
+  absl::optional<base::span<uint8_t>> result = region.MapAt(
+      real_offset, real_length, gin::GetSharedMemoryMapperForArrayBuffers());
+  if (!result.has_value()) {
+    return;
+  }
+
+  auto deleter = [](void* buffer, size_t length, void* data) {
+    size_t offset = reinterpret_cast<uintptr_t>(buffer) %
+                    base::SysInfo::VMAllocationGranularity();
+    uint8_t* base = static_cast<uint8_t*>(buffer) - offset;
+    base::span<uint8_t> mapping = base::make_span(base, length + offset);
+    gin::GetSharedMemoryMapperForArrayBuffers()->Unmap(mapping);
+  };
+  void* base = result.value().data() + offset_rounding;
+  backing_store_ =
+      v8::ArrayBuffer::NewBackingStore(base, length, deleter, nullptr);
+}
+
+ArrayBufferContents::ArrayBufferContents(
     size_t num_elements,
     size_t element_byte_size,
     SharingType is_shared,
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h
index 03fcc36..bea1fb3b9 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer/array_buffer_contents.h
@@ -28,6 +28,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TYPED_ARRAYS_ARRAY_BUFFER_ARRAY_BUFFER_CONTENTS_H_
 
 #include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/memory/platform_shared_memory_region.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -62,6 +63,10 @@
                       SharingType is_shared,
                       InitializationPolicy);
   ArrayBufferContents(void* data, size_t length, DataDeleter deleter);
+  ArrayBufferContents(
+      const base::subtle::PlatformSharedMemoryRegion& shared_memory_region,
+      uint64_t offset,
+      size_t length);
   ArrayBufferContents(ArrayBufferContents&&) = default;
   ArrayBufferContents(const ArrayBufferContents&) = default;
   explicit ArrayBufferContents(std::shared_ptr<v8::BackingStore> backing_store)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 12fb20a..59290ee 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1251,10 +1251,8 @@
     return RoleFromLayoutObjectOrNode();
 
   // Treat <iframe>, <frame> and <fencedframe> the same.
-  if (IsA<HTMLIFrameElement>(*GetNode()) || IsA<HTMLFrameElement>(*GetNode()) ||
-      IsA<HTMLFencedFrameElement>(*GetNode())) {
+  if (IsFrame(GetNode()))
     return ax::mojom::blink::Role::kIframe;
-  }
 
   // There should only be one banner/contentInfo per page. If header/footer are
   // being used within an article or section then it should not be exposed as
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index a35a845..2b03f03c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -4243,7 +4243,7 @@
   // It also states user agents should ignore the presentational role if
   // the element has global ARIA states and properties.
   if (ui::IsPresentational(role)) {
-    if (IsA<HTMLIFrameElement>(*GetNode()) || IsA<HTMLFrameElement>(*GetNode()))
+    if (IsFrame(GetNode()))
       return ax::mojom::blink::Role::kIframePresentational;
     if ((GetElement() && GetElement()->SupportsFocus()) ||
         HasAriaAttribute(true /* does_undo_role_presentation */)) {
@@ -5851,6 +5851,24 @@
 }
 
 // static
+bool AXObject::IsFrame(const Node* node) {
+  auto* frame_owner = DynamicTo<HTMLFrameOwnerElement>(node);
+  if (!frame_owner)
+    return false;
+  switch (frame_owner->OwnerType()) {
+    case FrameOwnerElementType::kIframe:
+    case FrameOwnerElementType::kFrame:
+    case FrameOwnerElementType::kFencedframe:
+      return true;
+    case FrameOwnerElementType::kObject:
+    case FrameOwnerElementType::kEmbed:
+    case FrameOwnerElementType::kPortal:
+    case FrameOwnerElementType::kNone:
+      return false;
+  }
+}
+
+// static
 bool AXObject::HasARIAOwns(Element* element) {
   if (!element)
     return false;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index d4f1009..c2bc2cd 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -1279,6 +1279,7 @@
   // TODO(accessibility) Move these to a static helper util class.
   static bool IsARIAControl(ax::mojom::blink::Role);
   static bool IsARIAInput(ax::mojom::blink::Role);
+  static bool IsFrame(const Node*);
   static bool HasARIAOwns(Element* element);
   // Is this a widget that requires container widget.
   bool IsSubWidget() const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index 13e8a6b..f581ff2 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -59,7 +59,6 @@
 #include "third_party/blink/renderer/core/html/forms/html_select_element.h"
 #include "third_party/blink/renderer/core/html/forms/listed_element.h"
 #include "third_party/blink/renderer/core/html/html_area_element.h"
-#include "third_party/blink/renderer/core/html/html_frame_element_base.h"
 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
 #include "third_party/blink/renderer/core/html/html_head_element.h"
 #include "third_party/blink/renderer/core/html/html_image_element.h"
@@ -493,6 +492,12 @@
   }
 
   if (node->IsTextNode()) {
+    // Children of an <iframe> tag will always be replaced by a new Document,
+    // either loaded from the iframe src or empty. In fact, we don't even parse
+    // them and they are treated like one text node. Consider irrelevant.
+    if (AXObject::IsFrame(node->parentElement()))
+      return false;
+
     // Layout has more info available to determine if whitespace is relevant.
     // If display-locked, layout object may be missing or stale:
     // Assume that all display-locked text nodes are relevant.
@@ -509,12 +514,6 @@
       return false;
     }
 
-    // Children of an <iframe> tag will always be replaced by a new Document,
-    // either loaded from the iframe src or empty. In fact, we don't even parse
-    // them and they are treated like one text node. Consider irrelevant.
-    if (IsA<HTMLIFrameElement>(node->parentElement()))
-      return false;
-
     // If unrendered and in <canvas>, consider even whitespace relevant.
     // TODO(aleventhal) Consider including all text, even unrendered whitespace,
     // whether or not in <canvas>. For now this matches previous behavior.
@@ -604,7 +603,7 @@
     // that is a child of the frame. In the case where descendants are allowed,
     // they will be in a different document, and therefore this loop will not
     // reach the frame/iframe.
-    if (IsA<HTMLFrameElementBase>(ancestor))
+    if (AXObject::IsFrame(ancestor))
       return false;
     // Objects inside an SVG <style> are irrelevant.
     // However, when can this condition be reached?
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4d25b5d..8397d73 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -271,12 +271,6 @@
       },
     },
     {
-      // https://github.com/chrishtr/battery-savings/blob/master/explainer.md
-      name: "BatterySavingsMeta",
-      origin_trial_feature_name: "BatterySavingsMeta",
-      status: "experimental",
-    },
-    {
       // https://github.com/WICG/display-locking/blob/master/explainer-beforematch.md
       name: "BeforeMatchEvent",
       origin_trial_feature_name: "BeforeMatchEvent",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c9d2ace6..a5c443be 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7725,3 +7725,7 @@
 
 # Sheriff 2022-04-25
 crbug.com/1319808 virtual/wbn-from-network/external/wpt/web-bundle/subresource-loading/script-reuse-web-bundle-resource.https.tentative.html [ Skip ]
+
+crbug.com/1267538 [ Linux ] http/tests/csspaint/border-color.html [ Failure Pass ]
+crbug.com/1267538 [ Mac ] http/tests/csspaint/border-color.html [ Failure Pass ]
+
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 29f0826..d966f6e 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: e8199c0728bb283d72cfca4986751d72e4ecf80d
+Version: b5410883dff5f0cc3608155543a1523c5f7895b5
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 7c87cda..045850b 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
@@ -101,6 +101,13 @@
        {}
       ]
      ],
+     "iframe-srcdoc.html": [
+      "74460048e09e65097aa5f2139a5d2819bb0207e0",
+      [
+       null,
+       {}
+      ]
+     ],
      "in-page-link-with-aria-hidden.html": [
       "237707ba074c6943a3922c8a49fa81acb173a566",
       [
@@ -1561,6 +1568,13 @@
         {}
        ]
       ],
+      "inline-with-spanner-in-overflowed-container-before-multicol-float.html": [
+       "69abc9bcf99a16c1529965b7bd9133a58fd295af",
+       [
+        null,
+        {}
+       ]
+      ],
       "multicol-block-in-inline-crash.html": [
        "037b558ac0be08da6dfa6868b06e7a03e44031de",
        [
@@ -1736,6 +1750,20 @@
         {}
        ]
       ],
+      "spanner-in-overflowed-container-before-float.html": [
+       "c15d8aa0faf159d25f87dc76df745e18318ccc61",
+       [
+        null,
+        {}
+       ]
+      ],
+      "spanner-in-overflowed-container-before-inline-content.html": [
+       "8fba171e25dfb8128d59243f8a72f0149986416e",
+       [
+        null,
+        {}
+       ]
+      ],
       "specified-height-with-just-spanner-and-oof.html": [
        "3c4d51b0f433f4cbdb9e76f4301162924b6918f1",
        [
@@ -140444,6 +140472,58 @@
        {}
       ]
      ],
+     "multicol-span-all-children-height-009.html": [
+      "49b06b5ed4eed9b4773702f2add1198843ed8c98",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "multicol-span-all-children-height-010.html": [
+      "0bcbf4da3ce8e1abff5b1e4c87b1106cc67c3af0",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "multicol-span-all-children-height-011.html": [
+      "a85c7c59e6e16c6215ad93d019d0d869a97da7ce",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "multicol-span-all-children-height-012.html": [
+      "49437f027c13092d6d03d7cf26d6f79b05039cc6",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "multicol-span-all-dynamic-add-001.html": [
       "d9bf40889f75db72835c279517341dbb786c8ba8",
       [
@@ -260056,6 +260136,10 @@
       "f596b559b0e26b9c03d47b7dcab966a4d091308b",
       []
      ],
+     "all-prop-revert-layer-expected.txt": [
+      "8ae9b7998ecf1b075405a6dad10e7e642f390b45",
+      []
+     ],
      "all-prop-revert-visited-ref.html": [
       "0ef326c2722a4f65f6657d7740a990c079b08289",
       []
@@ -292901,7 +292985,7 @@
       []
      ],
      "idlharness-expected.txt": [
-      "1977b31db37102d6ca51ce71ea610bf43575c652",
+      "44af348a500de338557b9ccf9e8f5ef0b4c91e8c",
       []
      ],
      "insertRule-from-script-ref.html": [
@@ -328240,7 +328324,7 @@
      []
     ],
     "idlharness.js": [
-     "53ca06ddf997c2559cf6a88cc994d6919bbf5c2e",
+     "b5eed06ce3e1380bc6bc1e8b75e2c545249fdc18",
      []
     ],
     "idlharness.js.headers": [
@@ -328815,6 +328899,10 @@
      "81374cc95b81514962979e4e076d615589fcd0d1",
      []
     ],
+    "authentication-requires-user-activation.https-expected.txt": [
+     "184762bba1fddbf63c30b492efc3590fde4ced6b",
+     []
+    ],
     "enrollment-in-iframe.sub.https-expected.txt": [
      "a0cabf07eeb4bcb0c88904cead5bb97060874526",
      []
@@ -328825,7 +328913,7 @@
     ],
     "resources": {
      "iframe-authenticate.html": [
-      "067cb588087124a422a2d3861334b410dd64ac60",
+      "e6a634ff5806d526fa7d586df31202413bb9a894",
       []
      ],
      "iframe-enroll.html": [
@@ -335166,26 +335254,10 @@
        []
       ]
      },
-     "idlharness.any-expected.txt": [
-      "845370fc1055ff291bf102d65281592f1a3552c1",
-      []
-     ],
-     "idlharness.any.worker-expected.txt": [
-      "845370fc1055ff291bf102d65281592f1a3552c1",
-      []
-     ],
      "instanceTestFactory.js": [
       "ac468947ec22e29310d0714cb7dfa0972893477d",
       []
      ],
-     "interface.any-expected.txt": [
-      "bc607da32f42c6aff73a1593504970a2398502f1",
-      []
-     ],
-     "interface.any.worker-expected.txt": [
-      "bc607da32f42c6aff73a1593504970a2398502f1",
-      []
-     ],
      "memory": {
       "assertions.js": [
        "b539513adcab7d84e67d65fcf97453e9bc22de43",
@@ -336506,7 +336578,7 @@
      },
      "the-audiocontext-interface": {
       "audiocontext-suspend-resume-expected.txt": [
-       "4b3708bcb872ac53b8e82e6d780745dcb59cafab",
+       "962df08002c896cc0e28e714396643a0bb26f271",
        []
       ],
       "constructor-allowed-to-start-expected.txt": [
@@ -377133,6 +377205,13 @@
        {}
       ]
      ],
+     "all-prop-revert-layer.html": [
+      "b031e76c1a6791402cf203fce6c171656c76f464",
+      [
+       null,
+       {}
+      ]
+     ],
      "all-prop-revert-noop.html": [
       "696f498421e54d469f8304d46f34d9c39f81a4ef",
       [
@@ -386773,6 +386852,13 @@
        {}
       ]
      ],
+     "multicol-fill-balance-023.html": [
+      "fbada1b6d800ca1b5e56459923d25cf5b35ae3eb",
+      [
+       null,
+       {}
+      ]
+     ],
      "multicol-gap-animation-001.html": [
       "6a3a8d33780fe048528e1e3cd6a26707f2df4b70",
       [
@@ -504102,6 +504188,13 @@
       {}
      ]
     ],
+    "subresource-integrity-font.html": [
+     "da705dcb135695e748f10cc3fd3b528259601f7c",
+     [
+      null,
+      {}
+     ]
+    ],
     "subresource-integrity-partial-image.html": [
      "108897c4d6531ad53921f67c294b9754e987bbc7",
      [
@@ -520124,7 +520217,7 @@
    },
    "secure-payment-confirmation": {
     "authentication-accepted.https.html": [
-     "2e9611f4cc4ffe5905dde5d84f4b10827c7de38a",
+     "1677de3f3c0b9c2f26b9b29adfd36169f9ed76de",
      [
       null,
       {
@@ -520142,7 +520235,7 @@
      ]
     ],
     "authentication-cross-origin.sub.https.html": [
-     "a9be91b824ca2a3648c74c6a25149b86c1cf61fb",
+     "efdb79841585ba94bd79759fbf8deb9fe4419319",
      [
       null,
       {
@@ -520151,7 +520244,7 @@
      ]
     ],
     "authentication-icon-data-url.https.html": [
-     "b4c01f34af202e143708bb6236e2bb5df2557546",
+     "dbdf666a990c28d0f91bfc47c712040cf09ed317",
      [
       null,
       {
@@ -520160,7 +520253,7 @@
      ]
     ],
     "authentication-in-iframe.sub.https.html": [
-     "b3a01e33d1a6be3a7a042db4b6a4ebbd9d326d1d",
+     "951b540f8bfbee28d0a608a350c6c3f03a905741",
      [
       null,
       {
@@ -520169,7 +520262,7 @@
      ]
     ],
     "authentication-invalid-icon.https.html": [
-     "8987c7b28f93e94b665d78f90f00f63b8b55af96",
+     "84c629fbe8bf7807003634b827f10340a87b9289",
      [
       null,
       {
@@ -520178,7 +520271,16 @@
      ]
     ],
     "authentication-rejected.https.html": [
-     "4973748b3ad27e27d0e28f340ca9c494254c0edc",
+     "444733bd17c9f5fc9512a227b63fa5f77d177adc",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
+    "authentication-requires-user-activation.https.html": [
+     "6519086ec872884efc56b9b67853eb34c9ef7b0b",
      [
       null,
       {
@@ -525981,6 +526083,13 @@
        }
       ]
      ],
+     "remove-script-element.html": [
+      "9de7656f5048f2268a10a14bd2cf19335dda1de0",
+      [
+       null,
+       {}
+      ]
+     ],
      "restriction-broadcast-channel.html": [
       "7225e64cf9848921de4e33982e733104265daa43",
       [
diff --git a/third_party/blink/web_tests/external/wpt/accessibility/crashtests/iframe-srcdoc.html b/third_party/blink/web_tests/external/wpt/accessibility/crashtests/iframe-srcdoc.html
new file mode 100644
index 0000000..74460048
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/accessibility/crashtests/iframe-srcdoc.html
@@ -0,0 +1 @@
+<iframe id="frame" style="content-visibility: hidden" srcdoc="\'<table>"</table>\'></iframe>xx
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/all-prop-revert-layer-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-cascade/all-prop-revert-layer-expected.txt
new file mode 100644
index 0000000..8ae9b79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/all-prop-revert-layer-expected.txt
@@ -0,0 +1,316 @@
+This is a testharness.js-based test.
+Found 312 tests; 304 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS accent-color
+PASS align-content
+PASS align-items
+PASS align-self
+PASS alignment-baseline
+PASS animation-delay
+PASS animation-direction
+PASS animation-duration
+PASS animation-fill-mode
+PASS animation-iteration-count
+PASS animation-name
+PASS animation-play-state
+PASS animation-timeline
+PASS animation-timing-function
+PASS app-region
+PASS appearance
+PASS backdrop-filter
+PASS backface-visibility
+PASS background-attachment
+PASS background-blend-mode
+PASS background-clip
+PASS background-color
+PASS background-image
+PASS background-origin
+PASS background-position
+PASS background-repeat
+PASS background-size
+PASS baseline-shift
+PASS block-size
+PASS border-block-end-color
+PASS border-block-end-style
+PASS border-block-end-width
+PASS border-block-start-color
+PASS border-block-start-style
+PASS border-block-start-width
+PASS border-bottom-color
+PASS border-bottom-left-radius
+PASS border-bottom-right-radius
+PASS border-bottom-style
+PASS border-bottom-width
+PASS border-collapse
+PASS border-end-end-radius
+PASS border-end-start-radius
+FAIL border-image-outset assert_equals: Layer 3 should rollback to layer 2. expected "123" but got "0"
+FAIL border-image-repeat assert_equals: Layer 3 should rollback to layer 2. expected "round" but got "stretch"
+FAIL border-image-slice assert_equals: Layer 3 should rollback to layer 2. expected "123" but got "100%"
+FAIL border-image-source assert_equals: Layer 3 should rollback to layer 2. expected "url(\"http://web-platform.test:8001/css/css-cascade/all-prop-revert-layer.html#ref\")" but got "none"
+FAIL border-image-width assert_equals: Layer 3 should rollback to layer 2. expected "123px" but got "1"
+PASS border-inline-end-color
+PASS border-inline-end-style
+PASS border-inline-end-width
+PASS border-inline-start-color
+PASS border-inline-start-style
+PASS border-inline-start-width
+PASS border-left-color
+PASS border-left-style
+PASS border-left-width
+PASS border-right-color
+PASS border-right-style
+PASS border-right-width
+PASS border-start-end-radius
+PASS border-start-start-radius
+PASS border-top-color
+PASS border-top-left-radius
+PASS border-top-right-radius
+PASS border-top-style
+PASS border-top-width
+PASS bottom
+PASS box-shadow
+PASS box-sizing
+PASS break-after
+PASS break-before
+PASS break-inside
+PASS buffered-rendering
+PASS caption-side
+PASS caret-color
+PASS clear
+PASS clip
+PASS clip-path
+PASS clip-rule
+PASS color
+PASS color-interpolation
+PASS color-interpolation-filters
+PASS color-rendering
+PASS column-count
+PASS column-gap
+PASS column-rule-color
+PASS column-rule-style
+PASS column-rule-width
+PASS column-span
+PASS column-width
+PASS contain-intrinsic-block-size
+PASS contain-intrinsic-height
+PASS contain-intrinsic-inline-size
+PASS contain-intrinsic-size
+PASS contain-intrinsic-width
+PASS container-name
+PASS container-type
+PASS content
+PASS cursor
+PASS cx
+PASS cy
+PASS d
+PASS direction
+PASS display
+PASS dominant-baseline
+PASS empty-cells
+PASS fill
+PASS fill-opacity
+PASS fill-rule
+PASS filter
+PASS flex-basis
+PASS flex-direction
+PASS flex-grow
+PASS flex-shrink
+PASS flex-wrap
+PASS float
+PASS flood-color
+PASS flood-opacity
+PASS font-family
+PASS font-kerning
+PASS font-optical-sizing
+PASS font-palette
+PASS font-size
+PASS font-size-adjust
+PASS font-stretch
+PASS font-style
+PASS font-synthesis-small-caps
+PASS font-synthesis-style
+PASS font-synthesis-weight
+PASS font-variant
+PASS font-variant-caps
+PASS font-variant-east-asian
+PASS font-variant-ligatures
+PASS font-variant-numeric
+PASS font-weight
+PASS grid-auto-columns
+PASS grid-auto-flow
+PASS grid-auto-rows
+PASS grid-column-end
+PASS grid-column-start
+PASS grid-row-end
+PASS grid-row-start
+PASS grid-template-areas
+PASS grid-template-columns
+PASS grid-template-rows
+PASS height
+PASS hyphens
+PASS image-orientation
+PASS image-rendering
+PASS inline-size
+PASS inset-block-end
+PASS inset-block-start
+PASS inset-inline-end
+PASS inset-inline-start
+PASS isolation
+PASS justify-content
+PASS justify-items
+PASS justify-self
+PASS left
+PASS letter-spacing
+PASS lighting-color
+FAIL line-break assert_equals: Layer 3 should rollback to layer 2. expected "anywhere" but got "auto"
+PASS line-height
+PASS line-height-step
+PASS list-style-image
+PASS list-style-position
+PASS list-style-type
+PASS margin-block-end
+PASS margin-block-start
+PASS margin-bottom
+PASS margin-inline-end
+PASS margin-inline-start
+PASS margin-left
+PASS margin-right
+PASS margin-top
+PASS marker-end
+PASS marker-mid
+PASS marker-start
+PASS mask-type
+PASS math-depth
+PASS math-shift
+PASS math-style
+PASS max-block-size
+PASS max-height
+PASS max-inline-size
+PASS max-width
+PASS min-block-size
+PASS min-height
+PASS min-inline-size
+PASS min-width
+PASS mix-blend-mode
+PASS object-fit
+PASS object-overflow
+PASS object-position
+PASS object-view-box
+PASS offset-anchor
+PASS offset-distance
+PASS offset-path
+PASS offset-position
+PASS offset-rotate
+PASS opacity
+PASS order
+PASS orphans
+PASS outline-color
+PASS outline-offset
+PASS outline-style
+PASS outline-width
+PASS overflow-anchor
+PASS overflow-block
+PASS overflow-clip-margin
+PASS overflow-inline
+PASS overflow-wrap
+PASS overflow-x
+PASS overflow-y
+PASS overscroll-behavior-block
+PASS overscroll-behavior-inline
+PASS padding-block-end
+PASS padding-block-start
+PASS padding-bottom
+PASS padding-inline-end
+PASS padding-inline-start
+PASS padding-left
+PASS padding-right
+PASS padding-top
+PASS paint-order
+PASS perspective
+FAIL perspective-origin assert_equals: Layer 3 should rollback to layer 2. expected "123px 0px" but got "0px 0px"
+PASS pointer-events
+PASS position
+PASS r
+PASS resize
+PASS right
+PASS rotate
+PASS row-gap
+PASS ruby-position
+PASS rx
+PASS ry
+PASS scale
+PASS scroll-behavior
+PASS scroll-margin-block-end
+PASS scroll-margin-block-start
+PASS scroll-margin-inline-end
+PASS scroll-margin-inline-start
+PASS scroll-padding-block-end
+PASS scroll-padding-block-start
+PASS scroll-padding-inline-end
+PASS scroll-padding-inline-start
+PASS scrollbar-gutter
+PASS scrollbar-width
+PASS shape-image-threshold
+PASS shape-margin
+PASS shape-outside
+PASS shape-rendering
+PASS speak
+PASS stop-color
+PASS stop-opacity
+PASS stroke
+PASS stroke-dasharray
+PASS stroke-dashoffset
+PASS stroke-linecap
+PASS stroke-linejoin
+PASS stroke-miterlimit
+PASS stroke-opacity
+PASS stroke-width
+PASS tab-size
+PASS table-layout
+PASS text-align
+PASS text-align-last
+PASS text-anchor
+PASS text-decoration
+PASS text-decoration-color
+PASS text-decoration-line
+PASS text-decoration-skip-ink
+PASS text-decoration-style
+PASS text-emphasis-color
+PASS text-emphasis-position
+PASS text-emphasis-style
+PASS text-indent
+PASS text-justify
+PASS text-overflow
+PASS text-rendering
+PASS text-shadow
+PASS text-size-adjust
+PASS text-transform
+PASS text-underline-position
+PASS top
+PASS touch-action
+PASS transform
+FAIL transform-origin assert_equals: Layer 3 should rollback to layer 2. expected "123px 123px 123px" but got "0px 0px"
+PASS transform-style
+PASS transition-delay
+PASS transition-duration
+PASS transition-property
+PASS transition-timing-function
+PASS translate
+PASS unicode-bidi
+PASS user-select
+PASS vector-effect
+PASS vertical-align
+PASS visibility
+PASS white-space
+PASS widows
+PASS width
+PASS will-change
+PASS word-break
+PASS word-spacing
+PASS writing-mode
+PASS x
+PASS y
+PASS z-index
+PASS zoom
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-cascade/all-prop-revert-layer.html b/third_party/blink/web_tests/external/wpt/css/css-cascade/all-prop-revert-layer.html
new file mode 100644
index 0000000..b031e76
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-cascade/all-prop-revert-layer.html
@@ -0,0 +1,457 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Cascade: "all: revert-layer"</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://www.w3.org/TR/css-cascade-5/#revert-layer">
+<meta name="assert" content="Checks that adding 'all: revert-layer' on the last layer has no effect.">
+<style>
+/* Set properties to a value different than the initial one. */
+#nothing {
+  accent-color: #123;
+  align-content: baseline;
+  align-items: baseline;
+  align-self: baseline;
+  align-tracks: baseline;
+  alignment-baseline: central;
+  alt: "a";
+  animation-composition: add;
+  animation-delay: 123s;
+  animation-direction: reverse;
+  animation-duration: 123s;
+  animation-fill-mode: both;
+  animation-iteration-count: 123;
+  animation-name: \.;
+  animation-play-state: paused;
+  animation-timeline: none;
+  animation-timing-function: linear;
+  app-region: drag;
+  appearance: auto;
+  aspect-ratio: 3 / 4;
+  backdrop-filter: invert(1);
+  backface-visibility: hidden;
+  background-attachment: fixed;
+  background-blend-mode: overlay;
+  background-clip: content-box;
+  background-color: #123;
+  background-image: url("#ref");
+  background-origin: border-box;
+  background-position: 123px;
+  background-repeat: round;
+  background-size: 123px;
+  baseline-shift: 123px;
+  block-size: 123px;
+  border-block-end: 123px dashed #123;
+  border-block-start: 123px dashed #123;
+  border-bottom: 123px dashed #123;
+  border-collapse: collapse;
+  border-end-end-radius: 123px;
+  border-end-start-radius: 123px;
+  border-image-outset: 123;
+  border-image-repeat: round;
+  border-image-slice: 123;
+  border-image-source: url("#ref");
+  border-image-width: 123px;
+  border-inline-end: 123px dashed #123;
+  border-inline-start: 123px dashed #123;
+  border-left: 123px dashed #123;
+  border-radius: 123px;
+  border-right: 123px dashed #123;
+  border-start-end-radius: 123px;
+  border-start-start-radius: 123px;
+  border-spacing: 123px;
+  border-top: 123px dashed #123;
+  bottom: 123px;
+  box-decoration-break: clone;
+  box-shadow: #123 123px 123px 123px 123px;
+  box-sizing: border-box;
+  break-after: avoid;
+  break-before: avoid;
+  break-inside: avoid;
+  buffered-rendering: static;
+  caption-side: bottom;
+  caret-color: #123;
+  clear: both;
+  clip: rect(123px, 123px, 123px, 123px);
+  clip-path: url("#ref");
+  clip-rule: evenodd;
+  color: #123;
+  color-interpolation: auto;
+  color-interpolation-filters: auto;
+  color-rendering: optimizespeed;
+  color-scheme: dark;
+  column-count: 123;
+  column-fill: auto;
+  column-gap: 123px;
+  column-rule-color: #123;
+  column-rule-style: dashed;
+  column-rule-width: 123px;
+  column-span: all;
+  column-width: 123px;
+  contain: size;
+  contain-intrinsic-block-size: 123px;
+  contain-intrinsic-inline-size: 123px;
+  contain-intrinsic-size: 123px 123px;
+  container-name: foo;
+  container-type: size;
+  content: "b";
+  counter-increment: add 123;
+  counter-reset: add 123;
+  counter-set: add 123;
+  cursor: none;
+  cx: 123px;
+  cy: 123px;
+  d: path("M 1 1");
+  direction: rtl;
+  display: flow-root;
+  dominant-baseline: middle;
+  empty-cells: hide;
+  fill: #123;
+  fill-opacity: 0.123;
+  fill-rule: evenodd;
+  filter: url("#ref");
+  flex-basis: 123px;
+  flex-direction: column;
+  flex-grow: 123;
+  flex-shrink: 123;
+  flex-wrap: wrap;
+  float: right;
+  flood-color: #123;
+  flood-opacity: 0.123;
+  font-family: "c";
+  font-feature-settings: "smcp";
+  font-kerning: none;
+  font-language-override: "d";
+  font-optical-sizing: none;
+  font-palette: dark;
+  font-size: 123px;
+  font-size-adjust: 123;
+  font-stretch: 123%;
+  font-style: italic;
+  font-synthesis: none;
+  font-variant-alternates: historical-forms;
+  font-variant-caps: small-caps;
+  font-variant-east-asian: full-width;
+  font-variant-ligatures: none;
+  font-variant-numeric: tabular-nums;
+  font-variant-position: super;
+  font-variation-settings: "smcp" 1;
+  font-weight: 123;
+  glyph-orientation-horizontal: 123deg;
+  glyph-orientation-vertical: 123deg;
+  grid-auto-columns: 123px;
+  grid-auto-flow: column;
+  grid-auto-rows: 123px;
+  grid-column-end: 123;
+  grid-column-start: 123;
+  grid-row-end: 123;
+  grid-row-start: 123;
+  grid-template-areas: ".";
+  grid-template-columns: 123fr;
+  grid-template-rows: 123fr;
+  hanging-punctuation: first;
+  height: 123px;
+  hyphenate-character: "e";
+  hyphens: auto;
+  image-orientation: none;
+  image-rendering: pixelated;
+  ime-mode: normal;
+  initial-letter: 123;
+  inline-size: 123px;
+  input-security: none;
+  inset-block-end: 123px;
+  inset-block-start: 123px;
+  inset-inline-end: 123px;
+  inset-inline-start: 123px;
+  isolation: isolate;
+  justify-content: center;
+  justify-items: baseline;
+  justify-self: baseline;
+  justify-tracks: center;
+  kerning: 123px;
+  left: 123px;
+  letter-spacing: 123px;
+  lighting-color: #123;
+  line-break: anywhere;
+  line-height: 123px;
+  line-height-step: 123px;
+  list-style-image: url("#ref");
+  list-style-position: inside;
+  list-style-type: square;
+  margin-block-end: 123px;
+  margin-block-start: 123px;
+  margin-bottom: 123px;
+  margin-inline-end: 123px;
+  margin-inline-start: 123px;
+  margin-left: 123px;
+  margin-right: 123px;
+  margin-top: 123px;
+  marker-end: url("#ref");
+  marker-mid: url("#ref");
+  marker-start: url("#ref");
+  mask-clip: content-box;
+  mask-composite: exclude;
+  mask-image: url("#ref");
+  mask-mode: alpha;
+  mask-origin: content-box;
+  mask-position-x: 123px;
+  mask-position-y: 123px;
+  mask-repeat: round;
+  mask-size: 123px;
+  mask-type: alpha;
+  masonry-auto-flow: ordered;
+  math-depth: 123;
+  math-shift: compact;
+  math-style: compact;
+  max-block-size: 123px;
+  max-height: 123px;
+  max-inline-size: 123px;
+  max-width: 123px;
+  min-block-size: 123px;
+  min-height: 123px;
+  min-inline-size: 123px;
+  min-width: 123px;
+  mix-blend-mode: overlay;
+  object-fit: contain;
+  object-overflow: visible;
+  object-position: 123px 123%;
+  object-view-box: inset(123px);
+  offset-anchor: 123px 123%;
+  offset-distance: 123px;
+  offset-path: path("M 1 1");
+  offset-position: 123px;
+  offset-rotate: 123deg;
+  opacity: 0.123;
+  order: 123;
+  orphans: 123;
+  outline-color: #123;
+  outline-offset: 123px;
+  outline-style: auto;
+  outline-width: 123px;
+  overflow-anchor: none;
+  overflow-block: auto;
+  overflow-clip-margin: 123px;
+  overflow-inline: hidden;
+  overflow-wrap: anywhere;
+  overflow-x: auto;
+  overflow-y: hidden;
+  overscroll-behavior-block: contain;
+  overscroll-behavior-inline: contain;
+  overscroll-behavior-x: contain;
+  overscroll-behavior-y: contain;
+  padding-block-end: 123px;
+  padding-block-start: 123px;
+  padding-bottom: 123px;
+  padding-inline-end: 123px;
+  padding-inline-start: 123px;
+  padding-left: 123px;
+  padding-right: 123px;
+  padding-top: 123px;
+  paint-order: fill;
+  perspective: 123px;
+  perspective-origin: 123px 123%;
+  pointer-events: all;
+  position: relative;
+  print-color-adjust: exact;
+  quotes: none;
+  r: 123px;
+  resize: both;
+  right: 123px;
+  rotate: 123deg;
+  row-gap: 123px;
+  ruby-align: center;
+  ruby-position: under;
+  rx: 123px;
+  ry: 123px;
+  scale: 123;
+  scroll-behavior: smooth;
+  scroll-margin-block-end: 123px;
+  scroll-margin-block-start: 123px;
+  scroll-margin-bottom: 123px;
+  scroll-margin-inline-end: 123px;
+  scroll-margin-inline-start: 123px;
+  scroll-margin-left: 123px;
+  scroll-margin-right: 123px;
+  scroll-margin-top: 123px;
+  scroll-padding-block-end: 123px;
+  scroll-padding-block-start: 123px;
+  scroll-padding-bottom: 123px;
+  scroll-padding-inline-end: 123px;
+  scroll-padding-inline-start: 123px;
+  scroll-padding-left: 123px;
+  scroll-padding-right: 123px;
+  scroll-padding-top: 123px;
+  scroll-snap-align: center;
+  scroll-snap-stop: always;
+  scroll-snap-type: both;
+  scrollbar-color: #123 #123;
+  scrollbar-gutter: stable;
+  scrollbar-width: none;
+  shape-image-threshold: 123;
+  shape-margin: 123px;
+  shape-outside: border-box;
+  shape-rendering: optimizespeed;
+  speak: spell-out;
+  speak-as: spell-out;
+  stop-color: #123;
+  stop-opacity: 0.123;
+  stroke: #123;
+  stroke-color: #123;
+  stroke-dasharray: 123px;
+  stroke-dashoffset: 123px;
+  stroke-linecap: round;
+  stroke-linejoin: round;
+  stroke-miterlimit: 123;
+  stroke-opacity: 0.123;
+  stroke-width: 123px;
+  tab-size: 123;
+  table-layout: fixed;
+  text-align: center;
+  text-align-last: center;
+  text-anchor: middle;
+  text-combine-upright: all;
+  text-decoration-color: #123;
+  text-decoration-line: underline;
+  text-decoration-skip-ink: none;
+  text-decoration-style: dashed;
+  text-decoration-thickness: 123px;
+  text-emphasis-color: #123;
+  text-emphasis-position: under right;
+  text-emphasis-style: dot;
+  text-indent: 123px;
+  text-justify: none;
+  text-orientation: sideways;
+  text-overflow: ellipsis;
+  text-rendering: optimizespeed;
+  text-shadow: #123 123px 123px 123px;
+  text-size-adjust: none;
+  text-transform: lowercase;
+  text-underline-offset: 123px;
+  text-underline-position: under;
+  top: 123px;
+  touch-action: none;
+  transform: scale(-1);
+  transform-box: fill-box;
+  transform-origin: 123px 123px 123px;
+  transform-style: preserve-3d;
+  transition-delay: 123s;
+  transition-duration: 123s;
+  transition-property: add;
+  transition-timing-function: linear;
+  translate: 123px;
+  unicode-bidi: plaintext;
+  user-select: all;
+  vector-effect: non-scaling-stroke;
+  vertical-align: 123px;
+  visibility: collapse;
+  white-space: nowrap;
+  widows: 123;
+  width: 123px;
+  will-change: height;
+  word-break: break-word;
+  word-spacing: 123px;
+  word-wrap: break-word;
+  writing-mode: vertical-lr;
+  x: 123px;
+  y: 123px;
+  z-index: 123;
+  zoom: 123;
+}
+
+@layer layer1 {
+  /* Reset properties to their initial value */
+  #target {
+    all: initial;
+  }
+}
+
+@layer layer2 {
+  /* This will be populated with properties set to a non-initial value */
+  #target {}
+}
+
+@layer layer3 {
+  /* This should roll back to the values from the previous layer */
+  #target.rollback {
+    all: revert-layer;
+  }
+}
+</style>
+
+<div id="log"></div>
+
+<!-- This custom element is unlikely to get important UA styles -->
+<foo-bar id="target"></foo-bar>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const { sheet } = document.querySelector("style");
+const nonInitialStyle = sheet.cssRules[0].style;
+const layer2Style = sheet.cssRules[2].cssRules[0].style;
+
+const target = document.getElementById("target");
+const cs = getComputedStyle(target);
+
+// Some properties can be forced to compute to their initial value
+// unless another property is set to a certain value.
+function prerequisites(property) {
+  switch (property) {
+    case "border-block-end-width":
+    case "border-block-start-width":
+    case "border-bottom-width":
+    case "border-inline-end-width":
+    case "border-inline-start-width":
+    case "border-left-width":
+    case "border-right-width":
+    case "border-top-width":
+      return "border-style: solid";
+    case "column-rule-width":
+      return "column-rule-style: solid";
+    case "outline-width":
+      return "outline-style: solid";
+    case "rotate":
+    case "scale":
+    case "transform":
+    case "transform-style":
+    case "translate":
+      return "display: block";
+    default:
+      return "";
+  }
+}
+
+const initialValues = Object.create(null);
+for (let property of cs) {
+  if (!property.startsWith("-")) {
+    initialValues[property] = cs.getPropertyValue(property);
+  }
+}
+
+for (let property in initialValues) {
+  // Skip property if the stylesheet above doesn't provide a non-initial value.
+  // This is to avoid having to update the test every time a new CSS property is added.
+  const nonInitialValue = nonInitialStyle.getPropertyValue(property);
+  if (nonInitialValue === "") {
+    continue;
+  }
+
+  test(function() {
+    const initialValue = initialValues[property];
+    assert_not_equals(initialValue, "", "Should have the initial value.");
+
+    this.add_cleanup(() => {
+      layer2Style.cssText = "";
+      target.classList.remove("rollback");
+    });
+
+    layer2Style.cssText = prerequisites(property);
+    layer2Style.setProperty(property, nonInitialValue);
+    const changedValue = cs.getPropertyValue(property);
+    assert_not_equals(changedValue, initialValue, "Should get a different computed value.");
+
+    target.classList.add("rollback");
+    const revertedValue = cs.getPropertyValue(property);
+    assert_equals(revertedValue, changedValue, "Layer 3 should rollback to layer 2.");
+  }, property);
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom/idlharness-expected.txt b/third_party/blink/web_tests/external/wpt/css/cssom/idlharness-expected.txt
index 1977b31d..44af348 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom/idlharness-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/cssom/idlharness-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 452 tests; 396 PASS, 56 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 452 tests; 398 PASS, 54 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Partial interface CSSStyleSheet: original interface defined
@@ -444,9 +444,9 @@
 PASS Document interface: attribute styleSheets
 PASS Document interface: attribute adoptedStyleSheets
 PASS Document interface: document must inherit property "styleSheets" with the proper type
-FAIL Document interface: document must inherit property "adoptedStyleSheets" with the proper type Right-hand side of 'instanceof' is not an object
+PASS Document interface: document must inherit property "adoptedStyleSheets" with the proper type
 PASS Document interface: new Document() must inherit property "styleSheets" with the proper type
-FAIL Document interface: new Document() must inherit property "adoptedStyleSheets" with the proper type Right-hand side of 'instanceof' is not an object
+PASS Document interface: new Document() must inherit property "adoptedStyleSheets" with the proper type
 PASS ShadowRoot interface: attribute styleSheets
 PASS ShadowRoot interface: attribute adoptedStyleSheets
 PASS ProcessingInstruction interface: attribute sheet
diff --git a/third_party/blink/web_tests/external/wpt/preload/subresource-integrity-font.html b/third_party/blink/web_tests/external/wpt/preload/subresource-integrity-font.html
new file mode 100644
index 0000000..da705dc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/subresource-integrity-font.html
@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<title>Subresource Integrity for font
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+    const integrities = {
+        sha256: 'sha256-xkrni1nquuAzPoWieTZ22i9RONF4y11sJyWgYQDVlxE=',
+        sha384: 'sha384-Vif8vpq+J5UhnTqtncDDyol01dZx9nurRqQcSGtlCf0L1G8P+YeTyUYyZn4LMGrl',
+        sha512: 'sha512-CVkJJeS4/8zBdqBHmpzMvbI987MEWpTVd1Y/w20UFU0+NWlJAQpl1d3lIyCF97CQ/N+t/gn4IkWP4pjuWWrg6A==',
+        incorrect_sha256: 'sha256-wrongwrongwrongwrongwrongwrongwrongvalue====',
+        incorrect_sha512: 'sha512-wrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrongwrong===',
+        unknown_algo: 'foo666-8aBiAJl3ukQwSJ6eTs5wl6hGjnOtyXjcTRdAf89uIfY='
+    };
+
+    const run_test = (preload_success, main_load_success, name,
+                      resource_url, extra_attributes, number_of_requests) => {
+        const test = async_test(name);
+        const link = document.createElement('link');
+        link.rel = 'preload';
+        link.as = 'font';
+        link.href = resource_url;
+
+        for (const attribute_name in extra_attributes) {
+            link[attribute_name] = extra_attributes[attribute_name];
+        }
+
+        const valid_preload_failed = test.step_func(() => {
+            assert_unreached('Valid preload fired error handler.');
+        });
+        const invalid_preload_succeeded = test.step_func(() => {
+            assert_unreached('Invalid preload load succeeded.');
+        });
+        const valid_main_load_failed = test.step_func(() => {
+            assert_unreached('Valid main load fired error handler.');
+        });
+        const invalid_main_load_succeeded = test.step_func(() => {
+            assert_unreached('Invalid main load succeeded.');
+        });
+        const main_load_pass = test.step_func(() => {
+            verifyNumberOfResourceTimingEntries(resource_url, number_of_requests);
+            test.done();
+        });
+
+        const preload_pass = test.step_func(async () => {
+            try {
+                await new FontFace('CanvasTest', `url("${resource_url}")`).load();
+            } catch (error) {
+                if (main_load_success) {
+                    valid_main_load_failed();
+                } else {
+                    main_load_pass();
+                }
+            }
+
+            if (main_load_success) {
+                main_load_pass();
+            } else {
+                invalid_main_load_succeeded();
+            }
+        });
+
+        if (preload_success) {
+            link.onload = preload_pass;
+            link.onerror = valid_preload_failed;
+        } else {
+            link.onload = invalid_preload_succeeded;
+            link.onerror = preload_pass;
+        }
+
+        document.body.appendChild(link);
+    };
+
+    verifyPreloadAndRTSupport();
+
+    const anonymous = '&pipe=header(Access-Control-Allow-Origin,*)';
+    const use_credentials = '&pipe=header(Access-Control-Allow-Credentials,true)|' +
+                            'header(Access-Control-Allow-Origin,' + location.origin + ')';
+    const cross_origin_prefix = get_host_info().REMOTE_ORIGIN;
+    const file_path = '/fonts/CanvasTest.ttf';
+
+    // Note: About preload + font + CORS
+    //
+    // The CSS Font spec defines that font files always have to be fetched using
+    // anonymous-mode CORS.
+    //
+    // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#cors-enabled_fetches
+    // https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements
+    //
+    // So that font loading (@font-face in CSS and FontFace.load()) always
+    // sends requests with anonymous-mode CORS. The crossOrigin attribute of
+    // <link rel="preload" as="font"> should be set as anonymout mode,
+    // too, even for same origin fetch. Otherwise, main font loading
+    // doesn't match the corresponding preloading due to credentials
+    // mode mismatch and the main font loading invokes another request.
+
+    // Needs CORS request even for same origin preload.
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha256 hash.',
+             file_path + '?' + token(),
+             {integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha384 hash.',
+             file_path + '?' + token(),
+             {integrity: integrities.sha384, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha512 hash.',
+             file_path + '?' + token(),
+             {integrity: integrities.sha512, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with empty integrity.',
+             file_path + '?' + token(),
+             {integrity: '', crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with no integrity.',
+             file_path + '?' + token(),
+             {crossOrigin: 'anonymous'}, 1);
+
+    run_test(false, false, '<crossorigin="anonymous"> Same-origin with incorrect hash.',
+             file_path + '?' + token(),
+             {integrity: integrities.incorrect_sha256, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with correct sha256 hash, options.',
+             file_path + '?' + token(),
+             {integrity: `${integrities.sha256}?foo=bar?spam=eggs`, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with unknown algorithm only.',
+             file_path + '?' + token(),
+             {integrity: integrities.unknown_algo, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with multiple sha256 hashes, including correct.',
+             file_path + '?' + token(),
+             {integrity: `${integrities.sha256} ${integrities.incorrect_sha256}`, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with multiple sha256 hashes, including unknown algorithm.',
+             file_path + '?' + token(),
+             {integrity: `${integrities.sha256} ${integrities.unknown_algo}`, crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, '<crossorigin="anonymous"> Same-origin with sha256 mismatch, sha512 match.',
+             file_path + '?' + token(),
+             {integrity: `${integrities.incorrect_sha256} ${integrities.sha512}`, crossOrigin: 'anonymous'}, 1);
+
+    run_test(false, false, '<crossorigin="anonymous"> Same-origin with sha256 match, sha512 mismatch.',
+             file_path + '?' + token(),
+             {integrity: `${integrities.sha256} ${integrities.incorrect_sha512}`, crossOrigin: 'anonymous'}, 1);
+
+    // Main loading shouldn't match preloading due to credentials mode mismatch
+    // so the number of requests should be two.
+    run_test(true, true, 'Same-origin, not CORS request, with correct sha256 hash.',
+             file_path + '?' + token(),
+             {integrity: integrities.sha256}, 2);
+
+    // Main loading shouldn't match preloading due to credentials mode mismatch
+    // and the main loading should invoke another request. The main font loading
+    // always sends CORS request and doesn't support SRI by itself, so it should succeed.
+    run_test(false, true, 'Same-origin, not CORS request, with incorrect sha256 hash.',
+             file_path + '?' + token(),
+             {integrity: integrities.incorrect_sha256}, 2);
+
+    run_test(true, true, '<crossorigin="anonymous"> Cross-origin with correct sha256 hash, ACAO: *.',
+             cross_origin_prefix + file_path + '?' + token() + anonymous,
+             {integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1);
+
+    run_test(false, false, '<crossorigin="anonymous"> Cross-origin with incorrect sha256 hash, ACAO: *.',
+             cross_origin_prefix + file_path + '?' + token() + anonymous,
+             {integrity: integrities.incorrect_sha256, crossOrigin: 'anonymous'}, 1);
+
+    run_test(false, false, '<crossorigin="anonymous"> Cross-origin with correct sha256 hash, with CORS-ineligible resource.',
+             cross_origin_prefix + file_path + '?' + token(),
+             {integrity: integrities.sha256, crossOrigin: 'anonymous'}, 1);
+
+    run_test(false, true, 'Cross-origin, not CORS request, with correct sha256.',
+             cross_origin_prefix + file_path + '?' + token() + anonymous,
+             {integrity: integrities.sha256}, 2);
+
+    run_test(false, true, 'Cross-origin, not CORS request, with incorrect sha256.',
+             cross_origin_prefix + file_path + '?' + token() + anonymous,
+             {integrity: integrities.incorrect_sha256}, 2);
+
+    run_test(true, true, '<crossorigin="anonymous"> Cross-origin with empty integrity.',
+             cross_origin_prefix + file_path + '?' + token() + anonymous,
+             {integrity: '', crossOrigin: 'anonymous'}, 1);
+
+    run_test(true, true, 'Cross-origin, not CORS request, with empty integrity.',
+             cross_origin_prefix + file_path + '?' + token() + anonymous,
+             {integrity: ''}, 2);
+
+    // Non-anonymous mode CORS preload request should mismatch the main load.
+    run_test(true, true, '<crossorigin="use-credentials"> Cross-origin with correct sha256 hash, CORS-eligible.',
+             cross_origin_prefix + file_path + '?' + token() + use_credentials,
+             {integrity: integrities.sha256, crossOrigin: 'use-credentials'}, 2);
+
+    run_test(false, true, '<crossorigin="use-credentials"> Cross-origin with incorrect sha256 hash, CORS-eligible.',
+             cross_origin_prefix + file_path + '?' + token() + use_credentials,
+             {integrity: integrities.incorrect_sha256, crossOrigin: 'use-credentials'}, 2);
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/resources/idlharness.js b/third_party/blink/web_tests/external/wpt/resources/idlharness.js
index 53ca06d..b5eed06 100644
--- a/third_party/blink/web_tests/external/wpt/resources/idlharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/idlharness.js
@@ -962,7 +962,7 @@
         return;
     }
 
-    if (type.generic === "sequence")
+    if (type.generic === "sequence" || type.generic == "ObservableArray")
     {
         assert_true(Array.isArray(value), "should be an Array");
         if (!value.length)
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-accepted.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-accepted.https.html
index 2e9611f..1677de3 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-accepted.https.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-accepted.https.html
@@ -42,6 +42,7 @@
     }
   }], PAYMENT_DETAILS);
 
+  await test_driver.bless('user activation');
   const responsePromise = request.show();
 
   const response = await responsePromise;
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https.html
index a9be91b8..efdb798 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https.html
@@ -47,6 +47,7 @@
     }
   }], PAYMENT_DETAILS);
 
+  await test_driver.bless('user activation');
   const responsePromise = request.show();
 
   const response = await responsePromise;
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https.html
index b4c01f3..dbdf666 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https.html
@@ -43,6 +43,7 @@
     }
   }], PAYMENT_DETAILS);
 
+  await test_driver.bless('user activation');
   const responsePromise = request.show();
 
   const response = await responsePromise;
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https.html
index b3a01e3..951b540 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https.html
@@ -36,7 +36,7 @@
   // Wait for the iframe to load.
   const readyPromise = new Promise(resolve => {
       window.addEventListener('message', function handler(evt) {
-        if (evt.source === frame.contentWindow) {
+        if (evt.source === frame.contentWindow && evt.data.type == 'loaded') {
           window.removeEventListener('message', handler);
 
           resolve(evt.data);
@@ -50,7 +50,7 @@
   // race.
   const resultPromise = new Promise(resolve => {
       window.addEventListener('message', function handler(evt) {
-        if (evt.source === frame.contentWindow) {
+        if (evt.source === frame.contentWindow && evt.data.type == 'spc_result') {
           // We're done with the child iframe now.
           document.body.removeChild(frame);
           window.removeEventListener('message', handler);
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https.html
index 8987c7b..84c629f 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https.html
@@ -42,6 +42,7 @@
       },
     }
   }], PAYMENT_DETAILS);
+  await test_driver.bless('user activation');
   await promise_rejects_dom(t, "NotSupportedError", request.show());
 
   // Now try an icon that cannot be decoded.
@@ -59,6 +60,7 @@
       },
     }
   }], PAYMENT_DETAILS);
+  await test_driver.bless('user activation');
   await promise_rejects_dom(t, "NotSupportedError", request.show());
 }, 'SPC authentication with an invalid icon');
 
@@ -96,6 +98,7 @@
     }
   }], PAYMENT_DETAILS);
 
+  await test_driver.bless('user activation');
   const responsePromise = request.show();
   const response = await responsePromise;
   await response.complete('success');
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-rejected.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-rejected.https.html
index 4973748b..444733b 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-rejected.https.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-rejected.https.html
@@ -43,6 +43,7 @@
     }
   }], PAYMENT_DETAILS);
 
+  await test_driver.bless('user activation');
   return promise_rejects_dom(t, "NotAllowedError", request.show());
 }, 'Rejected SPC authentication');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt
new file mode 100644
index 0000000..184762bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL SPC authentication not allowed without a user activation promise_test: Unhandled rejection with value: object "Error: unimplemented"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https.html
new file mode 100644
index 0000000..6519086
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for the 'secure-payment-confirmation' payment method authentication - requires user activation</title>
+<link rel="help" href="https://w3c.github.io/secure-payment-confirmation/sctn-authentication">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="utils.sub.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+  const authenticator = await window.test_driver.add_virtual_authenticator(
+      AUTHENTICATOR_OPTS);
+  t.add_cleanup(() => {
+    return window.test_driver.remove_virtual_authenticator(authenticator);
+  });
+
+  await window.test_driver.set_spc_transaction_mode("autoaccept");
+  t.add_cleanup(() => {
+    return window.test_driver.set_spc_transaction_mode("none");
+  });
+
+
+  const credential = await createCredential();
+
+  const challenge = 'server challenge';
+  const payeeOrigin = 'https://merchant.com';
+  const displayName = 'Troycard ***1234';
+  const request = new PaymentRequest([{
+    supportedMethods: 'secure-payment-confirmation',
+    data: {
+      credentialIds: [credential.rawId],
+      challenge: Uint8Array.from(challenge, c => c.charCodeAt(0)),
+      rpId: window.location.hostname,
+      payeeOrigin,
+      timeout: 60000,
+      instrument: {
+        displayName,
+        icon: ICON_URL,
+      },
+    }
+  }], PAYMENT_DETAILS);
+
+  return promise_rejects_dom(t, "SecurityError", request.show());
+}, 'SPC authentication not allowed without a user activation');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/resources/iframe-authenticate.html b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/resources/iframe-authenticate.html
index 067cb58..e6a634ff 100644
--- a/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/resources/iframe-authenticate.html
+++ b/third_party/blink/web_tests/external/wpt/secure-payment-confirmation/resources/iframe-authenticate.html
@@ -1,6 +1,8 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>SPC Authentication iframe</title>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
 <script src="../utils.sub.js"></script>
 <script>
 'use strict';
@@ -32,6 +34,8 @@
     }
   }], PAYMENT_DETAILS);
 
+  test_driver.set_test_context(window.parent);
+  await test_driver.bless('user activation');
   const responsePromise = request.show();
 
   const response = await responsePromise;
@@ -42,9 +46,9 @@
   // Let our parent know the results. Some WebAuthn fields cannot be cloned, so
   // we have to do some teardown ourselves.
   const clientDataJSON = JSON.parse(arrayBufferToString(cred.response.clientDataJSON))
-  window.parent.postMessage({ id: cred.id, clientDataJSON }, '*');
+  window.parent.postMessage({ type: 'spc_result', id: cred.id, clientDataJSON }, '*');
 });
 
 // Now let our parent know that we are ready to receive the credential ID.
-window.parent.postMessage(true, '*');
+window.parent.postMessage({ type: 'loaded' }, '*');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/remove-script-element.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/remove-script-element.html
new file mode 100644
index 0000000..9de7656f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/remove-script-element.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="resources/utils.js"></script>
+<head>
+<iframe id="iframe"></iframe>
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+async_test(t => {
+  const doc = iframe.contentDocument;
+  const script = doc.createElement('script');
+  script.type = 'speculationrules';
+  script.text = `{"prerender": [{"source": "list", "urls": [] }] }`;
+  doc.head.appendChild(script);
+  iframe.remove();
+  t.step_timeout(() => {
+    document.head.appendChild(script);
+    t.done();
+  }, 0);
+}, 'Removing speculationrules script from detached document should not crash');
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/cssom/idlharness-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/cssom/idlharness-expected.txt
new file mode 100644
index 0000000..8335ac6
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/css/cssom/idlharness-expected.txt
@@ -0,0 +1,456 @@
+This is a testharness.js-based test.
+Found 452 tests; 397 PASS, 55 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS idl_test setup
+PASS idl_test validation
+PASS Partial interface CSSStyleSheet: original interface defined
+PASS Partial interface CSSStyleSheet: member names are unique
+PASS Partial interface mixin DocumentOrShadowRoot: original interface mixin defined
+PASS Partial interface mixin DocumentOrShadowRoot: member names are unique
+PASS Partial interface Window: original interface defined
+PASS Partial interface Window: member names are unique
+PASS Partial interface mixin DocumentOrShadowRoot[2]: member names are unique
+PASS Partial interface HTMLLinkElement: member names are unique
+PASS Partial interface HTMLStyleElement: member names are unique
+PASS Partial interface Window[2]: member names are unique
+PASS ProcessingInstruction includes LinkStyle: member names are unique
+PASS HTMLElement includes ElementCSSInlineStyle: member names are unique
+PASS SVGElement includes ElementCSSInlineStyle: member names are unique
+PASS MathMLElement includes ElementCSSInlineStyle: member names are unique
+PASS SVGElement includes GlobalEventHandlers: member names are unique
+PASS SVGElement includes DocumentAndElementEventHandlers: member names are unique
+PASS SVGElement includes SVGElementInstance: member names are unique
+PASS SVGElement includes HTMLOrSVGElement: member names are unique
+PASS SVGStyleElement includes LinkStyle: member names are unique
+PASS HTMLElement includes GlobalEventHandlers: member names are unique
+PASS HTMLElement includes DocumentAndElementEventHandlers: member names are unique
+PASS HTMLElement includes ElementContentEditable: member names are unique
+PASS HTMLElement includes HTMLOrSVGElement: member names are unique
+PASS HTMLLinkElement includes LinkStyle: member names are unique
+PASS HTMLStyleElement includes LinkStyle: member names are unique
+PASS Window includes GlobalEventHandlers: member names are unique
+PASS Window includes WindowEventHandlers: member names are unique
+PASS Window includes WindowOrWorkerGlobalScope: member names are unique
+PASS Window includes AnimationFrameProvider: member names are unique
+PASS Window includes WindowSessionStorage: member names are unique
+PASS Window includes WindowLocalStorage: member names are unique
+PASS Document includes NonElementParentNode: member names are unique
+PASS DocumentFragment includes NonElementParentNode: member names are unique
+PASS Document includes DocumentOrShadowRoot: member names are unique
+PASS ShadowRoot includes DocumentOrShadowRoot: member names are unique
+PASS Document includes ParentNode: member names are unique
+PASS DocumentFragment includes ParentNode: member names are unique
+PASS Element includes ParentNode: member names are unique
+PASS Element includes NonDocumentTypeChildNode: member names are unique
+PASS CharacterData includes NonDocumentTypeChildNode: member names are unique
+PASS Element includes ChildNode: member names are unique
+PASS CharacterData includes ChildNode: member names are unique
+PASS Element includes Slottable: member names are unique
+PASS Document includes XPathEvaluatorBase: member names are unique
+PASS MathMLElement includes GlobalEventHandlers: member names are unique
+PASS MathMLElement includes DocumentAndElementEventHandlers: member names are unique
+PASS MathMLElement includes HTMLOrSVGElement: member names are unique
+PASS MediaList interface: existence and properties of interface object
+PASS MediaList interface object length
+PASS MediaList interface object name
+PASS MediaList interface: existence and properties of interface prototype object
+PASS MediaList interface: existence and properties of interface prototype object's "constructor" property
+PASS MediaList interface: existence and properties of interface prototype object's @@unscopables property
+PASS MediaList interface: attribute mediaText
+PASS MediaList interface: stringifier
+PASS MediaList interface: attribute length
+PASS MediaList interface: operation item(unsigned long)
+PASS MediaList interface: operation appendMedium(CSSOMString)
+PASS MediaList interface: operation deleteMedium(CSSOMString)
+PASS MediaList must be primary interface of sheet.media
+PASS Stringification of sheet.media
+PASS MediaList interface: sheet.media must inherit property "mediaText" with the proper type
+PASS MediaList interface: sheet.media must inherit property "length" with the proper type
+PASS MediaList interface: sheet.media must inherit property "item(unsigned long)" with the proper type
+PASS MediaList interface: calling item(unsigned long) on sheet.media with too few arguments must throw TypeError
+PASS MediaList interface: sheet.media must inherit property "appendMedium(CSSOMString)" with the proper type
+PASS MediaList interface: calling appendMedium(CSSOMString) on sheet.media with too few arguments must throw TypeError
+PASS MediaList interface: sheet.media must inherit property "deleteMedium(CSSOMString)" with the proper type
+PASS MediaList interface: calling deleteMedium(CSSOMString) on sheet.media with too few arguments must throw TypeError
+PASS StyleSheet interface: existence and properties of interface object
+PASS StyleSheet interface object length
+PASS StyleSheet interface object name
+PASS StyleSheet interface: existence and properties of interface prototype object
+PASS StyleSheet interface: existence and properties of interface prototype object's "constructor" property
+PASS StyleSheet interface: existence and properties of interface prototype object's @@unscopables property
+PASS StyleSheet interface: attribute type
+PASS StyleSheet interface: attribute href
+PASS StyleSheet interface: attribute ownerNode
+PASS StyleSheet interface: attribute parentStyleSheet
+PASS StyleSheet interface: attribute title
+PASS StyleSheet interface: attribute media
+PASS StyleSheet interface: attribute disabled
+PASS CSSStyleSheet interface: existence and properties of interface object
+PASS CSSStyleSheet interface object length
+PASS CSSStyleSheet interface object name
+PASS CSSStyleSheet interface: existence and properties of interface prototype object
+PASS CSSStyleSheet interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSStyleSheet interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSStyleSheet interface: attribute ownerRule
+PASS CSSStyleSheet interface: attribute cssRules
+PASS CSSStyleSheet interface: operation insertRule(CSSOMString, optional unsigned long)
+PASS CSSStyleSheet interface: operation deleteRule(unsigned long)
+PASS CSSStyleSheet interface: operation replace(USVString)
+PASS CSSStyleSheet interface: operation replaceSync(USVString)
+PASS CSSStyleSheet interface: attribute rules
+PASS CSSStyleSheet interface: operation addRule(optional DOMString, optional DOMString, optional unsigned long)
+PASS CSSStyleSheet interface: operation removeRule(optional unsigned long)
+PASS CSSStyleSheet must be primary interface of sheet
+PASS Stringification of sheet
+PASS CSSStyleSheet interface: sheet must inherit property "ownerRule" with the proper type
+PASS CSSStyleSheet interface: sheet must inherit property "cssRules" with the proper type
+PASS CSSStyleSheet interface: sheet must inherit property "insertRule(CSSOMString, optional unsigned long)" with the proper type
+PASS CSSStyleSheet interface: calling insertRule(CSSOMString, optional unsigned long) on sheet with too few arguments must throw TypeError
+PASS CSSStyleSheet interface: sheet must inherit property "deleteRule(unsigned long)" with the proper type
+PASS CSSStyleSheet interface: calling deleteRule(unsigned long) on sheet with too few arguments must throw TypeError
+PASS CSSStyleSheet interface: sheet must inherit property "replace(USVString)" with the proper type
+PASS CSSStyleSheet interface: calling replace(USVString) on sheet with too few arguments must throw TypeError
+PASS CSSStyleSheet interface: sheet must inherit property "replaceSync(USVString)" with the proper type
+PASS CSSStyleSheet interface: calling replaceSync(USVString) on sheet with too few arguments must throw TypeError
+PASS CSSStyleSheet interface: sheet must inherit property "rules" with the proper type
+PASS CSSStyleSheet interface: sheet must inherit property "addRule(optional DOMString, optional DOMString, optional unsigned long)" with the proper type
+PASS CSSStyleSheet interface: calling addRule(optional DOMString, optional DOMString, optional unsigned long) on sheet with too few arguments must throw TypeError
+PASS CSSStyleSheet interface: sheet must inherit property "removeRule(optional unsigned long)" with the proper type
+PASS CSSStyleSheet interface: calling removeRule(optional unsigned long) on sheet with too few arguments must throw TypeError
+PASS StyleSheet interface: sheet must inherit property "type" with the proper type
+PASS StyleSheet interface: sheet must inherit property "href" with the proper type
+PASS StyleSheet interface: sheet must inherit property "ownerNode" with the proper type
+PASS StyleSheet interface: sheet must inherit property "parentStyleSheet" with the proper type
+PASS StyleSheet interface: sheet must inherit property "title" with the proper type
+PASS StyleSheet interface: sheet must inherit property "media" with the proper type
+PASS StyleSheet interface: sheet must inherit property "disabled" with the proper type
+PASS StyleSheetList interface: existence and properties of interface object
+PASS StyleSheetList interface object length
+PASS StyleSheetList interface object name
+PASS StyleSheetList interface: existence and properties of interface prototype object
+PASS StyleSheetList interface: existence and properties of interface prototype object's "constructor" property
+PASS StyleSheetList interface: existence and properties of interface prototype object's @@unscopables property
+PASS StyleSheetList interface: operation item(unsigned long)
+PASS StyleSheetList interface: attribute length
+PASS StyleSheetList must be primary interface of document.styleSheets
+PASS Stringification of document.styleSheets
+PASS StyleSheetList interface: document.styleSheets must inherit property "item(unsigned long)" with the proper type
+PASS StyleSheetList interface: calling item(unsigned long) on document.styleSheets with too few arguments must throw TypeError
+PASS StyleSheetList interface: document.styleSheets must inherit property "length" with the proper type
+PASS CSSRuleList interface: existence and properties of interface object
+PASS CSSRuleList interface object length
+PASS CSSRuleList interface object name
+PASS CSSRuleList interface: existence and properties of interface prototype object
+PASS CSSRuleList interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSRuleList interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSRuleList interface: operation item(unsigned long)
+PASS CSSRuleList interface: attribute length
+PASS CSSRuleList must be primary interface of sheet.cssRules
+PASS Stringification of sheet.cssRules
+PASS CSSRuleList interface: sheet.cssRules must inherit property "item(unsigned long)" with the proper type
+PASS CSSRuleList interface: calling item(unsigned long) on sheet.cssRules with too few arguments must throw TypeError
+PASS CSSRuleList interface: sheet.cssRules must inherit property "length" with the proper type
+PASS CSSRule interface: existence and properties of interface object
+PASS CSSRule interface object length
+PASS CSSRule interface object name
+PASS CSSRule interface: existence and properties of interface prototype object
+PASS CSSRule interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSRule interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSRule interface: attribute cssText
+PASS CSSRule interface: attribute parentRule
+PASS CSSRule interface: attribute parentStyleSheet
+PASS CSSRule interface: attribute type
+PASS CSSRule interface: constant STYLE_RULE on interface object
+PASS CSSRule interface: constant STYLE_RULE on interface prototype object
+PASS CSSRule interface: constant CHARSET_RULE on interface object
+PASS CSSRule interface: constant CHARSET_RULE on interface prototype object
+PASS CSSRule interface: constant IMPORT_RULE on interface object
+PASS CSSRule interface: constant IMPORT_RULE on interface prototype object
+PASS CSSRule interface: constant MEDIA_RULE on interface object
+PASS CSSRule interface: constant MEDIA_RULE on interface prototype object
+PASS CSSRule interface: constant FONT_FACE_RULE on interface object
+PASS CSSRule interface: constant FONT_FACE_RULE on interface prototype object
+PASS CSSRule interface: constant PAGE_RULE on interface object
+PASS CSSRule interface: constant PAGE_RULE on interface prototype object
+FAIL CSSRule interface: constant MARGIN_RULE on interface object assert_own_property: expected property "MARGIN_RULE" missing
+FAIL CSSRule interface: constant MARGIN_RULE on interface prototype object assert_own_property: expected property "MARGIN_RULE" missing
+PASS CSSRule interface: constant NAMESPACE_RULE on interface object
+PASS CSSRule interface: constant NAMESPACE_RULE on interface prototype object
+PASS CSSStyleRule interface: existence and properties of interface object
+PASS CSSStyleRule interface object length
+PASS CSSStyleRule interface object name
+PASS CSSStyleRule interface: existence and properties of interface prototype object
+PASS CSSStyleRule interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSStyleRule interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSStyleRule interface: attribute selectorText
+PASS CSSStyleRule interface: attribute style
+PASS CSSStyleRule must be primary interface of sheet.cssRules[4]
+PASS Stringification of sheet.cssRules[4]
+PASS CSSStyleRule interface: sheet.cssRules[4] must inherit property "selectorText" with the proper type
+PASS CSSStyleRule interface: sheet.cssRules[4] must inherit property "style" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "cssText" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "parentRule" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "parentStyleSheet" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "type" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "STYLE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "CHARSET_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "IMPORT_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "MEDIA_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "FONT_FACE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "PAGE_RULE" with the proper type
+FAIL CSSRule interface: sheet.cssRules[4] must inherit property "MARGIN_RULE" with the proper type assert_inherits: property "MARGIN_RULE" not found in prototype chain
+PASS CSSRule interface: sheet.cssRules[4] must inherit property "NAMESPACE_RULE" with the proper type
+PASS CSSImportRule interface: existence and properties of interface object
+PASS CSSImportRule interface object length
+PASS CSSImportRule interface object name
+PASS CSSImportRule interface: existence and properties of interface prototype object
+PASS CSSImportRule interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSImportRule interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSImportRule interface: attribute href
+PASS CSSImportRule interface: attribute media
+PASS CSSImportRule interface: attribute styleSheet
+PASS CSSImportRule must be primary interface of sheet.cssRules[0]
+PASS Stringification of sheet.cssRules[0]
+PASS CSSImportRule interface: sheet.cssRules[0] must inherit property "href" with the proper type
+PASS CSSImportRule interface: sheet.cssRules[0] must inherit property "media" with the proper type
+PASS CSSImportRule interface: sheet.cssRules[0] must inherit property "styleSheet" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "cssText" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "parentRule" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "parentStyleSheet" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "type" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "STYLE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "CHARSET_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "IMPORT_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "MEDIA_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "FONT_FACE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "PAGE_RULE" with the proper type
+FAIL CSSRule interface: sheet.cssRules[0] must inherit property "MARGIN_RULE" with the proper type assert_inherits: property "MARGIN_RULE" not found in prototype chain
+PASS CSSRule interface: sheet.cssRules[0] must inherit property "NAMESPACE_RULE" with the proper type
+PASS CSSGroupingRule interface: existence and properties of interface object
+PASS CSSGroupingRule interface object length
+PASS CSSGroupingRule interface object name
+PASS CSSGroupingRule interface: existence and properties of interface prototype object
+PASS CSSGroupingRule interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSGroupingRule interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSGroupingRule interface: attribute cssRules
+FAIL CSSGroupingRule interface: operation insertRule(CSSOMString, optional unsigned long) assert_equals: property has wrong .length expected 1 but got 2
+PASS CSSGroupingRule interface: operation deleteRule(unsigned long)
+FAIL CSSPageRule interface: existence and properties of interface object assert_equals: prototype of CSSPageRule is not CSSGroupingRule expected function "function CSSGroupingRule() { [native code] }" but got function "function CSSRule() { [native code] }"
+PASS CSSPageRule interface object length
+PASS CSSPageRule interface object name
+FAIL CSSPageRule interface: existence and properties of interface prototype object assert_equals: prototype of CSSPageRule.prototype is not CSSGroupingRule.prototype expected object "[object CSSGroupingRule]" but got object "[object CSSRule]"
+PASS CSSPageRule interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSPageRule interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSPageRule interface: attribute selectorText
+PASS CSSPageRule interface: attribute style
+PASS CSSPageRule must be primary interface of sheet.cssRules[2]
+PASS Stringification of sheet.cssRules[2]
+PASS CSSPageRule interface: sheet.cssRules[2] must inherit property "selectorText" with the proper type
+PASS CSSPageRule interface: sheet.cssRules[2] must inherit property "style" with the proper type
+FAIL CSSGroupingRule interface: sheet.cssRules[2] must inherit property "cssRules" with the proper type assert_inherits: property "cssRules" not found in prototype chain
+FAIL CSSGroupingRule interface: sheet.cssRules[2] must inherit property "insertRule(CSSOMString, optional unsigned long)" with the proper type assert_inherits: property "insertRule" not found in prototype chain
+FAIL CSSGroupingRule interface: calling insertRule(CSSOMString, optional unsigned long) on sheet.cssRules[2] with too few arguments must throw TypeError assert_inherits: property "insertRule" not found in prototype chain
+FAIL CSSGroupingRule interface: sheet.cssRules[2] must inherit property "deleteRule(unsigned long)" with the proper type assert_inherits: property "deleteRule" not found in prototype chain
+FAIL CSSGroupingRule interface: calling deleteRule(unsigned long) on sheet.cssRules[2] with too few arguments must throw TypeError assert_inherits: property "deleteRule" not found in prototype chain
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "cssText" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "parentRule" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "parentStyleSheet" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "type" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "STYLE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "CHARSET_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "IMPORT_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "MEDIA_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "FONT_FACE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "PAGE_RULE" with the proper type
+FAIL CSSRule interface: sheet.cssRules[2] must inherit property "MARGIN_RULE" with the proper type assert_inherits: property "MARGIN_RULE" not found in prototype chain
+PASS CSSRule interface: sheet.cssRules[2] must inherit property "NAMESPACE_RULE" with the proper type
+FAIL CSSMarginRule interface: existence and properties of interface object assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface object length assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface object name assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface: attribute name assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule interface: attribute style assert_own_property: self does not have own property "CSSMarginRule" expected property "CSSMarginRule" missing
+FAIL CSSMarginRule must be primary interface of sheet.cssRules[2].cssRules[0] assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL Stringification of sheet.cssRules[2].cssRules[0] assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSMarginRule interface: sheet.cssRules[2].cssRules[0] must inherit property "name" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSMarginRule interface: sheet.cssRules[2].cssRules[0] must inherit property "style" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "cssText" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "parentRule" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "parentStyleSheet" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "type" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "STYLE_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "CHARSET_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "IMPORT_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "MEDIA_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "FONT_FACE_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "PAGE_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "MARGIN_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSRule interface: sheet.cssRules[2].cssRules[0] must inherit property "NAMESPACE_RULE" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+PASS CSSNamespaceRule interface: existence and properties of interface object
+PASS CSSNamespaceRule interface object length
+PASS CSSNamespaceRule interface object name
+PASS CSSNamespaceRule interface: existence and properties of interface prototype object
+PASS CSSNamespaceRule interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSNamespaceRule interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSNamespaceRule interface: attribute namespaceURI
+PASS CSSNamespaceRule interface: attribute prefix
+PASS CSSNamespaceRule must be primary interface of sheet.cssRules[1]
+PASS Stringification of sheet.cssRules[1]
+PASS CSSNamespaceRule interface: sheet.cssRules[1] must inherit property "namespaceURI" with the proper type
+PASS CSSNamespaceRule interface: sheet.cssRules[1] must inherit property "prefix" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "cssText" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "parentRule" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "parentStyleSheet" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "type" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "STYLE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "CHARSET_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "IMPORT_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "MEDIA_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "FONT_FACE_RULE" with the proper type
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "PAGE_RULE" with the proper type
+FAIL CSSRule interface: sheet.cssRules[1] must inherit property "MARGIN_RULE" with the proper type assert_inherits: property "MARGIN_RULE" not found in prototype chain
+PASS CSSRule interface: sheet.cssRules[1] must inherit property "NAMESPACE_RULE" with the proper type
+PASS CSSStyleDeclaration interface: existence and properties of interface object
+PASS CSSStyleDeclaration interface object length
+PASS CSSStyleDeclaration interface object name
+PASS CSSStyleDeclaration interface: existence and properties of interface prototype object
+PASS CSSStyleDeclaration interface: existence and properties of interface prototype object's "constructor" property
+PASS CSSStyleDeclaration interface: existence and properties of interface prototype object's @@unscopables property
+PASS CSSStyleDeclaration interface: attribute cssText
+PASS CSSStyleDeclaration interface: attribute length
+PASS CSSStyleDeclaration interface: operation item(unsigned long)
+PASS CSSStyleDeclaration interface: operation getPropertyValue(CSSOMString)
+PASS CSSStyleDeclaration interface: operation getPropertyPriority(CSSOMString)
+PASS CSSStyleDeclaration interface: operation setProperty(CSSOMString, CSSOMString, optional CSSOMString)
+PASS CSSStyleDeclaration interface: operation removeProperty(CSSOMString)
+PASS CSSStyleDeclaration interface: attribute parentRule
+PASS CSSStyleDeclaration interface: attribute cssFloat
+PASS CSSStyleDeclaration must be primary interface of sheet.cssRules[4].style
+PASS Stringification of sheet.cssRules[4].style
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "cssText" with the proper type
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "length" with the proper type
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "item(unsigned long)" with the proper type
+PASS CSSStyleDeclaration interface: calling item(unsigned long) on sheet.cssRules[4].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "getPropertyValue(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyValue(CSSOMString) on sheet.cssRules[4].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "getPropertyPriority(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyPriority(CSSOMString) on sheet.cssRules[4].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "setProperty(CSSOMString, CSSOMString, optional CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling setProperty(CSSOMString, CSSOMString, optional CSSOMString) on sheet.cssRules[4].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "removeProperty(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling removeProperty(CSSOMString) on sheet.cssRules[4].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "parentRule" with the proper type
+PASS CSSStyleDeclaration interface: sheet.cssRules[4].style must inherit property "cssFloat" with the proper type
+PASS CSSStyleDeclaration must be primary interface of sheet.cssRules[2].style
+PASS Stringification of sheet.cssRules[2].style
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "cssText" with the proper type
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "length" with the proper type
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "item(unsigned long)" with the proper type
+PASS CSSStyleDeclaration interface: calling item(unsigned long) on sheet.cssRules[2].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "getPropertyValue(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyValue(CSSOMString) on sheet.cssRules[2].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "getPropertyPriority(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyPriority(CSSOMString) on sheet.cssRules[2].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "setProperty(CSSOMString, CSSOMString, optional CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling setProperty(CSSOMString, CSSOMString, optional CSSOMString) on sheet.cssRules[2].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "removeProperty(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling removeProperty(CSSOMString) on sheet.cssRules[2].style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "parentRule" with the proper type
+PASS CSSStyleDeclaration interface: sheet.cssRules[2].style must inherit property "cssFloat" with the proper type
+FAIL CSSStyleDeclaration must be primary interface of sheet.cssRules[2].cssRules[0].style assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL Stringification of sheet.cssRules[2].cssRules[0].style assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "cssText" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "length" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "item(unsigned long)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: calling item(unsigned long) on sheet.cssRules[2].cssRules[0].style with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "getPropertyValue(CSSOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: calling getPropertyValue(CSSOMString) on sheet.cssRules[2].cssRules[0].style with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "getPropertyPriority(CSSOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: calling getPropertyPriority(CSSOMString) on sheet.cssRules[2].cssRules[0].style with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "setProperty(CSSOMString, CSSOMString, optional CSSOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: calling setProperty(CSSOMString, CSSOMString, optional CSSOMString) on sheet.cssRules[2].cssRules[0].style with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "removeProperty(CSSOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: calling removeProperty(CSSOMString) on sheet.cssRules[2].cssRules[0].style with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "parentRule" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+FAIL CSSStyleDeclaration interface: sheet.cssRules[2].cssRules[0].style must inherit property "cssFloat" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Cannot read properties of undefined (reading '0')"
+PASS CSSStyleDeclaration must be primary interface of style_element.style
+PASS Stringification of style_element.style
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "cssText" with the proper type
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "length" with the proper type
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "item(unsigned long)" with the proper type
+PASS CSSStyleDeclaration interface: calling item(unsigned long) on style_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "getPropertyValue(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyValue(CSSOMString) on style_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "getPropertyPriority(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyPriority(CSSOMString) on style_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "setProperty(CSSOMString, CSSOMString, optional CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling setProperty(CSSOMString, CSSOMString, optional CSSOMString) on style_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "removeProperty(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling removeProperty(CSSOMString) on style_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "parentRule" with the proper type
+PASS CSSStyleDeclaration interface: style_element.style must inherit property "cssFloat" with the proper type
+PASS CSSStyleDeclaration must be primary interface of svg_element.style
+PASS Stringification of svg_element.style
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "cssText" with the proper type
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "length" with the proper type
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "item(unsigned long)" with the proper type
+PASS CSSStyleDeclaration interface: calling item(unsigned long) on svg_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "getPropertyValue(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyValue(CSSOMString) on svg_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "getPropertyPriority(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyPriority(CSSOMString) on svg_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "setProperty(CSSOMString, CSSOMString, optional CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling setProperty(CSSOMString, CSSOMString, optional CSSOMString) on svg_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "removeProperty(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling removeProperty(CSSOMString) on svg_element.style with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "parentRule" with the proper type
+PASS CSSStyleDeclaration interface: svg_element.style must inherit property "cssFloat" with the proper type
+PASS CSSStyleDeclaration must be primary interface of getComputedStyle(svg_element)
+PASS Stringification of getComputedStyle(svg_element)
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "cssText" with the proper type
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "length" with the proper type
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "item(unsigned long)" with the proper type
+PASS CSSStyleDeclaration interface: calling item(unsigned long) on getComputedStyle(svg_element) with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "getPropertyValue(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyValue(CSSOMString) on getComputedStyle(svg_element) with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "getPropertyPriority(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling getPropertyPriority(CSSOMString) on getComputedStyle(svg_element) with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "setProperty(CSSOMString, CSSOMString, optional CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling setProperty(CSSOMString, CSSOMString, optional CSSOMString) on getComputedStyle(svg_element) with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "removeProperty(CSSOMString)" with the proper type
+PASS CSSStyleDeclaration interface: calling removeProperty(CSSOMString) on getComputedStyle(svg_element) with too few arguments must throw TypeError
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "parentRule" with the proper type
+PASS CSSStyleDeclaration interface: getComputedStyle(svg_element) must inherit property "cssFloat" with the proper type
+PASS CSS namespace: extended attributes
+PASS CSS namespace: property descriptor
+PASS CSS namespace: [[Extensible]] is true
+PASS CSS namespace: [[Prototype]] is Object.prototype
+PASS CSS namespace: typeof is "object"
+PASS CSS namespace: has no length property
+PASS CSS namespace: has no name property
+PASS CSS namespace: operation escape(CSSOMString)
+PASS SVGElement interface: attribute style
+PASS SVGElement interface: svg_element must inherit property "style" with the proper type
+PASS SVGStyleElement interface: attribute sheet
+PASS HTMLElement interface: attribute style
+PASS HTMLElement interface: style_element must inherit property "style" with the proper type
+PASS HTMLElement interface: document.createElement("unknownelement") must inherit property "style" with the proper type
+PASS HTMLLinkElement interface: attribute sheet
+PASS HTMLStyleElement interface: attribute sheet
+PASS Window interface: operation getComputedStyle(Element, optional CSSOMString?)
+PASS Window interface: window must inherit property "getComputedStyle(Element, optional CSSOMString?)" with the proper type
+PASS Window interface: calling getComputedStyle(Element, optional CSSOMString?) on window with too few arguments must throw TypeError
+PASS Document interface: attribute styleSheets
+PASS Document interface: attribute adoptedStyleSheets
+PASS Document interface: document must inherit property "styleSheets" with the proper type
+PASS Document interface: document must inherit property "adoptedStyleSheets" with the proper type
+PASS Document interface: new Document() must inherit property "styleSheets" with the proper type
+PASS Document interface: new Document() must inherit property "adoptedStyleSheets" with the proper type
+PASS ShadowRoot interface: attribute styleSheets
+PASS ShadowRoot interface: attribute adoptedStyleSheets
+PASS ProcessingInstruction interface: attribute sheet
+PASS ProcessingInstruction interface: xmlss_pi must inherit property "sheet" with the proper type
+FAIL MathMLElement interface: attribute style assert_own_property: self does not have own property "MathMLElement" expected property "MathMLElement" missing
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt
new file mode 100644
index 0000000..184762bb
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL SPC authentication not allowed without a user activation promise_test: Unhandled rejection with value: object "Error: unimplemented"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html b/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html
index a1ae72e..052ececc 100644
--- a/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html
+++ b/third_party/blink/web_tests/wpt_internal/fenced_frame/unfenced-top.https.html
@@ -82,11 +82,12 @@
   return [new_window, fenced_frame_url, nested_iframe_url];
 }
 
-async function checkNavigationSucceeded(new_window, new_referrer) {
+async function checkNavigationSucceeded(
+    new_window, new_referrer, expected_history_length) {
   message_received = new Promise(resolve => window.onmessage = () => resolve());
 
   // Check that the navigation happened in the correct frame.
-  await new_window.execute((expected_referrer) => {
+  await new_window.execute((expected_referrer, expected_history_length) => {
     assert_not_equals(window.opener, null,
                       `The browsing context has a window.opener, because it
                        is the top-level frame in the opened window.`);
@@ -97,14 +98,17 @@
     assert_equals(window.fenced_frame, undefined,
                   `The browsing context has been refreshed, so old variables
                    are gone.`);
-  }, [new_referrer]);
+    assert_equals(history.length, expected_history_length,
+                  `The history length should be as expected.`);
+  }, [new_referrer, expected_history_length]);
 
   // Await the postMessage from inside the frame to confirm that the opener
   // is unchanged.
   await message_received;
 }
 
-// Test successful top-level navigation from an opaque-ads fenced frame.
+// Test successful top-level navigation (non-refresh) from an opaque-ads
+// fenced frame.
 promise_test(async () => {
   // Create a new window, where we'll navigate the outermost frame.
   // (We can't do it in this window, or we'd lose the testing harness.)
@@ -112,6 +116,11 @@
   // become the document.referrer of the navigated outermost frame).
   const [new_window, new_referrer,] = await createAndValidateWindow();
 
+  // Check the history length before navigation.
+  const history_length = await new_window.execute(() => {
+    return history.length;
+  });
+
   // Navigate the window's top-level frame from inside the fenced frame.
   // (Navigate to the original URL, i.e. refresh the page, so that we can
   // still communicate via RemoteContext.)
@@ -120,6 +129,31 @@
   await new_window.execute(() => {
     window.executor.suspend(() => {
       window.fenced_frame.execute((refresh_url) => {
+        var norefresh_url = new URL(refresh_url);
+        norefresh_url.searchParams.append('norefresh', '');
+        const window_handle = window.open(norefresh_url, '_unfencedTop');
+        assert_equals(window_handle, null,
+                      `There should be no window handle returned from
+                       navigations through _unfencedTop.`);
+      }, [location.href]);
+    });
+  });
+
+  // Because this is a non-refresh navigation, the history should be extended.
+  await checkNavigationSucceeded(new_window, new_referrer, history_length+1);
+}, '_unfencedTop opaque-ads non-refresh success case');
+
+// Test successful top-level refresh from an opaque-ads fenced frame.
+promise_test(async () => {
+  const [new_window, new_referrer,] = await createAndValidateWindow();
+
+  const history_length = await new_window.execute(() => {
+    return history.length;
+  });
+
+  await new_window.execute(() => {
+    window.executor.suspend(() => {
+      window.fenced_frame.execute((refresh_url) => {
         const window_handle = window.open(refresh_url, '_unfencedTop');
         assert_equals(window_handle, null,
                       `There should be no window handle returned from
@@ -128,21 +162,29 @@
     });
   });
 
- await checkNavigationSucceeded(new_window, new_referrer);
-}, '_unfencedTop opaque-ads success case');
+  // Because this is a refresh, the history should not be extended.
+  await checkNavigationSucceeded(new_window, new_referrer, history_length);
+}, '_unfencedTop opaque-ads refresh success case');
 
-// Test successful top-level navigation out of an iframe nested inside an
-// opaque-ads fenced frame.
+// Test successful top-level navigation (non-refresh) out of an iframe nested
+// inside an opaque-ads fenced frame.
 promise_test(async () => {
   const [new_window,, new_referrer] = await createAndValidateWindow();
 
+  // Check the history length before navigation.
+  const history_length = await new_window.execute(() => {
+    return history.length;
+  });
+
   // Navigate the window's top-level frame from inside the fenced frame's
   // nested iframe.
   await new_window.execute(() => {
     window.executor.suspend(() => {
       window.fenced_frame.execute((refresh_url) => {
         window.nested_iframe.execute((refresh_url) => {
-          const window_handle = window.open(refresh_url, '_unfencedTop');
+          var norefresh_url = new URL(refresh_url);
+          norefresh_url.searchParams.append('norefresh', '');
+          const window_handle = window.open(norefresh_url, '_unfencedTop');
           assert_equals(window_handle, null,
                         `There should be no window handle returned from
                          navigations through _unfencedTop.`);
@@ -151,7 +193,8 @@
     });
   });
 
-  await checkNavigationSucceeded(new_window, new_referrer);
+  // Because this is a non-refresh navigation, the history should be extended.
+  await checkNavigationSucceeded(new_window, new_referrer, history_length+1);
 }, '_unfencedTop opaque-ads nested iframe success case');
 
 // Test unsuccessful navigation out of a fenced frame using _unfencedTop and
diff --git a/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html b/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
index fb66736..73ffb23 100644
--- a/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
+++ b/third_party/blink/web_tests/wpt_internal/webgpu/cts.https.html
@@ -2785,12 +2785,22 @@
 <meta name=variant content='?q=webgpu:shader,execution,expression,binary,f32_logical:less_equals:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,binary,f32_logical:greater_than:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,binary,f32_logical:greater_equals:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,abs:abstract_int:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,abs:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,abs:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,abs:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,abs:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,abs:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,acos:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,acos:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,acos:f16:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,all:bool:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,any:bool:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,arrayLength:array:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f32:storageClass="uniform";vectorize="_undef_";*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f32:storageClass="uniform";vectorize=2;*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f32:storageClass="uniform";vectorize=3;*'>
@@ -2803,44 +2813,92 @@
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f32:storageClass="storage_rw";vectorize=2;*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f32:storageClass="storage_rw";vectorize=3;*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f32:storageClass="storage_rw";vectorize=4;*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,atan2:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,ceil:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,ceil:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,ceil:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,clamp:abstract_int:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,clamp:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,clamp:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,clamp:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,clamp:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,clamp:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cos:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cos:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cos:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cosh:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cosh:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cosh:f16:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,countLeadingZeros:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,countLeadingZeros:i32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,countOneBits:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,countOneBits:i32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,countTrailingZeros:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,countTrailingZeros:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cross:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cross:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,cross:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,exp:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,exp:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,exp:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,exp2:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,exp2:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,exp2:f16:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,extractBits:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,extractBits:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,faceForward:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,faceForward:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,faceForward:f16:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,firstLeadingBit:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,firstLeadingBit:i32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,firstTrailingBit:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,firstTrailingBit:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,floor:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,floor:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,floor:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,fma:abstract_float:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,fma:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,fma:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,fract:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,fract:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,fract:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,frexp:scalar_f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,frexp:scalar_f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,frexp:vector_f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,frexp:vector_f16:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,insertBits:integer:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,inversesqrt:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,inversesqrt:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,inversesqrt:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,ldexp:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,ldexp:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,ldexp:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,log:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,log:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,log:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,log2:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,log2:f32:*'>
-<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,logical_built_in_functions:logical_builtin_functions,scalar_select:*'>
-<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,logical_built_in_functions:logical_builtin_functions,vector_select:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,log2:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,max:abstract_int:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,max:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,max:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,max:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,max:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,max:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,min:abstract_int:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,min:u32:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,min:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,min:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,min:f32:*'>
-<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,reverseBits:unsigned:*'>
-<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,reverseBits:signed:*'>
-<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,select:bool:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,min:f16:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,quantizeToF16:f32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,reverseBits:u32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,reverseBits:i32:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,select:scalar:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,select:vector:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,sin:abstract_float:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,sin:f32:*'>
-<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,value_testing_built_in_functions:value_testing_builtin_functions,runtime_sized_array_length:*'>
+<meta name=variant content='?q=webgpu:shader,execution,expression,call,builtin,sin:f16:*'>
 <meta name=variant content='?q=webgpu:shader,execution,expression,unary,f32_arithmetic:negation:*'>
 <meta name=variant content='?q=webgpu:shader,execution,memory_model,atomicity:atomicity:memType="atomic_storage";testType="inter_workgroup"'>
 <meta name=variant content='?q=webgpu:shader,execution,memory_model,atomicity:atomicity:memType="atomic_storage";testType="intra_workgroup";*'>
@@ -3311,9 +3369,11 @@
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,interpolate:type_and_sampling:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,interpolate:require_location:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,interpolate:integral_types:*'>
+<meta name=variant content='?q=webgpu:shader,validation,shader_io,interpolate:duplicate:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,invariant:valid_only_with_vertex_position_builtin:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,invariant:not_valid_on_user_defined_io:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,invariant:invalid_use_of_parameters:*'>
+<meta name=variant content='?q=webgpu:shader,validation,shader_io,invariant:duplicate:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,locations:stage_inout:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,locations:type:*'>
 <meta name=variant content='?q=webgpu:shader,validation,shader_io,locations:nesting:*'>
diff --git a/third_party/webgpu-cts/ts_sources.txt b/third_party/webgpu-cts/ts_sources.txt
index 65eb912..c34eee2c 100644
--- a/third_party/webgpu-cts/ts_sources.txt
+++ b/third_party/webgpu-cts/ts_sources.txt
@@ -269,33 +269,41 @@
 src/webgpu/shader/execution/expression/binary/f32_logical.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/builtin.ts
 src/webgpu/shader/execution/expression/call/builtin/abs.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/acos.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/all.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/any.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/arrayLength.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/atan.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/atan2.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/ceil.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/clamp.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/cos.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/cosh.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/countLeadingZeros.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/countOneBits.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/countTrailingZeros.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/cross.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/exp.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/exp2.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/extractBits.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/faceForward.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/firstLeadingBit.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/firstTrailingBit.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/floor.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/fma.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/fract.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/frexp.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/insertBits.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/inversesqrt.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/ldexp.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/log.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/log2.spec.ts
-src/webgpu/shader/execution/expression/call/builtin/logical_built_in_functions.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/max.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/min.spec.ts
+src/webgpu/shader/execution/expression/call/builtin/quantizeToF16.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/reverseBits.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/select.spec.ts
 src/webgpu/shader/execution/expression/call/builtin/sin.spec.ts
-src/webgpu/shader/execution/expression/call/builtin/value_testing_built_in_functions.spec.ts
 src/webgpu/shader/execution/expression/unary/unary.ts
 src/webgpu/shader/execution/expression/unary/f32_arithmetic.spec.ts
 src/webgpu/shader/execution/memory_model/memory_model_setup.ts
diff --git a/third_party/zlib/crc32.c b/third_party/zlib/crc32.c
index ee8d4725..41fe8915 100644
--- a/third_party/zlib/crc32.c
+++ b/third_party/zlib/crc32.c
@@ -622,6 +622,123 @@
     return (const z_crc_t FAR *)crc_table;
 }
 
+/* =========================================================================
+ * Use ARM machine instructions if available. This will compute the CRC about
+ * ten times faster than the braided calculation. This code does not check for
+ * the presence of the CRC instruction at run time. __ARM_FEATURE_CRC32 will
+ * only be defined if the compilation specifies an ARM processor architecture
+ * that has the instructions. For example, compiling with -march=armv8.1-a or
+ * -march=armv8-a+crc, or -march=native if the compile machine has the crc32
+ * instructions.
+ */
+#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32) && W == 8 \
+    && defined(USE_CANONICAL_ARMV8_CRC32)
+
+/*
+   Constants empirically determined to maximize speed. These values are from
+   measurements on a Cortex-A57. Your mileage may vary.
+ */
+#define Z_BATCH 3990                /* number of words in a batch */
+#define Z_BATCH_ZEROS 0xa10d3d0c    /* computed from Z_BATCH = 3990 */
+#define Z_BATCH_MIN 800             /* fewest words in a final batch */
+
+unsigned long ZEXPORT crc32_z(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    z_size_t len;
+{
+    z_crc_t val;
+    z_word_t crc1, crc2;
+    const z_word_t *word;
+    z_word_t val0, val1, val2;
+    z_size_t last, last2, i;
+    z_size_t num;
+
+    /* Return initial CRC, if requested. */
+    if (buf == Z_NULL) return 0;
+
+#ifdef DYNAMIC_CRC_TABLE
+    once(&made, make_crc_table);
+#endif /* DYNAMIC_CRC_TABLE */
+
+    /* Pre-condition the CRC */
+    crc ^= 0xffffffff;
+
+    /* Compute the CRC up to a word boundary. */
+    while (len && ((z_size_t)buf & 7) != 0) {
+        len--;
+        val = *buf++;
+        __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val));
+    }
+
+    /* Prepare to compute the CRC on full 64-bit words word[0..num-1]. */
+    word = (z_word_t const *)buf;
+    num = len >> 3;
+    len &= 7;
+
+    /* Do three interleaved CRCs to realize the throughput of one crc32x
+       instruction per cycle. Each CRC is calcuated on Z_BATCH words. The three
+       CRCs are combined into a single CRC after each set of batches. */
+    while (num >= 3 * Z_BATCH) {
+        crc1 = 0;
+        crc2 = 0;
+        for (i = 0; i < Z_BATCH; i++) {
+            val0 = word[i];
+            val1 = word[i + Z_BATCH];
+            val2 = word[i + 2 * Z_BATCH];
+            __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0));
+            __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc1) : "r"(val1));
+            __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc2) : "r"(val2));
+        }
+        word += 3 * Z_BATCH;
+        num -= 3 * Z_BATCH;
+        crc = multmodp(Z_BATCH_ZEROS, crc) ^ crc1;
+        crc = multmodp(Z_BATCH_ZEROS, crc) ^ crc2;
+    }
+
+    /* Do one last smaller batch with the remaining words, if there are enough
+       to pay for the combination of CRCs. */
+    last = num / 3;
+    if (last >= Z_BATCH_MIN) {
+        last2 = last << 1;
+        crc1 = 0;
+        crc2 = 0;
+        for (i = 0; i < last; i++) {
+            val0 = word[i];
+            val1 = word[i + last];
+            val2 = word[i + last2];
+            __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0));
+            __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc1) : "r"(val1));
+            __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc2) : "r"(val2));
+        }
+        word += 3 * last;
+        num -= 3 * last;
+        val = x2nmodp(last, 6);
+        crc = multmodp(val, crc) ^ crc1;
+        crc = multmodp(val, crc) ^ crc2;
+    }
+
+    /* Compute the CRC on any remaining words. */
+    for (i = 0; i < num; i++) {
+        val0 = word[i];
+        __asm__ volatile("crc32x %w0, %w0, %x1" : "+r"(crc) : "r"(val0));
+    }
+    word += num;
+
+    /* Complete the CRC on any remaining bytes. */
+    buf = (const unsigned char FAR *)word;
+    while (len) {
+        len--;
+        val = *buf++;
+        __asm__ volatile("crc32b %w0, %w0, %w1" : "+r"(crc) : "r"(val));
+    }
+
+    /* Return the CRC, post-conditioned. */
+    return crc ^ 0xffffffff;
+}
+
+#else
+
 /* ========================================================================= */
 unsigned long ZEXPORT crc32_z(crc, buf, len)
     unsigned long crc;
@@ -974,6 +1091,8 @@
     return crc ^ 0xffffffff;
 }
 
+#endif
+
 /* ========================================================================= */
 unsigned long ZEXPORT crc32(crc, buf, len)
     unsigned long crc;
diff --git a/third_party/zlib/zlib.h b/third_party/zlib/zlib.h
index a176f93..88961b9 100644
--- a/third_party/zlib/zlib.h
+++ b/third_party/zlib/zlib.h
@@ -1690,8 +1690,9 @@
 ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
 /*
      Update a running Adler-32 checksum with the bytes buf[0..len-1] and
-   return the updated checksum.  If buf is Z_NULL, this function returns the
-   required initial value for the checksum.
+   return the updated checksum. An Adler-32 value is in the range of a 32-bit
+   unsigned integer. If buf is Z_NULL, this function returns the required
+   initial value for the checksum.
 
      An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed
    much faster.
@@ -1727,9 +1728,10 @@
 ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
 /*
      Update a running CRC-32 with the bytes buf[0..len-1] and return the
-   updated CRC-32.  If buf is Z_NULL, this function returns the required
-   initial value for the crc.  Pre- and post-conditioning (one's complement) is
-   performed within this function so it shouldn't be done by the application.
+   updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer.
+   If buf is Z_NULL, this function returns the required initial value for the
+   crc. Pre- and post-conditioning (one's complement) is performed within this
+   function so it shouldn't be done by the application.
 
    Usage example:
 
diff --git a/tools/cast3p/OWNERS b/tools/cast3p/OWNERS
index 39eb6fde..e0fc48a2 100644
--- a/tools/cast3p/OWNERS
+++ b/tools/cast3p/OWNERS
@@ -3,5 +3,5 @@
 riazantsevv@google.com
 rwkeane@google.com
 
-per-file cast_core.version=chromium-autoroll@skia-public.iam.gserviceaccount.com
-per-file runtime.version=chromium-autoroll@skia-public.iam.gserviceaccount.com
\ No newline at end of file
+per-file cast_core.version=chromium-internal-autoroll@skia-public.iam.gserviceaccount.com
+per-file runtime.version=chromium-internal-autoroll@skia-public.iam.gserviceaccount.com
\ No newline at end of file
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 43fecfed..baac6d9 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -2525,15 +2525,6 @@
   </description>
 </action>
 
-<action name="Android.Bookmarks.BottomSheet.Open">
-  <owner>shaktisahu@chromium.org</owner>
-  <description>
-    User tapped on the star icon from the app menu to bookmark a page. This
-    resulted in opening the bookmarks bottom sheet that presents a list of
-    destination folders to choose from for saving the bookmark.
-  </description>
-</action>
-
 <action name="Android.ChromeHome.AcceptedSurvey">
   <obsolete>
     Deprecated 1/2018. Replaced with the
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 5e64066..a2a96b5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19838,6 +19838,7 @@
   <int value="65" label="VSH_CONNECT_FAILED"/>
   <int value="66" label="CONTAINER_STOP_FAILED"/>
   <int value="67" label="CONTAINER_STOP_CANCELLED"/>
+  <int value="68" label="WAYLAND_SERVER_CREATION_FAILED"/>
 </enum>
 
 <enum name="CrostiniSettingsEvent">
@@ -37177,7 +37178,7 @@
   <int value="3392" label="DOMWindowOpenPositioningFeaturesCrossScreen"/>
   <int value="3393" label="DOMWindowSetWindowRectCrossScreen"/>
   <int value="3394" label="FullscreenCrossScreen"/>
-  <int value="3395" label="BatterySavingsMeta"/>
+  <int value="3395" label="OBSOLETE_BatterySavingsMeta"/>
   <int value="3396" label="DigitalGoodsGetDigitalGoodsService"/>
   <int value="3397" label="DigitalGoodsGetDetails"/>
   <int value="3398" label="DigitalGoodsAcknowledge"/>
@@ -53817,6 +53818,7 @@
   <int value="-1134412904" label="PrivacySandboxSettings:disabled"/>
   <int value="-1134307340" label="stop-loading-in-background:enabled"/>
   <int value="-1132704128" label="AndroidPaymentAppsFilter:disabled"/>
+  <int value="-1131726331" label="BackGestureRefactorAndroid:disabled"/>
   <int value="-1128981647" label="EnableOAuthIpp:enabled"/>
   <int value="-1128912963" label="MediaControlsExpandGesture:disabled"/>
   <int value="-1128221789"
@@ -54388,6 +54390,7 @@
   <int value="-747072690" label="NtpRepeatableQueries:disabled"/>
   <int value="-746328467" label="ExpensiveBackgroundTimerThrottling:disabled"/>
   <int value="-746042208" label="QuickCommands:enabled"/>
+  <int value="-745969829" label="BackGestureRefactorAndroid:enabled"/>
   <int value="-745082968" label="SyncDeviceInfoInTransportMode:disabled"/>
   <int value="-745005043"
       label="enable-experimental-accessibility-switch-access-setup-guide"/>
@@ -83057,6 +83060,7 @@
       label="Unpacking the (possibly patched) uncompressed archive failed."/>
   <int value="71" label="Rotation of the device trust key failed."/>
   <int value="72" label="Rotation of the device trust key succeeded."/>
+  <int value="73" label="Creation of shortcuts succeeded."/>
 </enum>
 
 <enum name="SetupInstallServiceInstallResult">
@@ -97712,6 +97716,16 @@
   <int value="5" label="Load failed in preload and tab was never opened"/>
 </enum>
 
+<enum name="WhatsNewStartupType">
+  <int value="0" label="Called What's New ShouldShowForState"/>
+  <int value="1" label="Page is disabled by promotional tabs policy"/>
+  <int value="2" label="Page disabled due to invalid/unreadable local state"/>
+  <int value="3" label="Page is disabled by feature or command line flag"/>
+  <int value="4" label="Page has already been shown for this milestone"/>
+  <int value="5" label="User is ineligible for page"/>
+  <int value="6" label="Page is overridden by a different first run page"/>
+</enum>
+
 <enum name="WhitelistedDownloadType">
   <int value="0" label="Does not match any whitelists"/>
   <int value="1" label="URL whitelist"/>
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index deb63ef6..bf06f66 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -646,6 +646,26 @@
   </summary>
 </histogram>
 
+<histogram name="Android.ClipBoard.getImageDuration{ImageType}" units="ms"
+    expires_after="2022-10-31">
+  <owner>gangwu@chromium.org</owner>
+  <owner>wenyufu@chromium.org</owner>
+  <summary>
+    Records the duration for Android Clipboard to load the images from Uri
+    backed by a temporary file. Recorded when image is requested from Clipboard
+    (e.g Image paste), and read into memory as bytes. Record for {ImageType}
+  </summary>
+  <token key="ImageType">
+    <variant name=".NonPng"
+        summary="image with type other than png, so encoding is needed. The
+                 duration includes reading image as bitmap, and conversion
+                 into png."/>
+    <variant name=".Png"
+        summary="image with type as png, so no encoding is needed. The
+                 duration includes reading bytes from the file stream."/>
+  </token>
+</histogram>
+
 <histogram name="Android.ContactsPicker.AddressHasDerivedField"
     enum="AddressHasDerivedField" expires_after="2020-09-01">
   <owner>finnur@chromium.org</owner>
@@ -2741,7 +2761,7 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.ChosenSearchEngine"
-    enum="OmniboxSearchEngineType" expires_after="2022-06-01">
+    enum="OmniboxSearchEngineType" expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -2751,7 +2771,7 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.Events"
-    enum="AndroidSearchEngineChoiceEvents" expires_after="2022-06-01">
+    enum="AndroidSearchEngineChoiceEvents" expires_after="2023-06-01">
   <owner>wylieb@chromium.org</owner>
   <owner>fgorski@chromium.org</owner>
   <summary>
@@ -2760,7 +2780,7 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.EventsV2"
-    enum="AndroidSearchEngineChoiceEventsV2" expires_after="2022-10-04">
+    enum="AndroidSearchEngineChoiceEventsV2" expires_after="2023-06-01">
   <owner>wylieb@chromium.org</owner>
   <owner>pavely@chromium.org</owner>
   <owner>fgorski@chromium.org</owner>
@@ -2771,7 +2791,7 @@
 </histogram>
 
 <histogram name="Android.SearchEngineChoice.SearchEngineBeforeChoicePrompt"
-    enum="OmniboxSearchEngineType" expires_after="2022-07-31">
+    enum="OmniboxSearchEngineType" expires_after="2023-06-01">
   <owner>wylieb@chromium.org</owner>
   <owner>fgorski@chromium.org</owner>
   <summary>
@@ -4224,8 +4244,7 @@
 
 <histogram
     name="Android.WebView.UniversalAccess.OriginUrlMismatchInHistoryUtil"
-    enum="BooleanAllowed" expires_after="2022-08-07">
-  <owner>alexmitra@chromium.org</owner>
+    enum="BooleanAllowed" expires_after="2022-09-01">
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -4237,8 +4256,7 @@
 
 <histogram
     name="Android.WebView.UniversalAccess.OriginUrlMismatchInRenderFrame"
-    enum="BooleanAllowed" expires_after="2022-06-04">
-  <owner>alexmitra@chromium.org</owner>
+    enum="BooleanAllowed" expires_after="2022-09-01">
   <owner>torne@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
   <summary>
@@ -4392,7 +4410,7 @@
 </histogram>
 
 <histogram name="AndroidSearchEngineLogo.Events"
-    enum="AndroidSearchEngineLogoEvents" expires_after="2022-06-01">
+    enum="AndroidSearchEngineLogoEvents" expires_after="2023-06-01">
   <owner>wylieb@chromium.org</owner>
   <owner>fgorski@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index a9c2c64f..8456b27 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1066,6 +1066,21 @@
   <token key="TabletOrClamshell" variants="DisplayModes"/>
 </histogram>
 
+<histogram name="Ash.CaptureModeController.ScreenRecordingLength"
+    units="seconds" expires_after="2022-09-09">
+  <obsolete>
+    Replaced with
+    Ash.CaptureModeController.ScreenRecordingLength.{TabletOrClamshell} in M99.
+  </obsolete>
+  <owner>afakhry@chromium.org</owner>
+  <owner>gzadina@google.com</owner>
+  <summary>
+    Records the time of a successful video recording in capture mode. This
+    metric will not be recorded if a file was not successfully saved. The upper
+    limit of this histogram is 3 hours.
+  </summary>
+</histogram>
+
 <histogram
     name="Ash.CaptureModeController.ScreenRecordingLength.{TabletOrClamshell}"
     units="seconds" expires_after="2022-09-09">
diff --git a/tools/metrics/histograms/metadata/bookmarks/histograms.xml b/tools/metrics/histograms/metadata/bookmarks/histograms.xml
index 07d524f..e5ed634c 100644
--- a/tools/metrics/histograms/metadata/bookmarks/histograms.xml
+++ b/tools/metrics/histograms/metadata/bookmarks/histograms.xml
@@ -129,16 +129,6 @@
   </summary>
 </histogram>
 
-<histogram name="Bookmarks.BottomSheet.DestinationFolder" enum="BookmarkType"
-    expires_after="2022-06-01">
-  <owner>wylieb@chromium.org</owner>
-  <component>UI&gt;Browser&gt;Bookmarks</component>
-  <summary>
-    Logs the destination directory chosen by the user when saving a bookmark via
-    bottom sheet on Android.
-  </summary>
-</histogram>
-
 <histogram name="Bookmarks.Count.OnProfileLoad" units="bookmarks"
     expires_after="2022-10-23">
   <owner>supertri@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 6a1ffa8d..2a7fc13 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -151,7 +151,7 @@
 </histogram>
 
 <histogram name="CustomTabs.DetachedResourceRequest.FinalStatus"
-    enum="NetErrorCodes" expires_after="2022-08-21">
+    enum="NetErrorCodes" expires_after="2022-10-23">
   <owner>lizeb@chromium.org</owner>
   <owner>cct-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index 29cdfb1..4cae66a 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -124,7 +124,7 @@
 </histogram>
 
 <histogram name="Extensions.ActiveScriptController.DeniedExtensions"
-    units="Extension Count" expires_after="2022-08-21">
+    units="Extension Count" expires_after="2022-10-23">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -134,7 +134,7 @@
 </histogram>
 
 <histogram name="Extensions.ActiveScriptController.PermittedExtensions"
-    units="Extension Count" expires_after="2022-08-21">
+    units="Extension Count" expires_after="2022-10-23">
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index 2d512ef8..1c3141c 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -524,7 +524,7 @@
 </histogram>
 
 <histogram name="FileBrowser.MenuItemSelected" enum="FileManagerMenuCommands"
-    expires_after="2022-04-10">
+    expires_after="M113">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/image/histograms.xml b/tools/metrics/histograms/metadata/image/histograms.xml
index 5961057..2071bdc 100644
--- a/tools/metrics/histograms/metadata/image/histograms.xml
+++ b/tools/metrics/histograms/metadata/image/histograms.xml
@@ -316,7 +316,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.CacheMetadataCount{ImageFetcherCacheStrategy}"
-    units="records" expires_after="2022-06-01">
+    units="records" expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -328,7 +328,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.CacheSize{ImageFetcherCacheStrategy}" units="KB"
-    expires_after="2022-06-01">
+    expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -339,7 +339,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.Events{ImageFetcherClients}"
-    enum="ImageFetcherEvent" expires_after="2022-06-01">
+    enum="ImageFetcherEvent" expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -353,7 +353,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.ImageLoadFromCacheTimeJava{ImageFetcherClients}"
-    units="ms" expires_after="2022-06-01">
+    units="ms" expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -366,7 +366,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.ImageLoadFromCacheTime{ImageFetcherClients}"
-    units="ms" expires_after="2022-06-01">
+    units="ms" expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
@@ -379,7 +379,7 @@
 </histogram>
 
 <histogram name="ImageFetcher.ImageLoadFromNativeTimeJava{ImageFetcherClients}"
-    units="ms" expires_after="2022-06-01">
+    units="ms" expires_after="2023-06-01">
   <owner>fgorski@chromium.org</owner>
   <owner>wylieb@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml b/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
index eb4be80..2556a72 100644
--- a/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
+++ b/tools/metrics/histograms/metadata/leveldb_proto/histograms.xml
@@ -104,7 +104,7 @@
 </variants>
 
 <histogram name="LevelDB.ApproximateMemTableMemoryUse.{LevelDBClient}"
-    units="bytes" expires_after="2022-04-11">
+    units="bytes" expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>salg@google.com</owner>
   <owner>chrome-owp-storage@google.com</owner>
@@ -116,7 +116,7 @@
 </histogram>
 
 <histogram name="ProtoDB.DestroySuccess.{LevelDBClient}" enum="BooleanSuccess"
-    expires_after="2022-04-11">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>Whether a ProtoDB Destroy call was successful or not.</summary>
@@ -124,7 +124,7 @@
 </histogram>
 
 <histogram name="ProtoDB.GetErrorStatus.{LevelDBClient}" enum="LevelDBStatus"
-    expires_after="2022-04-11">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -134,7 +134,7 @@
 </histogram>
 
 <histogram name="ProtoDB.GetFound.{LevelDBClient}" enum="Boolean"
-    expires_after="2022-04-11">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>Whether a ProtoDB Get call found what was requested.</summary>
@@ -142,7 +142,7 @@
 </histogram>
 
 <histogram name="ProtoDB.GetSuccess.{LevelDBClient}" enum="BooleanSuccess"
-    expires_after="2022-04-11">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>Whether a ProtoDB Get call was successful or not.</summary>
@@ -150,7 +150,7 @@
 </histogram>
 
 <histogram name="ProtoDB.InitStatus.{LevelDBClient}" enum="LevelDBStatus"
-    expires_after="2022-04-17">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>The LevelDB Status from a ProtoDatabase Init call.</summary>
@@ -158,7 +158,7 @@
 </histogram>
 
 <histogram name="ProtoDB.LoadEntriesSuccess.{LevelDBClient}"
-    enum="BooleanSuccess" expires_after="2022-04-11">
+    enum="BooleanSuccess" expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>Whether a ProtoDB LoadEntries call was successful or not.</summary>
@@ -166,7 +166,7 @@
 </histogram>
 
 <histogram name="ProtoDB.LoadKeysAndEntriesSuccess.{LevelDBClient}"
-    enum="BooleanSuccess" expires_after="2022-04-11">
+    enum="BooleanSuccess" expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -176,7 +176,7 @@
 </histogram>
 
 <histogram name="ProtoDB.LoadKeysSuccess.{LevelDBClient}" enum="BooleanSuccess"
-    expires_after="2022-04-11">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>Whether a ProtoDB LoadKeys call was successful or not.</summary>
@@ -198,7 +198,7 @@
 </histogram>
 
 <histogram name="ProtoDB.UpdateErrorStatus.{LevelDBClient}"
-    enum="LevelDBStatus" expires_after="2022-04-11">
+    enum="LevelDBStatus" expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
@@ -208,7 +208,7 @@
 </histogram>
 
 <histogram name="ProtoDB.UpdateSuccess.{LevelDBClient}" enum="BooleanSuccess"
-    expires_after="2022-04-11">
+    expires_after="2022-10-23">
   <owner>nyquist@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <summary>Whether a ProtoDB UpdateEntries call was successful or not.</summary>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index dd257b4..6e9940e 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -946,7 +946,7 @@
 </histogram>
 
 <histogram name="Media.Audio.Render.GetSourceDataTimeMax.WebRTC"
-    units="microseconds" expires_after="2022-08-21">
+    units="microseconds" expires_after="2022-10-23">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -1298,7 +1298,7 @@
 </histogram>
 
 <histogram name="Media.AudioInputControllerSessionSilenceReport"
-    enum="AudioInputSilenceReport" expires_after="2022-08-21">
+    enum="AudioInputSilenceReport" expires_after="2022-10-23">
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -3358,7 +3358,7 @@
 </histogram>
 
 <histogram name="Media.MSE.Mp4ConsecutiveEmptySamples" units="samples"
-    expires_after="2022-08-21">
+    expires_after="2022-10-23">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
@@ -3382,7 +3382,7 @@
 </histogram>
 
 <histogram name="Media.MSE.Mp4SampleSize" units="bytes"
-    expires_after="2022-08-21">
+    expires_after="2022-10-23">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
@@ -3393,7 +3393,7 @@
 </histogram>
 
 <histogram name="Media.MSE.Mp4TrunSampleCount" units="samples"
-    expires_after="2022-08-21">
+    expires_after="2022-10-23">
   <owner>wolenetz@chromium.org</owner>
   <owner>sandersd@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index e5e0c86..337e440 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -14529,6 +14529,18 @@
   </summary>
 </histogram>
 
+<histogram name="WhatsNew.StartupType" enum="WhatsNewStartupType"
+    expires_after="2022-08-07">
+  <owner>rbpotter@chromium.org</owner>
+  <owner>mahmadi@chromium.org</owner>
+  <summary>
+    Records whether the What's New page should try to show, whether it is
+    overridden (e.g. by welcome) and whether the user is ineligible (e.g. due to
+    being in incognito mode). Recorded when startup tabs are being determined at
+    initialization. Desktop only.
+  </summary>
+</histogram>
+
 <histogram name="WheelScrolling.WasLatched" enum="BooleanLatched"
     expires_after="M77">
   <owner>flackr@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index ece53fb..294b660 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -1601,7 +1601,7 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="Power.Mac" units="mW" expires_after="2022-10-16">
+<histogram base="true" name="Power.Mac" units="mW" expires_after="2022-10-23">
   <owner>olivierli@chromium.org</owner>
   <owner>markchang@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index e3fd63c8..f212e213 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -2142,7 +2142,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.V4GetHash.CacheHit.Result"
-    enum="SafeBrowsingV4FullHashCacheResult" expires_after="2022-08-21">
+    enum="SafeBrowsingV4FullHashCacheResult" expires_after="2022-10-23">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>Track cache hits for V4 full hashes.</summary>
diff --git a/tools/metrics/histograms/metadata/subresource/histograms.xml b/tools/metrics/histograms/metadata/subresource/histograms.xml
index 56e9920..42fd6a1 100644
--- a/tools/metrics/histograms/metadata/subresource/histograms.xml
+++ b/tools/metrics/histograms/metadata/subresource/histograms.xml
@@ -579,7 +579,7 @@
 </histogram>
 
 <histogram name="SubresourceFilter.SafeBrowsing.TotalCheckTime" units="ms"
-    expires_after="2022-08-21">
+    expires_after="2022-10-23">
   <owner>alexmt@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index 6a170b3..1f33e9bc 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -2061,7 +2061,7 @@
 </histogram>
 
 <histogram name="V8.WasmTierUpModuleMicroSeconds" units="microseconds"
-    expires_after="2022-08-21">
+    expires_after="2022-10-23">
   <owner>ecmziegler@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
   <owner>clemensb@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/variations/histograms.xml b/tools/metrics/histograms/metadata/variations/histograms.xml
index bdd0911c..0ef0f0ca 100644
--- a/tools/metrics/histograms/metadata/variations/histograms.xml
+++ b/tools/metrics/histograms/metadata/variations/histograms.xml
@@ -47,7 +47,7 @@
 </histogram>
 
 <histogram name="Variations.AppSeedFreshness" units="minutes"
-    expires_after="2022-08-21">
+    expires_after="2022-10-23">
   <owner>rmcelrath@chromium.org</owner>
   <owner>ntfschr@chromium.org</owner>
   <owner>src/android_webview/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index 91195773..ba90d4e 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -463,6 +463,9 @@
 
 <histogram name="WebApp.InstallIphPromo.Result" enum="WebAppInstallIphResult"
     expires_after="2022-05-22">
+  <obsolete>
+    Deprecated on 2022-04. Collected metrics on IPH usage and no longer needed.
+  </obsolete>
   <owner>phillis@chromium.org</owner>
   <owner>dmurph@chromium.org</owner>
   <summary>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 57baf65..2d6a03e 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -348,7 +348,7 @@
  <item id="wallpaper_google_photos_albums" added_in_milestone="99" content_hash_code="00248f64" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
  <item id="webapk_create" added_in_milestone="99" content_hash_code="07b8fd35" os_list="android" file_path="chrome/browser/android/webapk/webapk_installer.cc" />
  <item id="webapk_update" added_in_milestone="99" content_hash_code="0763f8a7" os_list="android" file_path="chrome/browser/android/webapk/webapk_installer.cc" />
- <item id="safe_browsing_extension_telemetry" added_in_milestone="98" content_hash_code="07998e68" os_list="linux,windows,chromeos" file_path="chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc" />
+ <item id="safe_browsing_extension_telemetry" added_in_milestone="98" content_hash_code="06f81c76" os_list="linux,windows,chromeos" file_path="chrome/browser/safe_browsing/extension_telemetry/extension_telemetry_uploader.cc" />
  <item id="fast_pair_device_metadata_fetcher" added_in_milestone="100" content_hash_code="0655adc6" os_list="chromeos" file_path="ash/quick_pair/repository/fast_pair/device_metadata_fetcher.cc" />
  <item id="fast_pair_image_decoder" added_in_milestone="99" content_hash_code="04d764bc" os_list="chromeos" file_path="ash/quick_pair/repository/fast_pair/fast_pair_image_decoder_impl.cc" />
  <item id="wallpaper_google_photos_photos" added_in_milestone="100" content_hash_code="00c76007" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index 097a53a..0971948 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -218,7 +218,6 @@
   deps = [
     "//base:base_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_core_core_java",
   ]
@@ -442,7 +441,9 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//components/browser_ui/modaldialog/android:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
+    "//third_party/androidx:androidx_vectordrawable_vectordrawable_animated_java",
 
     # For androidx.test.espresso.ViewInteraction
     "//third_party/android_deps:espresso_java",
@@ -510,7 +511,6 @@
     "//base:base_junit_test_support",
     "//base/test:test_support_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_appcompat_appcompat_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_asynclayoutinflater_asynclayoutinflater_java",
     "//third_party/androidx:androidx_core_core_java",
diff --git a/ui/android/java/src/org/chromium/ui/base/ClipboardImpl.java b/ui/android/java/src/org/chromium/ui/base/ClipboardImpl.java
index 7c067f29..70009a4 100644
--- a/ui/android/java/src/org/chromium/ui/base/ClipboardImpl.java
+++ b/ui/android/java/src/org/chromium/ui/base/ClipboardImpl.java
@@ -15,6 +15,7 @@
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Build;
+import android.os.SystemClock;
 import android.text.Html;
 import android.text.Spanned;
 import android.text.TextUtils;
@@ -38,6 +39,7 @@
 import org.chromium.base.compat.ApiHelperForO;
 import org.chromium.base.compat.ApiHelperForP;
 import org.chromium.base.compat.ApiHelperForS;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.components.url_formatter.UrlFormatter;
@@ -298,12 +300,16 @@
             // Android system clipboard contains an image, but it is not a PNG.
             // Try reading it as a bitmap and encoding to a PNG.
             try {
+                final long startTime = SystemClock.elapsedRealtime();
                 // TODO(crbug.com/1280468): This uses the unsafe ImageDecoder class.
                 Bitmap bitmap = ApiCompatibilityUtils.getBitmapByUri(cr, uri);
                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
                 // |quality| is ignored since PNG encoding is lossless. See
                 // https://developer.android.com/reference/android/graphics/Bitmap.CompressFormat#PNG.
                 bitmap.compress(Bitmap.CompressFormat.PNG, /*quality=*/100, baos);
+                RecordHistogram.recordMediumTimesHistogram(
+                        "Android.ClipBoard.getImageDuration.NonPngImages",
+                        SystemClock.elapsedRealtime() - startTime);
                 if (baos.size() > MAX_ALLOWED_PNG_SIZE_BYTES) return null;
 
                 return baos.toByteArray();
@@ -320,10 +326,12 @@
                 return null;
             }
             byte[] data = new byte[(int) afd.getLength()];
-
+            final long startTime = SystemClock.elapsedRealtime();
             fileStream = new FileInputStream(afd.getFileDescriptor());
             fileStream.read(data);
-
+            RecordHistogram.recordMediumTimesHistogram(
+                    "Android.ClipBoard.getImageDuration.PngImages",
+                    SystemClock.elapsedRealtime() - startTime);
             return data;
         } catch (IOException e) {
             return null;
diff --git a/ui/webui/resources/css/widgets.css b/ui/webui/resources/css/widgets.css
index 2ba6a1a..6ccecb8 100644
--- a/ui/webui/resources/css/widgets.css
+++ b/ui/webui/resources/css/widgets.css
@@ -56,7 +56,10 @@
 select {
   -webkit-appearance: none;
   /* OVERRIDE */
-  background-image: url(../images/select.png),
+  background-image:
+      -webkit-image-set(
+          url(../images/select.png) 1x,
+          url(../images/2x/select.png) 2x),
       -webkit-linear-gradient(#ededed, #ededed 38%, #dedede);
   background-position: right center;
   background-repeat: no-repeat;
@@ -118,7 +121,9 @@
 /* Checked ********************************************************************/
 
 input[type='checkbox']:checked::before {
-  background-image: url(../images/check.png);
+  background-image: -webkit-image-set(
+      url(../images/check.png) 1x,
+      url(../images/2x/check.png) 2x);
   background-size: 100% 100%;
   content: '';
   display: block;
@@ -159,7 +164,10 @@
 
 :enabled:hover:-webkit-any(select) {
   /* OVERRIDE */
-  background-image: url(../images/select.png),
+  background-image:
+      -webkit-image-set(
+          url(../images/select.png) 1x,
+          url(../images/2x/select.png) 2x),
       -webkit-linear-gradient(#f0f0f0, #f0f0f0 38%, #e0e0e0);
 }
 </if>
@@ -201,7 +209,10 @@
 
 select:disabled {
   /* OVERRIDE */
-  background-image: url(../images/disabled_select.png),
+  background-image:
+      -webkit-image-set(
+          url(../images/disabled_select.png) 1x,
+          url(../images/2x/disabled_select.png) 2x),
       -webkit-linear-gradient(#f1f1f1, #f1f1f1 38%, #e6e6e6);
 }
 
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index 535d316e..15d4678b 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -45,8 +45,9 @@
     "$google_play_services_package:google_play_services_base_java",
     "//base:base_java",
     "//components/strictmode/android:java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
+    "//third_party/androidx:androidx_appcompat_appcompat_java",
+    "//third_party/androidx:androidx_fragment_fragment_java",
     "//weblayer/public/java",
   ]
   sources = [